@zocomputer/cli 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,22 @@ see [runtime requirements](../README.md#set-up).
6
6
 
7
7
  ## Install or upgrade
8
8
 
9
+ The standalone installer is implemented but awaits its first public release.
10
+ Once published, macOS 15+ and Linux glibc 2.35+ users on arm64 or x64 can run:
11
+
12
+ ```sh
13
+ curl -fsSL https://zo.computer/install.sh | sh
14
+ export PATH="$HOME/.local/bin:$PATH"
15
+ zo --version
16
+ ```
17
+
18
+ It includes Node, verifies the archive, and keeps versions under
19
+ `${XDG_DATA_HOME:-$HOME/.local/share}/zo/cli/` with a managed `~/.local/bin/zo`
20
+ symlink. Re-run it to upgrade; `--version`, `--bin-dir`, and `--no-modify-path`
21
+ control pinned installs, command placement and shell setup. Existing sign-ins
22
+ are preserved. The [CLI guide](../../src/hostagent/system_api/docs/guides/cli.md)
23
+ covers supported systems, PATH collisions, rollback and removal.
24
+
9
25
  Install the latest published version from npm:
10
26
 
11
27
  ```sh
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/generate-commands.mjs. Do not edit.
2
2
  export const commandManifest = {
3
- schemaDigest: "sha256:86eea2bc2e1d9b8d45c59a74750d572cc5c40f97b64786b2ef57dee9f77f9114",
3
+ schemaDigest: "sha256:3ea715bdec97fa1eee17b34583dc048bd5576ca2679b41cbc8f515f56a89e4ee",
4
4
  commands: [
5
5
  {
6
6
  action: "zo.activity.list",
@@ -355,8 +355,8 @@ export const commandManifest = {
355
355
  versions: [
356
356
  {
357
357
  version: 1,
358
- description: "Create a managed Postgres database and return it, usually already `available`. The required `idempotency_key` is ignored until central deduplication lands: a retried create can make a second database, so after an uncertain response (lost or timed out), list the workspace's databases before creating again.",
359
- inputJson: '{"additionalProperties":false,"properties":{"tags":{"default":[],"items":{"description":"A tag ID or name; unknown names are created, unknown IDs are refused.","maxLength":256,"minLength":1,"type":"string"},"maxItems":100,"title":"Tags","type":"array"},"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"idempotency_key":{"description":"Required but ignored: a retried create can make a second database.","maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"What the database holds. Members and their agents choose a database from `zo.database.list` by it.","maxLength":2000,"title":"Description","type":"string"},"engine":{"const":"postgres","default":"postgres","title":"Engine","type":"string"},"postgres":{"additionalProperties":false,"default":{},"properties":{"major_version":{"default":18,"description":"One of the offered major versions. Fixed for the life of the database.","maximum":2147483647,"minimum":1,"title":"Major Version","type":"integer"},"compute":{"additionalProperties":false,"default":{},"properties":{"min_memory_mib":{"default":1024,"description":"The autoscaling floor while the compute is awake, one of the shapes `zo.database.list_configurations` offers. Equal to `max_memory_mib`, it fixes the size. Above 4,096, set `max_memory_mib` too.","maximum":2147483647,"minimum":1,"title":"Min Memory Mib","type":"integer"},"max_memory_mib":{"default":4096,"description":"The autoscaling ceiling, from the same shapes and at least `min_memory_mib`. CPU scales with memory.","maximum":2147483647,"minimum":1,"title":"Max Memory Mib","type":"integer"},"scale_to_zero":{"default":true,"description":"Whether the compute suspends when idle. `false` keeps it running at its minimum or above around the clock, and is billed for it, on plans whose offer includes `false`.","title":"Scale To Zero","type":"boolean"},"idle_timeout_seconds":{"default":300,"description":"Seconds without a call before the compute scales to zero, within the offered bounds. No effect while `scale_to_zero` is `false`.","maximum":2147483647,"minimum":1,"title":"Idle Timeout Seconds","type":"integer"}},"title":"DatabaseComputeInput","type":"object","required":[]}},"title":"DatabasePostgresInput","type":"object","required":[]},"region":{"default":"us","description":"One of the offered regions. Fixed at creation.","pattern":"^[a-z0-9][a-z0-9-]{0,31}$","title":"Region","type":"string"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see it and use it as their role allows. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","idempotency_key","display_name"],"title":"DatabaseCreateInput","type":"object"}',
358
+ description: "Create a managed Postgres database and return it, usually already `available`. A retried create can make a second database, so after an uncertain response (lost or timed out), list the workspace's databases before creating again.",
359
+ inputJson: '{"additionalProperties":false,"properties":{"tags":{"default":[],"items":{"description":"A tag ID or name; unknown names are created, unknown IDs are refused.","maxLength":256,"minLength":1,"type":"string"},"maxItems":100,"title":"Tags","type":"array"},"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"What the database holds. Members and their agents choose a database from `zo.database.list` by it.","maxLength":2000,"title":"Description","type":"string"},"engine":{"const":"postgres","default":"postgres","title":"Engine","type":"string"},"postgres":{"additionalProperties":false,"default":{},"properties":{"major_version":{"default":18,"description":"One of the offered major versions. Fixed for the life of the database.","maximum":2147483647,"minimum":1,"title":"Major Version","type":"integer"},"compute":{"additionalProperties":false,"default":{},"properties":{"min_memory_mib":{"default":1024,"description":"The autoscaling floor while the compute is awake, one of the shapes `zo.database.list_configurations` offers. Equal to `max_memory_mib`, it fixes the size. Above 4,096, set `max_memory_mib` too.","maximum":2147483647,"minimum":1,"title":"Min Memory Mib","type":"integer"},"max_memory_mib":{"default":4096,"description":"The autoscaling ceiling, from the same shapes and at least `min_memory_mib`. CPU scales with memory.","maximum":2147483647,"minimum":1,"title":"Max Memory Mib","type":"integer"},"scale_to_zero":{"default":true,"description":"Whether the compute suspends when idle. `false` keeps it running at its minimum or above around the clock, and is billed for it, on plans whose offer includes `false`.","title":"Scale To Zero","type":"boolean"},"idle_timeout_seconds":{"default":300,"description":"Seconds without a call before the compute scales to zero, within the offered bounds. No effect while `scale_to_zero` is `false`.","maximum":2147483647,"minimum":1,"title":"Idle Timeout Seconds","type":"integer"}},"title":"DatabaseComputeInput","type":"object","required":[]}},"title":"DatabasePostgresInput","type":"object","required":[]},"region":{"default":"us","description":"One of the offered regions. Fixed at creation.","pattern":"^[a-z0-9][a-z0-9-]{0,31}$","title":"Region","type":"string"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see it and use it as their role allows. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name"],"title":"DatabaseCreateInput","type":"object"}',
360
360
  resultKind: "inline",
361
361
  },
362
362
  ],
@@ -732,7 +732,7 @@ export const commandManifest = {
732
732
  {
733
733
  version: 1,
734
734
  description: "Create a managed machine from a machine image and return its current view. Describe what this machine is for in the user's terms.",
735
- inputJson: '{"additionalProperties":false,"properties":{"tags":{"default":[],"items":{"description":"A tag ID or name; unknown names are created, unknown IDs are refused.","maxLength":256,"minLength":1,"type":"string"},"maxItems":100,"title":"Tags","type":"array"},"workspace_id":{"title":"Workspace Id","type":"string"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this machine is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"source":{"$ref":"#/$defs/MachineSourceInput"},"resources":{"$ref":"#/$defs/MachineResourcesInput"},"lifecycle":{"$ref":"#/$defs/MachineLifecycleInput"},"region":{"default":"us","pattern":"^[a-z0-9][a-z0-9-]{0,31}$","title":"Region","type":"string"},"init":{"default":"none","description":"Which process runs first at boot. `none` (default): nothing; the machine waits for exec. `image`: the init system inside the machine\'s image (systemd, OpenRC) becomes PID 1 and starts the services enabled on its disk. This does not choose the image; `source.machine_image_id` does. Debian and Ubuntu images need `systemd systemd-sysv` installed first. A unit restarts after a crash only when it sets `Restart=`. GPU machines require `none`.","enum":["none","image"],"title":"Init","type":"string"},"command":{"anyOf":[{"items":{"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Run these arguments as the main process on every boot, without a shell. Use [\\"sh\\", \\"-c\\", ...] for shell syntax. Find ENTRYPOINT and CMD on the image\'s registry page; Zo does not return them. Pass ENTRYPOINT then CMD. With `init: image`, the command must wait for services it needs. The machine stops when it exits; resume runs it again. It may run more than once after a lost create or resume response, so make it safe to rerun. Each argument is at most 4,096 UTF-8 bytes and all arguments total at most 65,536 bytes. A running command is not activity: set `idle_timeout_seconds` at least as long as the job. The maximum is published by `zo.machine.list_configurations`: 3,600 s for CPU machines and 86,400 s for GPU machines. Longer CPU jobs need Zo exec or file calls, or HTTP through a tunnel or connect token. After exit, `get` may show `running` for a few minutes until the stop is recorded. Zo reports no exit reason or code. Write a job\'s result to disk. A resume runs the command again, so to read it, clear the command, resume, then use `zo.machine.read_bytes`. With `init: image` and a command, a missing `/sbin/init` ends the generation as an exit stop, without `incompatible_image`.","title":"Command"},"environment":{"additionalProperties":{"maxLength":4096,"type":"string"},"default":{},"description":"Environment variables set, exactly as written, in the boot process and every command run on the machine. Plain strings, not secrets: they are part of the machine\'s configuration, which anyone who can read the machine sees. Names are upper-case letters, digits and underscores, and reserved names such as `PATH` or ones starting with `ZO_` are refused; at most 64 variables of at most 4096 characters each. A variable here wins over a connection in `credentials` that sets the same name.","maxProperties":64,"title":"Environment","type":"object"},"credentials":{"default":[],"description":"Connection IDs whose credentials the machine receives as environment variables, in the boot process and every command. Each slot of a listed connection sets the variable its definition names, with the variables the slot and the connection declare. The machine also needs a use grant on each connection (`zo.grant.create` with the machine as assignee); a listed connection without one delivers nothing. When two listed connections set the same variable, the later one in the list wins. At most 100 connections, each listed once.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":100,"title":"Credentials","type":"array"},"outbound_network_policies":{"default":[],"description":"Header rules: on HTTPS requests from the machine to a rule\'s hosts, Zo sets a header built from a connection\'s credential, and the credential never enters the machine. A rule names the connection (`credential`) and one of its definition\'s slots (`slot`). With only those two, it uses the hosts, header and format the slot declares in its `injections`. It may give its own `hosts`, `location` and `format`, all three together, only for a slot that declares an `environment_variable`. Formats: `bearer` (`Bearer <value>`), `basic` with a `username` (`Basic` with base64 of `<username>:<value>`) and `raw` (the value as stored). A rule sets its header only while the machine holds a use grant on the connection. Two rules can\'t set the same header on the same host, and all rules together set at most 25 header values. Requests to a host with a rule use HTTP/1.1.","items":{"$ref":"#/$defs/OutboundNetworkPolicy"},"maxItems":25,"title":"Outbound Network Policies","type":"array"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see and use it. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","idempotency_key","display_name","source","resources","lifecycle"],"title":"MachineCreateInput","type":"object","$defs":{"BasicFormat":{"additionalProperties":false,"properties":{"type":{"const":"basic","description":"`Basic` with base64 of `<username>:<value>`.","title":"Type","type":"string"},"username":{"pattern":"^[^:]{1,256}$","title":"Username","type":"string"}},"required":["type","username"],"title":"BasicFormat","type":"object"},"BearerFormat":{"additionalProperties":false,"properties":{"type":{"const":"bearer","description":"`Bearer <value>`.","title":"Type","type":"string"}},"required":["type"],"title":"BearerFormat","type":"object"},"GpuType":{"enum":["T4","L4","A10","L40S","A100","A100-40GB","A100-80GB","RTX-PRO-6000","H100","H100!","H200","B200","B200+","B300"],"type":"string"},"HeaderLocation":{"additionalProperties":false,"properties":{"type":{"const":"header","title":"Type","type":"string"},"name":{"description":"The header to set, such as `Authorization` or `X-API-Key`.","pattern":"^[A-Za-z0-9!#$%&\'*+.^_`|~-]{1,128}$","title":"Name","type":"string"}},"required":["type","name"],"title":"HeaderLocation","type":"object"},"MachineGpu":{"additionalProperties":false,"properties":{"type":{"$ref":"#/$defs/GpuType"},"count":{"maximum":8,"minimum":1,"title":"Count","type":"integer"}},"required":["type","count"],"title":"MachineGpu","type":"object"},"MachineLifecycleInput":{"additionalProperties":false,"properties":{"idle_timeout_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"description":"Omit for 900 seconds on CPU. For GPU, the default is the smaller of 300 seconds and max_run_seconds. An explicit GPU idle timeout must not exceed max_run_seconds.","title":"Idle Timeout Seconds"},"max_run_seconds":{"maximum":86400,"minimum":1,"title":"Max Run Seconds","type":"integer"}},"required":["max_run_seconds"],"title":"MachineLifecycleInput","type":"object"},"MachineResourcesInput":{"additionalProperties":false,"properties":{"vcpus":{"description":"Whole vCPUs of CPU capacity; each is half a Modal physical core. A quarter is reserved; the full allocation can burst when capacity allows. Guest logical CPU count is separate.","maximum":2147483647,"minimum":1,"title":"Vcpus","type":"integer"},"memory_mib":{"maximum":2147483647,"minimum":1,"title":"Memory Mib","type":"integer"},"gpu":{"anyOf":[{"$ref":"#/$defs/MachineGpu"},{"type":"null"}],"default":null}},"required":["vcpus","memory_mib"],"title":"MachineResourcesInput","type":"object"},"MachineSourceInput":{"additionalProperties":false,"properties":{"machine_image_id":{"description":"The machine image to boot (from `zo.machine_image.import`). Separate from `init`, which only chooses what runs first at boot.","minLength":1,"title":"Machine Image Id","type":"string"}},"required":["machine_image_id"],"title":"MachineSourceInput","type":"object"},"OutboundNetworkPolicy":{"additionalProperties":false,"description":"Send one slot\'s credential as a header on HTTPS requests to some hosts.","properties":{"credential":{"description":"The connection ID.","maxLength":200,"minLength":1,"title":"Credential","type":"string"},"slot":{"description":"The connection definition\'s slot, by name.","maxLength":200,"minLength":1,"title":"Slot","type":"string"},"hosts":{"anyOf":[{"items":{"description":"An exact lowercase DNS name, such as `api.openai.com`: no wildcard, port or path.","maxLength":253,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$","type":"string"},"maxItems":20,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"title":"Hosts"},"location":{"anyOf":[{"$ref":"#/$defs/HeaderLocation"},{"type":"null"}],"default":null},"format":{"anyOf":[{"discriminator":{"mapping":{"basic":"#/$defs/BasicFormat","bearer":"#/$defs/BearerFormat","raw":"#/$defs/RawFormat"},"propertyName":"type"},"oneOf":[{"$ref":"#/$defs/BearerFormat"},{"$ref":"#/$defs/BasicFormat"},{"$ref":"#/$defs/RawFormat"}]},{"type":"null"}],"default":null,"title":"Format"}},"required":["credential","slot"],"title":"OutboundNetworkPolicy","type":"object"},"RawFormat":{"additionalProperties":false,"properties":{"type":{"const":"raw","description":"The value as it is.","title":"Type","type":"string"}},"required":["type"],"title":"RawFormat","type":"object"}}}',
735
+ inputJson: '{"additionalProperties":false,"properties":{"tags":{"default":[],"items":{"description":"A tag ID or name; unknown names are created, unknown IDs are refused.","maxLength":256,"minLength":1,"type":"string"},"maxItems":100,"title":"Tags","type":"array"},"workspace_id":{"title":"Workspace Id","type":"string"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this machine is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"source":{"$ref":"#/$defs/MachineSourceInput"},"resources":{"$ref":"#/$defs/MachineResourcesInput"},"lifecycle":{"$ref":"#/$defs/MachineLifecycleInput"},"region":{"default":"us","pattern":"^[a-z0-9][a-z0-9-]{0,31}$","title":"Region","type":"string"},"init":{"default":"none","description":"Which process runs first at boot. `none` (default): nothing; the machine waits for exec. `image`: the init system inside the machine\'s image (systemd, OpenRC) becomes PID 1 and starts the services enabled on its disk. This does not choose the image; `source.machine_image_id` does. Debian and Ubuntu images need `systemd systemd-sysv` installed first. A unit restarts after a crash only when it sets `Restart=`. GPU machines require `none`.","enum":["none","image"],"title":"Init","type":"string"},"command":{"anyOf":[{"items":{"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Run these arguments as the main process on every boot, without a shell. Use [\\"sh\\", \\"-c\\", ...] for shell syntax. Find ENTRYPOINT and CMD on the image\'s registry page; Zo does not return them. Pass ENTRYPOINT then CMD. With `init: image`, the command must wait for services it needs. The machine stops when it exits; resume runs it again. It may run more than once after a lost create or resume response, so make it safe to rerun. Each argument is at most 4,096 UTF-8 bytes and all arguments total at most 65,536 bytes. A running command is not activity: set `idle_timeout_seconds` at least as long as the job. The maximum is published by `zo.machine.list_configurations`: 3,600 s for CPU machines and 86,400 s for GPU machines. Longer CPU jobs need Zo exec or file calls, or HTTP through a tunnel or connect token. After exit, `get` may show `running` for a few minutes until the stop is recorded. Zo reports no exit reason or code. Write a job\'s result to disk. A resume runs the command again, so to read it, clear the command, resume, then use `zo.machine.read_bytes`. With `init: image` and a command, a missing `/sbin/init` ends the generation as an exit stop, without `incompatible_image`.","title":"Command"},"environment":{"additionalProperties":{"maxLength":4096,"type":"string"},"default":{},"description":"Environment variables set, exactly as written, in the boot process and every command run on the machine. Plain strings, not secrets: they are part of the machine\'s configuration, which anyone who can read the machine sees. Names are upper-case letters, digits and underscores, and reserved names such as `PATH` or ones starting with `ZO_` are refused; at most 64 variables of at most 4096 characters each. A variable here wins over a connection in `credentials` that sets the same name.","maxProperties":64,"title":"Environment","type":"object"},"credentials":{"default":[],"description":"Connection IDs whose credentials the machine receives as environment variables, in the boot process and every command. Each slot of a listed connection sets the variable its definition names, with the variables the slot and the connection declare. The machine also needs a use grant on each connection (`zo.grant.create` with the machine as assignee); a listed connection without one delivers nothing. When two listed connections set the same variable, the later one in the list wins. At most 100 connections, each listed once.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":100,"title":"Credentials","type":"array"},"outbound_network_policies":{"default":[],"description":"Header rules: on HTTPS requests from the machine to a rule\'s hosts, Zo sets a header built from a connection\'s credential, and the credential never enters the machine. A rule names the connection (`credential`) and one of its definition\'s slots (`slot`). With only those two, it uses the hosts, header and format the slot declares in its `injections`. It may give its own `hosts`, `location` and `format`, all three together, only for a slot that declares an `environment_variable`. Formats: `bearer` (`Bearer <value>`), `basic` with a `username` (`Basic` with base64 of `<username>:<value>`) and `raw` (the value as stored). A rule sets its header only while the machine holds a use grant on the connection. Two rules can\'t set the same header on the same host, and all rules together set at most 25 header values. Requests to a host with a rule use HTTP/1.1.","items":{"$ref":"#/$defs/OutboundNetworkPolicy"},"maxItems":25,"title":"Outbound Network Policies","type":"array"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see and use it. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name","source","resources","lifecycle"],"title":"MachineCreateInput","type":"object","$defs":{"BasicFormat":{"additionalProperties":false,"properties":{"type":{"const":"basic","description":"`Basic` with base64 of `<username>:<value>`.","title":"Type","type":"string"},"username":{"pattern":"^[^:]{1,256}$","title":"Username","type":"string"}},"required":["type","username"],"title":"BasicFormat","type":"object"},"BearerFormat":{"additionalProperties":false,"properties":{"type":{"const":"bearer","description":"`Bearer <value>`.","title":"Type","type":"string"}},"required":["type"],"title":"BearerFormat","type":"object"},"GpuType":{"enum":["T4","L4","A10","L40S","A100","A100-40GB","A100-80GB","RTX-PRO-6000","H100","H100!","H200","B200","B200+","B300"],"type":"string"},"HeaderLocation":{"additionalProperties":false,"properties":{"type":{"const":"header","title":"Type","type":"string"},"name":{"description":"The header to set, such as `Authorization` or `X-API-Key`.","pattern":"^[A-Za-z0-9!#$%&\'*+.^_`|~-]{1,128}$","title":"Name","type":"string"}},"required":["type","name"],"title":"HeaderLocation","type":"object"},"MachineGpu":{"additionalProperties":false,"properties":{"type":{"$ref":"#/$defs/GpuType"},"count":{"maximum":8,"minimum":1,"title":"Count","type":"integer"}},"required":["type","count"],"title":"MachineGpu","type":"object"},"MachineLifecycleInput":{"additionalProperties":false,"properties":{"idle_timeout_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"description":"Omit for 900 seconds on CPU. For GPU, the default is the smaller of 300 seconds and max_run_seconds. An explicit GPU idle timeout must not exceed max_run_seconds.","title":"Idle Timeout Seconds"},"max_run_seconds":{"maximum":86400,"minimum":1,"title":"Max Run Seconds","type":"integer"}},"required":["max_run_seconds"],"title":"MachineLifecycleInput","type":"object"},"MachineResourcesInput":{"additionalProperties":false,"properties":{"vcpus":{"description":"Whole vCPUs of CPU capacity; each is half a Modal physical core. A quarter is reserved; the full allocation can burst when capacity allows. Guest logical CPU count is separate.","maximum":2147483647,"minimum":1,"title":"Vcpus","type":"integer"},"memory_mib":{"maximum":2147483647,"minimum":1,"title":"Memory Mib","type":"integer"},"gpu":{"anyOf":[{"$ref":"#/$defs/MachineGpu"},{"type":"null"}],"default":null}},"required":["vcpus","memory_mib"],"title":"MachineResourcesInput","type":"object"},"MachineSourceInput":{"additionalProperties":false,"properties":{"machine_image_id":{"description":"The machine image to boot (from `zo.machine_image.import`). Separate from `init`, which only chooses what runs first at boot.","minLength":1,"title":"Machine Image Id","type":"string"}},"required":["machine_image_id"],"title":"MachineSourceInput","type":"object"},"OutboundNetworkPolicy":{"additionalProperties":false,"description":"Send one slot\'s credential as a header on HTTPS requests to some hosts.","properties":{"credential":{"description":"The connection ID.","maxLength":200,"minLength":1,"title":"Credential","type":"string"},"slot":{"description":"The connection definition\'s slot, by name.","maxLength":200,"minLength":1,"title":"Slot","type":"string"},"hosts":{"anyOf":[{"items":{"description":"An exact lowercase DNS name, such as `api.openai.com`: no wildcard, port or path.","maxLength":253,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$","type":"string"},"maxItems":20,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"title":"Hosts"},"location":{"anyOf":[{"$ref":"#/$defs/HeaderLocation"},{"type":"null"}],"default":null},"format":{"anyOf":[{"discriminator":{"mapping":{"basic":"#/$defs/BasicFormat","bearer":"#/$defs/BearerFormat","raw":"#/$defs/RawFormat"},"propertyName":"type"},"oneOf":[{"$ref":"#/$defs/BearerFormat"},{"$ref":"#/$defs/BasicFormat"},{"$ref":"#/$defs/RawFormat"}]},{"type":"null"}],"default":null,"title":"Format"}},"required":["credential","slot"],"title":"OutboundNetworkPolicy","type":"object"},"RawFormat":{"additionalProperties":false,"properties":{"type":{"const":"raw","description":"The value as it is.","title":"Type","type":"string"}},"required":["type"],"title":"RawFormat","type":"object"}}}',
736
736
  resultKind: "inline",
