@vm0/cli 9.79.3 → 9.81.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/zero.js CHANGED
@@ -1,19 +1,2921 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- agentCommand,
3
+ ApiRequestError,
4
+ CONNECTOR_TYPES,
5
+ MODEL_PROVIDER_TYPES,
6
+ allowsCustomModel,
4
7
  configureGlobalProxyFromEnv,
5
- zeroConnectorCommand,
6
- zeroOrgCommand,
7
- zeroPreferenceCommand,
8
- zeroScheduleCommand,
9
- zeroSecretCommand,
10
- zeroVariableCommand
11
- } from "./chunk-MKJBEFRH.js";
8
+ connectorTypeSchema,
9
+ createZeroAgent,
10
+ createZeroComputerConnector,
11
+ createZeroConnectorSession,
12
+ decodeZeroTokenPayload,
13
+ deleteZeroAgent,
14
+ deleteZeroComputerConnector,
15
+ deleteZeroConnector,
16
+ deleteZeroOrg,
17
+ deleteZeroOrgModelProvider,
18
+ deleteZeroOrgSecret,
19
+ deleteZeroOrgVariable,
20
+ deleteZeroSchedule,
21
+ deleteZeroSecret,
22
+ deleteZeroVariable,
23
+ deployZeroSchedule,
24
+ disableZeroSchedule,
25
+ enableZeroSchedule,
26
+ getActiveOrg,
27
+ getApiUrl,
28
+ getAuthMethodsForType,
29
+ getBaseUrl,
30
+ getComposeByName,
31
+ getConnectorDerivedNames,
32
+ getCustomModelPlaceholder,
33
+ getDefaultAuthMethod,
34
+ getDefaultModel,
35
+ getModels,
36
+ getScopeDiff,
37
+ getSecretsForAuthMethod,
38
+ getSelectableProviderTypes,
39
+ getToken,
40
+ getZeroAgent,
41
+ getZeroAgentInstructions,
42
+ getZeroConnector,
43
+ getZeroConnectorSession,
44
+ getZeroOrg,
45
+ getZeroOrgMembers,
46
+ getZeroUserPreferences,
47
+ hasAuthMethods,
48
+ hasModelSelection,
49
+ hasRequiredScopes,
50
+ inviteZeroOrgMember,
51
+ isFeatureEnabled,
52
+ isInteractive,
53
+ leaveZeroOrg,
54
+ listZeroAgents,
55
+ listZeroConnectors,
56
+ listZeroOrgModelProviders,
57
+ listZeroOrgSecrets,
58
+ listZeroOrgVariables,
59
+ listZeroOrgs,
60
+ listZeroSchedules,
61
+ listZeroSecrets,
62
+ listZeroVariables,
63
+ promptConfirm,
64
+ promptPassword,
65
+ promptSelect,
66
+ promptText,
67
+ removeZeroOrgMember,
68
+ resolveZeroScheduleByAgent,
69
+ saveConfig,
70
+ setZeroOrgModelProviderDefault,
71
+ setZeroOrgSecret,
72
+ setZeroOrgVariable,
73
+ setZeroSecret,
74
+ setZeroVariable,
75
+ updateZeroAgent,
76
+ updateZeroAgentInstructions,
77
+ updateZeroOrg,
78
+ updateZeroOrgModelProviderModel,
79
+ updateZeroUserPreferences,
80
+ upsertZeroOrgModelProvider,
81
+ withErrorHandler
82
+ } from "./chunk-3EOIDO3I.js";
12
83
 
13
84
  // src/zero.ts
85
+ import { Command as Command52 } from "commander";
86
+
87
+ // src/commands/zero/org/index.ts
88
+ import { Command as Command23 } from "commander";
89
+
90
+ // src/commands/zero/org/status.ts
14
91
  import { Command } from "commander";
