Skip to content
Draft
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
58 changes: 58 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ downloaded the web runtime.
| `pascal project open <id-or-name>` | Explicit form of `pascal open <project>`. |
| `pascal agent claim [--no-open] [--json]` | Link an autonomous hosted agent to the person accountable for it. |
| `pascal agent status [--json]` | Verify the hosted agent credential and inspect its claim and organization scope. |
| `pascal architect run "<brief>"` | Start a hosted architect run from a brief and stream its output. |
| `pascal architect status <id> [--json]` | Show a hosted architect run's status and output. |
| `pascal architect cancel <id> [--json]` | Cancel a queued or running hosted architect run. |
| `pascal mcp connect` | Stable local connector for MCP clients; starts the bundled MCP service without the web runtime. |
| `pascal mcp status [--json]` | Show managed MCP health. |
| `pascal mcp config [--json]` | Print generic MCP client configuration. |
Expand Down Expand Up @@ -234,6 +237,61 @@ ownership, grant the agent access to the person's private projects, or grant the
to the agent's private projects. The local editor and its projects remain local unless a
separate hosted project action explicitly moves data.

## Hosted architect runs

The `pascal architect` commands ship in the next `@pascal-app/cli` release; the version
currently on npm does not include them.

You hand Pascal's hosted architect a brief, and it designs the building on Pascal while you
watch, detach, or come back later. You need an agent API key in `PASCAL_API_KEY`, the same
key `pascal agent status` checks. The CLI sends it to Pascal and never stores or prints it.

Start a run and stream its output:

```bash
PASCAL_API_KEY='sk_live_...' pascal architect run "A two-storey, three-bedroom house with an open-plan kitchen"
```

The design text streams to stdout and progress lines go to stderr, so you can pipe or
redirect the output. The run executes in the background on Pascal: when you press Ctrl+C you
detach, and the run keeps going. The CLI prints the run ID when it starts.

| Flag | Effect |
| --- | --- |
| `--effort low\|medium\|high\|pro` | Use the `architect-<effort>` model. Without it, the run uses `architect`. |
| `--project <id>` | Run in a hosted Pascal project; sent as `metadata.project_id`. |
| `--max-credits <n>` | Cap the credits this run may spend; sent as `metadata.pascal_max_credits`. |
| `--detach` | Queue the run, print its ID, and return without streaming. |
| `--json` | Print the run as JSON; waits for the run to finish unless you also pass `--detach`. |

Check on a run, including one you detached from:

```bash
PASCAL_API_KEY='sk_live_...' pascal architect status resp_abc123
```

You get the status (`queued`, `in progress`, `completed`, `failed`, `cancelled`, or
`incomplete`), the model, project, credit cap, and the output so far. `--json` returns the
same fields as a stable object with `schemaVersion: 1`.

Stop a run you no longer need:

```bash
PASCAL_API_KEY='sk_live_...' pascal architect cancel resp_abc123
```

When something goes wrong, the CLI tells you whether retrying can help. Exit code `75` means
Pascal is temporarily unable to take the request: `architect_executor_unavailable` (no
architect executor is free, so no run started), `architect_rate_limited`, or
`architect_unavailable`. Retry the same command later. `architect_unauthorized` and
`architect_forbidden` mean the key is invalid, revoked, or not allowed to use the architect;
check it with `pascal agent status`. `architect run` exits `1` when the run ends in any state
other than `completed`, `2` on invalid usage, and `130` when you detach with Ctrl+C. With
`--json`, errors go to stderr as `{ "error": "<code>", "message": "...", "details": { ... } }`.

The commands are a thin client over Pascal's OpenAI-compatible Responses API at
`https://editor.pascal.app/api/v1/responses`, using background mode.

## Plugins

The current CLI manages the local editor runtime; it does not yet download plugin code
Expand Down
25 changes: 12 additions & 13 deletions packages/cli/src/agent-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,22 @@ interface AgentAccountRequestOptions {
timeoutMs?: number
}

export async function startAgentClaim(
apiKey: string,
options: AgentAccountRequestOptions = {},
): Promise<AgentClaim> {
const credential = apiKey.trim()
export function requireAgentApiKey(apiKey: string | undefined, holder = "this agent's"): string {
const credential = apiKey?.trim()
if (!credential) {
throw new CliError(
'agent_api_key_missing',
"Set PASCAL_API_KEY to this autonomous agent's API key and try again.",
`Set PASCAL_API_KEY to ${holder} API key and try again.`,
)
}
return credential
}

export async function startAgentClaim(
apiKey: string,
options: AgentAccountRequestOptions = {},
): Promise<AgentClaim> {
const credential = requireAgentApiKey(apiKey, "this autonomous agent's")

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS)
Expand Down Expand Up @@ -89,13 +94,7 @@ export async function getAgentStatus(
apiKey: string,
options: AgentAccountRequestOptions = {},
): Promise<AgentStatus> {
const credential = apiKey.trim()
if (!credential) {
throw new CliError(
'agent_api_key_missing',
"Set PASCAL_API_KEY to this agent's API key and try again.",
)
}
const credential = requireAgentApiKey(apiKey)

const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS)
Expand Down
Loading
Loading