737
737
  },
738
738
  ],
@@ -744,7 +744,7 @@ export const commandManifest = {
744
744
  {
745
745
  version: 1,
746
746
  description: "Destroy a managed machine and return its current view.",
747
- inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"}},"required":["machine_id","expected_revision","idempotency_key"],"title":"MachineLifecycleActionInput","type":"object"}',
747
+ inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"}},"required":["machine_id","expected_revision"],"title":"MachineLifecycleActionInput","type":"object"}',
748
748
  resultKind: "inline",
749
749
  },
750
750
  ],
@@ -830,7 +830,7 @@ export const commandManifest = {
830
830
  {
831
831
  version: 1,
832
832
  description: "Start a stopped managed machine again and return its current view.",
833
- inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"},"accept_recovery":{"default":false,"title":"Accept Recovery","type":"boolean"}},"required":["machine_id","expected_revision","idempotency_key"],"title":"MachineResumeInput","type":"object"}',
833
+ inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"},"accept_recovery":{"default":false,"title":"Accept Recovery","type":"boolean"}},"required":["machine_id","expected_revision"],"title":"MachineResumeInput","type":"object"}',
834
834
  resultKind: "inline",
835
835
  },
836
836
  ],
@@ -854,7 +854,7 @@ export const commandManifest = {
854
854
  {
855
855
  version: 1,
856
856
  description: "Stop a managed machine, keeping its files, and return its current view.",
857
- inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"}},"required":["machine_id","expected_revision","idempotency_key"],"title":"MachineLifecycleActionInput","type":"object"}',
857
+ inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"}},"required":["machine_id","expected_revision"],"title":"MachineLifecycleActionInput","type":"object"}',
858
858
  resultKind: "inline",
859
859
  },
860
860
  ],
