Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ from your terminal—without cloning or building the Pascal repository.
npx @pascal-app/cli editor
```

`npx` makes the CLI available for that invocation only; it does not add a permanent
`pascal` command to your shell. Continue to prefix commands with
`npx @pascal-app/cli`, or use the optional global installation below when you want the
shorter command.

The first run walks through local storage, runtime installation, automatic port
selection, process startup, and a health check with live terminal feedback. It then
opens `http://pascal.localhost:<port>`. Your projects are stored separately from the
Expand Down Expand Up @@ -56,6 +61,10 @@ npm install --global @pascal-app/cli
pascal editor
```

After a global installation, `pascal status`, `pascal logs --follow`, and the other
commands work directly in new terminal sessions. A prior `npx` invocation alone does
not install this shortcut.

Use `--no-open` on a headless machine. Use `--foreground` when a process supervisor
should own the editor or when you want logs attached to the current terminal.
Pascal asks the operating system for an available loopback port by default, so it does
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/scripts/smoke-packed-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ try {
) {
throw new Error('a repeated editor command did not reuse the managed process')
}
const humanStart = await run(
process.execPath,
[smokeExecutable, 'editor', '--no-open'],
undefined,
smokeEnvironment,
)
if (
!humanStart.stdout.includes('npx @pascal-app/cli status') ||
!humanStart.stdout.includes('npm install --global @pascal-app/cli')
) {
throw new Error('human start output did not explain transient and global commands')
}
await run(
process.execPath,
[smokeExecutable, 'project', 'list', '--json'],
Expand Down
42 changes: 22 additions & 20 deletions packages/cli/src/bin/pascal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import { version } from '../version.js'

const HELP = `Pascal — local 3D editor

RUN WITHOUT INSTALLING:
npx @pascal-app/cli <command>

ENABLE THE SHORT GLOBAL COMMAND:
npm install --global @pascal-app/cli
pascal <command>

USAGE:
pascal editor [--foreground] [--no-open] [--port <n>]
pascal start [--foreground] [--port <n>]
Expand Down Expand Up @@ -93,20 +100,14 @@ async function runStart(args: string[], shouldOpen: boolean): Promise<void> {
if (values.help) return print(HELP)
const port = parseIntegerOption(values.port, 'port')
const progress = values.json ? undefined : new TerminalProgress()
let installedRuntime = false
progress?.start('Preparing your local Pascal editor')
let result: Awaited<ReturnType<typeof startEditor>>
try {
result = await startEditor({
paths,
port,
foreground: values.foreground,
onProgress: progress
? (event) => {
if (event.step === 'runtime-installing') installedRuntime = true
reportStartProgress(progress, event)
}
: undefined,
onProgress: progress ? (event) => reportStartProgress(progress, event) : undefined,
})
} catch (error) {
progress?.stop()
Expand All @@ -117,19 +118,20 @@ async function runStart(args: string[], shouldOpen: boolean): Promise<void> {
output(
values.json,
{ ...result.state, alreadyRunning: result.alreadyRunning },
result.alreadyRunning
? `Pascal is already running at ${result.state.url}`
: installedRuntime
? [
`Pascal is ready at ${result.state.url}`,
`Projects stay in ${paths.data}`,
'',
'Next steps:',
' npx @pascal-app/cli status Check the local editor',
' npx @pascal-app/cli logs --follow Follow editor logs',
' npx @pascal-app/cli stop Stop the background process',
].join('\n')
: `Pascal is running at ${result.state.url}`,
[
result.alreadyRunning
? `Pascal is already running at ${result.state.url}`
: `Pascal is ready at ${result.state.url}`,
`Projects stay in ${paths.data}`,
'',
'Manage it with npx:',
' npx @pascal-app/cli status Check the local editor',
' npx @pascal-app/cli logs --follow Follow editor logs',
' npx @pascal-app/cli stop Stop the background process',
'',
'To enable the shorter "pascal" command in your shell:',
' npm install --global @pascal-app/cli',
].join('\n'),
)
if (result.child) {
const exitCode = await new Promise<number>((resolve) =>
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ describe('command parsing', () => {

expect(result.exitCode).toBe(0)
expect(result.stdout).toContain('pascal editor')
expect(result.stdout).toContain('npx @pascal-app/cli <command>')
expect(result.stdout).toContain('npm install --global @pascal-app/cli')
})

test('rejects a partially numeric port', async () => {
Expand Down
Loading