@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 +16 -0
- package/dist/generated/commands.js +12 -12
- package/dist/main.d.ts +1 -14
- package/dist/main.js +3 -308
- package/dist/runner.d.ts +16 -0
- package/dist/runner.js +320 -0
- package/package.json +2 -2
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:
|
|
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`.
|
|
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":{"
|
|
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}$","
|
|
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}$","
|
|
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}$","
|
|
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}$","
|
|
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}$","
|
|
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":
|
|
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":
|
|
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
|
-
|
|
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 {
|
|
3
|
-
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
4
3
|
import { pathToFileURL } from "node:url";
|
|
5
|
-
import {
|
|
6
|
-
|
|
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();
|
package/dist/runner.d.ts
ADDED
|
@@ -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.
|
|
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.
|
|
17
|
+
"@zocomputer/system-api": "0.1.4"
|
|
18
18
|
},
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public",
|