@@ -902,7 +902,7 @@ export const commandManifest = {
902
902
  {
903
903
  version: 1,
904
904
  description: "Delete a machine image no machine uses and return the image view.",
905
- inputJson: '{"additionalProperties":false,"properties":{"machine_image_id":{"title":"Machine Image Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"}},"required":["machine_image_id","expected_revision","idempotency_key"],"title":"MachineImageDeleteInput","type":"object"}',
905
+ inputJson: '{"additionalProperties":false,"properties":{"machine_image_id":{"title":"Machine Image Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"}},"required":["machine_image_id","expected_revision"],"title":"MachineImageDeleteInput","type":"object"}',
906
906
  resultKind: "inline",
907
907
  },
908
908
  ],
@@ -926,7 +926,7 @@ export const commandManifest = {
926
926
  {
927
927
  version: 1,
928
928
  description: "Import a registry image, pinned to its manifest digest, and return the image view. Describe what this machine image is for in the user's terms.",
929
- inputJson: '{"additionalProperties":false,"properties":{"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this machine image is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see and use it. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"},"workspace_id":{"title":"Workspace Id","type":"string"},"idempotency_key":{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","title":"Idempotency Key","type":"string"},"source":{"$ref":"#/$defs/RegistrySourceInput"}},"required":["display_name","workspace_id","idempotency_key","source"],"title":"MachineImageImportInput","type":"object","$defs":{"RegistrySourceInput":{"additionalProperties":false,"properties":{"kind":{"const":"registry","title":"Kind","type":"string"},"ref":{"maxLength":1024,"minLength":1,"pattern":"^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*(?::[0-9]{1,5})?/)?[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*(?::[A-Za-z0-9_][A-Za-z0-9_.-]{0,127})?(?:@sha256:[0-9a-f]{64})?$","title":"Ref","type":"string"},"platform":{"const":"linux/amd64","default":"linux/amd64","title":"Platform","type":"string"}},"required":["kind","ref"],"title":"RegistrySourceInput","type":"object"}}}',
929
+ inputJson: '{"additionalProperties":false,"properties":{"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this machine image is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","description":"`workspace` (default): every member can see and use it. `protected`: only you, and whoever you share it with; workspace-wide grants don\'t reach it. Fixed at creation.","enum":["workspace","protected"],"title":"Visibility","type":"string"},"workspace_id":{"title":"Workspace Id","type":"string"},"idempotency_key":{"anyOf":[{"maxLength":255,"minLength":1,"pattern":"^[!-~]{1,255}$","type":"string"},{"type":"null"}],"default":null,"deprecated":true,"description":"Ignored compatibility field. Omit it; supplying a key does not deduplicate this operation.","title":"Idempotency Key"},"source":{"$ref":"#/$defs/RegistrySourceInput"}},"required":["display_name","workspace_id","source"],"title":"MachineImageImportInput","type":"object","$defs":{"RegistrySourceInput":{"additionalProperties":false,"properties":{"kind":{"const":"registry","title":"Kind","type":"string"},"ref":{"maxLength":1024,"minLength":1,"pattern":"^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*(?::[0-9]{1,5})?/)?[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*(?::[A-Za-z0-9_][A-Za-z0-9_.-]{0,127})?(?:@sha256:[0-9a-f]{64})?$","title":"Ref","type":"string"},"platform":{"const":"linux/amd64","default":"linux/amd64","title":"Platform","type":"string"}},"required":["kind","ref"],"title":"RegistrySourceInput","type":"object"}}}',
930
930
  resultKind: "inline",
931
931
  },
932
932
  ],