15
- var program = new Command();
16
- program.name("zero").description("Zero CLI - Manage your zero platform").version("9.79.3");
92
+ import chalk from "chalk";
93
+ var statusCommand = new Command().name("status").description("View current organization status").action(
94
+ withErrorHandler(async () => {
95
+ try {
96
+ const org = await getZeroOrg();
97
+ console.log(chalk.bold("Organization Information:"));
98
+ console.log(` Slug: ${chalk.green(org.slug)}`);
99
+ } catch (error) {
100
+ if (error instanceof ApiRequestError && error.status === 404) {
101
+ throw new Error("No organization configured", {
102
+ cause: new Error("Set your organization with: zero org set <slug>")
103
+ });
104
+ }
105
+ throw error;
106
+ }
107
+ })
108
+ );
109
+
110
+ // src/commands/zero/org/set.ts
111
+ import { Command as Command2 } from "commander";
112
+ import chalk2 from "chalk";
113
+ var setCommand = new Command2().name("set").description("Rename your organization slug").argument("<slug>", "The new organization slug").option(
114
+ "--force",
115
+ "Force change existing organization (may break references)"
116
+ ).action(
117
+ withErrorHandler(async (slug, options) => {
118
+ try {
119
+ const existingOrg = await getZeroOrg();
120
+ if (!options.force) {
121
+ throw new Error(
122
+ `You already have an organization: ${existingOrg.slug}`,
123
+ {
124
+ cause: new Error(`To change, use: zero org set ${slug} --force`)
125
+ }
126
+ );
127
+ }
128
+ const org = await updateZeroOrg({ slug, force: true });
129
+ await saveConfig({ activeOrg: org.slug });
130
+ console.log(chalk2.green(`\u2713 Organization updated to ${org.slug}`));
131
+ console.log();
132
+ console.log("Your agents will now be namespaced as:");
133
+ console.log(chalk2.cyan(` ${org.slug}/<agent-name>`));
134
+ } catch (error) {
135
+ if (error instanceof Error && error.message.includes("already exists")) {
136
+ throw new Error(
137
+ `Organization "${slug}" is already taken. Please choose a different slug.`
138
+ );
139
+ }
140
+ throw error;
141
+ }
142
+ })
143
+ );
144
+
145
+ // src/commands/zero/org/list.ts
146
+ import { Command as Command3 } from "commander";
147
+ import chalk3 from "chalk";
148
+ var listCommand = new Command3().name("list").description("List all accessible organizations").action(
149
+ withErrorHandler(async () => {
150
+ const result = await listZeroOrgs();
151
+ const activeOrg = await getActiveOrg();
152
+ console.log(chalk3.bold("Available organizations:"));
153
+ for (const org of result.orgs) {
154
+ const isCurrent = org.slug === activeOrg;
155
+ const marker = isCurrent ? chalk3.green("* ") : " ";
156
+ const roleLabel = org.role ? ` (${org.role})` : "";
157
+ const currentLabel = isCurrent ? chalk3.dim(" \u2190 current") : "";
158
+ console.log(`${marker}${org.slug}${roleLabel}${currentLabel}`);
159
+ }
160
+ })
161
+ );
162
+
163
+ // src/commands/zero/org/use.ts
164
+ import { Command as Command4 } from "commander";
165
+ import chalk4 from "chalk";
166
+ var useCommand = new Command4().name("use").description("Switch to a different organization").argument("<slug>", "Organization slug to switch to").action(
167
+ withErrorHandler(async (slug) => {
168
+ const orgList = await listZeroOrgs();
169
+ const target = orgList.orgs.find((s) => s.slug === slug);
170
+ if (!target) {
171
+ throw new Error(`Organization '${slug}' not found or not accessible.`);
172
+ }
173
+ await saveConfig({ activeOrg: slug });
174
+ console.log(chalk4.green(`\u2713 Switched to organization: ${slug}`));
175
+ })
176
+ );
177
+
178
+ // src/commands/zero/org/members.ts
179
+ import { Command as Command5 } from "commander";
180
+ import chalk5 from "chalk";
181
+ var membersCommand = new Command5().name("members").description("View organization members").action(
182
+ withErrorHandler(async () => {
183
+ const status = await getZeroOrgMembers();
184
+ console.log(chalk5.bold(`Organization: ${status.slug}`));
185
+ console.log(` Role: ${status.role}`);
186
+ console.log(
187
+ ` Created: ${new Date(status.createdAt).toLocaleDateString()}`
188
+ );
189
+ console.log();
190
+ console.log(chalk5.bold("Members:"));
191
+ for (const member of status.members) {
192
+ const roleTag = member.role === "admin" ? chalk5.yellow(` (${member.role})`) : chalk5.dim(` (${member.role})`);
193
+ console.log(` ${member.email}${roleTag}`);
194
+ }
195
+ })
196
+ );
197
+
198
+ // src/commands/zero/org/invite.ts
199
+ import { Command as Command6 } from "commander";
200
+ import chalk6 from "chalk";
201
+ var inviteCommand = new Command6().name("invite").description("Invite a member to the current organization").requiredOption("--email <email>", "Email address of the member to invite").action(
202
+ withErrorHandler(async (options) => {
203
+ await inviteZeroOrgMember(options.email);
204
+ console.log(chalk6.green(`\u2713 Invitation sent to ${options.email}`));
205
+ })
206
+ );
207
+
208
+ // src/commands/zero/org/remove.ts
209
+ import { Command as Command7 } from "commander";
210
+ import chalk7 from "chalk";
211
+ var removeCommand = new Command7().name("remove").description("Remove a member from the current organization").argument("<email>", "Email address of the member to remove").action(
212
+ withErrorHandler(async (email) => {
213
+ await removeZeroOrgMember(email);
214
+ console.log(chalk7.green(`\u2713 Removed ${email} from organization`));
215
+ })
216
+ );
217
+
218
+ // src/commands/zero/org/leave.ts
219
+ import { Command as Command8 } from "commander";
220
+ import chalk8 from "chalk";
221
+ var leaveCommand = new Command8().name("leave").description("Leave the current organization").action(
222
+ withErrorHandler(async () => {
223
+ await leaveZeroOrg();
224
+ const { orgs } = await listZeroOrgs();
225
+ if (orgs.length === 0) {
226
+ await saveConfig({ activeOrg: void 0 });
227
+ console.log(chalk8.green("\u2713 Left organization."));
228
+ console.log(
229
+ chalk8.yellow("No remaining organizations. Run: vm0 auth login")
230
+ );
231
+ return;
232
+ }
233
+ const nextOrg = orgs[0].slug;
234
+ await saveConfig({ activeOrg: nextOrg });
235
+ console.log(chalk8.green(`\u2713 Left organization. Switched to: ${nextOrg}`));
236
+ })
237
+ );
238
+
239
+ // src/commands/zero/org/delete.ts
240
+ import { Command as Command9 } from "commander";
241
+ import chalk9 from "chalk";
242
+ var deleteCommand = new Command9().name("delete").description("Delete the current organization (admin only)").argument("<slug>", "Organization slug to confirm deletion").action(
243
+ withErrorHandler(async (slug) => {
244
+ await deleteZeroOrg(slug);
245
+ console.log(chalk9.green(`\u2713 Organization '${slug}' has been deleted.`));
246
+ })
247
+ );
248
+
249
+ // src/commands/zero/org/secret/index.ts
250
+ import { Command as Command13 } from "commander";
251
+
252
+ // src/commands/zero/org/secret/list.ts
253
+ import { Command as Command10 } from "commander";
254
+ import chalk10 from "chalk";
255
+ var listCommand2 = new Command10().name("list").alias("ls").description("List all org-level secrets").action(
256
+ withErrorHandler(async () => {
257
+ const result = await listZeroOrgSecrets();
258
+ if (result.secrets.length === 0) {
259
+ console.log(chalk10.dim("No org secrets found"));
260
+ console.log();
261
+ console.log("To add an org secret:");
262
+ console.log(
263
+ chalk10.cyan(" zero org secret set MY_API_KEY --body <value>")
264
+ );
265
+ return;
266
+ }
267
+ console.log(chalk10.bold("Org Secrets:"));
268
+ console.log();
269
+ for (const secret of result.secrets) {
270
+ console.log(` ${chalk10.cyan(secret.name)}`);
271
+ if (secret.description) {
272
+ console.log(` ${chalk10.dim(secret.description)}`);
273
+ }
274
+ console.log(
275
+ ` ${chalk10.dim(`Updated: ${new Date(secret.updatedAt).toLocaleString()}`)}`
276
+ );
277
+ console.log();
278
+ }
279
+ console.log(chalk10.dim(`Total: ${result.secrets.length} secret(s)`));
280
+ })
281
+ );
282
+
283
+ // src/commands/zero/org/secret/set.ts
284
+ import { Command as Command11 } from "commander";
285
+ import chalk11 from "chalk";
286
+ var setCommand2 = new Command11().name("set").description("Create or update an org-level secret (admin only)").argument("<name>", "Secret name (uppercase, e.g., MY_API_KEY)").option(
287
+ "-b, --body <value>",
288
+ "Secret value (required in non-interactive mode)"
289
+ ).option("-d, --description <description>", "Optional description").action(
290
+ withErrorHandler(
291
+ async (name, options) => {
292
+ let value;
293
+ if (options.body !== void 0) {
294
+ value = options.body;
295
+ } else if (isInteractive()) {
296
+ const prompted = await promptPassword("Enter secret value:");
297
+ if (prompted === void 0) {
298
+ process.exit(0);
299
+ }
300
+ value = prompted;
301
+ } else {
302
+ throw new Error("--body is required in non-interactive mode", {
303
+ cause: new Error(
304
+ `Usage: zero org secret set ${name} --body "your-secret-value"`
305
+ )
306
+ });
307
+ }
308
+ let secret;
309
+ try {
310
+ secret = await setZeroOrgSecret({
311
+ name,
312
+ value,
313
+ description: options.description
314
+ });
315
+ } catch (error) {
316
+ if (error instanceof Error && error.message.includes("must contain only uppercase")) {
317
+ throw new Error(error.message, {
318
+ cause: new Error(
319
+ "Examples of valid secret names: MY_API_KEY, GITHUB_TOKEN, AWS_ACCESS_KEY_ID"
320
+ )
321
+ });
322
+ }
323
+ throw error;
324
+ }
325
+ console.log(chalk11.green(`\u2713 Org secret "${secret.name}" saved`));
326
+ }
327
+ )
328
+ );
329
+
330
+ // src/commands/zero/org/secret/remove.ts
331
+ import { Command as Command12 } from "commander";
332
+ import chalk12 from "chalk";
333
+ var removeCommand2 = new Command12().name("remove").description("Delete an org-level secret (admin only)").argument("<name>", "Secret name to delete").option("-y, --yes", "Skip confirmation prompt").action(
334
+ withErrorHandler(async (name, options) => {
335
+ if (!options.yes) {
336
+ if (!isInteractive()) {
337
+ throw new Error("--yes flag is required in non-interactive mode");
338
+ }
339
+ const confirmed = await promptConfirm(
340
+ `Are you sure you want to delete org secret "${name}"?`,
341
+ false
342
+ );
343
+ if (!confirmed) {
344
+ console.log(chalk12.dim("Cancelled"));
345
+ return;
346
+ }
347
+ }
348
+ await deleteZeroOrgSecret(name);
349
+ console.log(chalk12.green(`\u2713 Org secret "${name}" deleted`));
350
+ })
351
+ );
352
+
353
+ // src/commands/zero/org/secret/index.ts
354
+ var zeroOrgSecretCommand = new Command13().name("secret").description("Manage org-level secrets (admin)").addCommand(listCommand2).addCommand(setCommand2).addCommand(removeCommand2);
355
+
356
+ // src/commands/zero/org/variable/index.ts
357
+ import { Command as Command17 } from "commander";
358
+
359
+ // src/commands/zero/org/variable/list.ts
360
+ import { Command as Command14 } from "commander";
361
+ import chalk13 from "chalk";
362
+ function truncateValue(value, maxLength = 60) {
363
+ if (value.length <= maxLength) {
364
+ return value;
365
+ }
366
+ return value.slice(0, maxLength - 15) + "... [truncated]";
367
+ }
368
+ var listCommand3 = new Command14().name("list").alias("ls").description("List all org-level variables").action(
369
+ withErrorHandler(async () => {
370
+ const result = await listZeroOrgVariables();
371
+ if (result.variables.length === 0) {
372
+ console.log(chalk13.dim("No org variables found"));
373
+ console.log();
374
+ console.log("To add an org variable:");
375
+ console.log(chalk13.cyan(" zero org variable set MY_VAR <value>"));
376
+ return;
377
+ }
378
+ console.log(chalk13.bold("Org Variables:"));
379
+ console.log();
380
+ for (const variable of result.variables) {
381
+ const displayValue = truncateValue(variable.value);
382
+ console.log(` ${chalk13.cyan(variable.name)} = ${displayValue}`);
383
+ if (variable.description) {
384
+ console.log(` ${chalk13.dim(variable.description)}`);
385
+ }
386
+ console.log(
387
+ ` ${chalk13.dim(`Updated: ${new Date(variable.updatedAt).toLocaleString()}`)}`
388
+ );
389
+ console.log();
390
+ }
391
+ console.log(chalk13.dim(`Total: ${result.variables.length} variable(s)`));
392
+ })
393
+ );
394
+
395
+ // src/commands/zero/org/variable/set.ts
396
+ import { Command as Command15 } from "commander";
397
+ import chalk14 from "chalk";
398
+ var setCommand3 = new Command15().name("set").description("Create or update an org-level variable (admin only)").argument("<name>", "Variable name (uppercase, e.g., MY_VAR)").argument("<value>", "Variable value").option("-d, --description <description>", "Optional description").action(
399
+ withErrorHandler(
400
+ async (name, value, options) => {
401
+ let variable;
402
+ try {
403
+ variable = await setZeroOrgVariable({
404
+ name,
405
+ value,
406
+ description: options.description
407
+ });
408
+ } catch (error) {
409
+ if (error instanceof Error && error.message.includes("must contain only uppercase")) {
410
+ throw new Error(error.message, {
411
+ cause: new Error(
412
+ "Examples of valid variable names: MY_VAR, API_URL, DEBUG_MODE"
413
+ )
414
+ });
415
+ }
416
+ throw error;
417
+ }
418
+ console.log(chalk14.green(`\u2713 Org variable "${variable.name}" saved`));
419
+ }
420
+ )
421
+ );
422
+
423
+ // src/commands/zero/org/variable/remove.ts
424
+ import { Command as Command16 } from "commander";
425
+ import chalk15 from "chalk";
426
+ var removeCommand3 = new Command16().name("remove").description("Delete an org-level variable (admin only)").argument("<name>", "Variable name to delete").option("-y, --yes", "Skip confirmation prompt").action(
427
+ withErrorHandler(async (name, options) => {
428
+ if (!options.yes) {
429
+ if (!isInteractive()) {
430
+ throw new Error("--yes flag is required in non-interactive mode");
431
+ }
432
+ const confirmed = await promptConfirm(
433
+ `Are you sure you want to delete org variable "${name}"?`,
434
+ false
435
+ );
436
+ if (!confirmed) {
437
+ console.log(chalk15.dim("Cancelled"));
438
+ return;
439
+ }
440
+ }
441
+ await deleteZeroOrgVariable(name);
442
+ console.log(chalk15.green(`\u2713 Org variable "${name}" deleted`));
443
+ })
444
+ );
445
+
446
+ // src/commands/zero/org/variable/index.ts
447
+ var zeroOrgVariableCommand = new Command17().name("variable").description("Manage org-level variables (admin)").addCommand(listCommand3).addCommand(setCommand3).addCommand(removeCommand3);
448
+
449
+ // src/commands/zero/org/model-provider/index.ts
450
+ import { Command as Command22 } from "commander";
451
+
452
+ // src/commands/zero/org/model-provider/list.ts
453
+ import { Command as Command18 } from "commander";
454
+ import chalk16 from "chalk";
455
+ var listCommand4 = new Command18().name("list").alias("ls").description("List all org-level model providers").action(
456
+ withErrorHandler(async () => {
457
+ const result = await listZeroOrgModelProviders();
458
+ if (result.modelProviders.length === 0) {
459
+ console.log(chalk16.dim("No org-level model providers configured"));
460
+ console.log();
461
+ console.log("To add an org-level model provider:");
462
+ console.log(chalk16.cyan(" zero org model-provider setup"));
463
+ return;
464
+ }
465
+ const byFramework = result.modelProviders.reduce(
466
+ (acc, p) => {
467
+ const fw = p.framework;
468
+ if (!acc[fw]) {
469
+ acc[fw] = [];
470
+ }
471
+ acc[fw].push(p);
472
+ return acc;
473
+ },
474
+ {}
475
+ );
476
+ console.log(chalk16.bold("Org Model Providers:"));
477
+ console.log();
478
+ for (const [framework, providers] of Object.entries(byFramework)) {
479
+ console.log(` ${chalk16.cyan(framework)}:`);
480
+ for (const provider of providers) {
481
+ const defaultTag = provider.isDefault ? chalk16.green(" (default)") : "";
482
+ const modelTag = provider.selectedModel ? chalk16.dim(` [${provider.selectedModel}]`) : "";
483
+ console.log(` ${provider.type}${defaultTag}${modelTag}`);
484
+ console.log(
485
+ chalk16.dim(
486
+ ` Updated: ${new Date(provider.updatedAt).toLocaleString()}`
487
+ )
488
+ );
489
+ }
490
+ console.log();
491
+ }
492
+ console.log(
493
+ chalk16.dim(`Total: ${result.modelProviders.length} provider(s)`)
494
+ );
495
+ })
496
+ );
497
+
498
+ // src/commands/zero/org/model-provider/setup.ts
499
+ import { Command as Command19 } from "commander";
500
+ import chalk18 from "chalk";
501
+ import prompts2 from "prompts";
502
+
503
+ // src/lib/domain/model-provider/shared.ts
504
+ import chalk17 from "chalk";
505
+ import prompts from "prompts";
506
+ function validateProviderType(typeStr) {
507
+ if (!Object.keys(MODEL_PROVIDER_TYPES).includes(typeStr)) {
508
+ const validTypes = Object.keys(MODEL_PROVIDER_TYPES).join(", ");
509
+ throw new Error(`Invalid type "${typeStr}"`, {
510
+ cause: new Error(`Valid types: ${validTypes}`)
511
+ });
512
+ }
513
+ return typeStr;
514
+ }
515
+ function validateModel(type, modelStr) {
516
+ const models = getModels(type);
517
+ if (allowsCustomModel(type)) {
518
+ return modelStr;
519
+ }
520
+ if (models && !models.includes(modelStr)) {
521
+ throw new Error(`Invalid model "${modelStr}"`, {
522
+ cause: new Error(`Valid models: ${models.join(", ")}`)
523
+ });
524
+ }
525
+ return modelStr;
526
+ }
527
+ function validateAuthMethod(type, authMethodStr) {
528
+ const authMethods = getAuthMethodsForType(type);
529
+ if (!authMethods || !(authMethodStr in authMethods)) {
530
+ const validMethods = authMethods ? Object.keys(authMethods).join(", ") : "none";
531
+ throw new Error(`Invalid auth method "${authMethodStr}"`, {
532
+ cause: new Error(`Valid auth methods: ${validMethods}`)
533
+ });
534
+ }
535
+ return authMethodStr;
536
+ }
537
+ function parseSecrets(type, authMethod, secretArgs) {
538
+ const secretsConfig = getSecretsForAuthMethod(type, authMethod);
539
+ if (!secretsConfig) {
540
+ throw new Error(`Invalid auth method "${authMethod}"`);
541
+ }
542
+ const secretNames = Object.keys(secretsConfig);
543
+ const firstArg = secretArgs[0];
544
+ if (secretArgs.length === 1 && firstArg && !firstArg.includes("=")) {
545
+ if (secretNames.length !== 1) {
546
+ throw new Error(
547
+ "Must use KEY=VALUE format for multi-secret auth methods",
548
+ { cause: new Error(`Required secrets: ${secretNames.join(", ")}`) }
549
+ );
550
+ }
551
+ const firstSecretName = secretNames[0];
552
+ if (!firstSecretName) {
553
+ throw new Error("No secrets defined for this auth method");
554
+ }
555
+ return { [firstSecretName]: firstArg };
556
+ }
557
+ const secrets = {};
558
+ for (const arg of secretArgs) {
559
+ const eqIndex = arg.indexOf("=");
560
+ if (eqIndex === -1) {
561
+ throw new Error(`Invalid secret format "${arg}"`, {
562
+ cause: new Error("Use KEY=VALUE format (e.g., AWS_REGION=us-east-1)")
563
+ });
564
+ }
565
+ const key = arg.slice(0, eqIndex);
566
+ const value = arg.slice(eqIndex + 1);
567
+ secrets[key] = value;
568
+ }
569
+ return secrets;
570
+ }
571
+ function validateSecrets(type, authMethod, secrets) {
572
+ const secretsConfig = getSecretsForAuthMethod(type, authMethod);
573
+ if (!secretsConfig) {
574
+ throw new Error(`Invalid auth method "${authMethod}"`);
575
+ }
576
+ for (const [name, fieldConfig] of Object.entries(secretsConfig)) {
577
+ if (fieldConfig.required && !secrets[name]) {
578
+ const requiredNames = Object.entries(secretsConfig).filter(([, fc]) => fc.required).map(([n]) => n).join(", ");
579
+ throw new Error(`Missing required secret: ${name}`, {
580
+ cause: new Error(`Required secrets: ${requiredNames}`)
581
+ });
582
+ }
583
+ }
584
+ for (const name of Object.keys(secrets)) {
585
+ if (!(name in secretsConfig)) {
586
+ const validNames = Object.keys(secretsConfig).join(", ");
587
+ throw new Error(`Unknown secret: ${name}`, {
588
+ cause: new Error(`Valid secrets: ${validNames}`)
589
+ });
590
+ }
591
+ }
592
+ }
593
+ function handleNonInteractiveMode(options) {
594
+ const type = validateProviderType(options.type);
595
+ const cmdPrefix = options.commandPrefix ?? "zero org model-provider setup";
596
+ let selectedModel;
597
+ if (options.model) {
598
+ selectedModel = validateModel(type, options.model);
599
+ } else if (hasModelSelection(type)) {
600
+ const defaultModel = getDefaultModel(type);
601
+ selectedModel = defaultModel || void 0;
602
+ }
603
+ if (hasAuthMethods(type)) {
604
+ let authMethod;
605
+ if (options.authMethod) {
606
+ authMethod = validateAuthMethod(type, options.authMethod);
607
+ } else {
608
+ const defaultAuthMethod = getDefaultAuthMethod(type);
609
+ const authMethods = getAuthMethodsForType(type);
610
+ if (!defaultAuthMethod || !authMethods) {
611
+ throw new Error(`Provider "${type}" requires --auth-method`);
612
+ }
613
+ const authMethodNames = Object.keys(authMethods);
614
+ if (authMethodNames.length === 1) {
615
+ authMethod = authMethodNames[0];
616
+ } else {
617
+ const methods = authMethodNames.join(", ");
618
+ throw new Error(
619
+ `--auth-method is required for "${type}" (multiple auth methods available)`,
620
+ {
621
+ cause: new Error(
622
+ `Available: ${methods}. Example: ${cmdPrefix} --type ${type} --auth-method ${authMethodNames[0]} --secret KEY=VALUE`
623
+ )
624
+ }
625
+ );
626
+ }
627
+ }
628
+ const secrets = parseSecrets(type, authMethod, options.secret);
629
+ validateSecrets(type, authMethod, secrets);
630
+ return {
631
+ type,
632
+ authMethod,
633
+ secrets,
634
+ selectedModel,
635
+ isInteractiveMode: false
636
+ };
637
+ }
638
+ const secretArgs = options.secret;
639
+ const firstArg = secretArgs[0];
640
+ if (!firstArg) {
641
+ throw new Error("Secret is required");
642
+ }
643
+ let secret;
644
+ if (firstArg.includes("=")) {
645
+ secret = firstArg.slice(firstArg.indexOf("=") + 1);
646
+ } else {
647
+ secret = firstArg;
648
+ }
649
+ return {
650
+ type,
651
+ secret,
652
+ selectedModel,
653
+ isInteractiveMode: false
654
+ };
655
+ }
656
+ async function promptForModelSelection(type) {
657
+ if (!hasModelSelection(type)) {
658
+ return void 0;
659
+ }
660
+ const models = getModels(type) ?? [];
661
+ const defaultModel = getDefaultModel(type);
662
+ const supportsCustomModel = allowsCustomModel(type);
663
+ const modelChoices = [];
664
+ if (defaultModel === "") {
665
+ modelChoices.push({ title: "auto (Recommended)", value: "" });
666
+ }
667
+ for (const model of models) {
668
+ modelChoices.push({
669
+ title: model === defaultModel ? `${model} (Recommended)` : model,
670
+ value: model
671
+ });
672
+ }
673
+ if (supportsCustomModel) {
674
+ modelChoices.push({ title: "Custom model ID", value: "__custom__" });
675
+ }
676
+ const modelResponse = await prompts(
677
+ {
678
+ type: "select",
679
+ name: "model",
680
+ message: "Select model:",
681
+ choices: modelChoices
682
+ },
683
+ { onCancel: () => process.exit(0) }
684
+ );
685
+ const selected = modelResponse.model;
686
+ if (selected === "__custom__") {
687
+ const placeholder = getCustomModelPlaceholder(type);
688
+ if (placeholder) {
689
+ console.log(chalk17.dim(`Example: ${placeholder}`));
690
+ }
691
+ const customResponse = await prompts(
692
+ {
693
+ type: "text",
694
+ name: "customModel",
695
+ message: "Enter model ID:",
696
+ validate: (value) => value.length > 0 || "Model ID is required"
697
+ },
698
+ { onCancel: () => process.exit(0) }
699
+ );
700
+ return customResponse.customModel;
701
+ }
702
+ return selected === "" ? void 0 : selected;
703
+ }
704
+ async function promptForAuthMethod(type) {
705
+ const authMethods = getAuthMethodsForType(type);
706
+ const defaultAuthMethod = getDefaultAuthMethod(type);
707
+ if (!authMethods) {
708
+ return "default";
709
+ }
710
+ const choices = Object.entries(authMethods).map(([method, config]) => ({
711
+ title: method === defaultAuthMethod ? `${config.label} (Recommended)` : config.label,
712
+ value: method
713
+ }));
714
+ const response = await prompts(
715
+ {
716
+ type: "select",
717
+ name: "authMethod",
718
+ message: "Select authentication method:",
719
+ choices
720
+ },
721
+ { onCancel: () => process.exit(0) }
722
+ );
723
+ return response.authMethod;
724
+ }
725
+ function isSensitiveSecret(name) {
726
+ const nonSecretPatterns = ["REGION", "ENDPOINT", "URL"];
727
+ return !nonSecretPatterns.some(
728
+ (pattern) => name.toUpperCase().includes(pattern)
729
+ );
730
+ }
731
+ async function promptForSecrets(type, authMethod) {
732
+ const secretsConfig = getSecretsForAuthMethod(type, authMethod);
733
+ if (!secretsConfig) {
734
+ throw new Error(`Invalid auth method "${authMethod}"`);
735
+ }
736
+ const secrets = {};
737
+ for (const [name, fieldConfig] of Object.entries(secretsConfig)) {
738
+ if (fieldConfig.helpText) {
739
+ console.log(chalk17.dim(fieldConfig.helpText));
740
+ }
741
+ const isSensitive = isSensitiveSecret(name);
742
+ const placeholder = "placeholder" in fieldConfig ? fieldConfig.placeholder : "";
743
+ if (fieldConfig.required) {
744
+ const response = await prompts(
745
+ {
746
+ type: isSensitive ? "password" : "text",
747
+ name: "value",
748
+ message: `${fieldConfig.label}:`,
749
+ initial: placeholder ? "" : void 0,
750
+ validate: (value) => value.length > 0 || `${fieldConfig.label} is required`
751
+ },
752
+ { onCancel: () => process.exit(0) }
753
+ );
754
+ secrets[name] = response.value;
755
+ } else {
756
+ const response = await prompts(
757
+ {
758
+ type: isSensitive ? "password" : "text",
759
+ name: "value",
760
+ message: `${fieldConfig.label} (optional):`
761
+ },
762
+ { onCancel: () => process.exit(0) }
763
+ );
764
+ const value = response.value;
765
+ if (value && value.trim()) {
766
+ secrets[name] = value.trim();
767
+ }
768
+ }
769
+ }
770
+ return secrets;
771
+ }
772
+ function collectSecrets(value, previous) {
773
+ return previous.concat([value]);
774
+ }
775
+
776
+ // src/commands/zero/org/model-provider/setup.ts
777
+ async function handleInteractiveMode() {
778
+ if (!isInteractive()) {
779
+ throw new Error("Interactive mode requires a TTY", {
780
+ cause: new Error(
781
+ 'Use non-interactive mode: zero org model-provider setup --type <type> --secret "<value>"'
782
+ )
783
+ });
784
+ }
785
+ const { modelProviders: configuredProviders } = await listZeroOrgModelProviders();
786
+ const configuredTypes = new Set(configuredProviders.map((p) => p.type));
787
+ const annotatedChoices = getSelectableProviderTypes().map((type2) => {
788
+ const config2 = MODEL_PROVIDER_TYPES[type2];
789
+ const isConfigured = configuredTypes.has(type2);
790
+ const isExperimental = hasAuthMethods(type2);
791
+ let title = config2.label;
792
+ if (isConfigured) {
793
+ title = `${title} \u2713`;
794
+ }
795
+ if (isExperimental) {
796
+ title = `${title} ${chalk18.dim("(experimental)")}`;
797
+ }
798
+ return {
799
+ title,
800
+ value: type2
801
+ };
802
+ });
803
+ const typeResponse = await prompts2(
804
+ {
805
+ type: "select",
806
+ name: "type",
807
+ message: "Select provider type:",
808
+ choices: annotatedChoices
809
+ },
810
+ { onCancel: () => process.exit(0) }
811
+ );
812
+ const type = typeResponse.type;
813
+ const existingProvider = configuredProviders.find((p) => p.type === type);
814
+ if (existingProvider) {
815
+ console.log();
816
+ console.log(`"${type}" is already configured`);
817
+ console.log();
818
+ const actionResponse = await prompts2(
819
+ {
820
+ type: "select",
821
+ name: "action",
822
+ message: "",
823
+ choices: [
824
+ { title: "Keep existing secret", value: "keep" },
825
+ { title: "Update secret", value: "update" }
826
+ ]
827
+ },
828
+ { onCancel: () => process.exit(0) }
829
+ );
830
+ if (actionResponse.action === "keep") {
831
+ const selectedModel2 = await promptForModelSelection(type);
832
+ return {
833
+ type,
834
+ keepExistingSecret: true,
835
+ selectedModel: selectedModel2,
836
+ isInteractiveMode: true
837
+ };
838
+ }
839
+ }
840
+ const config = MODEL_PROVIDER_TYPES[type];
841
+ console.log();
842
+ if ("helpText" in config) {
843
+ console.log(chalk18.dim(config.helpText));
844
+ }
845
+ console.log();
846
+ if (hasAuthMethods(type)) {
847
+ const authMethod = await promptForAuthMethod(type);
848
+ const secrets = await promptForSecrets(type, authMethod);
849
+ const selectedModel2 = await promptForModelSelection(type);
850
+ return {
851
+ type,
852
+ authMethod,
853
+ secrets,
854
+ selectedModel: selectedModel2,
855
+ isInteractiveMode: true
856
+ };
857
+ }
858
+ const secretLabel = "secretLabel" in config ? config.secretLabel : "secret";
859
+ const secretResponse = await prompts2(
860
+ {
861
+ type: "password",
862
+ name: "secret",
863
+ message: `Enter your ${secretLabel}:`,
864
+ validate: (value) => value.length > 0 || `${secretLabel} is required`
865
+ },
866
+ { onCancel: () => process.exit(0) }
867
+ );
868
+ const secret = secretResponse.secret;
869
+ const selectedModel = await promptForModelSelection(type);
870
+ return { type, secret, selectedModel, isInteractiveMode: true };
871
+ }
872
+ async function promptSetAsDefault(type, framework, isDefault) {
873
+ if (isDefault) return;
874
+ const response = await prompts2(
875
+ {
876
+ type: "confirm",
877
+ name: "setDefault",
878
+ message: "Set this provider as default?",
879
+ initial: false
880
+ },
881
+ { onCancel: () => process.exit(0) }
882
+ );
883
+ if (response.setDefault) {
884
+ await setZeroOrgModelProviderDefault(type);
885
+ console.log(chalk18.green(`\u2713 Default for ${framework} set to "${type}"`));
886
+ }
887
+ }
888
+ var setupCommand = new Command19().name("setup").description("Configure an org-level model provider").option("-t, --type <type>", "Provider type (for non-interactive mode)").option(
889
+ "-s, --secret <value>",
890
+ "Secret value (can be used multiple times, supports VALUE or KEY=VALUE format)",
891
+ collectSecrets,
892
+ []
893
+ ).option(
894
+ "-a, --auth-method <method>",
895
+ "Auth method (required for multi-auth providers like aws-bedrock)"
896
+ ).option("-m, --model <model>", "Model selection (for non-interactive mode)").action(
897
+ withErrorHandler(
898
+ async (options) => {
899
+ let input;
900
+ const secretArgs = options.secret ?? [];
901
+ if (options.type && secretArgs.length > 0) {
902
+ input = handleNonInteractiveMode({
903
+ type: options.type,
904
+ secret: secretArgs,
905
+ authMethod: options.authMethod,
906
+ model: options.model,
907
+ commandPrefix: "zero org model-provider setup"
908
+ });
909
+ } else if (options.type || secretArgs.length > 0) {
910
+ throw new Error("Both --type and --secret are required");
911
+ } else {
912
+ const result = await handleInteractiveMode();
913
+ if (result === null) {
914
+ return;
915
+ }
916
+ input = result;
917
+ }
918
+ if (input.keepExistingSecret) {
919
+ const provider2 = await updateZeroOrgModelProviderModel(
920
+ input.type,
921
+ input.selectedModel
922
+ );
923
+ const defaultNote2 = provider2.isDefault ? ` (default for ${provider2.framework})` : "";
924
+ const modelNote2 = provider2.selectedModel ? ` with model: ${provider2.selectedModel}` : "";
925
+ if (!hasModelSelection(input.type)) {
926
+ console.log(
927
+ chalk18.green(`\u2713 Org model provider "${input.type}" unchanged`)
928
+ );
929
+ } else {
930
+ console.log(
931
+ chalk18.green(
932
+ `\u2713 Org model provider "${input.type}" updated${defaultNote2}${modelNote2}`
933
+ )
934
+ );
935
+ }
936
+ if (input.isInteractiveMode) {
937
+ await promptSetAsDefault(
938
+ input.type,
939
+ provider2.framework,
940
+ provider2.isDefault
941
+ );
942
+ }
943
+ return;
944
+ }
945
+ const { provider, created } = await upsertZeroOrgModelProvider({
946
+ type: input.type,
947
+ secret: input.secret,
948
+ authMethod: input.authMethod,
949
+ secrets: input.secrets,
950
+ selectedModel: input.selectedModel
951
+ });
952
+ const action = created ? "created" : "updated";
953
+ const defaultNote = provider.isDefault ? ` (default for ${provider.framework})` : "";
954
+ const modelNote = provider.selectedModel ? ` with model: ${provider.selectedModel}` : "";
955
+ console.log(
956
+ chalk18.green(
957
+ `\u2713 Org model provider "${input.type}" ${action}${defaultNote}${modelNote}`
958
+ )
959
+ );
960
+ if (input.isInteractiveMode) {
961
+ await promptSetAsDefault(
962
+ input.type,
963
+ provider.framework,
964
+ provider.isDefault
965
+ );
966
+ }
967
+ }
968
+ )
969
+ );
970
+
971
+ // src/commands/zero/org/model-provider/remove.ts
972
+ import { Command as Command20 } from "commander";
973
+ import chalk19 from "chalk";
974
+ var removeCommand4 = new Command20().name("remove").description("Remove an org-level model provider").argument("<type>", "Model provider type to remove").action(
975
+ withErrorHandler(async (type) => {
976
+ if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
977
+ const validTypes = Object.keys(MODEL_PROVIDER_TYPES).join(", ");
978
+ throw new Error(`Invalid type "${type}"`, {
979
+ cause: new Error(`Valid types: ${validTypes}`)
980
+ });
981
+ }
982
+ await deleteZeroOrgModelProvider(type);
983
+ console.log(chalk19.green(`\u2713 Org model provider "${type}" removed`));
984
+ })
985
+ );
986
+
987
+ // src/commands/zero/org/model-provider/set-default.ts
988
+ import { Command as Command21 } from "commander";
989
+ import chalk20 from "chalk";
990
+ var setDefaultCommand = new Command21().name("set-default").description("Set an org-level model provider as default for its framework").argument("<type>", "Model provider type to set as default").action(
991
+ withErrorHandler(async (type) => {
992
+ if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
993
+ const validTypes = Object.keys(MODEL_PROVIDER_TYPES).join(", ");
994
+ throw new Error(`Invalid type "${type}"`, {
995
+ cause: new Error(`Valid types: ${validTypes}`)
996
+ });
997
+ }
998
+ const provider = await setZeroOrgModelProviderDefault(
999
+ type
1000
+ );
1001
+ console.log(
1002
+ chalk20.green(
1003
+ `\u2713 Default for ${provider.framework} set to "${provider.type}"`
1004
+ )
1005
+ );
1006
+ })
1007
+ );
1008
+
1009
+ // src/commands/zero/org/model-provider/index.ts
1010
+ var zeroOrgModelProviderCommand = new Command22().name("model-provider").description("Manage org-level model providers").addCommand(listCommand4).addCommand(setupCommand).addCommand(removeCommand4).addCommand(setDefaultCommand);
1011
+
1012
+ // src/commands/zero/org/index.ts
1013
+ var zeroOrgCommand = new Command23().name("org").description("Manage your organization").addCommand(statusCommand).addCommand(setCommand).addCommand(listCommand).addCommand(useCommand).addCommand(membersCommand).addCommand(inviteCommand).addCommand(removeCommand).addCommand(leaveCommand).addCommand(deleteCommand).addCommand(zeroOrgSecretCommand).addCommand(zeroOrgVariableCommand).addCommand(zeroOrgModelProviderCommand);
1014
+
1015
+ // src/commands/zero/agent/index.ts
1016
+ import { Command as Command29 } from "commander";
1017
+
1018
+ // src/commands/zero/agent/create.ts
1019
+ import { Command as Command24 } from "commander";
1020
+ import { readFileSync } from "fs";
1021
+ import chalk21 from "chalk";
1022
+ var createCommand = new Command24().name("create").description("Create a new zero agent").requiredOption(
1023
+ "--connectors <items>",
1024
+ "Comma-separated connector short names (e.g. github,linear)"
1025
+ ).option("--display-name <name>", "Agent display name").option("--description <text>", "Agent description").option(
1026
+ "--sound <tone>",
1027
+ "Agent tone: professional, friendly, direct, supportive"
1028
+ ).option("--instructions-file <path>", "Path to instructions file").action(
1029
+ withErrorHandler(
1030
+ async (options) => {
1031
+ const connectors = options.connectors.split(",").map((s) => s.trim());
1032
+ const agent = await createZeroAgent({
1033
+ connectors,
1034
+ displayName: options.displayName,
1035
+ description: options.description,
1036
+ sound: options.sound
1037
+ });
1038
+ if (options.instructionsFile) {
1039
+ const content = readFileSync(options.instructionsFile, "utf-8");
1040
+ await updateZeroAgentInstructions(agent.agentId, content);
1041
+ }
1042
+ console.log(chalk21.green(`\u2713 Zero agent '${agent.agentId}' created`));
1043
+ console.log(` Agent ID: ${agent.agentId}`);
1044
+ console.log(` Connectors: ${agent.connectors.join(", ")}`);
1045
+ if (agent.displayName) {
1046
+ console.log(` Display Name: ${agent.displayName}`);
1047
+ }
1048
+ }
1049
+ )
1050
+ );
1051
+
1052
+ // src/commands/zero/agent/edit.ts
1053
+ import { Command as Command25 } from "commander";
1054
+ import { readFileSync as readFileSync2 } from "fs";
1055
+ import chalk22 from "chalk";
1056
+ var editCommand = new Command25().name("edit").description("Edit a zero agent").argument("<agent-id>", "Agent ID").option(
1057
+ "--connectors <items>",
1058
+ "Comma-separated connector short names (e.g. github,linear)"
1059
+ ).option("--display-name <name>", "New display name").option("--description <text>", "New description").option(
1060
+ "--sound <tone>",
1061
+ "New tone: professional, friendly, direct, supportive"
1062
+ ).option("--instructions-file <path>", "Path to new instructions file").action(
1063
+ withErrorHandler(
1064
+ async (agentId, options) => {
1065
+ const hasAgentUpdate = options.connectors !== void 0 || options.displayName !== void 0 || options.description !== void 0 || options.sound !== void 0;
1066
+ if (!hasAgentUpdate && !options.instructionsFile) {
1067
+ throw new Error(
1068
+ "At least one option is required (--connectors, --display-name, --description, --sound, --instructions-file)"
1069
+ );
1070
+ }
1071
+ if (hasAgentUpdate) {
1072
+ const current = await getZeroAgent(agentId);
1073
+ const connectors = options.connectors ? options.connectors.split(",").map((s) => s.trim()) : current.connectors;
1074
+ await updateZeroAgent(agentId, {
1075
+ connectors,
1076
+ displayName: options.displayName !== void 0 ? options.displayName : current.displayName ?? void 0,
1077
+ description: options.description !== void 0 ? options.description : current.description ?? void 0,
1078
+ sound: options.sound !== void 0 ? options.sound : current.sound ?? void 0
1079
+ });
1080
+ }
1081
+ if (options.instructionsFile) {
1082
+ const content = readFileSync2(options.instructionsFile, "utf-8");
1083
+ await updateZeroAgentInstructions(agentId, content);
1084
+ }
1085
+ console.log(chalk22.green(`\u2713 Zero agent '${agentId}' updated`));
1086
+ }
1087
+ )
1088
+ );
1089
+
1090
+ // src/commands/zero/agent/view.ts
1091
+ import { Command as Command26 } from "commander";
1092
+ import chalk23 from "chalk";
1093
+ var viewCommand = new Command26().name("view").description("View a zero agent").argument("<agent-id>", "Agent ID").option("--instructions", "Also show instructions content").action(
1094
+ withErrorHandler(
1095
+ async (agentId, options) => {
1096
+ const agent = await getZeroAgent(agentId);
1097
+ console.log(chalk23.bold(agent.agentId));
1098
+ if (agent.displayName) console.log(chalk23.dim(agent.displayName));
1099
+ console.log();
1100
+ console.log(`Agent ID: ${agent.agentId}`);
1101
+ console.log(`Connectors: ${agent.connectors.join(", ") || "-"}`);
1102
+ if (agent.description)
1103
+ console.log(`Description: ${agent.description}`);
1104
+ if (agent.sound) console.log(`Sound: ${agent.sound}`);
1105
+ if (options.instructions) {
1106
+ console.log();
1107
+ const result = await getZeroAgentInstructions(agentId);
1108
+ if (result.content) {
1109
+ console.log(chalk23.dim("\u2500\u2500 Instructions \u2500\u2500"));
1110
+ console.log(result.content);
1111
+ } else {
1112
+ console.log(chalk23.dim("No instructions set"));
1113
+ }
1114
+ }
1115
+ }
1116
+ )
1117
+ );
1118
+
1119
+ // src/commands/zero/agent/list.ts
1120
+ import { Command as Command27 } from "commander";
1121
+ import chalk24 from "chalk";
1122
+ var listCommand5 = new Command27().name("list").alias("ls").description("List all zero agents").action(
1123
+ withErrorHandler(async () => {
1124
+ const agents = await listZeroAgents();
1125
+ if (agents.length === 0) {
1126
+ console.log(chalk24.dim("No zero agents found"));
1127
+ console.log(
1128
+ chalk24.dim(
1129
+ ' Create one with: zero agent create --connectors github --display-name "My Agent"'
1130
+ )
1131
+ );
1132
+ return;
1133
+ }
1134
+ const idWidth = Math.max(8, ...agents.map((a) => a.agentId.length));
1135
+ const displayWidth = Math.max(
1136
+ 12,
1137
+ ...agents.map((a) => (a.displayName ?? "").length)
1138
+ );
1139
+ const header = [
1140
+ "AGENT ID".padEnd(idWidth),
1141
+ "DISPLAY NAME".padEnd(displayWidth),
1142
+ "CONNECTORS"
1143
+ ].join(" ");
1144
+ console.log(chalk24.dim(header));
1145
+ for (const agent of agents) {
1146
+ const row = [
1147
+ agent.agentId.padEnd(idWidth),
1148
+ (agent.displayName ?? "-").padEnd(displayWidth),
1149
+ agent.connectors.join(", ") || "-"
1150
+ ].join(" ");
1151
+ console.log(row);
1152
+ }
1153
+ })
1154
+ );
1155
+
1156
+ // src/commands/zero/agent/delete.ts
1157
+ import { Command as Command28 } from "commander";
1158
+ import chalk25 from "chalk";
1159
+ var deleteCommand2 = new Command28().name("delete").alias("rm").description("Delete a zero agent").argument("<agent-id>", "Agent ID").option("-y, --yes", "Skip confirmation prompt").action(
1160
+ withErrorHandler(async (agentId, options) => {
1161
+ await getZeroAgent(agentId);
1162
+ if (!options.yes) {
1163
+ if (!isInteractive()) {
1164
+ throw new Error("--yes flag is required in non-interactive mode");
1165
+ }
1166
+ const confirmed = await promptConfirm(
1167
+ `Delete zero agent '${agentId}'?`,
1168
+ false
1169
+ );
1170
+ if (!confirmed) {
1171
+ console.log(chalk25.dim("Cancelled"));
1172
+ return;
1173
+ }
1174
+ }
1175
+ await deleteZeroAgent(agentId);
1176
+ console.log(chalk25.green(`\u2713 Zero agent '${agentId}' deleted`));
1177
+ })
1178
+ );
1179
+
1180
+ // src/commands/zero/agent/index.ts
1181
+ var agentCommand = new Command29("agent").description("Manage zero agents").addCommand(createCommand).addCommand(editCommand).addCommand(viewCommand).addCommand(listCommand5).addCommand(deleteCommand2);
1182
+
1183
+ // src/commands/zero/connector/index.ts
1184
+ import { Command as Command34 } from "commander";
1185
+
1186
+ // src/commands/zero/connector/connect.ts
1187
+ import { Command as Command30 } from "commander";
1188
+ import chalk27 from "chalk";
1189
+
1190
+ // src/lib/computer/start-services.ts
1191
+ import { spawn } from "child_process";
1192
+ import { access, constants } from "fs/promises";
1193
+ import { createServer } from "net";
1194
+ import { homedir } from "os";
1195
+ import { join } from "path";
1196
+ import chalk26 from "chalk";
1197
+
1198
+ // src/lib/computer/ngrok.ts
1199
+ import ngrok from "@ngrok/ngrok";
1200
+ async function startNgrokTunnels(ngrokToken, endpointPrefix, webdavPort, cdpPort) {
1201
+ await ngrok.forward({
1202
+ addr: `localhost:${webdavPort}`,
1203
+ authtoken: ngrokToken,
1204
+ domain: `webdav.${endpointPrefix}.internal`
1205
+ });
1206
+ await ngrok.forward({
1207
+ addr: `localhost:${cdpPort}`,
1208
+ authtoken: ngrokToken,
1209
+ domain: `chrome.${endpointPrefix}.internal`
1210
+ });
1211
+ }
1212
+ async function stopNgrokTunnels() {
1213
+ await ngrok.kill();
1214
+ }
1215
+
1216
+ // src/lib/computer/start-services.ts
1217
+ var CHROME_CANDIDATES = [
1218
+ // macOS absolute paths
1219
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
1220
+ "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
1221
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
1222
+ // Linux / PATH-based
1223
+ "google-chrome",
1224
+ "google-chrome-stable",
1225
+ "chromium-browser",
1226
+ "chromium",
1227
+ "chrome"
1228
+ ];
1229
+ async function getRandomPort() {
1230
+ return new Promise((resolve, reject) => {
1231
+ const server = createServer();
1232
+ server.listen(0, "127.0.0.1", () => {
1233
+ const { port } = server.address();
1234
+ server.close(() => resolve(port));
1235
+ });
1236
+ server.on("error", reject);
1237
+ });
1238
+ }
1239
+ async function findBinary(...candidates) {
1240
+ for (const candidate of candidates) {
1241
+ if (candidate.startsWith("/")) {
1242
+ try {
1243
+ await access(candidate, constants.X_OK);
1244
+ return candidate;
1245
+ } catch {
1246
+ }
1247
+ } else {
1248
+ const found = await new Promise((resolve) => {
1249
+ const child = spawn("which", [candidate]);
1250
+ child.on("close", (code) => resolve(code === 0));
1251
+ });
1252
+ if (found) return candidate;
1253
+ }
1254
+ }
1255
+ return null;
1256
+ }
1257
+ async function checkComputerDependencies() {
1258
+ const wsgidavBinary = await findBinary("wsgidav");
1259
+ if (!wsgidavBinary) {
1260
+ throw new Error(
1261
+ "wsgidav not found\n\nInstall with: pip install wsgidav[cheroot]"
1262
+ );
1263
+ }
1264
+ const chromeBinary = await findBinary(...CHROME_CANDIDATES);
1265
+ if (!chromeBinary) {
1266
+ throw new Error("Chrome not found\n\nInstall Google Chrome or Chromium");
1267
+ }
1268
+ }
1269
+ async function startComputerServices(credentials) {
1270
+ console.log(chalk26.cyan("Starting computer connector services..."));
1271
+ const wsgidavBinary = await findBinary("wsgidav");
1272
+ if (!wsgidavBinary) {
1273
+ throw new Error(
1274
+ "wsgidav not found\n\nInstall with: pip install wsgidav[cheroot]"
1275
+ );
1276
+ }
1277
+ const chromeBinary = await findBinary(...CHROME_CANDIDATES);
1278
+ if (!chromeBinary) {
1279
+ throw new Error("Chrome not found\n\nInstall Google Chrome or Chromium");
1280
+ }
1281
+ const webdavPort = await getRandomPort();
1282
+ const cdpPort = await getRandomPort();
1283
+ const downloadsPath = join(homedir(), "Downloads");
1284
+ const wsgidav = spawn(
1285
+ wsgidavBinary,
1286
+ [
1287
+ "--host=127.0.0.1",
1288
+ `--port=${webdavPort}`,
1289
+ `--root=${downloadsPath}`,
1290
+ "--auth=anonymous",
1291
+ "--no-config"
1292
+ ],
1293
+ { stdio: ["ignore", "pipe", "pipe"] }
1294
+ );
1295
+ wsgidav.stdout?.on("data", (data) => process.stdout.write(data));
1296
+ wsgidav.stderr?.on("data", (data) => process.stderr.write(data));
1297
+ console.log(chalk26.green("\u2713 WebDAV server started"));
1298
+ const chrome = spawn(
1299
+ chromeBinary,
1300
+ [
1301
+ `--remote-debugging-port=${cdpPort}`,
1302
+ "--remote-debugging-address=127.0.0.1",
1303
+ "--headless=new",
1304
+ "--no-sandbox",
1305
+ "--disable-gpu"
1306
+ ],
1307
+ { stdio: ["ignore", "pipe", "pipe"] }
1308
+ );
1309
+ chrome.stdout?.on("data", (data) => process.stdout.write(data));
1310
+ chrome.stderr?.on("data", (data) => process.stderr.write(data));
1311
+ console.log(chalk26.green("\u2713 Chrome started"));
1312
+ try {
1313
+ await startNgrokTunnels(
1314
+ credentials.ngrokToken,
1315
+ credentials.endpointPrefix,
1316
+ webdavPort,
1317
+ cdpPort
1318
+ );
1319
+ console.log(
1320
+ chalk26.green(
1321
+ `\u2713 ngrok tunnels: webdav.${credentials.domain}, chrome.${credentials.domain}`
1322
+ )
1323
+ );
1324
+ console.log();
1325
+ console.log(chalk26.green("\u2713 Computer connector active"));
1326
+ console.log(` WebDAV: ~/Downloads \u2192 webdav.${credentials.domain}`);
1327
+ console.log(
1328
+ ` Chrome CDP: port ${cdpPort} \u2192 chrome.${credentials.domain}`
1329
+ );
1330
+ console.log();
1331
+ console.log(chalk26.dim("Press ^C twice to disconnect"));
1332
+ console.log();
1333
+ let sigintCount = 0;
1334
+ await new Promise((resolve) => {
1335
+ const keepAlive = setInterval(() => {
1336
+ }, 6e4);
1337
+ const done = () => {
1338
+ clearInterval(keepAlive);
1339
+ process.removeListener("SIGINT", onSigint);
1340
+ resolve();
1341
+ };
1342
+ const onSigint = () => {
1343
+ sigintCount++;
1344
+ if (sigintCount === 1) {
1345
+ console.log(chalk26.dim("\nPress ^C again to disconnect and exit..."));
1346
+ } else {
1347
+ done();
1348
+ }
1349
+ };
1350
+ process.on("SIGINT", onSigint);
1351
+ process.once("SIGTERM", done);
1352
+ });
1353
+ } finally {
1354
+ console.log();
1355
+ console.log(chalk26.cyan("Stopping services..."));
1356
+ wsgidav.kill("SIGTERM");
1357
+ chrome.kill("SIGTERM");
1358
+ await stopNgrokTunnels();
1359
+ console.log(chalk26.green("\u2713 Services stopped"));
1360
+ }
1361
+ }
1362
+
1363
+ // src/commands/zero/connector/connect.ts
1364
+ function delay(ms) {
1365
+ return new Promise((resolve) => setTimeout(resolve, ms));
1366
+ }
1367
+ function renderHelpText(text) {
1368
+ return text.replace(
1369
+ /\[([^\]]+)\]\(([^)]+)\)/g,
1370
+ (_m, label, url) => `${label} (${chalk27.cyan(url)})`
1371
+ ).replace(/\*\*([^*]+)\*\*/g, (_m, content) => chalk27.bold(content)).replace(
1372
+ /^> (.+)$/gm,
1373
+ (_m, content) => chalk27.yellow(` ${content}`)
1374
+ );
1375
+ }
1376
+ async function connectViaApiToken(connectorType, tokenValue) {
1377
+ const config = CONNECTOR_TYPES[connectorType];
1378
+ const apiTokenConfig = config.authMethods["api-token"];
1379
+ if (!apiTokenConfig) {
1380
+ throw new Error(
1381
+ `${config.label} does not support API token authentication`
1382
+ );
1383
+ }
1384
+ const secretEntries = Object.entries(apiTokenConfig.secrets);
1385
+ const inputSecrets = {};
1386
+ if (tokenValue && secretEntries.length === 1) {
1387
+ const [secretName] = secretEntries[0];
1388
+ inputSecrets[secretName] = tokenValue;
1389
+ } else {
1390
+ if (apiTokenConfig.helpText) {
1391
+ console.log();
1392
+ console.log(renderHelpText(apiTokenConfig.helpText));
1393
+ console.log();
1394
+ }
1395
+ for (const [secretName, secretConfig] of secretEntries) {
1396
+ if (!secretConfig.required) continue;
1397
+ const value = await promptPassword(
1398
+ `${secretConfig.label}${secretConfig.placeholder ? chalk27.dim(` (${secretConfig.placeholder})`) : ""}:`
1399
+ );
1400
+ if (!value) {
1401
+ throw new Error("Cancelled");
1402
+ }
1403
+ inputSecrets[secretName] = value;
1404
+ }
1405
+ }
1406
+ for (const [name, value] of Object.entries(inputSecrets)) {
1407
+ await setZeroSecret({
1408
+ name,
1409
+ value,
1410
+ description: `API token for ${config.label} connector`
1411
+ });
1412
+ }
1413
+ console.log(
1414
+ chalk27.green(`
1415
+ \u2713 ${config.label} connected successfully via API token!`)
1416
+ );
1417
+ }
1418
+ async function connectComputer() {
1419
+ await checkComputerDependencies();
1420
+ console.log(chalk27.cyan("Setting up computer connector..."));
1421
+ const credentials = await createZeroComputerConnector();
1422
+ await startComputerServices(credentials);
1423
+ console.log(chalk27.cyan("Disconnecting computer connector..."));
1424
+ await deleteZeroComputerConnector();
1425
+ console.log(chalk27.green("\u2713 Disconnected computer"));
1426
+ }
1427
+ async function resolveAuthMethod(connectorType, tokenFlag) {
1428
+ const config = CONNECTOR_TYPES[connectorType];
1429
+ const oauthFlag = CONNECTOR_TYPES[connectorType].featureFlag;
1430
+ const oauthAvailable = "oauth" in config.authMethods && (!oauthFlag || await isFeatureEnabled(oauthFlag));
1431
+ const apiTokenAvailable = "api-token" in config.authMethods;
1432
+ if (tokenFlag) {
1433
+ if (!apiTokenAvailable) {
1434
+ throw new Error(
1435
+ `${config.label} does not support API token authentication`
1436
+ );
1437
+ }
1438
+ return "api-token";
1439
+ }
1440
+ if (oauthAvailable && apiTokenAvailable) {
1441
+ const selected = await promptSelect(
1442
+ `How would you like to connect ${config.label}?`,
1443
+ [
1444
+ { title: "OAuth (Sign in with browser)", value: "oauth" },
1445
+ {
1446
+ title: `API Token (${config.authMethods["api-token"].label})`,
1447
+ value: "api-token"
1448
+ }
1449
+ ]
1450
+ );
1451
+ if (!selected) {
1452
+ throw new Error("Cancelled");
1453
+ }
1454
+ return selected;
1455
+ }
1456
+ if (apiTokenAvailable) return "api-token";
1457
+ if (oauthAvailable) return "oauth";
1458
+ throw new Error(
1459
+ `${config.label} has no available auth methods. OAuth may not be enabled yet.`
1460
+ );
1461
+ }
1462
+ async function connectViaOAuth(connectorType) {
1463
+ console.log(`Connecting ${chalk27.cyan(connectorType)}...`);
1464
+ const session = await createZeroConnectorSession(connectorType);
1465
+ const apiUrl = await getBaseUrl();
1466
+ const verificationUrl = `${apiUrl}${session.verificationUrl}`;
1467
+ console.log(chalk27.green("\nSession created"));
1468
+ console.log(chalk27.cyan(`
1469
+ To connect, visit: ${verificationUrl}`));
1470
+ console.log(
1471
+ `
1472
+ The session expires in ${Math.floor(session.expiresIn / 60)} minutes.`
1473
+ );
1474
+ console.log("\nWaiting for authorization...");
1475
+ const startTime = Date.now();
1476
+ const maxWaitTime = session.expiresIn * 1e3;
1477
+ const pollInterval = (session.interval || 5) * 1e3;
1478
+ let isFirstPoll = true;
1479
+ while (Date.now() - startTime < maxWaitTime) {
1480
+ if (!isFirstPoll) {
1481
+ await delay(pollInterval);
1482
+ }
1483
+ isFirstPoll = false;
1484
+ const status = await getZeroConnectorSession(connectorType, session.id);
1485
+ switch (status.status) {
1486
+ case "complete":
1487
+ console.log(
1488
+ chalk27.green(`
1489
+
1490
+ ${connectorType} connected successfully!`)
1491
+ );
1492
+ return;
1493
+ case "expired":
1494
+ throw new Error("Session expired, please try again");
1495
+ case "error":
1496
+ throw new Error(
1497
+ `Connection failed: ${status.errorMessage || "Unknown error"}`
1498
+ );
1499
+ case "pending":
1500
+ process.stdout.write(chalk27.dim("."));
1501
+ break;
1502
+ }
1503
+ }
1504
+ throw new Error("Session timed out, please try again");
1505
+ }
1506
+ var connectCommand = new Command30().name("connect").description("Connect a third-party service (e.g., GitHub)").argument("<type>", "Connector type (e.g., github)").option("--token <value>", "API token value (skip interactive prompt)").action(
1507
+ withErrorHandler(async (type, options) => {
1508
+ const parseResult = connectorTypeSchema.safeParse(type);
1509
+ if (!parseResult.success) {
1510
+ const available = Object.keys(CONNECTOR_TYPES).join(", ");
1511
+ throw new Error(`Unknown connector type: ${type}`, {
1512
+ cause: new Error(`Available connectors: ${available}`)
1513
+ });
1514
+ }
1515
+ const connectorType = parseResult.data;
1516
+ if (connectorType === "computer") {
1517
+ await connectComputer();
1518
+ return;
1519
+ }
1520
+ const authMethod = await resolveAuthMethod(connectorType, options.token);
1521
+ if (authMethod === "api-token") {
1522
+ await connectViaApiToken(connectorType, options.token);
1523
+ return;
1524
+ }
1525
+ await connectViaOAuth(connectorType);
1526
+ })
1527
+ );
1528
+
1529
+ // src/commands/zero/connector/list.ts
1530
+ import { Command as Command31 } from "commander";
1531
+ import chalk28 from "chalk";
1532
+ var listCommand6 = new Command31().name("list").alias("ls").description("List all connectors and their status").action(
1533
+ withErrorHandler(async () => {
1534
+ const result = await listZeroConnectors();
1535
+ const connectedMap = new Map(result.connectors.map((c) => [c.type, c]));
1536
+ const allTypesRaw = Object.keys(CONNECTOR_TYPES);
1537
+ const allTypes = [];
1538
+ for (const type of allTypesRaw) {
1539
+ const flag = CONNECTOR_TYPES[type].featureFlag;
1540
+ const hasApiToken = "api-token" in CONNECTOR_TYPES[type].authMethods;
1541
+ if (flag && !await isFeatureEnabled(flag) && !hasApiToken) {
1542
+ continue;
1543
+ }
1544
+ allTypes.push(type);
1545
+ }
1546
+ const typeWidth = Math.max(4, ...allTypes.map((t) => t.length));
1547
+ const statusText = "STATUS";
1548
+ const statusWidth = statusText.length;
1549
+ const header = [
1550
+ "TYPE".padEnd(typeWidth),
1551
+ statusText.padEnd(statusWidth),
1552
+ "ACCOUNT"
1553
+ ].join(" ");
1554
+ console.log(chalk28.dim(header));
1555
+ for (const type of allTypes) {
1556
+ const connector = connectedMap.get(type);
1557
+ const scopeMismatch = connector !== void 0 && connector.authMethod === "oauth" && !hasRequiredScopes(type, connector.oauthScopes);
1558
+ const status = connector ? connector.needsReconnect ? chalk28.yellow("!".padEnd(statusWidth)) : scopeMismatch ? chalk28.yellow("!".padEnd(statusWidth)) : chalk28.green("\u2713".padEnd(statusWidth)) : chalk28.dim("-".padEnd(statusWidth));
1559
+ const account = connector?.needsReconnect ? chalk28.yellow("(reconnect needed)") : scopeMismatch ? chalk28.yellow("(permissions update available)") : connector?.externalUsername ? `@${connector.externalUsername}` : chalk28.dim("-");
1560
+ const row = [type.padEnd(typeWidth), status, account].join(" ");
1561
+ console.log(row);
1562
+ }
1563
+ console.log();
1564
+ console.log(chalk28.dim("To connect a service:"));
1565
+ console.log(chalk28.dim(" zero connector connect <type>"));
1566
+ })
1567
+ );
1568
+
1569
+ // src/commands/zero/connector/status.ts
1570
+ import { Command as Command32 } from "commander";
1571
+ import chalk29 from "chalk";
1572
+
1573
+ // src/lib/domain/schedule-utils.ts
1574
+ function formatRelativeTime(dateStr) {
1575
+ if (!dateStr) return "-";
1576
+ const date = new Date(dateStr);
1577
+ const now = /* @__PURE__ */ new Date();
1578
+ const diffMs = date.getTime() - now.getTime();
1579
+ const diffAbs = Math.abs(diffMs);
1580
+ const minutes = Math.floor(diffAbs / (1e3 * 60));
1581
+ const hours = Math.floor(diffAbs / (1e3 * 60 * 60));
1582
+ const days = Math.floor(diffAbs / (1e3 * 60 * 60 * 24));
1583
+ const isPast = diffMs < 0;
1584
+ if (days > 0) {
1585
+ return isPast ? `${days}d ago` : `in ${days}d`;
1586
+ } else if (hours > 0) {
1587
+ return isPast ? `${hours}h ago` : `in ${hours}h`;
1588
+ } else if (minutes > 0) {
1589
+ return isPast ? `${minutes}m ago` : `in ${minutes}m`;
1590
+ } else {
1591
+ return isPast ? "just now" : "soon";
1592
+ }
1593
+ }
1594
+ function formatDateTime(dateStr) {
1595
+ if (!dateStr) return "-";
1596
+ const date = new Date(dateStr);
1597
+ const year = date.getFullYear();
1598
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1599
+ const day = String(date.getDate()).padStart(2, "0");
1600
+ const hours = String(date.getHours()).padStart(2, "0");
1601
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1602
+ const formatted = `${year}-${month}-${day} ${hours}:${minutes}`;
1603
+ const relative = formatRelativeTime(dateStr);
1604
+ return `${formatted} (${relative})`;
1605
+ }
1606
+ function generateCronExpression(frequency, time, day) {
1607
+ const [hourStr, minuteStr] = time.split(":");
1608
+ const hour = parseInt(hourStr ?? "0", 10);
1609
+ const minute = parseInt(minuteStr ?? "0", 10);
1610
+ switch (frequency) {
1611
+ case "daily":
1612
+ return `${minute} ${hour} * * *`;
1613
+ case "weekly":
1614
+ return `${minute} ${hour} * * ${day ?? 1}`;
1615
+ case "monthly":
1616
+ return `${minute} ${hour} ${day ?? 1} * *`;
1617
+ }
1618
+ }
1619
+ function detectTimezone() {
1620
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
1621
+ }
1622
+ function validateTimeFormat(time) {
1623
+ const match = time.match(/^(\d{1,2}):(\d{2})$/);
1624
+ if (!match) {
1625
+ return "Invalid format. Use HH:MM (e.g., 09:00)";
1626
+ }
1627
+ const hour = parseInt(match[1], 10);
1628
+ const minute = parseInt(match[2], 10);
1629
+ if (hour < 0 || hour > 23) {
1630
+ return "Hour must be 0-23";
1631
+ }
1632
+ if (minute < 0 || minute > 59) {
1633
+ return "Minute must be 0-59";
1634
+ }
1635
+ if (minute % 5 !== 0) {
1636
+ return "Minute must be a multiple of 5 (0, 5, 10, ..., 55)";
1637
+ }
1638
+ return true;
1639
+ }
1640
+ function validateDateFormat(date) {
1641
+ const match = date.match(/^(\d{4})-(\d{2})-(\d{2})$/);
1642
+ if (!match) {
1643
+ return "Invalid format. Use YYYY-MM-DD (e.g., 2025-01-15)";
1644
+ }
1645
+ const year = parseInt(match[1], 10);
1646
+ const month = parseInt(match[2], 10);
1647
+ const day = parseInt(match[3], 10);
1648
+ if (year < 2e3 || year > 2100) {
1649
+ return "Year must be between 2000 and 2100";
1650
+ }
1651
+ if (month < 1 || month > 12) {
1652
+ return "Month must be 1-12";
1653
+ }
1654
+ if (day < 1 || day > 31) {
1655
+ return "Day must be 1-31";
1656
+ }
1657
+ const testDate = new Date(year, month - 1, day);
1658
+ if (testDate.getFullYear() !== year || testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
1659
+ return "Invalid date";
1660
+ }
1661
+ return true;
1662
+ }
1663
+ function getTomorrowDateLocal() {
1664
+ const tomorrow = /* @__PURE__ */ new Date();
1665
+ tomorrow.setDate(tomorrow.getDate() + 1);
1666
+ const year = tomorrow.getFullYear();
1667
+ const month = String(tomorrow.getMonth() + 1).padStart(2, "0");
1668
+ const day = String(tomorrow.getDate()).padStart(2, "0");
1669
+ return `${year}-${month}-${day}`;
1670
+ }
1671
+ function getCurrentTimeLocal() {
1672
+ const now = /* @__PURE__ */ new Date();
1673
+ const hours = String(now.getHours()).padStart(2, "0");
1674
+ const minutes = String(now.getMinutes()).padStart(2, "0");
1675
+ return `${hours}:${minutes}`;
1676
+ }
1677
+ function toISODateTime(dateTimeStr) {
1678
+ if (dateTimeStr.includes("T") && dateTimeStr.endsWith("Z")) {
1679
+ return dateTimeStr;
1680
+ }
1681
+ const isoStr = dateTimeStr.replace(" ", "T") + ":00";
1682
+ const date = new Date(isoStr);
1683
+ return date.toISOString();
1684
+ }
1685
+
1686
+ // src/commands/zero/connector/status.ts
1687
+ var LABEL_WIDTH = 16;
1688
+ var statusCommand2 = new Command32().name("status").description("Show detailed status of a connector").argument("<type>", "Connector type (e.g., github)").action(
1689
+ withErrorHandler(async (type) => {
1690
+ const parseResult = connectorTypeSchema.safeParse(type);
1691
+ if (!parseResult.success) {
1692
+ const available = Object.keys(CONNECTOR_TYPES).join(", ");
1693
+ throw new Error(`Unknown connector type: ${type}`, {
1694
+ cause: new Error(`Available connectors: ${available}`)
1695
+ });
1696
+ }
1697
+ const connector = await getZeroConnector(parseResult.data);
1698
+ console.log(`Connector: ${chalk29.cyan(type)}`);
1699
+ console.log();
1700
+ if (connector) {
1701
+ console.log(
1702
+ `${"Status:".padEnd(LABEL_WIDTH)}${chalk29.green("connected")}`
1703
+ );
1704
+ console.log(
1705
+ `${"Account:".padEnd(LABEL_WIDTH)}@${connector.externalUsername}`
1706
+ );
1707
+ console.log(
1708
+ `${"Auth Method:".padEnd(LABEL_WIDTH)}${connector.authMethod}`
1709
+ );
1710
+ if (connector.oauthScopes && connector.oauthScopes.length > 0) {
1711
+ console.log(
1712
+ `${"OAuth Scopes:".padEnd(LABEL_WIDTH)}${connector.oauthScopes.join(", ")}`
1713
+ );
1714
+ }
1715
+ if (connector.authMethod === "oauth" && !hasRequiredScopes(parseResult.data, connector.oauthScopes)) {
1716
+ const diff = getScopeDiff(parseResult.data, connector.oauthScopes);
1717
+ console.log(
1718
+ `${"Permissions:".padEnd(LABEL_WIDTH)}${chalk29.yellow("update available")}`
1719
+ );
1720
+ if (diff.addedScopes.length > 0) {
1721
+ console.log(
1722
+ `${" Added:".padEnd(LABEL_WIDTH)}${diff.addedScopes.join(", ")}`
1723
+ );
1724
+ }
1725
+ if (diff.removedScopes.length > 0) {
1726
+ console.log(
1727
+ `${" Removed:".padEnd(LABEL_WIDTH)}${diff.removedScopes.join(", ")}`
1728
+ );
1729
+ }
1730
+ }
1731
+ console.log(
1732
+ `${"Connected:".padEnd(LABEL_WIDTH)}${formatDateTime(connector.createdAt)}`
1733
+ );
1734
+ if (connector.updatedAt !== connector.createdAt) {
1735
+ console.log(
1736
+ `${"Last Updated:".padEnd(LABEL_WIDTH)}${formatDateTime(connector.updatedAt)}`
1737
+ );
1738
+ }
1739
+ console.log();
1740
+ console.log(chalk29.dim("To disconnect:"));
1741
+ console.log(chalk29.dim(` zero connector disconnect ${type}`));
1742
+ } else {
1743
+ console.log(
1744
+ `${"Status:".padEnd(LABEL_WIDTH)}${chalk29.dim("not connected")}`
1745
+ );
1746
+ console.log();
1747
+ console.log(chalk29.dim("To connect:"));
1748
+ console.log(chalk29.dim(` zero connector connect ${type}`));
1749
+ }
1750
+ })
1751
+ );
1752
+
1753
+ // src/commands/zero/connector/disconnect.ts
1754
+ import { Command as Command33 } from "commander";
1755
+ import chalk30 from "chalk";
1756
+ var disconnectCommand = new Command33().name("disconnect").description("Disconnect a third-party service").argument("<type>", "Connector type to disconnect (e.g., github)").action(
1757
+ withErrorHandler(async (type) => {
1758
+ const parseResult = connectorTypeSchema.safeParse(type);
1759
+ if (!parseResult.success) {
1760
+ const available = Object.keys(CONNECTOR_TYPES).join(", ");
1761
+ throw new Error(`Unknown connector type: ${type}`, {
1762
+ cause: new Error(`Available connectors: ${available}`)
1763
+ });
1764
+ }
1765
+ const connectorType = parseResult.data;
1766
+ await deleteZeroConnector(connectorType);
1767
+ console.log(chalk30.green(`\u2713 Disconnected ${type}`));
1768
+ })
1769
+ );
1770
+
1771
+ // src/commands/zero/connector/index.ts
1772
+ var zeroConnectorCommand = new Command34().name("connector").description("Manage third-party service connections (zero)").addCommand(listCommand6).addCommand(statusCommand2).addCommand(connectCommand).addCommand(disconnectCommand);
1773
+
1774
+ // src/commands/zero/preference/index.ts
1775
+ import { Command as Command35 } from "commander";
1776
+ import chalk31 from "chalk";
1777
+ function detectTimezone2() {
1778
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
1779
+ }
1780
+ function isValidTimezone(timezone) {
1781
+ try {
1782
+ Intl.DateTimeFormat(void 0, { timeZone: timezone });
1783
+ return true;
1784
+ } catch {
1785
+ return false;
1786
+ }
1787
+ }
1788
+ function parseOnOff(flag, value) {
1789
+ const lower = value.toLowerCase();
1790
+ if (lower === "on" || lower === "true" || lower === "1") return true;
1791
+ if (lower === "off" || lower === "false" || lower === "0") return false;
1792
+ throw new Error(
1793
+ `Invalid value for --${flag}: "${value}". Use "on" or "off".`
1794
+ );
1795
+ }
1796
+ function displayPreferences(prefs) {
1797
+ console.log(chalk31.bold("Current preferences:"));
1798
+ console.log(
1799
+ ` Timezone: ${prefs.timezone ? chalk31.cyan(prefs.timezone) : chalk31.dim("not set")}`
1800
+ );
1801
+ console.log(
1802
+ ` Email notify: ${prefs.notifyEmail ? chalk31.green("on") : chalk31.dim("off")}`
1803
+ );
1804
+ console.log(
1805
+ ` Slack notify: ${prefs.notifySlack ? chalk31.green("on") : chalk31.dim("off")}`
1806
+ );
1807
+ }
1808
+ function buildUpdates(opts) {
1809
+ const hasTimezone = opts.timezone !== void 0;
1810
+ const hasNotifyEmail = opts.notifyEmail !== void 0;
1811
+ const hasNotifySlack = opts.notifySlack !== void 0;
1812
+ if (!hasTimezone && !hasNotifyEmail && !hasNotifySlack) return null;
1813
+ const updates = {};
1814
+ if (hasTimezone) {
1815
+ if (!isValidTimezone(opts.timezone)) {
1816
+ throw new Error(`Invalid timezone: ${opts.timezone}`, {
1817
+ cause: new Error(
1818
+ "Use an IANA timezone identifier (e.g., America/New_York, Asia/Shanghai)"
1819
+ )
1820
+ });
1821
+ }
1822
+ updates.timezone = opts.timezone;
1823
+ }
1824
+ if (hasNotifyEmail) {
1825
+ updates.notifyEmail = parseOnOff("notify-email", opts.notifyEmail);
1826
+ }
1827
+ if (hasNotifySlack) {
1828
+ updates.notifySlack = parseOnOff("notify-slack", opts.notifySlack);
1829
+ }
1830
+ return updates;
1831
+ }
1832
+ function printUpdateResult(updates, result) {
1833
+ if (updates.timezone !== void 0) {
1834
+ console.log(
1835
+ chalk31.green(
1836
+ `Timezone set to ${chalk31.cyan(result.timezone ?? updates.timezone)}`
1837
+ )
1838
+ );
1839
+ }
1840
+ if (updates.notifyEmail !== void 0) {
1841
+ console.log(
1842
+ chalk31.green(
1843
+ `Email notifications ${result.notifyEmail ? "enabled" : "disabled"}`
1844
+ )
1845
+ );
1846
+ }
1847
+ if (updates.notifySlack !== void 0) {
1848
+ console.log(
1849
+ chalk31.green(
1850
+ `Slack notifications ${result.notifySlack ? "enabled" : "disabled"}`
1851
+ )
1852
+ );
1853
+ }
1854
+ }
1855
+ async function interactiveSetup(prefs) {
1856
+ if (!prefs.timezone) {
1857
+ const detectedTz = detectTimezone2();
1858
+ console.log(chalk31.dim(`
1859
+ System timezone detected: ${detectedTz}`));
1860
+ const tz = await promptText(
1861
+ "Set timezone? (enter timezone or leave empty to skip)",
1862
+ detectedTz
1863
+ );
1864
+ if (tz?.trim()) {
1865
+ if (!isValidTimezone(tz.trim())) {
1866
+ throw new Error(`Invalid timezone: ${tz.trim()}`);
1867
+ }
1868
+ await updateZeroUserPreferences({ timezone: tz.trim() });
1869
+ console.log(chalk31.green(`Timezone set to ${chalk31.cyan(tz.trim())}`));
1870
+ }
1871
+ }
1872
+ if (!prefs.notifyEmail) {
1873
+ const enable = await promptConfirm(
1874
+ "\nEnable email notifications for scheduled runs?",
1875
+ false
1876
+ );
1877
+ if (enable) {
1878
+ await updateZeroUserPreferences({ notifyEmail: true });
1879
+ console.log(chalk31.green("Email notifications enabled"));
1880
+ }
1881
+ }
1882
+ }
1883
+ var zeroPreferenceCommand = new Command35().name("preference").description("View or update your preferences").option("--timezone <timezone>", "IANA timezone (e.g., America/New_York)").option("--notify-email <on|off>", "Enable or disable email notifications").option("--notify-slack <on|off>", "Enable or disable Slack notifications").action(
1884
+ withErrorHandler(async (opts) => {
1885
+ const updates = buildUpdates(opts);
1886
+ if (updates) {
1887
+ const result = await updateZeroUserPreferences(updates);
1888
+ printUpdateResult(updates, result);
1889
+ return;
1890
+ }
1891
+ const prefs = await getZeroUserPreferences();
1892
+ displayPreferences(prefs);
1893
+ if (isInteractive()) {
1894
+ await interactiveSetup(prefs);
1895
+ } else if (!prefs.timezone) {
1896
+ console.log();
1897
+ console.log(
1898
+ `To set timezone: ${chalk31.cyan("zero preference --timezone <timezone>")}`
1899
+ );
1900
+ console.log(
1901
+ chalk31.dim("Example: zero preference --timezone America/New_York")
1902
+ );
1903
+ }
1904
+ })
1905
+ );
1906
+
1907
+ // src/commands/zero/schedule/index.ts
1908
+ import { Command as Command42 } from "commander";
1909
+
1910
+ // src/commands/zero/schedule/setup.ts
1911
+ import { Command as Command36 } from "commander";
1912
+ import chalk32 from "chalk";
1913
+ var FREQUENCY_CHOICES = [
1914
+ { title: "Daily", value: "daily", description: "Run every day" },
1915
+ {
1916
+ title: "Weekly",
1917
+ value: "weekly",
1918
+ description: "Run once per week"
1919
+ },
1920
+ {
1921
+ title: "Monthly",
1922
+ value: "monthly",
1923
+ description: "Run once per month"
1924
+ },
1925
+ {
1926
+ title: "One-time",
1927
+ value: "once",
1928
+ description: "Run once at specific time"
1929
+ },
1930
+ {
1931
+ title: "Loop",
1932
+ value: "loop",
1933
+ description: "Run repeatedly at fixed intervals"
1934
+ }
1935
+ ];
1936
+ var DAY_OF_WEEK_CHOICES = [
1937
+ { title: "Monday", value: 1 },
1938
+ { title: "Tuesday", value: 2 },
1939
+ { title: "Wednesday", value: 3 },
1940
+ { title: "Thursday", value: 4 },
1941
+ { title: "Friday", value: 5 },
1942
+ { title: "Saturday", value: 6 },
1943
+ { title: "Sunday", value: 0 }
1944
+ ];
1945
+ function parseDayOption(day, frequency) {
1946
+ if (frequency === "weekly") {
1947
+ const dayMap = {
1948
+ sun: 0,
1949
+ mon: 1,
1950
+ tue: 2,
1951
+ wed: 3,
1952
+ thu: 4,
1953
+ fri: 5,
1954
+ sat: 6
1955
+ };
1956
+ return dayMap[day.toLowerCase()];
1957
+ } else if (frequency === "monthly") {
1958
+ const num = parseInt(day, 10);
1959
+ if (num >= 1 && num <= 31) {
1960
+ return num;
1961
+ }
1962
+ }
1963
+ return void 0;
1964
+ }
1965
+ function formatInTimezone(isoDate, timezone) {
1966
+ const date = new Date(isoDate);
1967
+ const parts = new Intl.DateTimeFormat("en-CA", {
1968
+ timeZone: timezone,
1969
+ year: "numeric",
1970
+ month: "2-digit",
1971
+ day: "2-digit",
1972
+ hour: "2-digit",
1973
+ minute: "2-digit",
1974
+ hour12: false
1975
+ }).formatToParts(date);
1976
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
1977
+ return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}`;
1978
+ }
1979
+ function parseFrequencyFromCron(cron) {
1980
+ const parts = cron.split(" ");
1981
+ if (parts.length !== 5) return null;
1982
+ const [minute, hour, dayOfMonth, , dayOfWeek] = parts;
1983
+ const time = `${hour.padStart(2, "0")}:${minute.padStart(2, "0")}`;
1984
+ if (dayOfMonth === "*" && dayOfWeek === "*") {
1985
+ return { frequency: "daily", time };
1986
+ } else if (dayOfMonth === "*" && dayOfWeek !== "*") {
1987
+ return { frequency: "weekly", day: parseInt(dayOfWeek, 10), time };
1988
+ } else if (dayOfMonth !== "*" && dayOfWeek === "*") {
1989
+ return { frequency: "monthly", day: parseInt(dayOfMonth, 10), time };
1990
+ }
1991
+ return null;
1992
+ }
1993
+ function getExistingDefaults(existingSchedule) {
1994
+ const defaults = {};
1995
+ if (existingSchedule?.triggerType === "loop") {
1996
+ defaults.frequency = "loop";
1997
+ defaults.intervalSeconds = existingSchedule.intervalSeconds ?? void 0;
1998
+ } else if (existingSchedule?.cronExpression) {
1999
+ const parsed = parseFrequencyFromCron(existingSchedule.cronExpression);
2000
+ if (parsed) {
2001
+ defaults.frequency = parsed.frequency;
2002
+ defaults.day = parsed.day;
2003
+ defaults.time = parsed.time;
2004
+ }
2005
+ } else if (existingSchedule?.atTime) {
2006
+ defaults.frequency = "once";
2007
+ }
2008
+ return defaults;
2009
+ }
2010
+ async function gatherFrequency(optionFrequency, existingFrequency) {
2011
+ let frequency = optionFrequency;
2012
+ if (frequency && ["daily", "weekly", "monthly", "once", "loop"].includes(frequency)) {
2013
+ return frequency;
2014
+ }
2015
+ if (!isInteractive()) {
2016
+ throw new Error("--frequency is required (daily|weekly|monthly|once|loop)");
2017
+ }
2018
+ const defaultIndex = existingFrequency ? FREQUENCY_CHOICES.findIndex((c) => c.value === existingFrequency) : 0;
2019
+ frequency = await promptSelect(
2020
+ "Schedule frequency",
2021
+ FREQUENCY_CHOICES,
2022
+ defaultIndex >= 0 ? defaultIndex : 0
2023
+ );
2024
+ return frequency || null;
2025
+ }
2026
+ async function gatherDay(frequency, optionDay, existingDay) {
2027
+ if (frequency !== "weekly" && frequency !== "monthly") {
2028
+ return null;
2029
+ }
2030
+ if (optionDay) {
2031
+ const day2 = parseDayOption(optionDay, frequency);
2032
+ if (day2 === void 0) {
2033
+ throw new Error(
2034
+ `Invalid day: ${optionDay}. Use mon-sun for weekly or 1-31 for monthly.`
2035
+ );
2036
+ }
2037
+ return day2;
2038
+ }
2039
+ if (!isInteractive()) {
2040
+ throw new Error("--day is required for weekly/monthly");
2041
+ }
2042
+ if (frequency === "weekly") {
2043
+ const defaultDayIndex = existingDay !== void 0 ? DAY_OF_WEEK_CHOICES.findIndex((c) => c.value === existingDay) : 0;
2044
+ const day2 = await promptSelect(
2045
+ "Day of week",
2046
+ DAY_OF_WEEK_CHOICES,
2047
+ defaultDayIndex >= 0 ? defaultDayIndex : 0
2048
+ );
2049
+ return day2 ?? null;
2050
+ }
2051
+ const dayStr = await promptText(
2052
+ "Day of month (1-31)",
2053
+ existingDay?.toString() || "1"
2054
+ );
2055
+ if (!dayStr) return null;
2056
+ const day = parseInt(dayStr, 10);
2057
+ if (isNaN(day) || day < 1 || day > 31) {
2058
+ throw new Error("Day must be between 1 and 31");
2059
+ }
2060
+ return day;
2061
+ }
2062
+ async function gatherRecurringTime(optionTime, existingTime) {
2063
+ if (optionTime) {
2064
+ const validation = validateTimeFormat(optionTime);
2065
+ if (validation !== true) {
2066
+ throw new Error(`Invalid time: ${validation}`);
2067
+ }
2068
+ return optionTime;
2069
+ }
2070
+ if (!isInteractive()) {
2071
+ throw new Error("--time is required (HH:MM format)");
2072
+ }
2073
+ return await promptText(
2074
+ "Time (HH:MM)",
2075
+ existingTime || "09:00",
2076
+ validateTimeFormat
2077
+ );
2078
+ }
2079
+ async function gatherOneTimeSchedule(optionDay, optionTime, existingTime) {
2080
+ if (optionDay && optionTime) {
2081
+ if (!validateDateFormat(optionDay)) {
2082
+ throw new Error(
2083
+ `Invalid date format: ${optionDay}. Use YYYY-MM-DD format.`
2084
+ );
2085
+ }
2086
+ if (!validateTimeFormat(optionTime)) {
2087
+ throw new Error(`Invalid time format: ${optionTime}. Use HH:MM format.`);
2088
+ }
2089
+ return `${optionDay} ${optionTime}`;
2090
+ }
2091
+ if (!isInteractive()) {
2092
+ throw new Error("One-time schedules require interactive mode", {
2093
+ cause: new Error(
2094
+ "Or provide --day (YYYY-MM-DD) and --time (HH:MM) flags"
2095
+ )
2096
+ });
2097
+ }
2098
+ const tomorrowDate = getTomorrowDateLocal();
2099
+ const date = await promptText(
2100
+ "Date (YYYY-MM-DD, default tomorrow)",
2101
+ tomorrowDate,
2102
+ validateDateFormat
2103
+ );
2104
+ if (!date) return null;
2105
+ const currentTime = getCurrentTimeLocal();
2106
+ const time = await promptText(
2107
+ "Time (HH:MM)",
2108
+ existingTime || currentTime,
2109
+ validateTimeFormat
2110
+ );
2111
+ if (!time) return null;
2112
+ return `${date} ${time}`;
2113
+ }
2114
+ async function gatherTimezone(optionTimezone, existingTimezone) {
2115
+ if (optionTimezone) return optionTimezone;
2116
+ let userTimezone = null;
2117
+ try {
2118
+ const prefs = await getZeroUserPreferences();
2119
+ userTimezone = prefs.timezone;
2120
+ } catch {
2121
+ console.log(
2122
+ chalk32.dim("Could not fetch timezone preference, using detected timezone")
2123
+ );
2124
+ }
2125
+ const defaultTimezone = userTimezone || detectTimezone();
2126
+ if (!isInteractive()) {
2127
+ return defaultTimezone;
2128
+ }
2129
+ return await promptText("Timezone", existingTimezone || defaultTimezone);
2130
+ }
2131
+ async function gatherPromptText(optionPrompt, existingPrompt) {
2132
+ if (optionPrompt) return optionPrompt;
2133
+ if (!isInteractive()) {
2134
+ throw new Error("--prompt is required");
2135
+ }
2136
+ return await promptText(
2137
+ "Prompt to run",
2138
+ existingPrompt || "let's start working."
2139
+ );
2140
+ }
2141
+ async function gatherNotificationPreferences(optionNotifyEmail, optionNotifySlack, existingSchedule) {
2142
+ if (optionNotifyEmail !== void 0 && optionNotifySlack !== void 0) {
2143
+ return {
2144
+ notifyEmail: optionNotifyEmail,
2145
+ notifySlack: optionNotifySlack
2146
+ };
2147
+ }
2148
+ if (!isInteractive()) {
2149
+ return {
2150
+ notifyEmail: optionNotifyEmail,
2151
+ notifySlack: optionNotifySlack
2152
+ };
2153
+ }
2154
+ const notifyEmail = optionNotifyEmail ?? await promptConfirm(
2155
+ "Enable email notifications?",
2156
+ existingSchedule?.notifyEmail ?? true
2157
+ );
2158
+ const notifySlack = optionNotifySlack ?? await promptConfirm(
2159
+ "Enable Slack notifications?",
2160
+ existingSchedule?.notifySlack ?? true
2161
+ );
2162
+ return { notifyEmail, notifySlack };
2163
+ }
2164
+ async function gatherInterval(optionInterval, existingInterval) {
2165
+ if (optionInterval) {
2166
+ const val = parseInt(optionInterval, 10);
2167
+ if (isNaN(val) || val < 0) {
2168
+ throw new Error(
2169
+ "Invalid interval. Must be a non-negative integer (seconds)"
2170
+ );
2171
+ }
2172
+ return val;
2173
+ }
2174
+ if (!isInteractive()) {
2175
+ throw new Error("--interval is required for loop schedules (seconds)");
2176
+ }
2177
+ const defaultVal = existingInterval !== void 0 ? String(existingInterval) : "300";
2178
+ const result = await promptText(
2179
+ "Interval in seconds (time between runs)",
2180
+ defaultVal,
2181
+ (v) => {
2182
+ const n = parseInt(v, 10);
2183
+ if (isNaN(n) || n < 0) return "Must be a non-negative integer";
2184
+ return true;
2185
+ }
2186
+ );
2187
+ if (!result) return null;
2188
+ return parseInt(result, 10);
2189
+ }
2190
+ async function gatherTiming(frequency, options, defaults) {
2191
+ if (frequency === "loop") {
2192
+ const intervalSeconds = await gatherInterval(
2193
+ options.interval,
2194
+ defaults.intervalSeconds
2195
+ );
2196
+ if (intervalSeconds === null) return null;
2197
+ return {
2198
+ day: void 0,
2199
+ time: void 0,
2200
+ atTime: void 0,
2201
+ intervalSeconds
2202
+ };
2203
+ }
2204
+ if (frequency === "once") {
2205
+ const result = await gatherOneTimeSchedule(
2206
+ options.day,
2207
+ options.time,
2208
+ defaults.time
2209
+ );
2210
+ if (!result) return null;
2211
+ return {
2212
+ day: void 0,
2213
+ time: void 0,
2214
+ atTime: result,
2215
+ intervalSeconds: void 0
2216
+ };
2217
+ }
2218
+ const day = await gatherDay(frequency, options.day, defaults.day) ?? void 0;
2219
+ if (day === null && (frequency === "weekly" || frequency === "monthly")) {
2220
+ return null;
2221
+ }
2222
+ const time = await gatherRecurringTime(options.time, defaults.time);
2223
+ if (!time) return null;
2224
+ return { day, time, atTime: void 0, intervalSeconds: void 0 };
2225
+ }
2226
+ async function findExistingSchedule(agentId, scheduleName) {
2227
+ const { schedules } = await listZeroSchedules();
2228
+ return schedules.find(
2229
+ (s) => s.agentId === agentId && s.name === scheduleName
2230
+ );
2231
+ }
2232
+ async function buildAndDeploy(params) {
2233
+ let cronExpression;
2234
+ let atTimeISO;
2235
+ if (params.frequency === "loop") {
2236
+ } else if (params.atTime) {
2237
+ atTimeISO = toISODateTime(params.atTime);
2238
+ } else if (params.time && params.frequency !== "once") {
2239
+ cronExpression = generateCronExpression(
2240
+ params.frequency,
2241
+ params.time,
2242
+ params.day
2243
+ );
2244
+ }
2245
+ console.log(
2246
+ `
2247
+ Deploying schedule for agent ${chalk32.cyan(params.agentName)}...`
2248
+ );
2249
+ const deployResult = await deployZeroSchedule({
2250
+ name: params.scheduleName,
2251
+ agentId: params.agentId,
2252
+ cronExpression,
2253
+ atTime: atTimeISO,
2254
+ intervalSeconds: params.intervalSeconds,
2255
+ timezone: params.timezone,
2256
+ prompt: params.prompt,
2257
+ artifactName: params.artifactName,
2258
+ ...params.notifyEmail !== void 0 && {
2259
+ notifyEmail: params.notifyEmail
2260
+ },
2261
+ ...params.notifySlack !== void 0 && {
2262
+ notifySlack: params.notifySlack
2263
+ }
2264
+ });
2265
+ return deployResult;
2266
+ }
2267
+ function displayDeployResult(agentName, deployResult) {
2268
+ if (deployResult.created) {
2269
+ console.log(
2270
+ chalk32.green(`\u2713 Created schedule for agent ${chalk32.cyan(agentName)}`)
2271
+ );
2272
+ } else {
2273
+ console.log(
2274
+ chalk32.green(`\u2713 Updated schedule for agent ${chalk32.cyan(agentName)}`)
2275
+ );
2276
+ }
2277
+ console.log(chalk32.dim(` Timezone: ${deployResult.schedule.timezone}`));
2278
+ if (deployResult.schedule.triggerType === "loop" && deployResult.schedule.intervalSeconds != null) {
2279
+ console.log(
2280
+ chalk32.dim(
2281
+ ` Mode: Loop (interval ${deployResult.schedule.intervalSeconds}s)`
2282
+ )
2283
+ );
2284
+ } else if (deployResult.schedule.cronExpression) {
2285
+ console.log(chalk32.dim(` Cron: ${deployResult.schedule.cronExpression}`));
2286
+ if (deployResult.schedule.nextRunAt) {
2287
+ const nextRun = formatInTimezone(
2288
+ deployResult.schedule.nextRunAt,
2289
+ deployResult.schedule.timezone
2290
+ );
2291
+ console.log(chalk32.dim(` Next run: ${nextRun}`));
2292
+ }
2293
+ } else if (deployResult.schedule.atTime) {
2294
+ const atTimeFormatted = formatInTimezone(
2295
+ deployResult.schedule.atTime,
2296
+ deployResult.schedule.timezone
2297
+ );
2298
+ console.log(chalk32.dim(` At: ${atTimeFormatted}`));
2299
+ }
2300
+ }
2301
+ async function tryEnableSchedule(scheduleName, agentId, agentName) {
2302
+ try {
2303
+ await enableZeroSchedule({ name: scheduleName, agentId });
2304
+ console.log(
2305
+ chalk32.green(`\u2713 Enabled schedule for agent ${chalk32.cyan(agentName)}`)
2306
+ );
2307
+ } catch (error) {
2308
+ console.error(chalk32.yellow("\u26A0 Failed to enable schedule"));
2309
+ if (error instanceof ApiRequestError) {
2310
+ if (error.code === "SCHEDULE_PAST") {
2311
+ console.error(chalk32.dim(" Scheduled time has already passed"));
2312
+ } else {
2313
+ console.error(chalk32.dim(` ${error.message}`));
2314
+ }
2315
+ } else if (error instanceof Error) {
2316
+ console.error(chalk32.dim(` ${error.message}`));
2317
+ }
2318
+ console.log(
2319
+ ` To enable manually: ${chalk32.cyan(`zero schedule enable ${agentName}`)}`
2320
+ );
2321
+ }
2322
+ }
2323
+ function showEnableHint(agentName) {
2324
+ console.log();
2325
+ console.log(
2326
+ ` To enable: ${chalk32.cyan(`zero schedule enable ${agentName}`)}`
2327
+ );
2328
+ }
2329
+ async function handleScheduleEnabling(params) {
2330
+ const { scheduleName, agentId, agentName, enableFlag, shouldPromptEnable } = params;
2331
+ if (enableFlag) {
2332
+ await tryEnableSchedule(scheduleName, agentId, agentName);
2333
+ return;
2334
+ }
2335
+ if (shouldPromptEnable && isInteractive()) {
2336
+ const enableNow = await promptConfirm("Enable this schedule?", true);
2337
+ if (enableNow) {
2338
+ await tryEnableSchedule(scheduleName, agentId, agentName);
2339
+ } else {
2340
+ showEnableHint(agentName);
2341
+ }
2342
+ return;
2343
+ }
2344
+ if (shouldPromptEnable) {
2345
+ showEnableHint(agentName);
2346
+ }
2347
+ }
2348
+ var setupCommand2 = new Command36().name("setup").description("Create or edit a schedule for a zero agent").argument("<agent-name>", "Agent name to configure schedule for").option("-n, --name <schedule-name>", 'Schedule name (default: "default")').option("-f, --frequency <type>", "Frequency: daily|weekly|monthly|once|loop").option("-t, --time <HH:MM>", "Time to run (24-hour format)").option("-d, --day <day>", "Day of week (mon-sun) or day of month (1-31)").option("-i, --interval <seconds>", "Interval in seconds for loop mode").option("-z, --timezone <tz>", "IANA timezone").option("-p, --prompt <text>", "Prompt to run").option("--artifact-name <name>", "Artifact name", "artifact").option("-e, --enable", "Enable schedule immediately after creation").option("--notify-email", "Enable email notifications (default: true)").option("--no-notify-email", "Disable email notifications").option("--notify-slack", "Enable Slack notifications (default: true)").option("--no-notify-slack", "Disable Slack notifications").action(
2349
+ withErrorHandler(async (agentName, options) => {
2350
+ const compose = await getComposeByName(agentName);
2351
+ if (!compose) {
2352
+ throw new Error(`Agent not found: ${agentName}`);
2353
+ }
2354
+ const agentId = compose.id;
2355
+ const scheduleName = options.name || "default";
2356
+ const existingSchedule = await findExistingSchedule(
2357
+ agentId,
2358
+ scheduleName
2359
+ );
2360
+ console.log(
2361
+ chalk32.dim(
2362
+ existingSchedule ? `Editing existing schedule for agent ${agentName}` : `Creating new schedule for agent ${agentName}`
2363
+ )
2364
+ );
2365
+ const defaults = getExistingDefaults(existingSchedule);
2366
+ const frequency = await gatherFrequency(
2367
+ options.frequency,
2368
+ defaults.frequency
2369
+ );
2370
+ if (!frequency) {
2371
+ console.log(chalk32.dim("Cancelled"));
2372
+ return;
2373
+ }
2374
+ const timing = await gatherTiming(frequency, options, defaults);
2375
+ if (!timing) {
2376
+ console.log(chalk32.dim("Cancelled"));
2377
+ return;
2378
+ }
2379
+ const { day, time, atTime, intervalSeconds } = timing;
2380
+ const timezone = await gatherTimezone(
2381
+ options.timezone,
2382
+ existingSchedule?.timezone
2383
+ );
2384
+ if (!timezone) {
2385
+ console.log(chalk32.dim("Cancelled"));
2386
+ return;
2387
+ }
2388
+ const promptText_ = await gatherPromptText(
2389
+ options.prompt,
2390
+ existingSchedule?.prompt
2391
+ );
2392
+ if (!promptText_) {
2393
+ console.log(chalk32.dim("Cancelled"));
2394
+ return;
2395
+ }
2396
+ const { notifyEmail, notifySlack } = await gatherNotificationPreferences(
2397
+ options.notifyEmail,
2398
+ options.notifySlack,
2399
+ existingSchedule
2400
+ );
2401
+ const deployResult = await buildAndDeploy({
2402
+ scheduleName,
2403
+ agentId,
2404
+ agentName,
2405
+ frequency,
2406
+ time,
2407
+ day,
2408
+ atTime,
2409
+ intervalSeconds,
2410
+ timezone,
2411
+ prompt: promptText_,
2412
+ artifactName: options.artifactName,
2413
+ notifyEmail,
2414
+ notifySlack
2415
+ });
2416
+ displayDeployResult(agentName, deployResult);
2417
+ const shouldPromptEnable = deployResult.created || existingSchedule !== void 0 && !existingSchedule.enabled;
2418
+ await handleScheduleEnabling({
2419
+ scheduleName,
2420
+ agentId,
2421
+ agentName,
2422
+ enableFlag: options.enable ?? false,
2423
+ shouldPromptEnable
2424
+ });
2425
+ })
2426
+ );
2427
+
2428
+ // src/commands/zero/schedule/list.ts
2429
+ import { Command as Command37 } from "commander";
2430
+ import chalk33 from "chalk";
2431
+ var listCommand7 = new Command37().name("list").alias("ls").description("List all zero schedules").action(
2432
+ withErrorHandler(async () => {
2433
+ const result = await listZeroSchedules();
2434
+ if (result.schedules.length === 0) {
2435
+ console.log(chalk33.dim("No schedules found"));
2436
+ console.log(
2437
+ chalk33.dim(" Create one with: zero schedule setup <agent-name>")
2438
+ );
2439
+ return;
2440
+ }
2441
+ const agentWidth = Math.max(
2442
+ 5,
2443
+ ...result.schedules.map((s) => s.agentId.length)
2444
+ );
2445
+ const scheduleWidth = Math.max(
2446
+ 8,
2447
+ ...result.schedules.map((s) => s.name.length)
2448
+ );
2449
+ const triggerWidth = Math.max(
2450
+ 7,
2451
+ ...result.schedules.map(
2452
+ (s) => s.cronExpression ? s.cronExpression.length + s.timezone.length + 3 : s.atTime?.length || 0
2453
+ )
2454
+ );
2455
+ const header = [
2456
+ "AGENT".padEnd(agentWidth),
2457
+ "SCHEDULE".padEnd(scheduleWidth),
2458
+ "TRIGGER".padEnd(triggerWidth),
2459
+ "STATUS".padEnd(8),
2460
+ "NEXT RUN"
2461
+ ].join(" ");
2462
+ console.log(chalk33.dim(header));
2463
+ for (const schedule of result.schedules) {
2464
+ const trigger = schedule.cronExpression ? `${schedule.cronExpression} (${schedule.timezone})` : schedule.atTime || "-";
2465
+ const status = schedule.enabled ? chalk33.green("enabled") : chalk33.yellow("disabled");
2466
+ const nextRun = schedule.enabled ? formatRelativeTime(schedule.nextRunAt) : "-";
2467
+ const row = [
2468
+ schedule.agentId.padEnd(agentWidth),
2469
+ schedule.name.padEnd(scheduleWidth),
2470
+ trigger.padEnd(triggerWidth),
2471
+ status.padEnd(8 + (schedule.enabled ? 0 : 2)),
2472
+ nextRun
2473
+ ].join(" ");
2474
+ console.log(row);
2475
+ }
2476
+ })
2477
+ );
2478
+
2479
+ // src/commands/zero/schedule/status.ts
2480
+ import { Command as Command38 } from "commander";
2481
+ import chalk34 from "chalk";
2482
+ function formatDateTimeStyled(dateStr) {
2483
+ if (!dateStr) return chalk34.dim("-");
2484
+ const formatted = formatDateTime(dateStr);
2485
+ return formatted.replace(/\(([^)]+)\)$/, chalk34.dim("($1)"));
2486
+ }
2487
+ function formatTrigger(schedule) {
2488
+ if (schedule.triggerType === "loop" && schedule.intervalSeconds !== null) {
2489
+ return `interval ${schedule.intervalSeconds}s ${chalk34.dim("(loop)")}`;
2490
+ }
2491
+ if (schedule.cronExpression) {
2492
+ return schedule.cronExpression;
2493
+ }
2494
+ if (schedule.atTime) {
2495
+ return `${schedule.atTime} ${chalk34.dim("(one-time)")}`;
2496
+ }
2497
+ return chalk34.dim("-");
2498
+ }
2499
+ function printRunConfiguration(schedule) {
2500
+ const statusText = schedule.enabled ? chalk34.green("enabled") : chalk34.yellow("disabled");
2501
+ console.log(`${"Status:".padEnd(16)}${statusText}`);
2502
+ console.log(
2503
+ `${"Agent:".padEnd(16)}${schedule.agentId} ${chalk34.dim(`(${schedule.orgSlug})`)}`
2504
+ );
2505
+ const promptPreview = schedule.prompt.length > 60 ? schedule.prompt.slice(0, 57) + "..." : schedule.prompt;
2506
+ console.log(`${"Prompt:".padEnd(16)}${chalk34.dim(promptPreview)}`);
2507
+ if (schedule.vars && Object.keys(schedule.vars).length > 0) {
2508
+ console.log(
2509
+ `${"Variables:".padEnd(16)}${Object.keys(schedule.vars).join(", ")}`
2510
+ );
2511
+ }
2512
+ if (schedule.secretNames && schedule.secretNames.length > 0) {
2513
+ console.log(`${"Secrets:".padEnd(16)}${schedule.secretNames.join(", ")}`);
2514
+ }
2515
+ if (schedule.artifactName) {
2516
+ const artifactInfo = schedule.artifactVersion ? `${schedule.artifactName}:${schedule.artifactVersion}` : schedule.artifactName;
2517
+ console.log(`${"Artifact:".padEnd(16)}${artifactInfo}`);
2518
+ }
2519
+ if (schedule.volumeVersions && Object.keys(schedule.volumeVersions).length > 0) {
2520
+ console.log(
2521
+ `${"Volumes:".padEnd(16)}${Object.keys(schedule.volumeVersions).join(", ")}`
2522
+ );
2523
+ }
2524
+ }
2525
+ function printTimeSchedule(schedule) {
2526
+ console.log();
2527
+ console.log(`${"Trigger:".padEnd(16)}${formatTrigger(schedule)}`);
2528
+ console.log(`${"Timezone:".padEnd(16)}${detectTimezone()}`);
2529
+ if (schedule.enabled) {
2530
+ console.log(
2531
+ `${"Next Run:".padEnd(16)}${formatDateTimeStyled(schedule.nextRunAt)}`
2532
+ );
2533
+ }
2534
+ if (schedule.triggerType === "loop") {
2535
+ const failureText = schedule.consecutiveFailures > 0 ? chalk34.yellow(`${schedule.consecutiveFailures}/3`) : chalk34.dim("0/3");
2536
+ console.log(`${"Failures:".padEnd(16)}${failureText}`);
2537
+ }
2538
+ }
2539
+ var statusCommand3 = new Command38().name("status").description("Show detailed status of a zero schedule").argument("<agent-name>", "Agent name").option(
2540
+ "-n, --name <schedule-name>",
2541
+ "Schedule name (required when agent has multiple schedules)"
2542
+ ).action(
2543
+ withErrorHandler(async (agentName, options) => {
2544
+ const schedule = await resolveZeroScheduleByAgent(
2545
+ agentName,
2546
+ options.name
2547
+ );
2548
+ console.log();
2549
+ console.log(`Schedule for agent: ${chalk34.cyan(agentName)}`);
2550
+ console.log(chalk34.dim("\u2501".repeat(50)));
2551
+ printRunConfiguration(schedule);
2552
+ printTimeSchedule(schedule);
2553
+ console.log();
2554
+ })
2555
+ );
2556
+
2557
+ // src/commands/zero/schedule/delete.ts
2558
+ import { Command as Command39 } from "commander";
2559
+ import chalk35 from "chalk";
2560
+ var deleteCommand3 = new Command39().name("delete").alias("rm").description("Delete a zero schedule").argument("<agent-name>", "Agent name").option(
2561
+ "-n, --name <schedule-name>",
2562
+ "Schedule name (required when agent has multiple schedules)"
2563
+ ).option("-y, --yes", "Skip confirmation prompt").action(
2564
+ withErrorHandler(
2565
+ async (agentName, options) => {
2566
+ const resolved = await resolveZeroScheduleByAgent(
2567
+ agentName,
2568
+ options.name
2569
+ );
2570
+ if (!options.yes) {
2571
+ if (!isInteractive()) {
2572
+ throw new Error("--yes flag is required in non-interactive mode");
2573
+ }
2574
+ const confirmed = await promptConfirm(
2575
+ `Delete schedule for agent ${chalk35.cyan(agentName)}?`,
2576
+ false
2577
+ );
2578
+ if (!confirmed) {
2579
+ console.log(chalk35.dim("Cancelled"));
2580
+ return;
2581
+ }
2582
+ }
2583
+ await deleteZeroSchedule({
2584
+ name: resolved.name,
2585
+ agentId: resolved.agentId
2586
+ });
2587
+ console.log(
2588
+ chalk35.green(`\u2713 Deleted schedule for agent ${chalk35.cyan(agentName)}`)
2589
+ );
2590
+ }
2591
+ )
2592
+ );
2593
+
2594
+ // src/commands/zero/schedule/enable.ts
2595
+ import { Command as Command40 } from "commander";
2596
+ import chalk36 from "chalk";
2597
+ var enableCommand = new Command40().name("enable").description("Enable a zero schedule").argument("<agent-name>", "Agent name").option(
2598
+ "-n, --name <schedule-name>",
2599
+ "Schedule name (required when agent has multiple schedules)"
2600
+ ).action(
2601
+ withErrorHandler(async (agentName, options) => {
2602
+ const resolved = await resolveZeroScheduleByAgent(
2603
+ agentName,
2604
+ options.name
2605
+ );
2606
+ await enableZeroSchedule({
2607
+ name: resolved.name,
2608
+ agentId: resolved.agentId
2609
+ });
2610
+ console.log(
2611
+ chalk36.green(`\u2713 Enabled schedule for agent ${chalk36.cyan(agentName)}`)
2612
+ );
2613
+ })
2614
+ );
2615
+
2616
+ // src/commands/zero/schedule/disable.ts
2617
+ import { Command as Command41 } from "commander";
2618
+ import chalk37 from "chalk";
2619
+ var disableCommand = new Command41().name("disable").description("Disable a zero schedule").argument("<agent-name>", "Agent name").option(
2620
+ "-n, --name <schedule-name>",
2621
+ "Schedule name (required when agent has multiple schedules)"
2622
+ ).action(
2623
+ withErrorHandler(async (agentName, options) => {
2624
+ const resolved = await resolveZeroScheduleByAgent(
2625
+ agentName,
2626
+ options.name
2627
+ );
2628
+ await disableZeroSchedule({
2629
+ name: resolved.name,
2630
+ agentId: resolved.agentId
2631
+ });
2632
+ console.log(
2633
+ chalk37.green(`\u2713 Disabled schedule for agent ${chalk37.cyan(agentName)}`)
2634
+ );
2635
+ })
2636
+ );
2637
+
2638
+ // src/commands/zero/schedule/index.ts
2639
+ var zeroScheduleCommand = new Command42().name("schedule").description("Manage zero agent schedules").addCommand(setupCommand2).addCommand(listCommand7).addCommand(statusCommand3).addCommand(deleteCommand3).addCommand(enableCommand).addCommand(disableCommand);
2640
+
2641
+ // src/commands/zero/secret/index.ts
2642
+ import { Command as Command46 } from "commander";
2643
+
2644
+ // src/commands/zero/secret/list.ts
2645
+ import { Command as Command43 } from "commander";
2646
+ import chalk38 from "chalk";
2647
+ var listCommand8 = new Command43().name("list").alias("ls").description("List all secrets").action(
2648
+ withErrorHandler(async () => {
2649
+ const result = await listZeroSecrets();
2650
+ if (result.secrets.length === 0) {
2651
+ console.log(chalk38.dim("No secrets found"));
2652
+ console.log();
2653
+ console.log("To add a secret:");
2654
+ console.log(chalk38.cyan(" zero secret set MY_API_KEY --body <value>"));
2655
+ return;
2656
+ }
2657
+ console.log(chalk38.bold("Secrets:"));
2658
+ console.log();
2659
+ for (const secret of result.secrets) {
2660
+ let typeIndicator = "";
2661
+ let derivedLine = null;
2662
+ if (secret.type === "model-provider") {
2663
+ typeIndicator = chalk38.dim(" [model-provider]");
2664
+ } else if (secret.type === "connector") {
2665
+ const derived = getConnectorDerivedNames(secret.name);
2666
+ if (derived) {
2667
+ typeIndicator = chalk38.dim(` [${derived.connectorLabel} connector]`);
2668
+ derivedLine = chalk38.dim(
2669
+ `Available as: ${derived.envVarNames.join(", ")}`
2670
+ );
2671
+ } else {
2672
+ typeIndicator = chalk38.dim(" [connector]");
2673
+ }
2674
+ } else if (secret.type === "user") {
2675
+ const derived = getConnectorDerivedNames(secret.name);
2676
+ if (derived) {
2677
+ typeIndicator = chalk38.dim(` [${derived.connectorLabel} connector]`);
2678
+ derivedLine = chalk38.dim(
2679
+ `Available as: ${derived.envVarNames.join(", ")}`
2680
+ );
2681
+ }
2682
+ }
2683
+ console.log(` ${chalk38.cyan(secret.name)}${typeIndicator}`);
2684
+ if (derivedLine) {
2685
+ console.log(` ${derivedLine}`);
2686
+ }
2687
+ if (secret.description) {
2688
+ console.log(` ${chalk38.dim(secret.description)}`);
2689
+ }
2690
+ console.log(
2691
+ ` ${chalk38.dim(`Updated: ${new Date(secret.updatedAt).toLocaleString()}`)}`
2692
+ );
2693
+ console.log();
2694
+ }
2695
+ console.log(chalk38.dim(`Total: ${result.secrets.length} secret(s)`));
2696
+ })
2697
+ );
2698
+
2699
+ // src/commands/zero/secret/set.ts
2700
+ import { Command as Command44 } from "commander";
2701
+ import chalk39 from "chalk";
2702
+ var setCommand4 = new Command44().name("set").description("Create or update a secret").argument("<name>", "Secret name (uppercase, e.g., MY_API_KEY)").option(
2703
+ "-b, --body <value>",
2704
+ "Secret value (required in non-interactive mode)"
2705
+ ).option("-d, --description <description>", "Optional description").action(
2706
+ withErrorHandler(
2707
+ async (name, options) => {
2708
+ let value;
2709
+ if (options.body !== void 0) {
2710
+ value = options.body;
2711
+ } else if (isInteractive()) {
2712
+ const prompted = await promptPassword("Enter secret value:");
2713
+ if (prompted === void 0) {
2714
+ process.exit(0);
2715
+ }
2716
+ value = prompted;
2717
+ } else {
2718
+ throw new Error("--body is required in non-interactive mode", {
2719
+ cause: new Error(
2720
+ `Usage: zero secret set ${name} --body "your-secret-value"`
2721
+ )
2722
+ });
2723
+ }
2724
+ let secret;
2725
+ try {
2726
+ secret = await setZeroSecret({
2727
+ name,
2728
+ value,
2729
+ description: options.description
2730
+ });
2731
+ } catch (error) {
2732
+ if (error instanceof Error && error.message.includes("must contain only uppercase")) {
2733
+ throw new Error(error.message, {
2734
+ cause: new Error(
2735
+ "Examples of valid secret names: MY_API_KEY, GITHUB_TOKEN, AWS_ACCESS_KEY_ID"
2736
+ )
2737
+ });
2738
+ }
2739
+ throw error;
2740
+ }
2741
+ console.log(chalk39.green(`\u2713 Secret "${secret.name}" saved`));
2742
+ }
2743
+ )
2744
+ );
2745
+
2746
+ // src/commands/zero/secret/delete.ts
2747
+ import { Command as Command45 } from "commander";
2748
+ import chalk40 from "chalk";
2749
+ var deleteCommand4 = new Command45().name("delete").description("Delete a secret").argument("<name>", "Secret name to delete").option("-y, --yes", "Skip confirmation prompt").action(
2750
+ withErrorHandler(async (name, options) => {
2751
+ if (!options.yes) {
2752
+ if (!isInteractive()) {
2753
+ throw new Error("--yes flag is required in non-interactive mode");
2754
+ }
2755
+ const confirmed = await promptConfirm(
2756
+ `Are you sure you want to delete secret "${name}"?`,
2757
+ false
2758
+ );
2759
+ if (!confirmed) {
2760
+ console.log(chalk40.dim("Cancelled"));
2761
+ return;
2762
+ }
2763
+ }
2764
+ await deleteZeroSecret(name);
2765
+ console.log(chalk40.green(`\u2713 Secret "${name}" deleted`));
2766
+ })
2767
+ );
2768
+
2769
+ // src/commands/zero/secret/index.ts
2770
+ var zeroSecretCommand = new Command46().name("secret").description("Manage secrets").addCommand(listCommand8).addCommand(setCommand4).addCommand(deleteCommand4);
2771
+
2772
+ // src/commands/zero/variable/index.ts
2773
+ import { Command as Command50 } from "commander";
2774
+
2775
+ // src/commands/zero/variable/list.ts
2776
+ import { Command as Command47 } from "commander";
2777
+ import chalk41 from "chalk";
2778
+ function truncateValue2(value, maxLength = 60) {
2779
+ if (value.length <= maxLength) {
2780
+ return value;
2781
+ }
2782
+ return value.slice(0, maxLength - 15) + "... [truncated]";
2783
+ }
2784
+ var listCommand9 = new Command47().name("list").alias("ls").description("List all variables").action(
2785
+ withErrorHandler(async () => {
2786
+ const result = await listZeroVariables();
2787
+ if (result.variables.length === 0) {
2788
+ console.log(chalk41.dim("No variables found"));
2789
+ console.log();
2790
+ console.log("To add a variable:");
2791
+ console.log(chalk41.cyan(" zero variable set MY_VAR <value>"));
2792
+ return;
2793
+ }
2794
+ console.log(chalk41.bold("Variables:"));
2795
+ console.log();
2796
+ for (const variable of result.variables) {
2797
+ const displayValue = truncateValue2(variable.value);
2798
+ console.log(` ${chalk41.cyan(variable.name)} = ${displayValue}`);
2799
+ if (variable.description) {
2800
+ console.log(` ${chalk41.dim(variable.description)}`);
2801
+ }
2802
+ console.log(
2803
+ ` ${chalk41.dim(`Updated: ${new Date(variable.updatedAt).toLocaleString()}`)}`
2804
+ );
2805
+ console.log();
2806
+ }
2807
+ console.log(chalk41.dim(`Total: ${result.variables.length} variable(s)`));
2808
+ })
2809
+ );
2810
+
2811
+ // src/commands/zero/variable/set.ts
2812
+ import { Command as Command48 } from "commander";
2813
+ import chalk42 from "chalk";
2814
+ var setCommand5 = new Command48().name("set").description("Create or update a variable").argument("<name>", "Variable name (uppercase, e.g., MY_VAR)").argument("<value>", "Variable value").option("-d, --description <description>", "Optional description").action(
2815
+ withErrorHandler(
2816
+ async (name, value, options) => {
2817
+ let variable;
2818
+ try {
2819
+ variable = await setZeroVariable({
2820
+ name,
2821
+ value,
2822
+ description: options.description
2823
+ });
2824
+ } catch (error) {
2825
+ if (error instanceof Error && error.message.includes("must contain only uppercase")) {
2826
+ throw new Error(error.message, {
2827
+ cause: new Error(
2828
+ "Examples of valid variable names: MY_VAR, API_URL, DEBUG_MODE"
2829
+ )
2830
+ });
2831
+ }
2832
+ throw error;
2833
+ }
2834
+ console.log(chalk42.green(`\u2713 Variable "${variable.name}" saved`));
2835
+ }
2836
+ )
2837
+ );
2838
+
2839
+ // src/commands/zero/variable/delete.ts
2840
+ import { Command as Command49 } from "commander";
2841
+ import chalk43 from "chalk";
2842
+ var deleteCommand5 = new Command49().name("delete").description("Delete a variable").argument("<name>", "Variable name to delete").option("-y, --yes", "Skip confirmation prompt").action(
2843
+ withErrorHandler(async (name, options) => {
2844
+ if (!options.yes) {
2845
+ if (!isInteractive()) {
2846
+ throw new Error("--yes flag is required in non-interactive mode");
2847
+ }
2848
+ const confirmed = await promptConfirm(
2849
+ `Are you sure you want to delete variable "${name}"?`,
2850
+ false
2851
+ );
2852
+ if (!confirmed) {
2853
+ console.log(chalk43.dim("Cancelled"));
2854
+ return;
2855
+ }
2856
+ }
2857
+ await deleteZeroVariable(name);
2858
+ console.log(chalk43.green(`\u2713 Variable "${name}" deleted`));
2859
+ })
2860
+ );
2861
+
2862
+ // src/commands/zero/variable/index.ts
2863
+ var zeroVariableCommand = new Command50().name("variable").description("Manage variables").addCommand(listCommand9).addCommand(setCommand5).addCommand(deleteCommand5);
2864
+
2865
+ // src/commands/zero/whoami.ts
2866
+ import { Command as Command51 } from "commander";
2867
+ import chalk44 from "chalk";
2868
+ function isInsideSandbox() {
2869
+ return !!process.env.ZERO_AGENT_ID;
2870
+ }
2871
+ async function showSandboxInfo() {
2872
+ const agentId = process.env.ZERO_AGENT_ID;
2873
+ const payload = decodeZeroTokenPayload();
2874
+ console.log(chalk44.bold("Agent:"));
2875
+ console.log(` ID: ${agentId}`);
2876
+ console.log();
2877
+ console.log(chalk44.bold("Run:"));
2878
+ console.log(` ID: ${payload?.runId ?? chalk44.dim("unavailable")}`);
2879
+ console.log(` Org: ${payload?.orgId ?? chalk44.dim("unavailable")}`);
2880
+ if (payload?.capabilities?.length) {
2881
+ console.log();
2882
+ console.log(chalk44.bold("Capabilities:"));
2883
+ console.log(` ${payload.capabilities.join(", ")}`);
2884
+ }
2885
+ }
2886
+ async function showLocalInfo() {
2887
+ const token = await getToken();
2888
+ const apiUrl = await getApiUrl();
2889
+ const activeOrg = await getActiveOrg();
2890
+ console.log(chalk44.bold("Auth:"));
2891
+ if (token) {
2892
+ const tokenSource = process.env.ZERO_TOKEN ? "ZERO_TOKEN env var" : process.env.VM0_TOKEN ? "VM0_TOKEN env var" : "config file";
2893
+ console.log(
2894
+ ` Status: ${chalk44.green("Authenticated")} (via ${tokenSource})`
2895
+ );
2896
+ } else {
2897
+ console.log(` Status: ${chalk44.dim("Not authenticated")}`);
2898
+ }
2899
+ console.log(` API: ${apiUrl}`);
2900
+ console.log();
2901
+ if (activeOrg) {
2902
+ console.log(chalk44.bold("Org:"));
2903
+ console.log(` Active: ${activeOrg}`);
2904
+ }
2905
+ }
2906
+ var zeroWhoamiCommand = new Command51().name("whoami").description("Show current identity and environment information").action(
2907
+ withErrorHandler(async () => {
2908
+ if (isInsideSandbox()) {
2909
+ await showSandboxInfo();
2910
+ } else {
2911
+ await showLocalInfo();
2912
+ }
2913
+ })
2914
+ );
2915
+
2916
+ // src/zero.ts
2917
+ var program = new Command52();
2918
+ program.name("zero").description("Zero CLI - Manage your zero platform").version("9.81.0");
17
2919
  program.addCommand(zeroOrgCommand);
18
2920
  program.addCommand(agentCommand);
19
2921
  program.addCommand(zeroConnectorCommand);
@@ -21,6 +2923,7 @@ program.addCommand(zeroPreferenceCommand);
21
2923
  program.addCommand(zeroScheduleCommand);
22
2924
  program.addCommand(zeroSecretCommand);
23
2925
  program.addCommand(zeroVariableCommand);
2926
+ program.addCommand(zeroWhoamiCommand);
24
2927
  if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {
25
2928
  process.stdout.on("error", (err) => {
26
2929
  if (err.code === "EPIPE") process.exit(0);