@zocomputer/cli 0.1.0 → 0.2.0

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
@@ -3,30 +3,29 @@
3
3
  The public Zo CLI signs in through your browser and selects a workspace for
4
4
  System API commands. It supports the maintained Node 22, 24 and 26 LTS lines on macOS and Linux;
5
5
  see [runtime requirements](../README.md#set-up).
6
- Version 0.1.0 is a release candidate. Publish it only after the production
7
- OAuth client and session endpoint are live and the release checks pass.
8
6
 
9
7
  ## Install or upgrade
10
8
 
11
- After publication, install the versioned package:
9
+ Install the latest published version from npm:
12
10
 
13
11
  ```sh
14
- npm install --global @zocomputer/cli@0.1.0
12
+ npm install --global @zocomputer/cli@latest
15
13
  zo --version
16
14
  zo login
17
15
  ```
18
16
 
19
- Use the same install command with the desired version to upgrade. The CLI
17
+ Run the same install command again to upgrade. The CLI
20
18
  installs its matching public SDK dependency. It performs no update check when
21
19
  you run a command.
22
20
 
23
- To test unpublished artifacts, install both tarballs together:
21
+ To try the CLI without installing it globally:
24
22
 
25
23
  ```sh
26
- npm install --global ./zocomputer-system-api-0.1.0.tgz ./zocomputer-cli-0.1.0.tgz
27
- zo --version
24
+ npx -y @zocomputer/cli@latest --help
28
25
  ```
29
26
 
27
+ Replace `--help` with a command such as `login` or `--version`.
28
+
30
29
  ## Develop locally
31
30
 
32
31
  Install the independent client workspace, then build the CLI:
@@ -52,8 +51,10 @@ node dist/main.js auth status
52
51
  ```
53
52
 
54
53
  Use `node dist/main.js` while working in this repository. The repository's
55
- internal operator CLI is `./zocli/zo` from the repository root. Run `type -a zo`
56
- to diagnose a PATH collision; the two clients store separate sign-ins.
54
+ internal operator CLI is `zd` (`./zocli/zd` from the repository root).
55
+ If an old internal `zo` symlink is still on PATH, run `./zocli/zd setup` from
56
+ your main checkout and refresh your shell's command cache. Run `type -a zd zo`
57
+ to check both commands; the two clients store separate sign-ins.
57
58
 
58
59
  ## Accounts and workspaces
59
60
 
@@ -80,9 +81,26 @@ workspaces, a terminal prompts for a selection; scripts must supply
80
81
  person separately from the effective subject in the selected workspace.
81
82
  `context list`, `context use` and status refresh that view from the server.
82
83
 
83
- `login --no-browser` prints the authorization URL on stderr and listens for
84
- the same loopback callback. It is not a device-code login. The browser must
85
- reach the CLI's localhost callback.
84
+ `login` uses the OAuth device authorization grant (RFC 8628). It prints a
85
+ sign-in URL and a code on stderr, opens your browser, and polls the token
86
+ endpoint until you approve or deny. Approve only if the page shows the same
87
+ code. Nothing connects back to the CLI's machine, so the URL works from any
88
+ browser or device. The request expires after 10 minutes; Ctrl-C cancels it
89
+ (exit 130). Denial exits 4 with `access_denied`; expiry exits 3 with
90
+ `login_timeout`.
91
+
92
+ Over SSH (`SSH_CONNECTION` or `SSH_TTY` set), or with no `DISPLAY` or
93
+ `WAYLAND_DISPLAY` outside macOS, login doesn't open a browser and asks you to
94
+ visit the URL on any device. `login --no-browser` does the same anywhere.
95
+
96
+ After approval, login lists every approved workspace with the identity the CLI
97
+ acts as there (`as you` or `as service account <id>`), even when there is only
98
+ one. A workspace you don't recognize means someone else approved your sign-in
99
+ link; run `logout` to revoke it.
100
+
101
+ Each poll waits up to 20 seconds. After a timeout or network error the CLI
102
+ retries once at once, inside the issuer's 30-second redemption window, then
103
+ resumes polling every `interval` seconds; `slow_down` and a 429 add 5 seconds.
86
104
 
87
105
  ## Tokens and logout
88
106
 
@@ -129,7 +147,9 @@ Before sending, the CLI durably marks the outcome uncertain. If syncing that
129
147
  marker fails after replacement, it attempts to restore the original ready
130
148
  credential and reports the storage error; no exchange was sent. A lost response
131
149
  may be retried once under the lock within the server's 30-second recovery
132
- window. Saving the received successor clears the marker. A crash, failure to
150
+ window. A 429 comes only from the rate limiter, before any exchange, so the
151
+ CLI restores the ready state, does not retry, and asks you to try again
152
+ shortly; a 429 on that retry leaves the marker in place. Saving the received successor clears the marker. A crash, failure to
133
153
  replace the credential file, or unresolved second request leaves the marker
134
154
  in place and requires login;
135
155
  later processes cannot unknowingly replay that refresh token.
package/dist/auth.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface AuthOptions {
12
12
  environment?: Binding;
13
13
  env?: NodeJS.ProcessEnv;
14
14
  now?: () => number;
15
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
15
16
  signal?: AbortSignal;
16
17
  }
17
18
  export declare class AuthClient {
@@ -22,6 +23,7 @@ export declare class AuthClient {
22
23
  private readonly fetcher;
23
24
  private readonly env;
24
25
  private readonly now;
26
+ private readonly sleep;
25
27
  private readonly signal?;
26
28
  constructor(profile: Profile, directory: string, options?: AuthOptions);
27
29
  private locked;
@@ -31,6 +33,8 @@ export declare class AuthClient {
31
33
  session(): Promise<Session>;
32
34
  private revoke;
33
35
  private cleanup;
36
+ /** RFC 8628 section 3.4 polling until approval, denial or expiry. */
37
+ private poll;
34
38
  login(options?: LoginOptions): Promise<{
35
39
  cleanupPending: boolean;
36
40
  }>;
package/dist/auth.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { join } from "node:path";
2
2
  import { CliError, record, throwIfInterrupted } from "./errors.js";
3
3
  import { requestJson, RequestFailure } from "./http.js";
4
- import { loginCode } from "./login.js";
4
+ import { browserAvailable, DEVICE_CODE_GRANT, deviceAuthorization, openBrowser, pause, } from "./login.js";
5
5
  import { environments, profileName } from "./profiles.js";
6
6
  import { parseSession } from "./session.js";
7
7
  import { privateDirectory, PrivateWriteDurabilityError, readPrivate, removePrivate, withLock, writePrivate, } from "./storage.js";
8
+ // Longer than requestJson's 8-second default, and short enough that a retry
9
+ // after a lost approval lands inside the issuer's 30-second redemption window.
10
+ const DEVICE_POLL_TIMEOUT_MS = 20_000;
8
11
  function tokens(raw, now) {
9
12
  if (!record(raw) ||
10
13
  raw.error !== undefined ||
@@ -36,6 +39,7 @@ export class AuthClient {
36
39
  fetcher;
37
40
  env;
38
41
  now;
42
+ sleep;
39
43
  signal;
40
44
  constructor(profile, directory, options = {}) {
41
45
  this.profile = profile;
@@ -45,6 +49,7 @@ export class AuthClient {
45
49
  this.fetcher = options.fetch ?? fetch;
46
50
  this.env = options.env ?? process.env;
47
51
  this.now = options.now ?? Date.now;
52
+ this.sleep = options.sleep ?? pause;
48
53
  this.signal = options.signal;
49
54
  }
50
55
  async locked(work, signal = this.signal) {
@@ -97,7 +102,7 @@ export class AuthClient {
97
102
  refreshState: raw.refreshState,
98
103
  };
99
104
  }
100
- async tokenRequest(form, signal = this.signal) {
105
+ async tokenRequest(form, signal = this.signal, timeoutMs) {
101
106
  const issuedAt = this.now();
102
107
  return tokens(await requestJson(`${this.binding.issuer}/zo-system-api/oauth/token`, {
103
108
  method: "POST",
@@ -107,7 +112,7 @@ export class AuthClient {
107
112
  client_id: this.binding.clientId,
108
113
  resource: this.binding.audience,
109
114
  }),
110
- }, this.fetcher), issuedAt);
115
+ }, this.fetcher, timeoutMs === undefined ? {} : { timeoutMs }), issuedAt);
111
116
  }
112
117
  async accessToken() {
113
118
  throwIfInterrupted(this.signal);
@@ -150,6 +155,13 @@ export class AuthClient {
150
155
  }
151
156
  catch (error) {
152
157
  throwIfInterrupted(this.signal);
158
+ if (error instanceof RequestFailure && error.status === 429) {
159
+ // Only the rate limiter answers 429, before any exchange, so the
160
+ // refresh token is unused. Retrying now would hit the same limit.
161
+ credential.refreshState = "ready";
162
+ await writePrivate(this.path, credential);
163
+ throw new CliError("Too many sign-in requests right now. Try again shortly.", 5, "rate_limited");
164
+ }
153
165
  // Two eight-second requests fit the issuer's thirty-second recovery
154
166
  // window. Wall time also catches suspension of the monotonic clock.
155
167
  const elapsed = this.now() - startedAt;
@@ -208,6 +220,61 @@ export class AuthClient {
208
220
  throwIfInterrupted(signal);
209
221
  }
210
222
  }
223
+ /** RFC 8628 section 3.4 polling until approval, denial or expiry. */
224
+ async poll(device, signal) {
225
+ const deadline = this.now() + device.expiresInMs;
226
+ const form = {
227
+ grant_type: DEVICE_CODE_GRANT,
228
+ device_code: device.deviceCode,
229
+ };
230
+ const expired = () => new CliError("Login timed out. Run zo login to try again.", 3, "login_timeout");
231
+ let interval = device.intervalMs;
232
+ let retryNow = false;
233
+ for (;;) {
234
+ if (!retryNow)
235
+ await this.sleep(interval, signal);
236
+ throwIfInterrupted(signal);
237
+ if (this.now() >= deadline)
238
+ throw expired();
239
+ const retry = retryNow;
240
+ retryNow = false;
241
+ try {
242
+ return await this.tokenRequest(form, signal, DEVICE_POLL_TIMEOUT_MS);
243
+ }
244
+ catch (error) {
245
+ throwIfInterrupted(signal);
246
+ if (!(error instanceof RequestFailure))
247
+ throw error;
248
+ if (error.status === 400) {
249
+ if (error.code === "authorization_pending")
250
+ continue;
251
+ if (error.code === "slow_down") {
252
+ interval += 5_000;
253
+ continue;
254
+ }
255
+ if (error.code === "access_denied")
256
+ throw new CliError("Authorization was denied. Run zo login to try again.", 4, "access_denied");
257
+ if (error.code === "expired_token")
258
+ throw expired();
259
+ if (error.code === "invalid_grant")
260
+ throw new CliError("This sign-in request can no longer be completed. Run zo login again.", 3, "invalid_grant");
261
+ throw error;
262
+ }
263
+ // The rate limiter answered before the issuer saw the poll.
264
+ if (error.status === 429) {
265
+ interval += 5_000;
266
+ continue;
267
+ }
268
+ // A timeout, network error or 5xx may have lost an approval the issuer
269
+ // already redeemed. One immediate retry gets the same refresh token.
270
+ if (error.ambiguous) {
271
+ retryNow = !retry;
272
+ continue;
273
+ }
274
+ throw error;
275
+ }
276
+ }
277
+ }
211
278
  async login(options = {}) {
212
279
  const signal = this.signal && options.signal
213
280
  ? AbortSignal.any([this.signal, options.signal])
@@ -218,13 +285,18 @@ export class AuthClient {
218
285
  return this.locked(async () => {
219
286
  const previous = await this.read();
220
287
  throwIfInterrupted(signal);
221
- const code = await loginCode(this.binding.issuer, this.binding.audience, this.binding.clientId, { ...options, signal });
222
- const active = await this.tokenRequest({
223
- grant_type: "authorization_code",
224
- code: code.code,
225
- code_verifier: code.verifier,
226
- redirect_uri: code.redirectUri,
227
- }, signal);
288
+ const device = await deviceAuthorization(this.binding.issuer, this.binding.audience, this.binding.clientId, this.fetcher, signal);
289
+ const browser = !options.noBrowser &&
290
+ browserAvailable(this.env, options.platform ?? process.platform);
291
+ options.announce?.({
292
+ url: device.url,
293
+ userCode: device.userCode,
294
+ browser,
295
+ });
296
+ // The printed URL is the fallback when the opener fails.
297
+ if (browser)
298
+ void (options.openBrowser ?? openBrowser)(device.url).catch(() => { });
299
+ const active = await this.poll(device, signal);
228
300
  const credential = {
229
301
  version: 1,
230
302
  binding: this.binding,
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/generate-commands.mjs. Do not edit.
2
2
  export const commandManifest = {
3
- schemaDigest: "sha256:9810dc73ea029387f60fac79a200625e075c8a0c052d0c7312a9559537f35268",
3
+ schemaDigest: "sha256:8f3267de41a6d06b71c959bca7eae3d05a1bda6e63304a51a9bbfa315ef0af31",
4
4
  commands: [
5
5
  {
6
6
  action: "zo.activity.list",
@@ -33,8 +33,8 @@ export const commandManifest = {
33
33
  versions: [
34
34
  {
35
35
  version: 1,
36
- description: "Create an Agent in a workspace. A zo.native Agent requires the workspace to be on Metronome billing.",
37
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"},"implementation":{"const":"zo.native","title":"Implementation","type":"string"},"configuration":{"$ref":"#/$defs/NativeConfiguration"}},"required":["workspace_id","display_name","implementation","configuration"],"title":"AgentCreateInput","type":"object","$defs":{"NativeConfiguration":{"additionalProperties":false,"description":"The ``zo.native`` configuration. Unknown fields are rejected.","properties":{"model":{"description":"Model identifier the runner calls.","maxLength":200,"minLength":1,"title":"Model","type":"string"},"system_prompt":{"anyOf":[{"maxLength":50000,"type":"string"},{"type":"null"}],"default":null,"description":"The Agent\'s instructions.","title":"System Prompt"},"instructions_file":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"Knowledge file ID, such as an AGENTS.md, appended to the instructions each turn.","title":"Instructions File"},"skill_files":{"default":[],"description":"Knowledge file IDs of skills, each a Markdown file with Agent Skills frontmatter, indexed in the instructions.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":32,"title":"Skill Files","type":"array"}},"required":["model"],"title":"NativeConfiguration","type":"object"}}}',
36
+ description: "Create an Agent in a workspace. A zo.native Agent requires the workspace to be on Metronome billing. Describe what this Agent is for in the user's terms.",
37
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this Agent is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"},"implementation":{"const":"zo.native","title":"Implementation","type":"string"},"configuration":{"$ref":"#/$defs/NativeConfiguration"}},"required":["workspace_id","display_name","implementation","configuration"],"title":"AgentCreateInput","type":"object","$defs":{"NativeConfiguration":{"additionalProperties":false,"description":"The ``zo.native`` configuration. Unknown fields are rejected.","properties":{"model":{"description":"Model identifier the runner calls.","maxLength":200,"minLength":1,"title":"Model","type":"string"},"system_prompt":{"anyOf":[{"maxLength":50000,"type":"string"},{"type":"null"}],"default":null,"description":"The Agent\'s instructions.","title":"System Prompt"},"instructions_file":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"Knowledge file ID, such as an AGENTS.md, appended to the instructions each turn.","title":"Instructions File"},"skill_files":{"default":[],"description":"Knowledge file IDs of skills, each a Markdown file with Agent Skills frontmatter, indexed in the instructions.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":32,"title":"Skill Files","type":"array"}},"required":["model"],"title":"NativeConfiguration","type":"object"}}}',
38
38
  resultKind: "inline",
39
39
  },
40
40
  ],
@@ -108,7 +108,7 @@ export const commandManifest = {
108
108
  {
109
109
  version: 1,
110
110
  description: "Rename an Agent or replace its configuration. Changes apply from each session's next turn.",
111
- inputJson: '{"additionalProperties":false,"description":"Omitted fields keep their value; ``configuration`` replaces the whole configuration.","properties":{"agent_id":{"maxLength":200,"minLength":1,"title":"Agent Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":120,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"configuration":{"anyOf":[{"$ref":"#/$defs/NativeConfiguration"},{"type":"null"}],"default":null}},"required":["agent_id","expected_revision"],"title":"AgentUpdateInput","type":"object","$defs":{"NativeConfiguration":{"additionalProperties":false,"description":"The ``zo.native`` configuration. Unknown fields are rejected.","properties":{"model":{"description":"Model identifier the runner calls.","maxLength":200,"minLength":1,"title":"Model","type":"string"},"system_prompt":{"anyOf":[{"maxLength":50000,"type":"string"},{"type":"null"}],"default":null,"description":"The Agent\'s instructions.","title":"System Prompt"},"instructions_file":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"Knowledge file ID, such as an AGENTS.md, appended to the instructions each turn.","title":"Instructions File"},"skill_files":{"default":[],"description":"Knowledge file IDs of skills, each a Markdown file with Agent Skills frontmatter, indexed in the instructions.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":32,"title":"Skill Files","type":"array"}},"required":["model"],"title":"NativeConfiguration","type":"object"}}}',
111
+ inputJson: '{"additionalProperties":false,"description":"Omitted fields keep their value; ``configuration`` replaces the whole configuration.","properties":{"agent_id":{"maxLength":200,"minLength":1,"title":"Agent Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":120,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this Agent is for in the user\'s terms.","title":"Description"},"configuration":{"anyOf":[{"$ref":"#/$defs/NativeConfiguration"},{"type":"null"}],"default":null}},"required":["agent_id","expected_revision"],"title":"AgentUpdateInput","type":"object","$defs":{"NativeConfiguration":{"additionalProperties":false,"description":"The ``zo.native`` configuration. Unknown fields are rejected.","properties":{"model":{"description":"Model identifier the runner calls.","maxLength":200,"minLength":1,"title":"Model","type":"string"},"system_prompt":{"anyOf":[{"maxLength":50000,"type":"string"},{"type":"null"}],"default":null,"description":"The Agent\'s instructions.","title":"System Prompt"},"instructions_file":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"Knowledge file ID, such as an AGENTS.md, appended to the instructions each turn.","title":"Instructions File"},"skill_files":{"default":[],"description":"Knowledge file IDs of skills, each a Markdown file with Agent Skills frontmatter, indexed in the instructions.","items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":32,"title":"Skill Files","type":"array"}},"required":["model"],"title":"NativeConfiguration","type":"object"}}}',
112
112
  resultKind: "inline",
113
113
  },
114
114
  ],
@@ -182,8 +182,8 @@ export const commandManifest = {
182
182
  versions: [
183
183
  {
184
184
  version: 1,
185
- description: "Create a connection from a published definition. A protected connection is reachable only by its owner and the principals they grant.",
186
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"definition_id":{"maxLength":200,"minLength":1,"title":"Definition Id","type":"string"},"definition_version":{"exclusiveMinimum":0,"maximum":2147483647,"title":"Definition Version","type":"integer"},"display_name":{"maxLength":256,"minLength":1,"title":"Display Name","type":"string"},"configuration":{"additionalProperties":{"x-zo-json-value":true},"title":"Configuration","type":"object"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","definition_id","definition_version","display_name","configuration"],"title":"ConnectionCreateInput","type":"object"}',
185
+ description: "Create a connection from a published definition, or from named values on a private definition Zo publishes for it. A protected connection is reachable only by its owner and the principals they grant. Describe what this connection is for in the user's terms.",
186
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"definition_id":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"The published definition to connect to. Give it, or `values`, not both.","title":"Definition Id"},"definition_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"description":"The version of `definition_id` to connect to; required with it.","title":"Definition Version"},"display_name":{"maxLength":256,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this connection is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"configuration":{"anyOf":[{"additionalProperties":{"x-zo-json-value":true},"type":"object"},{"type":"null"}],"default":null,"description":"The connection\'s configuration; required with `definition_id`.","title":"Configuration"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"},"values":{"anyOf":[{"additionalProperties":{"description":"Secret: the value. Stored sealed and never returned.","maxLength":16384,"minLength":1,"type":"string"},"maxProperties":100,"minProperties":1,"type":"object"},{"type":"null"}],"default":null,"description":"Named values to store, in place of a definition: Zo publishes a private definition named after `display_name`, with one required key credential per name delivered as the environment variable of that name, creates the connection and stores every value. A definition name already taken in the workspace returns `conflict`. A name Zo refuses as an environment variable returns `invalid_input` with the `invalid_environment` violation.","title":"Values"},"environment":{"additionalProperties":{"maxLength":4096,"type":"string"},"default":{},"description":"Environment variables delivered, exactly as written, to machines that list this connection, beside its credential and the variables its definition sets. Plain strings, not secrets: put a secret in the vault. Names are upper-case letters, digits and underscores; at most 64 variables of at most 4096 characters each.","maxProperties":64,"title":"Environment","type":"object"}},"required":["workspace_id","display_name"],"title":"ConnectionCreateInput","type":"object"}',
187
187
  resultKind: "inline",
188
188
  },
189
189
  ],
@@ -249,14 +249,38 @@ export const commandManifest = {
249
249
  },
250
250
  ],
251
251
  },
252
+ {
253
+ action: "zo.connection.slack.complete_authorization",
254
+ path: ["connection", "slack", "complete-authorization"],
255
+ versions: [
256
+ {
257
+ version: 1,
258
+ description: "Exchange a Slack challenge code and store its service token. Attempts are single-use and expire after ten minutes.",
259
+ inputJson: '{"additionalProperties":false,"properties":{"connection_id":{"maxLength":200,"minLength":1,"title":"Connection Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"attempt_id":{"maxLength":200,"minLength":1,"title":"Attempt Id","type":"string"},"code":{"maxLength":256,"minLength":1,"title":"Code","type":"string"}},"required":["connection_id","expected_revision","attempt_id","code"],"title":"SlackAuthorizationCompleteInput","type":"object"}',
260
+ resultKind: "inline",
261
+ },
262
+ ],
263
+ },
264
+ {
265
+ action: "zo.connection.slack.start_authorization",
266
+ path: ["connection", "slack", "start-authorization"],
267
+ versions: [
268
+ {
269
+ version: 1,
270
+ description: "Get a Slack service-token authorization command. Requires a workspace owner and a Slack service-token connection.",
271
+ inputJson: '{"additionalProperties":false,"properties":{"connection_id":{"maxLength":200,"minLength":1,"title":"Connection Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"}},"required":["connection_id","expected_revision"],"title":"ConnectionTargetInput","type":"object"}',
272
+ resultKind: "inline",
273
+ },
274
+ ],
275
+ },
252
276
  {
253
277
  action: "zo.connection.update",
254
278
  path: ["connection", "update"],
255
279
  versions: [
256
280
  {
257
281
  version: 1,
258
- description: "Replace a connection's configuration, rename it, or both; a field left out keeps its current value.",
259
- inputJson: '{"additionalProperties":false,"properties":{"connection_id":{"maxLength":200,"minLength":1,"title":"Connection Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"configuration":{"anyOf":[{"additionalProperties":{"x-zo-json-value":true},"type":"object"},{"type":"null"}],"default":null,"description":"The configuration that replaces the stored one; omit it to keep the stored configuration, which reads never return.","title":"Configuration"},"display_name":{"anyOf":[{"maxLength":256,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"A new name for the connection; omit it to keep the current name.","title":"Display Name"}},"required":["connection_id","expected_revision"],"title":"ConnectionUpdateInput","type":"object"}',
282
+ description: "Replace a connection's configuration or environment, rename it, move it to another version of its definition, or set and remove its named values; a field left out keeps its current value.",
283
+ inputJson: '{"additionalProperties":false,"properties":{"connection_id":{"maxLength":200,"minLength":1,"title":"Connection Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"configuration":{"anyOf":[{"additionalProperties":{"x-zo-json-value":true},"type":"object"},{"type":"null"}],"default":null,"description":"The configuration that replaces the stored one; omit it to keep the stored configuration, which reads never return.","title":"Configuration"},"display_name":{"anyOf":[{"maxLength":256,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"description":"A new name for the connection; omit it to keep the current name.","title":"Display Name"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this connection is for in the user\'s terms.","title":"Description"},"definition_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"description":"Move the connection to this version of its definition, keeping its id and grants. Credentials in both versions keep their values, credentials only the current version has are erased, and credentials only the new version has start empty. Omit it to stay on the current version.","title":"Definition Version"},"values":{"anyOf":[{"additionalProperties":{"anyOf":[{"description":"Secret: the value. Stored sealed and never returned.","maxLength":16384,"minLength":1,"type":"string"},{"type":"null"}]},"maxProperties":100,"minProperties":1,"type":"object"},{"type":"null"}],"default":null,"description":"Named values to set, or `null` to remove a name, on a connection whose definition is private to its workspace and holds only single-value key credentials, as one created from `values` does. Changing a value replaces that credential. Adding or removing a name publishes the definition\'s next version from the connection\'s own version and moves the connection to it, keeping its id and grants; other connections to the definition stay on their versions. A connection keeps at least one name. Any other connection returns `invalid_input`.","title":"Values"},"environment":{"anyOf":[{"additionalProperties":{"maxLength":4096,"type":"string"},"maxProperties":64,"type":"object"},{"type":"null"}],"default":null,"description":"The environment that replaces the stored one; omit it to keep the stored environment. Environment variables delivered, exactly as written, to machines that list this connection, beside its credential and the variables its definition sets. Plain strings, not secrets: put a secret in the vault. Names are upper-case letters, digits and underscores; at most 64 variables of at most 4096 characters each.","title":"Environment"}},"required":["connection_id","expected_revision"],"title":"ConnectionUpdateInput","type":"object"}',
260
284
  resultKind: "inline",
261
285
  },
262
286
  ],
@@ -534,8 +558,8 @@ export const commandManifest = {
534
558
  versions: [
535
559
  {
536
560
  version: 1,
537
- description: "Create a workspace-owned function.",
538
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name"],"title":"FunctionCreateInput","type":"object"}',
561
+ description: "Create a workspace-owned function. Describe what this function is for in the user's terms.",
562
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this function is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name"],"title":"FunctionCreateInput","type":"object"}',
539
563
  resultKind: "inline",
540
564
  },
541
565
  ],
@@ -656,8 +680,8 @@ export const commandManifest = {
656
680
  versions: [
657
681
  {
658
682
  version: 1,
659
- description: "Create a managed machine from a machine image and return its current view.",
660
- inputJson: '{"additionalProperties":false,"properties":{"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"},"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=`.","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 today); a longer job needs 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"},"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":{"MachineLifecycleInput":{"additionalProperties":false,"properties":{"idle_timeout_seconds":{"default":900,"maximum":86400,"minimum":1,"title":"Idle Timeout Seconds","type":"integer"},"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"}},"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"}}}',
683
+ 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.",
684
+ inputJson: '{"additionalProperties":false,"properties":{"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"},"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":{"GpuType":{"enum":["T4","L4","A10","L40S","A100","A100-40GB","A100-80GB","RTX-PRO-6000","H100","H100!","H200","B200","B200+","B300"],"type":"string"},"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"}}}',
661
685
  resultKind: "inline",