@@ -1207,14 +1207,14 @@ export const commandManifest = {
1207
1207
  version: 1,
1208
1208
  description: "List subscriptions accessible to the caller in a workspace, optionally for one event definition.",
1209
1209
  inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"SubscriptionListInput","type":"object"}',
1210
- queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":7,"description":"Rows per page, at most 7; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"filter":{"additionalProperties":false,"properties":{"event_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Only subscriptions to this event definition.","title":"Event Id"},"handler":{"anyOf":[{"$ref":"#/$defs/SubscriptionHandlerRef"},{"type":"null"}],"default":null,"description":"Only subscriptions whose handler is this resource: its handler_resource_type and handler_resource_id, such as an Agent."}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"SubscriptionHandlerRef":{"additionalProperties":false,"properties":{"type":{"enum":["function","workflow","agent"],"title":"Type","type":"string"},"id":{"maxLength":200,"minLength":1,"title":"Id","type":"string"}},"required":["type","id"],"title":"SubscriptionHandlerRef","type":"object"}}}',
1210
+ queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":100,"description":"Rows per page, at most 100; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"filter":{"additionalProperties":false,"properties":{"event_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Only subscriptions to this event definition.","title":"Event Id"},"handler":{"anyOf":[{"$ref":"#/$defs/SubscriptionHandlerRef"},{"type":"null"}],"default":null,"description":"Only subscriptions whose handler is this resource: its handler_resource_type and handler_resource_id, such as an Agent."}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"SubscriptionHandlerRef":{"additionalProperties":false,"properties":{"type":{"enum":["function","workflow","agent"],"title":"Type","type":"string"},"id":{"maxLength":200,"minLength":1,"title":"Id","type":"string"}},"required":["type","id"],"title":"SubscriptionHandlerRef","type":"object"}}}',
1211
1211
  resultKind: "inline",
1212
1212
  },
1213
1213
  {
1214
1214
  version: 2,
1215
1215
  description: "List subscriptions accessible to the caller in a workspace, optionally for one event definition.",
1216
1216
  inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"SubscriptionListInput","type":"object"}',
1217
- queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":7,"description":"Rows per page, at most 7; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"filter":{"additionalProperties":false,"properties":{"event_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Only subscriptions to this event definition.","title":"Event Id"},"handler":{"anyOf":[{"$ref":"#/$defs/SubscriptionHandlerRef"},{"type":"null"}],"default":null,"description":"Only subscriptions whose handler is this resource: its handler_resource_type and handler_resource_id, such as an Agent."}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"SubscriptionHandlerRef":{"additionalProperties":false,"properties":{"type":{"enum":["function","workflow","agent"],"title":"Type","type":"string"},"id":{"maxLength":200,"minLength":1,"title":"Id","type":"string"}},"required":["type","id"],"title":"SubscriptionHandlerRef","type":"object"}}}',
1217
+ queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":100,"description":"Rows per page, at most 100; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"filter":{"additionalProperties":false,"properties":{"event_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"Only subscriptions to this event definition.","title":"Event Id"},"handler":{"anyOf":[{"$ref":"#/$defs/SubscriptionHandlerRef"},{"type":"null"}],"default":null,"description":"Only subscriptions whose handler is this resource: its handler_resource_type and handler_resource_id, such as an Agent."}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"SubscriptionHandlerRef":{"additionalProperties":false,"properties":{"type":{"enum":["function","workflow","agent"],"title":"Type","type":"string"},"id":{"maxLength":200,"minLength":1,"title":"Id","type":"string"}},"required":["type","id"],"title":"SubscriptionHandlerRef","type":"object"}}}',
1218
1218
  resultKind: "inline",
1219
1219
  },
1220
1220
  ],