662
686
  },
663
687
  ],
@@ -717,7 +741,7 @@ export const commandManifest = {
717
741
  versions: [
718
742
  {
719
743
  version: 1,
720
- description: "List independent whole-vCPU and whole-MiB ranges, regions, timeout bounds and per-second unit prices.",
744
+ description: "List independent CPU/RAM bounds, GPU allocations, runtime capabilities, timers and per-second unit prices.",
721
745
  inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"}},"required":["workspace_id"],"title":"MachineListConfigurationsInput","type":"object"}',
722
746
  resultKind: "inline",
723
747
  },
@@ -766,8 +790,8 @@ export const commandManifest = {
766
790
  versions: [
767
791
  {
768
792
  version: 1,
769
- description: "Save a running managed machine's filesystem as a ready image. Destination options live under image. Image visibility defaults to the source; choose workspace or protected explicitly to override it. Finish filesystem writes first. Processes and credentials are not rebound from the source. User-written secrets remain in captured files. Never automatically retry an uncertain result.",
770
- inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"image":{"$ref":"#/$defs/MachineImageCreateOptions"},"timeout_seconds":{"default":60,"description":"Maximum seconds for filesystem capture, including provider lookup. The remaining request deadline can shorten this budget.","maximum":180,"minimum":1,"title":"Timeout Seconds","type":"integer"}},"required":["machine_id","expected_revision","image"],"title":"MachineSnapshotInput","type":"object","$defs":{"MachineImageCreateOptions":{"additionalProperties":false,"properties":{"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"visibility":{"anyOf":[{"enum":["workspace","protected"],"type":"string"},{"type":"null"}],"default":null,"description":"Image visibility. Omit or pass null to use the creating operation\'s default. Protected images use ordinary verified-human creator ownership.","title":"Visibility"}},"required":["display_name"],"title":"MachineImageCreateOptions","type":"object"}}}',
793
+ description: "Save a running managed machine's filesystem as a ready image. Destination options live under image. Image visibility defaults to the source; choose workspace or protected explicitly to override it. Finish filesystem writes first. Processes and credentials are not rebound from the source. User-written secrets remain in captured files. Never automatically retry an uncertain result. Describe what this machine image is for in the user's terms.",
794
+ inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"image":{"$ref":"#/$defs/MachineImageCreateOptions"},"timeout_seconds":{"default":60,"description":"Maximum seconds for filesystem capture, including provider lookup. The remaining request deadline can shorten this budget.","maximum":180,"minimum":1,"title":"Timeout Seconds","type":"integer"}},"required":["machine_id","expected_revision","image"],"title":"MachineSnapshotInput","type":"object","$defs":{"MachineImageCreateOptions":{"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":{"anyOf":[{"enum":["workspace","protected"],"type":"string"},{"type":"null"}],"default":null,"description":"Image visibility. Omit or pass null to use the creating operation\'s default. Protected images use ordinary verified-human creator ownership.","title":"Visibility"}},"required":["display_name"],"title":"MachineImageCreateOptions","type":"object"}}}',
771
795
  resultKind: "inline",
772
796
  },
773
797
  ],
@@ -803,7 +827,7 @@ export const commandManifest = {
803
827
  {
804
828
  version: 1,
805
829
  description: "Change a managed machine's name, timers or next-boot settings against its current revision.",
806
- inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":120,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"lifecycle":{"anyOf":[{"$ref":"#/$defs/MachineLifecycleUpdate"},{"type":"null"}],"default":null},"init":{"anyOf":[{"enum":["none","image"],"type":"string"},{"type":"null"}],"default":null,"description":"Which process runs first at boot. `none` (default): nothing; the machine waits for exec. `image`: the image\'s init system (systemd, OpenRC) becomes PID 1 and starts enabled services. `source.machine_image_id` chooses the image. Debian and Ubuntu need `systemd systemd-sysv`. Setting `image` needs exec permission; approval-gated exec is denied. Write access to boot files can run code as root at next boot.","title":"Init"},"command":{"anyOf":[{"items":{"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Set the main process for the next resume. Arguments run 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`, wait for needed services. The machine stops when the command 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 today); a longer job needs Zo exec or file calls, or HTTP through a tunnel or connect token. After exit, `get` may show `running` for a few minutes. 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`. Changing the command needs exec permission; approval-gated exec is denied. Write access to its script can run code as root at next boot.","title":"Command"},"clear_command":{"default":false,"description":"Remove the main process on the next resume. Requires manage permission only.","title":"Clear Command","type":"boolean"}},"required":["machine_id","expected_revision"],"title":"MachineUpdateInput","type":"object","$defs":{"MachineLifecycleUpdate":{"additionalProperties":false,"description":"The timers a caller may change after create. Modal fixes them at\\nsandbox create, so a change takes effect at the next resume.","properties":{"idle_timeout_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Idle Timeout Seconds"},"max_run_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Max Run Seconds"}},"title":"MachineLifecycleUpdate","type":"object","required":[]}}}',
830
+ inputJson: '{"additionalProperties":false,"properties":{"machine_id":{"title":"Machine Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":120,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this machine is for in the user\'s terms.","title":"Description"},"lifecycle":{"anyOf":[{"$ref":"#/$defs/MachineLifecycleUpdate"},{"type":"null"}],"default":null},"init":{"anyOf":[{"enum":["none","image"],"type":"string"},{"type":"null"}],"default":null,"description":"Which process runs first at boot. `none` (default): nothing; the machine waits for exec. `image`: the image\'s init system (systemd, OpenRC) becomes PID 1 and starts enabled services. `source.machine_image_id` chooses the image. Debian and Ubuntu need `systemd systemd-sysv`. Write access to boot files can run code as root at next boot.","title":"Init"},"command":{"anyOf":[{"items":{"minLength":1,"type":"string"},"maxItems":64,"minItems":1,"type":"array"},{"type":"null"}],"default":null,"description":"Set the main process for the next resume. Arguments run 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`, wait for needed services. The machine stops when the command 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. 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`. Write access to its script can run code as root at next boot.","title":"Command"},"clear_command":{"default":false,"description":"Remove the main process on the next resume.","title":"Clear Command","type":"boolean"},"environment":{"anyOf":[{"additionalProperties":{"maxLength":4096,"type":"string"},"maxProperties":64,"type":"object"},{"type":"null"}],"default":null,"description":"The environment that replaces the stored one; omit it to keep it. 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.","title":"Environment"},"credentials":{"anyOf":[{"items":{"maxLength":200,"minLength":1,"type":"string"},"maxItems":100,"type":"array"},{"type":"null"}],"default":null,"description":"The list that replaces the stored one; omit it to keep it. 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.","title":"Credentials"}},"required":["machine_id","expected_revision"],"title":"MachineUpdateInput","type":"object","$defs":{"MachineLifecycleUpdate":{"additionalProperties":false,"description":"The timers a caller may change after create. Modal fixes them at\\nsandbox create, so a change takes effect at the next resume.","properties":{"idle_timeout_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Idle Timeout Seconds"},"max_run_seconds":{"anyOf":[{"maximum":86400,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Max Run Seconds"}},"title":"MachineLifecycleUpdate","type":"object","required":[]}}}',
807
831
  resultKind: "inline",
808
832
  },
809
833
  ],
@@ -850,8 +874,8 @@ export const commandManifest = {
850
874
  versions: [
851
875
  {
852
876
  version: 1,
853
- description: "Import a registry image, pinned to its manifest digest, and return the image view.",
854
- inputJson: '{"additionalProperties":false,"properties":{"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","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"}}}',
877
+ 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.",
878
+ 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"}}}',
855
879
  resultKind: "inline",
856
880
  },
857
881
  ],
@@ -948,8 +972,8 @@ export const commandManifest = {
948
972
  versions: [
949
973
  {
950
974
  version: 1,
951
- description: "Create a schedule.",
952
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"rrule":{"maxLength":1000,"minLength":1,"title":"Rrule","type":"string"},"start_at":{"title":"Start At","type":"string"},"timezone":{"title":"Timezone","type":"string"},"data":{"additionalProperties":{"x-zo-json-value":true},"default":{},"title":"Data","type":"object"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name","rrule","start_at","timezone"],"title":"ScheduleCreateInput","type":"object"}',
975
+ description: "Create a schedule. Describe what this schedule is for in the user's terms.",
976
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this schedule is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"rrule":{"maxLength":1000,"minLength":1,"title":"Rrule","type":"string"},"start_at":{"title":"Start At","type":"string"},"timezone":{"title":"Timezone","type":"string"},"data":{"additionalProperties":{"x-zo-json-value":true},"default":{},"title":"Data","type":"object"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name","rrule","start_at","timezone"],"title":"ScheduleCreateInput","type":"object"}',
953
977
  resultKind: "inline",
954
978
  },
955
979
  ],
@@ -998,7 +1022,7 @@ export const commandManifest = {
998
1022
  {
999
1023
  version: 1,
1000
1024
  description: "Update a schedule.",
1001
- inputJson: '{"additionalProperties":false,"properties":{"schedule_id":{"maxLength":200,"minLength":1,"title":"Schedule Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"rrule":{"maxLength":1000,"minLength":1,"title":"Rrule","type":"string"},"start_at":{"title":"Start At","type":"string"},"timezone":{"title":"Timezone","type":"string"},"data":{"additionalProperties":{"x-zo-json-value":true},"title":"Data","type":"object"},"lifecycle":{"enum":["active","paused"],"title":"Lifecycle","type":"string"}},"required":["schedule_id","expected_revision","display_name","rrule","start_at","timezone","data","lifecycle"],"title":"ScheduleUpdateInput","type":"object"}',
1025
+ inputJson: '{"additionalProperties":false,"properties":{"schedule_id":{"maxLength":200,"minLength":1,"title":"Schedule Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this schedule is for in the user\'s terms.","title":"Description"},"rrule":{"maxLength":1000,"minLength":1,"title":"Rrule","type":"string"},"start_at":{"title":"Start At","type":"string"},"timezone":{"title":"Timezone","type":"string"},"data":{"additionalProperties":{"x-zo-json-value":true},"title":"Data","type":"object"},"lifecycle":{"enum":["active","paused"],"title":"Lifecycle","type":"string"}},"required":["schedule_id","expected_revision","display_name","rrule","start_at","timezone","data","lifecycle"],"title":"ScheduleUpdateInput","type":"object"}',
1002
1026
  resultKind: "inline",
1003
1027
  },
1004
1028
  ],
@@ -1051,8 +1075,8 @@ export const commandManifest = {
1051
1075
  },
1052
1076
  {
1053
1077
  version: 2,
1054
- description: "Bind an event definition to a function entrypoint or a workflow's start run as a non-human execution principal, or to an Agent whose sessions receive each occurrence.",
1055
- inputJson: '{"additionalProperties":false,"description":"Bind an event definition to a handler. ``handler_resource_type`` is\\n``function``, ``workflow`` or ``agent``. A Function target names\\n``handler_resource_id``, ``handler_entrypoint``, ``input_contract_version``,\\n``input_contract_kind`` and the ``execution_principal_id`` service account it\\nruns as. A workflow target names ``workflow_id`` and the same fields but\\n``handler_resource_id``: ``handler_entrypoint`` is ``start``, which begins a\\nnew run per occurrence with the envelope as its ``event`` input, and\\n``input_contract_version`` is 1, the contract of that input. An Agent\\ntarget names ``agent_id``, the ``agent_priority`` an occurrence enters a turn\\nwith (``queue``, ``steer`` or ``interrupt``), the ``agent_session_policy``\\nthat picks its session (``new``; ``fixed``, with ``agent_session_id``; or\\n``per_conversation``, for an event with a ``conversation_path``, keyed in\\n``agent_session_scope``, the event\'s ``<publisher>/<name>`` when omitted)\\nand optional ``agent_instructions`` shown with every occurrence. Fields of\\nthe other handler type are refused. Omit ``filter`` to receive every\\noccurrence.","properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"event_id":{"title":"Event Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"filter":{"anyOf":[{"discriminator":{"mapping":{"all":"#/$defs/All1","any":"#/$defs/Any1","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All1"},{"$ref":"#/$defs/Any1"}]},{"type":"null"}],"default":null,"title":"Filter"},"handler_resource_type":{"enum":["function","workflow","agent"],"title":"Handler Resource Type","type":"string"},"handler_resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Handler Resource Id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workflow Id"},"handler_entrypoint":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Handler Entrypoint"},"input_contract_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"title":"Input Contract Version"},"input_contract_kind":{"anyOf":[{"$ref":"#/$defs/InputContractKind"},{"type":"null"}],"default":null},"execution_principal_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Execution Principal Id"},"agent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Id"},"agent_priority":{"default":"queue","enum":["queue","steer","interrupt"],"title":"Agent Priority","type":"string"},"agent_session_policy":{"default":"new","enum":["new","fixed","per_conversation"],"title":"Agent Session Policy","type":"string"},"agent_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Id"},"agent_session_scope":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Scope"},"agent_instructions":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Instructions"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","event_id","display_name","handler_resource_type"],"title":"SubscriptionCreateInputV2","type":"object","$defs":{"All1":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All1","type":"object"},"All2":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All2","type":"object"},"All3":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All3","type":"object"},"Any1":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any1","type":"object"},"Any2":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any2","type":"object"},"Any3":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any3","type":"object"},"EqFilter":{"additionalProperties":false,"properties":{"op":{"const":"eq","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"value":{"x-zo-json-value":true}},"required":["op","path","value"],"title":"EqFilter","type":"object"},"ExistsFilter":{"additionalProperties":false,"properties":{"op":{"const":"exists","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"}},"required":["op","path"],"title":"ExistsFilter","type":"object"},"InFilter":{"additionalProperties":false,"properties":{"op":{"const":"in","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"values":{"items":{"x-zo-json-value":true},"maxItems":32,"minItems":1,"title":"Values","type":"array"}},"required":["op","path","values"],"title":"InFilter","type":"object"},"InputContractKind":{"enum":["exact_event","generic_envelope"],"type":"string"}}}',
1078
+ description: "Bind an event definition to a function entrypoint or a workflow's start run as a non-human execution principal, or to an Agent whose sessions receive each occurrence. Describe what this subscription is for in the user's terms.",
1079
+ inputJson: '{"additionalProperties":false,"description":"Bind an event definition to a handler. ``handler_resource_type`` is\\n``function``, ``workflow`` or ``agent``. A Function target names\\n``handler_resource_id``, ``handler_entrypoint``, ``input_contract_version``,\\n``input_contract_kind`` and the ``execution_principal_id`` service account it\\nruns as. A workflow target names ``workflow_id`` and the same fields but\\n``handler_resource_id``: ``handler_entrypoint`` is ``start``, which begins a\\nnew run per occurrence with the envelope as its ``event`` input, and\\n``input_contract_version`` is 1, the contract of that input. An Agent\\ntarget names ``agent_id``, the ``agent_priority`` an occurrence enters a turn\\nwith (``queue``, ``steer`` or ``interrupt``), the ``agent_session_policy``\\nthat picks its session (``new``; ``fixed``, with ``agent_session_id``; or\\n``per_conversation``, for an event with a ``conversation_path``, keyed in\\n``agent_session_scope``, the event\'s ``<publisher>/<name>`` when omitted)\\nand optional ``agent_instructions`` shown with every occurrence. Fields of\\nthe other handler type are refused. Omit ``filter`` to receive every\\noccurrence.","properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"event_id":{"title":"Event Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this subscription is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"filter":{"anyOf":[{"discriminator":{"mapping":{"all":"#/$defs/All1","any":"#/$defs/Any1","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All1"},{"$ref":"#/$defs/Any1"}]},{"type":"null"}],"default":null,"title":"Filter"},"handler_resource_type":{"enum":["function","workflow","agent"],"title":"Handler Resource Type","type":"string"},"handler_resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Handler Resource Id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workflow Id"},"handler_entrypoint":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Handler Entrypoint"},"input_contract_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"title":"Input Contract Version"},"input_contract_kind":{"anyOf":[{"$ref":"#/$defs/InputContractKind"},{"type":"null"}],"default":null},"execution_principal_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Execution Principal Id"},"agent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Id"},"agent_priority":{"default":"queue","enum":["queue","steer","interrupt"],"title":"Agent Priority","type":"string"},"agent_session_policy":{"default":"new","enum":["new","fixed","per_conversation"],"title":"Agent Session Policy","type":"string"},"agent_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Id"},"agent_session_scope":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Scope"},"agent_instructions":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Instructions"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","event_id","display_name","handler_resource_type"],"title":"SubscriptionCreateInputV2","type":"object","$defs":{"All1":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All1","type":"object"},"All2":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All2","type":"object"},"All3":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All3","type":"object"},"Any1":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any1","type":"object"},"Any2":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any2","type":"object"},"Any3":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any3","type":"object"},"EqFilter":{"additionalProperties":false,"properties":{"op":{"const":"eq","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"value":{"x-zo-json-value":true}},"required":["op","path","value"],"title":"EqFilter","type":"object"},"ExistsFilter":{"additionalProperties":false,"properties":{"op":{"const":"exists","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"}},"required":["op","path"],"title":"ExistsFilter","type":"object"},"InFilter":{"additionalProperties":false,"properties":{"op":{"const":"in","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"values":{"items":{"x-zo-json-value":true},"maxItems":32,"minItems":1,"title":"Values","type":"array"}},"required":["op","path","values"],"title":"InFilter","type":"object"},"InputContractKind":{"enum":["exact_event","generic_envelope"],"type":"string"}}}',
1056
1080
  resultKind: "inline",
1057
1081
  },
1058
1082
  ],
@@ -1120,7 +1144,7 @@ export const commandManifest = {
1120
1144
  {
1121
1145
  version: 2,
1122
1146
  description: "Replace a subscription's configuration and lifecycle at the expected revision.",
1123
- inputJson: '{"additionalProperties":false,"description":"Full replacement of every create field but the workspace and the event (a new\\ndefinition or version is a new subscription), plus ``lifecycle``: ``active``\\nresumes a paused or suspended subscription, ``paused`` pauses it;\\n``zo.subscription.delete`` retires. ``handler_resource_type`` must be the\\nsubscription\'s own: a different handler type is a new subscription.","properties":{"subscription_id":{"title":"Subscription Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"filter":{"anyOf":[{"discriminator":{"mapping":{"all":"#/$defs/All1","any":"#/$defs/Any1","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All1"},{"$ref":"#/$defs/Any1"}]},{"type":"null"}],"default":null,"title":"Filter"},"handler_resource_type":{"enum":["function","workflow","agent"],"title":"Handler Resource Type","type":"string"},"handler_resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Handler Resource Id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workflow Id"},"handler_entrypoint":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Handler Entrypoint"},"input_contract_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"title":"Input Contract Version"},"input_contract_kind":{"anyOf":[{"$ref":"#/$defs/InputContractKind"},{"type":"null"}],"default":null},"execution_principal_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Execution Principal Id"},"agent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Id"},"agent_priority":{"default":"queue","enum":["queue","steer","interrupt"],"title":"Agent Priority","type":"string"},"agent_session_policy":{"default":"new","enum":["new","fixed","per_conversation"],"title":"Agent Session Policy","type":"string"},"agent_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Id"},"agent_session_scope":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Scope"},"agent_instructions":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Instructions"},"lifecycle":{"enum":["active","paused"],"title":"Lifecycle","type":"string"}},"required":["subscription_id","expected_revision","display_name","handler_resource_type","lifecycle"],"title":"SubscriptionUpdateInputV2","type":"object","$defs":{"All1":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All1","type":"object"},"All2":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All2","type":"object"},"All3":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All3","type":"object"},"Any1":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any1","type":"object"},"Any2":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any2","type":"object"},"Any3":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any3","type":"object"},"EqFilter":{"additionalProperties":false,"properties":{"op":{"const":"eq","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"value":{"x-zo-json-value":true}},"required":["op","path","value"],"title":"EqFilter","type":"object"},"ExistsFilter":{"additionalProperties":false,"properties":{"op":{"const":"exists","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"}},"required":["op","path"],"title":"ExistsFilter","type":"object"},"InFilter":{"additionalProperties":false,"properties":{"op":{"const":"in","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"values":{"items":{"x-zo-json-value":true},"maxItems":32,"minItems":1,"title":"Values","type":"array"}},"required":["op","path","values"],"title":"InFilter","type":"object"},"InputContractKind":{"enum":["exact_event","generic_envelope"],"type":"string"}}}',
1147
+ inputJson: '{"additionalProperties":false,"description":"Replace create fields except workspace, event and optional description.\\nA new event definition or version requires a new subscription.\\nOmitted or null description keeps its value. ``lifecycle``: ``active``\\nresumes a paused or suspended subscription, ``paused`` pauses it;\\n``zo.subscription.delete`` retires. ``handler_resource_type`` must be the\\nsubscription\'s own: a different handler type is a new subscription.","properties":{"subscription_id":{"title":"Subscription Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this subscription is for in the user\'s terms.","title":"Description"},"filter":{"anyOf":[{"discriminator":{"mapping":{"all":"#/$defs/All1","any":"#/$defs/Any1","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All1"},{"$ref":"#/$defs/Any1"}]},{"type":"null"}],"default":null,"title":"Filter"},"handler_resource_type":{"enum":["function","workflow","agent"],"title":"Handler Resource Type","type":"string"},"handler_resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Handler Resource Id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Workflow Id"},"handler_entrypoint":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Handler Entrypoint"},"input_contract_version":{"anyOf":[{"exclusiveMinimum":0,"maximum":2147483647,"type":"integer"},{"type":"null"}],"default":null,"title":"Input Contract Version"},"input_contract_kind":{"anyOf":[{"$ref":"#/$defs/InputContractKind"},{"type":"null"}],"default":null},"execution_principal_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Execution Principal Id"},"agent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Id"},"agent_priority":{"default":"queue","enum":["queue","steer","interrupt"],"title":"Agent Priority","type":"string"},"agent_session_policy":{"default":"new","enum":["new","fixed","per_conversation"],"title":"Agent Session Policy","type":"string"},"agent_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Id"},"agent_session_scope":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Session Scope"},"agent_instructions":{"anyOf":[{"maxLength":4096,"type":"string"},{"type":"null"}],"default":null,"title":"Agent Instructions"},"lifecycle":{"enum":["active","paused"],"title":"Lifecycle","type":"string"}},"required":["subscription_id","expected_revision","display_name","handler_resource_type","lifecycle"],"title":"SubscriptionUpdateInputV2","type":"object","$defs":{"All1":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All1","type":"object"},"All2":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All2","type":"object"},"All3":{"additionalProperties":false,"properties":{"op":{"const":"all","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"All3","type":"object"},"Any1":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All2","any":"#/$defs/Any2","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All2"},{"$ref":"#/$defs/Any2"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any1","type":"object"},"Any2":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"all":"#/$defs/All3","any":"#/$defs/Any3","eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"},{"$ref":"#/$defs/All3"},{"$ref":"#/$defs/Any3"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any2","type":"object"},"Any3":{"additionalProperties":false,"properties":{"op":{"const":"any","title":"Op","type":"string"},"filters":{"items":{"discriminator":{"mapping":{"eq":"#/$defs/EqFilter","exists":"#/$defs/ExistsFilter","in":"#/$defs/InFilter"},"propertyName":"op"},"oneOf":[{"$ref":"#/$defs/EqFilter"},{"$ref":"#/$defs/InFilter"},{"$ref":"#/$defs/ExistsFilter"}]},"maxItems":32,"minItems":1,"title":"Filters","type":"array"}},"required":["op","filters"],"title":"Any3","type":"object"},"EqFilter":{"additionalProperties":false,"properties":{"op":{"const":"eq","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"value":{"x-zo-json-value":true}},"required":["op","path","value"],"title":"EqFilter","type":"object"},"ExistsFilter":{"additionalProperties":false,"properties":{"op":{"const":"exists","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"}},"required":["op","path"],"title":"ExistsFilter","type":"object"},"InFilter":{"additionalProperties":false,"properties":{"op":{"const":"in","title":"Op","type":"string"},"path":{"maxLength":256,"minLength":1,"pattern":"^(?:/[^/]*)+$","title":"Path","type":"string"},"values":{"items":{"x-zo-json-value":true},"maxItems":32,"minItems":1,"title":"Values","type":"array"}},"required":["op","path","values"],"title":"InFilter","type":"object"},"InputContractKind":{"enum":["exact_event","generic_envelope"],"type":"string"}}}',
1124
1148
  resultKind: "inline",
1125
1149
  },
1126
1150
  ],
@@ -1424,8 +1448,8 @@ export const commandManifest = {
1424
1448
  versions: [
1425
1449
  {
1426
1450
  version: 1,
1427
- description: "Create a Website address in a workspace.",
1428
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"slug":{"pattern":"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$","title":"Slug","type":"string"},"display_name":{"maxLength":200,"minLength":1,"title":"Display Name","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","slug","display_name"],"title":"WebsiteCreateInput","type":"object"}',
1451
+ description: "Create a Website address in a workspace. Describe what this Website is for in the user's terms.",
1452
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"maxLength":200,"minLength":1,"title":"Workspace Id","type":"string"},"slug":{"pattern":"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$","title":"Slug","type":"string"},"display_name":{"maxLength":200,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this Website is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","slug","display_name"],"title":"WebsiteCreateInput","type":"object"}',
1429
1453
  resultKind: "inline",
1430
1454
  },
1431
1455
  ],
@@ -1474,7 +1498,7 @@ export const commandManifest = {
1474
1498
  {
1475
1499
  version: 1,
1476
1500
  description: "Update a Website display name or custom hostname.",
1477
- inputJson: '{"additionalProperties":false,"properties":{"website_id":{"maxLength":200,"minLength":1,"title":"Website Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"custom_hostname":{"anyOf":[{"$ref":"#/$defs/CustomHostnameChange"},{"type":"null"}],"default":null}},"required":["website_id","expected_revision"],"title":"WebsiteUpdateInput","type":"object","$defs":{"CustomHostnameChange":{"additionalProperties":false,"properties":{"hostname":{"anyOf":[{"maxLength":253,"minLength":4,"type":"string"},{"type":"null"}],"title":"Hostname"}},"required":["hostname"],"title":"CustomHostnameChange","type":"object"}}}',
1501
+ inputJson: '{"additionalProperties":false,"properties":{"website_id":{"maxLength":200,"minLength":1,"title":"Website Id","type":"string"},"expected_revision":{"exclusiveMinimum":0,"maximum":9007199254740991,"title":"Expected Revision","type":"integer"},"display_name":{"anyOf":[{"maxLength":200,"minLength":1,"type":"string"},{"type":"null"}],"default":null,"title":"Display Name"},"description":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"default":null,"description":"Describe what this Website is for in the user\'s terms.","title":"Description"},"custom_hostname":{"anyOf":[{"$ref":"#/$defs/CustomHostnameChange"},{"type":"null"}],"default":null}},"required":["website_id","expected_revision"],"title":"WebsiteUpdateInput","type":"object","$defs":{"CustomHostnameChange":{"additionalProperties":false,"properties":{"hostname":{"anyOf":[{"maxLength":253,"minLength":4,"type":"string"},{"type":"null"}],"title":"Hostname"}},"required":["hostname"],"title":"CustomHostnameChange","type":"object"}}}',
1478
1502
  resultKind: "inline",
1479
1503
  },
1480
1504
  ],
@@ -1594,8 +1618,8 @@ export const commandManifest = {
1594
1618
  versions: [
1595
1619
  {
1596
1620
  version: 1,
1597
- description: "Create a workflow in a workspace.",
1598
- inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name"],"title":"WorkflowCreateInput","type":"object"}',
1621
+ description: "Create a workflow in a workspace. Describe what this workflow is for in the user's terms.",
1622
+ inputJson: '{"additionalProperties":false,"properties":{"workspace_id":{"title":"Workspace Id","type":"string"},"display_name":{"maxLength":120,"minLength":1,"title":"Display Name","type":"string"},"description":{"default":"","description":"Describe what this workflow is for in the user\'s terms.","maxLength":2000,"title":"Description","type":"string"},"visibility":{"default":"workspace","enum":["workspace","protected"],"title":"Visibility","type":"string"}},"required":["workspace_id","display_name"],"title":"WorkflowCreateInput","type":"object"}',
1599
1623
  resultKind: "inline",
1600
1624
  },
1601
1625
  ],
package/dist/http.d.ts CHANGED
@@ -3,7 +3,11 @@ import { CliError } from "./errors.ts";
3
3
  export declare function systemApiFailure(response: Response, raw: string | Uint8Array, operation: string): SystemAPIError;
4
4
  export declare class RequestFailure extends CliError {
5
5
  readonly ambiguous: boolean;
6
- constructor(ambiguous: boolean, code?: string, exitCode?: number);
6
+ /** The HTTP status when the server answered; absent for transport failures. */
7
+ readonly status?: number | undefined;
8
+ constructor(ambiguous: boolean, code?: string, exitCode?: number,
9
+ /** The HTTP status when the server answered; absent for transport failures. */
10
+ status?: number | undefined);
7
11
  }
8
12
  export declare function requestJson(url: string, init?: RequestInit, fetcher?: typeof fetch, options?: {
9
13
  timeoutMs?: number;
package/dist/http.js CHANGED
@@ -46,11 +46,15 @@ export function systemApiFailure(response, raw, operation) {
46
46
  }
47
47
  export class RequestFailure extends CliError {
48
48
  ambiguous;
49
- constructor(ambiguous, code = "transport_error", exitCode = 5) {
49
+ status;
50
+ constructor(ambiguous, code = "transport_error", exitCode = 5,
51
+ /** The HTTP status when the server answered; absent for transport failures. */
52
+ status) {
50
53
  super(code === "invalid_grant"
51
54
  ? "Your sign-in ended. Run zo login."
52
55
  : "The authentication request failed. Try again or run zo login.", exitCode, code);
53
56
  this.ambiguous = ambiguous;
57
+ this.status = status;
54
58
  }
55
59
  }
56
60
  export async function requestJson(url, init = {}, fetcher = fetch, options = {}) {
@@ -106,6 +110,10 @@ export async function requestJson(url, init = {}, fetcher = fetch, options = {})
106
110
  body = JSON.parse(raw);
107
111
  }
108
112
  catch {
113
+ // Cloudflare's edge rate limits answer 429 with a non-JSON page before the
114
+ // worker runs; the status alone says the request was not processed.
115
+ if (response.status === 429)
116
+ throw new RequestFailure(true, "temporarily_unavailable", 5, 429);
109
117
  throw new RequestFailure(false, "invalid_response");
110
118
  }
111
119
  if (!response.ok) {
@@ -118,7 +126,7 @@ export async function requestJson(url, init = {}, fetcher = fetch, options = {})
118
126
  ? 3
119
127
  : response.status === 403
120
128
  ? 4
121
- : 5);
129
+ : 5, response.status);
122
130
  }
123
131
  return body;
124
132
  }
package/dist/login.d.ts CHANGED
@@ -1,14 +1,30 @@
1
+ export declare const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
2
+ export interface LoginPrompt {
3
+ url: string;
4
+ userCode: string;
5
+ /** False when the CLI won't open a browser: --no-browser, SSH or no display. */
6
+ browser: boolean;
7
+ }
1
8
  export interface LoginOptions {
2
9
  noBrowser?: boolean;
3
- timeoutMs?: number;
4
10
  signal?: AbortSignal;
5
- announce?: (url: string) => void;
11
+ platform?: NodeJS.Platform;
12
+ announce?: (prompt: LoginPrompt) => void;
6
13
  openBrowser?: (url: string) => Promise<void>;
7
14
  }
8
- export interface LoginCode {
9
- code: string;
10
- verifier: string;
11
- redirectUri: string;
15
+ export interface DeviceAuthorization {
16
+ deviceCode: string;
17
+ userCode: string;
18
+ url: string;
19
+ expiresInMs: number;
20
+ intervalMs: number;
12
21
  }
13
22
  export declare function openBrowser(url: string): Promise<void>;
14
- export declare function loginCode(issuer: string, audience: string, clientId: string, options?: LoginOptions): Promise<LoginCode>;
23
+ /**
24
+ * A browser opened over SSH, or with no display, lands on the wrong machine or
25
+ * nowhere. xdg-open needs a display on every system other than macOS.
26
+ */
27
+ export declare function browserAvailable(env: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
28
+ export declare function pause(ms: number, signal?: AbortSignal | null): Promise<void>;
29
+ export declare function parseDeviceAuthorization(raw: unknown, issuer: string): DeviceAuthorization;
30
+ export declare function deviceAuthorization(issuer: string, audience: string, clientId: string, fetcher: typeof fetch, signal?: AbortSignal): Promise<DeviceAuthorization>;
package/dist/login.js CHANGED
@@ -1,101 +1,93 @@
1
- import { createHash, randomBytes } from "node:crypto";
2
- import { createServer } from "node:http";
3
1
  import { spawn } from "node:child_process";
4
- import { CliError } from "./errors.js";
2
+ import { CliError, record, throwIfInterrupted } from "./errors.js";
3
+ import { requestJson, RequestFailure } from "./http.js";
4
+ export const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
5
5
  export async function openBrowser(url) {
6
6
  await new Promise((resolve, reject) => {
7
7
  const child = spawn(process.platform === "darwin" ? "open" : "xdg-open", [url], { stdio: "ignore", detached: true });
8
8
  child.unref();
9
- child.once("error", () => reject(new CliError("Could not open a browser. Retry with --no-browser.")));
10
- child.once("exit", (status) => status === 0
11
- ? resolve()
12
- : reject(new CliError("Could not open a browser. Retry with --no-browser.")));
9
+ child.once("error", reject);
10
+ child.once("exit", (status) => status === 0 ? resolve() : reject(new Error("Browser opener failed.")));
13
11
  });
14
12
  }
15
- export async function loginCode(issuer, audience, clientId, options = {}) {
16
- const state = randomBytes(32).toString("base64url");
17
- const verifier = randomBytes(32).toString("base64url");
18
- let complete = () => { };
19
- let fail = () => { };
20
- const received = new Promise((resolve, reject) => {
21
- complete = resolve;
22
- fail = reject;
23
- });
24
- // The callback may arrive while the browser opener is still running.
25
- void received.catch(() => { });
26
- let accepted = false;
27
- const server = createServer((request, response) => {
28
- let url;
29
- try {
30
- url = new URL(request.url ?? "/", "http://127.0.0.1");
31
- }
32
- catch {
33
- response.writeHead(400).end("Invalid callback.");
34
- return;
35
- }
36
- const params = url.searchParams;
37
- const keys = [...params.keys()];
38
- const valid = !accepted &&
39
- request.method === "GET" &&
40
- url.pathname === "/callback" &&
41
- keys.length === new Set(keys).size &&
42
- params.get("state") === state &&
43
- params.get("iss") === issuer &&
44
- params.has("code") !== params.has("error");
45
- response.setHeader("Content-Type", "text/plain; charset=utf-8");
46
- response.setHeader("Cache-Control", "no-store");
47
- if (!valid) {
48
- response.writeHead(400).end("Invalid callback.");
49
- return;
50
- }
51
- accepted = true;
52
- response.end("You may close this window and return to Zo.");
53
- if (params.has("error"))
54
- fail(new CliError("Authorization was denied. Run zo login to try again.", 4, "access_denied"));
55
- else {
56
- const code = params.get("code");
57
- if (code)
58
- complete(code);
59
- else
60
- fail(new CliError("Authorization returned an empty code.", 3, "invalid_response"));
61
- }
62
- });
63
- server.requestTimeout = 5_000;
64
- server.headersTimeout = 5_000;
65
- await new Promise((resolve, reject) => {
66
- server.once("error", reject);
67
- server.listen(0, "127.0.0.1", resolve);
13
+ /**
14
+ * A browser opened over SSH, or with no display, lands on the wrong machine or
15
+ * nowhere. xdg-open needs a display on every system other than macOS.
16
+ */
17
+ export function browserAvailable(env, platform = process.platform) {
18
+ if (env.SSH_CONNECTION || env.SSH_TTY)
19
+ return false;
20
+ return platform === "darwin" || !!env.DISPLAY || !!env.WAYLAND_DISPLAY;
21
+ }
22
+ export function pause(ms, signal) {
23
+ throwIfInterrupted(signal);
24
+ return new Promise((resolve, reject) => {
25
+ const abort = () => {
26
+ clearTimeout(timer);
27
+ reject(new CliError("Interrupted.", 130, "interrupted"));
28
+ };
29
+ const timer = setTimeout(() => {
30
+ signal?.removeEventListener("abort", abort);
31
+ resolve();
32
+ }, ms);
33
+ signal?.addEventListener("abort", abort, { once: true });
68
34
  });
69
- const address = server.address();
70
- if (!address || typeof address === "string")
71
- throw new CliError("Could not bind the login callback.");
72
- const redirectUri = `http://127.0.0.1:${address.port}/callback`;
73
- const authorize = new URL("/zo-system-api/oauth/authorize", issuer);
74
- authorize.search = new URLSearchParams({
75
- response_type: "code",
76
- client_id: clientId,
77
- redirect_uri: redirectUri,
78
- resource: audience,
79
- code_challenge: createHash("sha256").update(verifier).digest("base64url"),
80
- code_challenge_method: "S256",
81
- state,
82
- }).toString();
83
- const timer = setTimeout(() => fail(new CliError("Login timed out. Run zo login to try again.", 3, "login_timeout")), options.timeoutMs ?? 300_000);
84
- const abort = () => fail(new CliError("Login interrupted.", 130, "interrupted"));
85
- options.signal?.addEventListener("abort", abort, { once: true });
35
+ }
36
+ function seconds(value, maximum) {
37
+ return typeof value === "number" &&
38
+ Number.isSafeInteger(value) &&
39
+ value > 0 &&
40
+ value <= maximum
41
+ ? value
42
+ : undefined;
43
+ }
44
+ export function parseDeviceAuthorization(raw, issuer) {
45
+ const invalid = () => new CliError("The sign-in service returned an invalid response.", 5, "invalid_response");
46
+ if (!record(raw) ||
47
+ typeof raw.device_code !== "string" ||
48
+ !/^[A-Za-z0-9._~-]{1,512}$/.test(raw.device_code) ||
49
+ typeof raw.user_code !== "string" ||
50
+ !/^[A-Z0-9]{1,8}(?:-[A-Z0-9]{1,8}){0,3}$/.test(raw.user_code) ||
51
+ typeof raw.verification_uri_complete !== "string")
52
+ throw invalid();
53
+ const expiresIn = seconds(raw.expires_in, 3_600);
54
+ const interval = raw.interval === undefined ? 2 : seconds(raw.interval, 60);
55
+ if (expiresIn === undefined || interval === undefined)
56
+ throw invalid();
57
+ let url;
58
+ try {
59
+ url = new URL(raw.verification_uri_complete);
60
+ }
61
+ catch {
62
+ throw invalid();
63
+ }
64
+ // The URL goes to the system opener; only the issuer's own scheme is safe.
65
+ // Production is https; the local test issuer is http.
66
+ if (url.protocol !== new URL(issuer).protocol || url.username || url.password)
67
+ throw invalid();
68
+ return {
69
+ deviceCode: raw.device_code,
70
+ userCode: raw.user_code,
71
+ url: url.href,
72
+ expiresInMs: expiresIn * 1000,
73
+ intervalMs: interval * 1000,
74
+ };
75
+ }
76
+ export async function deviceAuthorization(issuer, audience, clientId, fetcher, signal) {
77
+ let raw;
86
78
  try {
87
- if (options.signal?.aborted)
88
- abort();
89
- else if (options.noBrowser)
90
- options.announce?.(authorize.href);
91
- else
92
- void (options.openBrowser ?? openBrowser)(authorize.href).catch(fail);
93
- return { code: await received, verifier, redirectUri };
79
+ raw = await requestJson(`${issuer}/zo-system-api/oauth/device_authorization`, {
80
+ method: "POST",
81
+ signal,
82
+ body: new URLSearchParams({ client_id: clientId, resource: audience }),
83
+ }, fetcher);
94
84
  }
95
- finally {
96
- clearTimeout(timer);
97
- options.signal?.removeEventListener("abort", abort);
98
- server.closeAllConnections();
99
- await new Promise((resolve) => server.close(() => resolve()));
85
+ catch (error) {
86
+ if (error instanceof RequestFailure && error.status === 429)
87
+ throw new CliError("Too many sign-in attempts from this network. Wait a minute, then run zo login again.", 5, "rate_limited");
88
+ if (error instanceof RequestFailure && error.code === "unauthorized_client")
89
+ throw new CliError("This environment does not allow Zo CLI sign-in yet.", 5, "unauthorized_client");
90
+ throw error;
100
91
  }
92
+ return parseDeviceAuthorization(raw, issuer);
101
93
  }
package/dist/main.js CHANGED
@@ -26,7 +26,7 @@ Use --profile to keep separate sign-ins. New profiles use production.
26
26
  ZO_SYSTEM_API_ACCESS_TOKEN overrides saved credentials; ZO_WORKSPACE_ID selects a workspace.
27
27
  `;
28
28
  const localHelp = {
29
- login: "Usage: zo login [--profile <name>] [--workspace <id>] [--no-browser]\nNew profiles use production. Browser consent approves workspaces and identities.\n--no-browser prints the URL and still uses a localhost callback.\nInternal testing: --environment staging explicitly binds a separate profile to staging.\n",
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
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
31
  auth: "Usage: zo auth status | token [--headers]\nStatus shows the current identity; token explicitly prints an access token.\n",
32
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",
@@ -41,6 +41,16 @@ const localHelp = {
41
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
42
  completion: "Usage: zo completion bash|zsh|fish\nPrint completion from installed metadata without credentials or network requests.\n",
43
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
+ }
44
54
  async function showHelp(args, stdout) {
45
55
  if (!args.length) {
46
56
  stdout.write(help);
@@ -121,12 +131,17 @@ export async function run(argv = process.argv.slice(2), runtime = {}) {
121
131
  const login = await auth.login({
122
132
  noBrowser: parsed.flags.has("no-browser"),
123
133
  signal,
124
- announce: (url) => stderr.write(`Open this URL to sign in:\n${url}\n`),
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`),
125
137
  });
126
- stderr.write(`Sign-in saved for profile ${profile.name}.\n`);
138
+ stderr.write(`\nApproved in the browser. Sign-in saved for profile ${profile.name}.\n`);
127
139
  if (login.cleanupPending)
128
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");
129
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`));
130
145
  const desired = options.workspace ?? env.ZO_WORKSPACE_ID ?? profile.workspaceId;
131
146
  let chosen;
132
147
  if (options.workspace !== undefined)
@@ -145,7 +160,6 @@ export async function run(argv = process.argv.slice(2), runtime = {}) {
145
160
  process.stdin.isTTY &&
146
161
  stderr.isTTY &&
147
162
  session.workspaces.length > 0) {
148
- session.workspaces.forEach((workspace, index) => stderr.write(`${index + 1}. ${safeText(workspace.name)} (${workspace.workspace_id})\n`));
149
163
  const input = createInterface({
150
164
  input: process.stdin,
151
165
  output: process.stderr,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zocomputer/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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.0"
17
+ "@zocomputer/system-api": "0.1.2"
18
18
  },
19
19
  "publishConfig": {
20
20
  "access": "public",