@@ -1440,7 +1440,7 @@ export const commandManifest = {
1440
1440
  version: 1,
1441
1441
  description: 'List the workspace\'s tasks, most recently updated first. Filter by status, assignee (principals or "unassigned"), parent and blocked state. Summaries carry no body or comments.',
1442
1442
  inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"parent":{"anyOf":[{"description":"The task\'s number (\\"42\\" or \\"#42\\") or its tsk_ ID.","pattern":"^(#?[1-9][0-9]{0,15}|tsk_[0-9A-Za-z]{16})$","type":"string"},{"type":"null"}],"default":null,"description":"Only this task\'s subtasks.","title":"Parent"}},"required":["workspace_id"],"title":"TaskListInput","type":"object"}',
1443
- queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":50,"description":"Rows per page, at most 200; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"sort":{"type":"string","enum":["updated_at.desc","number.asc"],"default":"updated_at.desc"},"filter":{"additionalProperties":false,"properties":{"status":{"anyOf":[{"items":{"enum":["backlog","ready","in_progress","failed","done","cancelled"],"type":"string"},"maxItems":6,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Tasks in any of these statuses.","title":"Status"},"assignee":{"anyOf":[{"items":{"$ref":"#/$defs/AssigneeFilter"},"maxItems":32,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Tasks assigned to any of these; {\\"type\\": \\"unassigned\\"} matches tasks with no assignee.","title":"Assignee"},"is_blocked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Only blocked tasks, or only unblocked ones.","title":"Is Blocked"}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"AssigneeFilter":{"additionalProperties":false,"description":"A principal, or ``{\\"type\\": \\"unassigned\\"}`` for tasks with no assignee.","properties":{"type":{"enum":["user","agent","service_account","unassigned"],"title":"Type","type":"string"},"id":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"The principal\'s ID; omitted for \\"unassigned\\".","title":"Id"}},"required":["type"],"title":"AssigneeFilter","type":"object"}}}',
1443
+ queryJson: '{"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":50,"description":"Rows per page, at most 200; a larger value is reduced to it."},"cursor":{"type":"string","minLength":1,"maxLength":5504,"description":"The previous page\'s next_cursor, sent with the same sort, filter and search."},"sort":{"type":"string","enum":["updated_at.desc","number.asc"],"default":"updated_at.desc"},"filter":{"additionalProperties":false,"properties":{"status":{"anyOf":[{"items":{"enum":["backlog","ready","in_progress","failed","done","cancelled"],"type":"string"},"maxItems":6,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Tasks in any of these statuses.","title":"Status"},"assignee":{"anyOf":[{"items":{"$ref":"#/$defs/AssigneeFilter"},"maxItems":32,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Tasks assigned to any of these; {\\"type\\": \\"unassigned\\"} matches tasks with no assignee.","title":"Assignee"},"is_blocked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":null,"description":"Only blocked tasks, or only unblocked ones.","title":"Is Blocked"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"default":null,"description":"Tasks carrying any of these workspace tag IDs.","title":"Tags"}},"type":"object","required":[],"default":{}}},"required":[],"$defs":{"AssigneeFilter":{"additionalProperties":false,"description":"A principal, or ``{\\"type\\": \\"unassigned\\"}`` for tasks with no assignee.","properties":{"type":{"enum":["user","agent","service_account","unassigned"],"title":"Type","type":"string"},"id":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"The principal\'s ID; omitted for \\"unassigned\\".","title":"Id"}},"required":["type"],"title":"AssigneeFilter","type":"object"}}}',
1444
1444
  resultKind: "inline",
1445
1445
  },
1446
1446
  ],
package/dist/main.d.ts CHANGED
@@ -1,15 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import { Profiles } from "./profiles.ts";
3
- import type { InvocationContext } from "./catalog.ts";
4
- import type { OutputOptions } from "./output.ts";
5
- import type { RpcOptions } from "./rpc.ts";
6
- export { parseGlobals } from "./argv.ts";
7
- export interface RunOptions extends OutputOptions {
8
- profiles?: Profiles;
9
- env?: NodeJS.ProcessEnv;
10
- fetch?: InvocationContext["fetch"];
11
- stdin?: RpcOptions["stdin"];
12
- signal?: AbortSignal;
13
- }
14
- export declare function run(argv?: string[], runtime?: RunOptions): Promise<void>;
15
- export declare function execute(argv?: string[], runtime?: RunOptions): Promise<number>;
2
+ export * from "./runner.ts";
package/dist/main.js CHANGED
@@ -1,313 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { readFile, realpath } from "node:fs/promises";
3
- import { createInterface } from "node:readline/promises";
2
+ import { realpath } from "node:fs/promises";
4
3
  import { pathToFileURL } from "node:url";
5
- import { AuthClient } from "./auth.js";
6
- import { commandOptions, parseGlobals } from "./argv.js";
7
- import { authorizedWorkspace, contextStatus, useContext } from "./context.js";
8
- import { CliError, record, safeText, throwIfInterrupted } from "./errors.js";
9
- import { Profiles, selectedWorkspace } from "./profiles.js";
10
- export { parseGlobals } from "./argv.js";
11
- const help = `Zo public command line client
12
-
13
- Usage: zo <command> [--profile <name>] [--workspace <id>] [--json]
14
-
15
- login / logout Sign in through your browser or revoke the sign-in
16
- auth status / token Inspect your identity or explicitly print an access token
17
- context list/use/show Select an authorized workspace
18
- actions list/describe Discover live action contracts
19
- rpc <action> Invoke any callable action from JSON
20
- <resource> <action> Invoke an installed built-in command
21
- completion <shell> Print bash, zsh or fish completion (offline)
22
- --version Show the installed CLI version
23
-
24
- Run zo <command> --help for options; for example, zo machine list --help.
25
- Use --profile to keep separate sign-ins. New profiles use production.
26
- ZO_SYSTEM_API_ACCESS_TOKEN overrides saved credentials; ZO_WORKSPACE_ID selects a workspace.
27
- `;
28
- const localHelp = {
29
- login: "Usage: zo login [--profile <name>] [--workspace <id>] [--no-browser]\nPrint a sign-in URL and code, open your browser, and wait for approval.\nNew profiles use production. Browser consent approves workspaces and identities.\n--no-browser prints the URL without opening a browser; approve it on any device.\nInternal testing: --environment staging explicitly binds a separate profile to staging.\n",
30
- logout: "Usage: zo logout [--profile <name>] [--local-only]\nRevoke the saved sign-in and pending replaced sign-ins. --local-only removes local credentials without revoking server access.\n",
31
- auth: "Usage: zo auth status | token [--headers]\nStatus shows the current identity; token explicitly prints an access token.\n",
32
- "auth status": "Usage: zo auth status [--profile <name>] [--workspace <id>] [--json]\nShow the approving person, sign-in expiry and effective identity in the selected workspace.\n",
33
- "auth token": "Usage: zo auth token [--profile <name>] [--headers]\nExplicitly print a short-lived access token; --headers prints Authorization JSON.\n",
34
- context: "Usage: zo context list | use <workspace-id> | show\nWorkspace selection does not change permissions.\n",
35
- "context list": "Usage: zo context list [--profile <name>] [--json]\nList currently authorized workspaces and effective identities.\n",
36
- "context use": "Usage: zo context use <workspace-id> [--profile <name>]\nValidate access and save the workspace as this profile's default.\n",
37
- "context show": "Usage: zo context show [--profile <name>] [--workspace <id>] [--json]\nShow and validate the selected workspace and identity.\n",
38
- actions: "Usage: zo actions list | describe <action> [--version <integer>] [--catalog scoped|all]\nRead the live catalog. --catalog all includes definitions beyond current availability.\n",
39
- "actions list": "Usage: zo actions list [--catalog scoped|all] [--profile <name>] [--workspace <id>] [--json]\nList callable contracts from the live catalog; --catalog all includes definitions.\n",
40
- "actions describe": "Usage: zo actions describe <action> [--version <integer>] [--catalog scoped|all] [--json]\nShow live input, output and query schemas. Omit --version to inspect every published version.\n",
41
- rpc: "Usage: zo rpc <action> [--input <json> | --input-file <path|->] [--version <integer>]\n --query <json> Declared list-query options; one page per call\n --idempotency-key <key> Envelope key where the action supports it\n --preview Print the request without invoking\n --envelope Print the complete success envelope\n --json Print exact result JSON\n --profile <name> --workspace <id>\nThe live catalog supplies the default version. RPC preserves supplied inputs. Invocations are never retried.\n",
42
- completion: "Usage: zo completion bash|zsh|fish\nPrint completion from installed metadata without credentials or network requests.\n",
43
- };
44
- function identity(session, workspace) {
45
- const { type, id } = workspace.subject;
46
- if (type === "user" &&
47
- session.approving_subject?.type === "user" &&
48
- session.approving_subject.id === id)
49
- return "as you";
50
- if (type === "service_account")
51
- return `as service account ${safeText(id)}`;
52
- return `as ${type} ${safeText(id)}`;
53
- }
54
- async function showHelp(args, stdout) {
55
- if (!args.length) {
56
- stdout.write(help);
57
- return;
58
- }
59
- const path = args.slice(0, args.findIndex((arg) => arg.startsWith("--")) === -1
60
- ? undefined
61
- : args.findIndex((arg) => arg.startsWith("--")));
62
- const key = path
63
- .slice(0, ["auth", "context", "actions"].includes(path[0] ?? "") ? 2 : 1)
64
- .join(" ");
65
- if (Object.hasOwn(localHelp, key)) {
66
- stdout.write(localHelp[key] ?? "");
67
- return;
68
- }
69
- stdout.write((await import("./commands.js")).builtinHelp(path));
70
- }
71
- export async function run(argv = process.argv.slice(2), runtime = {}) {
72
- const { args, options } = parseGlobals(argv);
73
- const stdout = runtime.stdout ?? process.stdout;
74
- const stderr = runtime.stderr ?? process.stderr;
75
- const output = {
76
- stdout,
77
- stderr,
78
- json: options.json,
79
- envelope: options.envelope,
80
- };
81
- const env = runtime.env ?? process.env;
82
- if (options.help || !args.length) {
83
- await showHelp(args, stdout);
84
- return;
85
- }
86
- if (args[0] === "--version") {
87
- if (args.length !== 1)
88
- throw new CliError("Usage: zo --version");
89
- const manifest = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
90
- if (!record(manifest) || typeof manifest.version !== "string")
91
- throw new CliError("The installed package has no version.", 5);
92
- stdout.write(`${manifest.version}\n`);
93
- return;
94
- }
95
- if (args[0] === "completion") {
96
- if (args.length !== 2 || !args[1])
97
- throw new CliError(localHelp.completion ?? "Invalid completion command.");
98
- stdout.write((await import("./commands.js")).completionScript(args[1]));
99
- return;
100
- }
101
- const local = ["login", "logout", "auth", "context"].includes(args[0] ?? "");
102
- if (options.envelope && local)
103
- throw new CliError("--envelope is only supported for action invocation.");
104
- const controller = new AbortController();
105
- const interrupt = () => controller.abort();
106
- process.once("SIGINT", interrupt);
107
- const signal = runtime.signal
108
- ? AbortSignal.any([controller.signal, runtime.signal])
109
- : controller.signal;
110
- try {
111
- throwIfInterrupted(signal);
112
- const profiles = runtime.profiles ?? new Profiles();
113
- const { writeJson } = await import("./output.js");
114
- const writeLocalJson = (value) => {
115
- throwIfInterrupted(signal);
116
- writeJson(value, output);
117
- };
118
- if (args[0] === "login") {
119
- const parsed = commandOptions(args.slice(1), ["environment"], ["no-browser"]);
120
- const requested = parsed.values.get("environment");
121
- if (requested !== undefined &&
122
- requested !== "production" &&
123
- requested !== "staging")
124
- throw new CliError("--environment must be production or staging.");
125
- const profile = await profiles.create(options.profile, requested, signal);
126
- const auth = new AuthClient(profile, profiles.directory, {
127
- env,
128
- fetch: runtime.fetch,
129
- signal,
130
- });
131
- const login = await auth.login({
132
- noBrowser: parsed.flags.has("no-browser"),
133
- signal,
134
- announce: ({ url, userCode, browser }) => stderr.write(`${browser
135
- ? "Opening your browser to sign in. If it doesn't open, visit:"
136
- : "To sign in, visit this URL on any device:"}\n\n ${url}\n\nCode: ${userCode}. Approve only if your browser shows the same code.\nWaiting for approval… (Ctrl-C to cancel)\n`),
137
- });
138
- stderr.write(`\nApproved in the browser. Sign-in saved for profile ${profile.name}.\n`);
139
- if (login.cleanupPending)
140
- stderr.write("A previous sign-in could not be revoked. Cleanup remains saved for the next login; the current sign-in is active. You can revoke the previous sign-in in Authorized apps.\n");
141
- const session = await auth.session();
142
- // Always list the approved workspaces, so a sign-in someone else
143
- // approved shows up as workspaces or identities you don't recognize.
144
- session.workspaces.forEach((workspace, index) => stderr.write(`${index + 1}. ${safeText(workspace.name)} (${workspace.workspace_id}), ${identity(session, workspace)}\n`));
145
- const desired = options.workspace ?? env.ZO_WORKSPACE_ID ?? profile.workspaceId;
146
- let chosen;
147
- if (options.workspace !== undefined)
148
- chosen = authorizedWorkspace(session, options.workspace).workspace_id;
149
- else if (desired !== undefined) {
150
- chosen = session.workspaces.find((workspace) => workspace.workspace_id === desired)?.workspace_id;
151
- if (!chosen)
152
- stderr.write(env.ZO_WORKSPACE_ID !== undefined
153
- ? "ZO_WORKSPACE_ID is not authorized by the new sign-in. Choose a workspace, then update or unset ZO_WORKSPACE_ID (or use --workspace) before invoking.\n"
154
- : "The saved workspace is not authorized by the new sign-in. Choose a workspace explicitly; the previous selection is unchanged.\n");
155
- }
156
- else if (session.workspaces.length === 1)
157
- chosen = session.workspaces[0]?.workspace_id;
158
- if (!chosen &&
159
- !runtime.stdin &&
160
- process.stdin.isTTY &&
161
- stderr.isTTY &&
162
- session.workspaces.length > 0) {
163
- const input = createInterface({
164
- input: process.stdin,
165
- output: process.stderr,
166
- });
167
- try {
168
- const answer = await input.question("Choose a workspace number: ", {
169
- signal,
170
- });
171
- if (/^[1-9][0-9]*$/.test(answer))
172
- chosen = session.workspaces[Number(answer) - 1]?.workspace_id;
173
- }
174
- finally {
175
- input.close();
176
- }
177
- }
178
- if (!chosen) {
179
- stderr.write("Select an authorized workspace with zo context use <workspace-id>.\n");
180
- writeLocalJson({
181
- profile: profile.name,
182
- signed_in: true,
183
- workspace_id: null,
184
- selection_required: true,
185
- });
186
- return;
187
- }
188
- await profiles.select(profile, chosen, signal);
189
- writeLocalJson({ profile: profile.name, workspace_id: chosen });
190
- return;
191
- }
192
- const profile = await profiles.resolve(options.profile);
193
- throwIfInterrupted(signal);
194
- const auth = new AuthClient(profile, profiles.directory, {
195
- env,
196
- fetch: runtime.fetch,
197
- signal,
198
- });
199
- if (args[0] === "logout") {
200
- const parsed = commandOptions(args.slice(1), [], ["local-only"]);
201
- const localOnly = parsed.flags.has("local-only");
202
- await auth.logout(localOnly);
203
- throwIfInterrupted(signal);
204
- stderr.write(localOnly
205
- ? "Local credentials removed. Server access was not revoked.\n"
206
- : "Signed out; saved sign-ins revoked.\n");
207
- return;
208
- }
209
- if (args[0] === "auth" && args[1] === "token") {
210
- const parsed = commandOptions(args.slice(2), [], ["headers"]);
211
- const token = await auth.accessToken();
212
- throwIfInterrupted(signal);
213
- if (parsed.flags.has("headers"))
214
- writeLocalJson({ Authorization: `Bearer ${token}` });
215
- else
216
- stdout.write(`${token}\n`);
217
- return;
218
- }
219
- if ((args[0] === "auth" && args[1] === "status") ||
220
- (args[0] === "context" && args[1] === "show")) {
221
- commandOptions(args.slice(2), []);
222
- writeLocalJson(await contextStatus(profile, auth, options.workspace, env));
223
- return;
224
- }
225
- if (args[0] === "context" && args[1] === "list") {
226
- commandOptions(args.slice(2), []);
227
- writeLocalJson((await auth.session()).workspaces);
228
- return;
229
- }
230
- if (args[0] === "context" && args[1] === "use") {
231
- if (args.length !== 3 || !args[2])
232
- throw new CliError("Usage: zo context use <workspace-id>");
233
- writeLocalJson(await useContext(profiles, profile, auth, args[2], signal));
234
- return;
235
- }
236
- if (local)
237
- throw new CliError("Unknown command. Run zo --help.");
238
- const context = {
239
- workspaceId: selectedWorkspace(profile, options.workspace, env),
240
- apiOrigin: auth.binding.api,
241
- accessToken: () => auth.accessToken(),
242
- signal,
243
- ...(runtime.fetch ? { fetch: runtime.fetch } : {}),
244
- };
245
- if (args[0] === "rpc") {
246
- const action = args[1];
247
- if (!action || action.startsWith("--"))
248
- throw new CliError("Usage: zo rpc <action> [options]");
249
- const parsed = commandOptions(args.slice(2), ["input", "input-file", "version", "query", "idempotency-key"], ["preview"]);
250
- const { parseVersion } = await import("./catalog.js");
251
- const version = parsed.values.get("version");
252
- const result = await (await import("./rpc.js")).runRpc(context, {
253
- action,
254
- input: parsed.values.get("input"),
255
- inputFile: parsed.values.get("input-file"),
256
- query: parsed.values.get("query"),
257
- version: version === undefined ? undefined : parseVersion(version),
258
- idempotencyKey: parsed.values.get("idempotency-key"),
259
- preview: parsed.flags.has("preview"),
260
- stdin: runtime.stdin,
261
- });
262
- (await import("./output.js")).writeRpcResult(result, output);
263
- return;
264
- }
265
- if (args[0] === "actions") {
266
- const describe = args[1] === "describe";
267
- if (args[1] !== "list" && !describe)
268
- throw new CliError("Usage: zo actions list | describe <action>");
269
- const action = describe ? args[2] : undefined;
270
- if (describe && (!action || action.startsWith("--")))
271
- throw new CliError("Usage: zo actions describe <action>");
272
- if (options.envelope)
273
- throw new CliError("--envelope is only supported for action invocation.");
274
- const parsed = commandOptions(args.slice(describe ? 3 : 2), describe ? ["catalog", "version"] : ["catalog"]);
275
- const mode = parsed.values.get("catalog") ?? "scoped";
276
- if (mode !== "scoped" && mode !== "all")
277
- throw new CliError("--catalog must be scoped or all.");
278
- const { loadCatalog, listActions, describeAction, parseVersion } = await import("./catalog.js");
279
- const version = parsed.values.get("version");
280
- const parsedVersion = version === undefined ? undefined : parseVersion(version);
281
- const { catalog } = await loadCatalog(context, { catalog: mode });
282
- writeJson(action
283
- ? describeAction(catalog, action, parsedVersion)
284
- : listActions(catalog), output);
285
- return;
286
- }
287
- const { matchBuiltinCommand, runBuiltin } = await import("./commands.js");
288
- const command = matchBuiltinCommand(args);
289
- if (!command)
290
- throw new CliError("Unknown command. Run zo --help.");
291
- const result = await runBuiltin(context, command.action, command.args, runtime.stdin);
292
- (await import("./presentation.js")).writeBuiltinResult(command.action, result, { ...output, workspaceId: context.workspaceId });
293
- }
294
- finally {
295
- process.removeListener("SIGINT", interrupt);
296
- }
297
- }
298
- export async function execute(argv = process.argv.slice(2), runtime = {}) {
299
- try {
300
- await run(argv, runtime);
301
- return 0;
302
- }
303
- catch (error) {
304
- const { writeError } = await import("./output.js");
305
- return writeError(error, {
306
- ...runtime,
307
- json: argv.some((arg) => arg === "--json"),
308
- });
309
- }
310
- }
4
+ import { execute } from "./runner.js";
5
+ export * from "./runner.js";
311
6
  if (process.argv[1] &&
312
7
  import.meta.url === pathToFileURL(await realpath(process.argv[1])).href)
313
8
  process.exitCode = await execute();
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { Profiles } from "./profiles.ts";
3
+ import type { InvocationContext } from "./catalog.ts";
4
+ import type { OutputOptions } from "./output.ts";
5
+ import type { RpcOptions } from "./rpc.ts";
6
+ export { parseGlobals } from "./argv.ts";
7
+ export interface RunOptions extends OutputOptions {
8
+ version?: string;
9
+ profiles?: Profiles;
10
+ env?: NodeJS.ProcessEnv;
11
+ fetch?: InvocationContext["fetch"];
12
+ stdin?: RpcOptions["stdin"];
13
+ signal?: AbortSignal;
14
+ }
15
+ export declare function run(argv?: string[], runtime?: RunOptions): Promise<void>;
16
+ export declare function execute(argv?: string[], runtime?: RunOptions): Promise<number>;
package/dist/runner.js ADDED
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import { createInterface } from "node:readline/promises";
4
+ import { AuthClient } from "./auth.js";
5
+ import { commandOptions, parseGlobals } from "./argv.js";
6
+ import { authorizedWorkspace, contextStatus, useContext } from "./context.js";
7
+ import { CliError, record, safeText, throwIfInterrupted } from "./errors.js";
8
+ import { Profiles, selectedWorkspace } from "./profiles.js";
9
+ export { parseGlobals } from "./argv.js";
10
+ const help = `Zo public command line client
11
+
12
+ Usage: zo <command> [--profile <name>] [--workspace <id>] [--json]
13
+
14
+ login / logout Sign in through your browser or revoke the sign-in
15
+ auth status / token Inspect your identity or explicitly print an access token
16
+ context list/use/show Select an authorized workspace
17
+ actions list/describe Discover live action contracts
18
+ rpc <action> Invoke any callable action from JSON
19
+ <resource> <action> Invoke an installed built-in command
20
+ completion <shell> Print bash, zsh or fish completion (offline)
21
+ --version Show the installed CLI version
22
+
23
+ Run zo <command> --help for options; for example, zo machine list --help.
24
+ Use --profile to keep separate sign-ins. New profiles use production.
25
+ ZO_SYSTEM_API_ACCESS_TOKEN overrides saved credentials; ZO_WORKSPACE_ID selects a workspace.
26
+ `;
27
+ const localHelp = {
28
+ login: "Usage: zo login [--profile <name>] [--workspace <id>] [--no-browser]\nPrint a sign-in URL and code, open your browser, and wait for approval.\nNew profiles use production. Browser consent approves workspaces and identities.\n--no-browser prints the URL without opening a browser; approve it on any device.\nInternal testing: --environment staging explicitly binds a separate profile to staging.\n",
29
+ logout: "Usage: zo logout [--profile <name>] [--local-only]\nRevoke the saved sign-in and pending replaced sign-ins. --local-only removes local credentials without revoking server access.\n",
30
+ auth: "Usage: zo auth status | token [--headers]\nStatus shows the current identity; token explicitly prints an access token.\n",
31
+ "auth status": "Usage: zo auth status [--profile <name>] [--workspace <id>] [--json]\nShow the approving person, sign-in expiry and effective identity in the selected workspace.\n",
32
+ "auth token": "Usage: zo auth token [--profile <name>] [--headers]\nExplicitly print a short-lived access token; --headers prints Authorization JSON.\n",
33
+ context: "Usage: zo context list | use <workspace-id> | show\nWorkspace selection does not change permissions.\n",
34
+ "context list": "Usage: zo context list [--profile <name>] [--json]\nList currently authorized workspaces and effective identities.\n",
35
+ "context use": "Usage: zo context use <workspace-id> [--profile <name>]\nValidate access and save the workspace as this profile's default.\n",
36
+ "context show": "Usage: zo context show [--profile <name>] [--workspace <id>] [--json]\nShow and validate the selected workspace and identity.\n",
37
+ actions: "Usage: zo actions list | describe <action> [--version <integer>] [--catalog scoped|all]\nRead the live catalog. --catalog all includes definitions beyond current availability.\n",
38
+ "actions list": "Usage: zo actions list [--catalog scoped|all] [--profile <name>] [--workspace <id>] [--json]\nList callable contracts from the live catalog; --catalog all includes definitions.\n",
39
+ "actions describe": "Usage: zo actions describe <action> [--version <integer>] [--catalog scoped|all] [--json]\nShow live input, output and query schemas. Omit --version to inspect every published version.\n",
40
+ rpc: "Usage: zo rpc <action> [--input <json> | --input-file <path|->] [--version <integer>]\n --query <json> Declared list-query options; one page per call\n --idempotency-key <key> Envelope key where the action supports it\n --preview Print the request without invoking\n --envelope Print the complete success envelope\n --json Print exact result JSON\n --profile <name> --workspace <id>\nThe live catalog supplies the default version. RPC preserves supplied inputs. Invocations are never retried.\n",
41
+ completion: "Usage: zo completion bash|zsh|fish\nPrint completion from installed metadata without credentials or network requests.\n",
42
+ };
43
+ // Printed after login so a first-time caller, person or agent, knows the next
44
+ // command and that one sign-in serves the CLI, HTTP and MCP alike.
45
+ const nextSteps = "Calls act as the identity shown for each workspace, with its current permissions.\n" +
46
+ "Next: zo workspace get for a first call, then zo actions list to see what you can call.\n" +
47
+ "For HTTP or MCP, zo auth token --headers prints an Authorization header for this sign-in.\n";
48
+ function identity(session, workspace) {
49
+ const { type, id } = workspace.subject;
50
+ if (type === "user" &&
51
+ session.approving_subject?.type === "user" &&
52
+ session.approving_subject.id === id)
53
+ return "as you";
54
+ if (type === "service_account")
55
+ return `as service account ${safeText(id)}`;
56
+ return `as ${type} ${safeText(id)}`;
57
+ }
58
+ async function showHelp(args, stdout) {
59
+ if (!args.length) {
60
+ stdout.write(help);
61
+ return;
62
+ }
63
+ const path = args.slice(0, args.findIndex((arg) => arg.startsWith("--")) === -1
64
+ ? undefined
65
+ : args.findIndex((arg) => arg.startsWith("--")));
66
+ const key = path
67
+ .slice(0, ["auth", "context", "actions"].includes(path[0] ?? "") ? 2 : 1)
68
+ .join(" ");
69
+ if (Object.hasOwn(localHelp, key)) {
70
+ stdout.write(localHelp[key] ?? "");
71
+ return;
72
+ }
73
+ stdout.write((await import("./commands.js")).builtinHelp(path));
74
+ }
75
+ export async function run(argv = process.argv.slice(2), runtime = {}) {
76
+ const { args, options } = parseGlobals(argv);
77
+ const stdout = runtime.stdout ?? process.stdout;
78
+ const stderr = runtime.stderr ?? process.stderr;
79
+ const output = {
80
+ stdout,
81
+ stderr,
82
+ json: options.json,
83
+ envelope: options.envelope,
84
+ };
85
+ const env = runtime.env ?? process.env;
86
+ if (options.help || !args.length) {
87
+ await showHelp(args, stdout);
88
+ return;
89
+ }
90
+ if (args[0] === "--version") {
91
+ if (args.length !== 1)
92
+ throw new CliError("Usage: zo --version");
93
+ if (runtime.version !== undefined) {
94
+ stdout.write(`${runtime.version}\n`);
95
+ return;
96
+ }
97
+ const manifest = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
98
+ if (!record(manifest) || typeof manifest.version !== "string")
99
+ throw new CliError("The installed package has no version.", 5);
100
+ stdout.write(`${manifest.version}\n`);
101
+ return;
102
+ }
103
+ if (args[0] === "completion") {
104
+ if (args.length !== 2 || !args[1])
105
+ throw new CliError(localHelp.completion ?? "Invalid completion command.");
106
+ stdout.write((await import("./commands.js")).completionScript(args[1]));
107
+ return;
108
+ }
109
+ const local = ["login", "logout", "auth", "context"].includes(args[0] ?? "");
110
+ if (options.envelope && local)
111
+ throw new CliError("--envelope is only supported for action invocation.");
112
+ const controller = new AbortController();
113
+ const interrupt = () => controller.abort();
114
+ process.once("SIGINT", interrupt);
115
+ const signal = runtime.signal
116
+ ? AbortSignal.any([controller.signal, runtime.signal])
117
+ : controller.signal;
118
+ try {
119
+ throwIfInterrupted(signal);
120
+ const profiles = runtime.profiles ?? new Profiles();
121
+ const { writeJson } = await import("./output.js");
122
+ const writeLocalJson = (value) => {
123
+ throwIfInterrupted(signal);
124
+ writeJson(value, output);
125
+ };
126
+ if (args[0] === "login") {
127
+ const parsed = commandOptions(args.slice(1), ["environment"], ["no-browser"]);
128
+ const requested = parsed.values.get("environment");
129
+ if (requested !== undefined &&
130
+ requested !== "production" &&
131
+ requested !== "staging")
132
+ throw new CliError("--environment must be production or staging.");
133
+ const profile = await profiles.create(options.profile, requested, signal);
134
+ const auth = new AuthClient(profile, profiles.directory, {
135
+ env,
136
+ fetch: runtime.fetch,
137
+ signal,
138
+ });
139
+ const login = await auth.login({
140
+ noBrowser: parsed.flags.has("no-browser"),
141
+ signal,
142
+ announce: ({ url, userCode, browser }) => stderr.write(`${browser
143
+ ? "Opening your browser to sign in. If it doesn't open, visit:"
144
+ : "To sign in, visit this URL on any device:"}\n\n ${url}\n\nCode: ${userCode}. Approve only if your browser shows the same code.\nWaiting for approval… (Ctrl-C to cancel)\n`),
145
+ });
146
+ stderr.write(`\nApproved in the browser. Sign-in saved for profile ${profile.name}.\n`);
147
+ if (login.cleanupPending)
148
+ stderr.write("A previous sign-in could not be revoked. Cleanup remains saved for the next login; the current sign-in is active. You can revoke the previous sign-in in Authorized apps.\n");
149
+ const session = await auth.session();
150
+ // Always list the approved workspaces, so a sign-in someone else
151
+ // approved shows up as workspaces or identities you don't recognize.
152
+ session.workspaces.forEach((workspace, index) => stderr.write(`${index + 1}. ${safeText(workspace.name)} (${workspace.workspace_id}), ${identity(session, workspace)}\n`));
153
+ const desired = options.workspace ?? env.ZO_WORKSPACE_ID ?? profile.workspaceId;
154
+ let chosen;
155
+ if (options.workspace !== undefined)
156
+ chosen = authorizedWorkspace(session, options.workspace).workspace_id;
157
+ else if (desired !== undefined) {
158
+ chosen = session.workspaces.find((workspace) => workspace.workspace_id === desired)?.workspace_id;
159
+ if (!chosen)
160
+ stderr.write(env.ZO_WORKSPACE_ID !== undefined
161
+ ? "ZO_WORKSPACE_ID is not authorized by the new sign-in. Choose a workspace, then update or unset ZO_WORKSPACE_ID (or use --workspace) before invoking.\n"
162
+ : "The saved workspace is not authorized by the new sign-in. Choose a workspace explicitly; the previous selection is unchanged.\n");
163
+ }
164
+ else if (session.workspaces.length === 1)
165
+ chosen = session.workspaces[0]?.workspace_id;
166
+ if (!chosen &&
167
+ !runtime.stdin &&
168
+ process.stdin.isTTY &&
169
+ stderr.isTTY &&
170
+ session.workspaces.length > 0) {
171
+ const input = createInterface({
172
+ input: process.stdin,
173
+ output: process.stderr,
174
+ });
175
+ try {
176
+ const answer = await input.question("Choose a workspace number: ", {
177
+ signal,
178
+ });
179
+ if (/^[1-9][0-9]*$/.test(answer))
180
+ chosen = session.workspaces[Number(answer) - 1]?.workspace_id;
181
+ }
182
+ finally {
183
+ input.close();
184
+ }
185
+ }
186
+ if (!chosen) {
187
+ stderr.write("Select an authorized workspace with zo context use <workspace-id>.\n" +
188
+ nextSteps);
189
+ writeLocalJson({
190
+ profile: profile.name,
191
+ signed_in: true,
192
+ workspace_id: null,
193
+ selection_required: true,
194
+ });
195
+ return;
196
+ }
197
+ await profiles.select(profile, chosen, signal);
198
+ stderr.write(nextSteps);
199
+ writeLocalJson({ profile: profile.name, workspace_id: chosen });
200
+ return;
201
+ }
202
+ const profile = await profiles.resolve(options.profile);
203
+ throwIfInterrupted(signal);
204
+ const auth = new AuthClient(profile, profiles.directory, {
205
+ env,
206
+ fetch: runtime.fetch,
207
+ signal,
208
+ });
209
+ if (args[0] === "logout") {
210
+ const parsed = commandOptions(args.slice(1), [], ["local-only"]);
211
+ const localOnly = parsed.flags.has("local-only");
212
+ await auth.logout(localOnly);
213
+ throwIfInterrupted(signal);
214
+ stderr.write(localOnly
215
+ ? "Local credentials removed. Server access was not revoked.\n"
216
+ : "Signed out; saved sign-ins revoked.\n");
217
+ return;
218
+ }
219
+ if (args[0] === "auth" && args[1] === "token") {
220
+ const parsed = commandOptions(args.slice(2), [], ["headers"]);
221
+ const token = await auth.accessToken();
222
+ throwIfInterrupted(signal);
223
+ if (parsed.flags.has("headers"))
224
+ writeLocalJson({ Authorization: `Bearer ${token}` });
225
+ else
226
+ stdout.write(`${token}\n`);
227
+ return;
228
+ }
229
+ if ((args[0] === "auth" && args[1] === "status") ||
230
+ (args[0] === "context" && args[1] === "show")) {
231
+ commandOptions(args.slice(2), []);
232
+ writeLocalJson(await contextStatus(profile, auth, options.workspace, env));
233
+ return;
234
+ }
235
+ if (args[0] === "context" && args[1] === "list") {
236
+ commandOptions(args.slice(2), []);
237
+ writeLocalJson((await auth.session()).workspaces);
238
+ return;
239
+ }
240
+ if (args[0] === "context" && args[1] === "use") {
241
+ if (args.length !== 3 || !args[2])
242
+ throw new CliError("Usage: zo context use <workspace-id>");
243
+ writeLocalJson(await useContext(profiles, profile, auth, args[2], signal));
244
+ return;
245
+ }
246
+ if (local)
247
+ throw new CliError("Unknown command. Run zo --help.");
248
+ const context = {
249
+ workspaceId: selectedWorkspace(profile, options.workspace, env),
250
+ apiOrigin: auth.binding.api,
251
+ accessToken: () => auth.accessToken(),
252
+ signal,
253
+ ...(runtime.fetch ? { fetch: runtime.fetch } : {}),
254
+ };
255
+ if (args[0] === "rpc") {
256
+ const action = args[1];
257
+ if (!action || action.startsWith("--"))
258
+ throw new CliError("Usage: zo rpc <action> [options]");
259
+ const parsed = commandOptions(args.slice(2), ["input", "input-file", "version", "query", "idempotency-key"], ["preview"]);
260
+ const { parseVersion } = await import("./catalog.js");
261
+ const version = parsed.values.get("version");
262
+ const result = await (await import("./rpc.js")).runRpc(context, {
263
+ action,
264
+ input: parsed.values.get("input"),
265
+ inputFile: parsed.values.get("input-file"),
266
+ query: parsed.values.get("query"),
267
+ version: version === undefined ? undefined : parseVersion(version),
268
+ idempotencyKey: parsed.values.get("idempotency-key"),
269
+ preview: parsed.flags.has("preview"),
270
+ stdin: runtime.stdin,
271
+ });
272
+ (await import("./output.js")).writeRpcResult(result, output);
273
+ return;
274
+ }
275
+ if (args[0] === "actions") {
276
+ const describe = args[1] === "describe";
277
+ if (args[1] !== "list" && !describe)
278
+ throw new CliError("Usage: zo actions list | describe <action>");
279
+ const action = describe ? args[2] : undefined;
280
+ if (describe && (!action || action.startsWith("--")))
281
+ throw new CliError("Usage: zo actions describe <action>");
282
+ if (options.envelope)
283
+ throw new CliError("--envelope is only supported for action invocation.");
284
+ const parsed = commandOptions(args.slice(describe ? 3 : 2), describe ? ["catalog", "version"] : ["catalog"]);
285
+ const mode = parsed.values.get("catalog") ?? "scoped";
286
+ if (mode !== "scoped" && mode !== "all")
287
+ throw new CliError("--catalog must be scoped or all.");
288
+ const { loadCatalog, listActions, describeAction, parseVersion } = await import("./catalog.js");
289
+ const version = parsed.values.get("version");
290
+ const parsedVersion = version === undefined ? undefined : parseVersion(version);
291
+ const { catalog } = await loadCatalog(context, { catalog: mode });
292
+ writeJson(action
293
+ ? describeAction(catalog, action, parsedVersion)
294
+ : listActions(catalog), output);
295
+ return;
296
+ }
297
+ const { matchBuiltinCommand, runBuiltin } = await import("./commands.js");
298
+ const command = matchBuiltinCommand(args);
299
+ if (!command)
300
+ throw new CliError("Unknown command. Run zo --help.");
301
+ const result = await runBuiltin(context, command.action, command.args, runtime.stdin);
302
+ (await import("./presentation.js")).writeBuiltinResult(command.action, result, { ...output, workspaceId: context.workspaceId });
303
+ }
304
+ finally {
305
+ process.removeListener("SIGINT", interrupt);
306
+ }
307
+ }
308
+ export async function execute(argv = process.argv.slice(2), runtime = {}) {
309
+ try {
310
+ await run(argv, runtime);
311
+ return 0;
312
+ }
313
+ catch (error) {
314
+ const { writeError } = await import("./output.js");
315
+ return writeError(error, {
316
+ ...runtime,
317
+ json: argv.some((arg) => arg === "--json"),
318
+ });
319
+ }
320
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zocomputer/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "description": "Public Zo command line client",
6
6
  "bin": {
@@ -14,7 +14,7 @@
14
14
  "node": "^22.23.4 || ^24.22.1 || ^26.13.0"
15
15
  },
16
16
  "dependencies": {
17
- "@zocomputer/system-api": "0.1.3"
17
+ "@zocomputer/system-api": "0.1.4"
18
18
  },
19
19
  "publishConfig": {
20
20
  "access": "public",