@vm0/cli 9.202.2 → 9.204.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
@@ -13,7 +13,6 @@ import {
13
13
  Option,
14
14
  PRESENTATION_REQUIRED_RESOURCE_IDS,
15
15
  UNKNOWN_PERMISSION_GRANT,
16
- addAutomationTrigger,
17
16
  blockGoal,
18
17
  callZeroBanking,
19
18
  callZeroMaps,
@@ -38,6 +37,7 @@ import {
38
37
  createWorkflowTrigger,
39
38
  createZeroAgent,
40
39
  createZeroCreditCheckout,
40
+ customConnectorProposalSchema,
41
41
  decodeZeroTokenPayload,
42
42
  deleteAutomation,
43
43
  deleteGithubLabelListener,
@@ -51,7 +51,6 @@ import {
51
51
  deleteZeroSecret,
52
52
  deleteZeroVariable,
53
53
  disableAutomation,
54
- disableAutomationTrigger,
55
54
  disableWorkflowTrigger,
56
55
  downloadGithubFile,
57
56
  downloadPhoneFile,
@@ -60,7 +59,6 @@ import {
60
59
  downloadWebFile,
61
60
  editGoal,
62
61
  enableAutomation,
63
- enableAutomationTrigger,
64
62
  enableWorkflowTrigger,
65
63
  extractSecretNamesFromApis,
66
64
  fetchComputerUseScreenshot,
@@ -99,10 +97,12 @@ import {
99
97
  getWorkflow,
100
98
  getWorkflowTrigger,
101
99
  getZeroAgent,
100
+ getZeroAgentCustomConnectors,
102
101
  getZeroAgentInstructions,
103
102
  getZeroAgentUserConnectors,
104
103
  getZeroBillingStatus,
105
104
  getZeroConnector,
105
+ getZeroCustomConnector,
106
106
  getZeroOrg,
107
107
  getZeroOrgMembers,
108
108
  getZeroRunAgentEvents,
@@ -120,7 +120,6 @@ import {
120
120
  isInteractive,
121
121
  isUUID,
122
122
  leaveZeroOrg,
123
- listAutomationTriggers,
124
123
  listAutomations,
125
124
  listDesignSystems,
126
125
  listImageStyles,
@@ -131,6 +130,7 @@ import {
131
130
  listWorkflows,
132
131
  listZeroAgents,
133
132
  listZeroConnectors,
133
+ listZeroCustomConnectors,
134
134
  listZeroLogs,
135
135
  listZeroModelPolicies,
136
136
  listZeroOrgModelProviders,
@@ -149,7 +149,6 @@ import {
149
149
  promptConfirm,
150
150
  promptPassword,
151
151
  promptText,
152
- removeAutomationTrigger,
153
152
  removeZeroOrgMember,
154
153
  requestDeveloperSupportConsent,
155
154
  require_prompts,
@@ -173,7 +172,6 @@ import {
173
172
  setZeroSecret,
174
173
  setZeroVariable,
175
174
  showAutomation,
176
- showAutomationTrigger,
177
175
  source_default,
178
176
  submitDeveloperSupport,
179
177
  switchZeroOrg,
@@ -192,7 +190,7 @@ import {
192
190
  uploadWebFile,
193
191
  upsertZeroOrgModelProvider,
194
192
  withErrorHandler
195
- } from "./chunk-ABA3BGIH.js";
193
+ } from "./chunk-NQ6BMAY6.js";
196
194
  import "./chunk-NR42YJMI.js";
197
195
  import {
198
196
  __toESM,
@@ -2370,8 +2368,157 @@ var statusCommand2 = new Command().name("status").description("Show detailed sta
2370
2368
  })
2371
2369
  );
2372
2370
 
2371
+ // src/commands/zero/connector/custom/index.ts
2372
+ init_esm_shims();
2373
+ import { readFile } from "fs/promises";
2374
+ var LABEL_WIDTH2 = 18;
2375
+ function renderConnected(connector) {
2376
+ if (connector.connected) {
2377
+ return source_default.green("connected");
2378
+ }
2379
+ if (connector.missingRequiredFields.length === 0) {
2380
+ return source_default.dim("not connected");
2381
+ }
2382
+ return source_default.yellow(`missing ${connector.missingRequiredFields.join(", ")}`);
2383
+ }
2384
+ function encodeBase64UrlJson(value) {
2385
+ return Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
2386
+ }
2387
+ function isJsonObject(value) {
2388
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2389
+ }
2390
+ async function resolveCustomAgentContext(agentId) {
2391
+ const resolvedAgentId = agentId ?? process.env.ZERO_AGENT_ID;
2392
+ if (!resolvedAgentId) {
2393
+ return null;
2394
+ }
2395
+ const [agent, enabledIds] = await Promise.all([
2396
+ getZeroAgent(resolvedAgentId),
2397
+ getZeroAgentCustomConnectors(resolvedAgentId)
2398
+ ]);
2399
+ return {
2400
+ agentId: agent.agentId,
2401
+ displayName: agent.displayName ?? agent.agentId,
2402
+ authorizedIds: new Set(enabledIds)
2403
+ };
2404
+ }
2405
+ var listCommand7 = new Command().name("list").alias("ls").description("List org custom connectors").option("--agent <id>", "Show per-agent authorization column").action(
2406
+ withErrorHandler(async (options) => {
2407
+ const [connectors, agentCtx] = await Promise.all([
2408
+ listZeroCustomConnectors(),
2409
+ resolveCustomAgentContext(options.agent)
2410
+ ]);
2411
+ const idWidth = Math.max(
2412
+ 2,
2413
+ ...connectors.map((connector) => {
2414
+ return connector.id.length;
2415
+ })
2416
+ );
2417
+ const nameWidth = Math.max(
2418
+ 4,
2419
+ ...connectors.map((connector) => {
2420
+ return connector.displayName.length;
2421
+ })
2422
+ );
2423
+ const header = ["ID".padEnd(idWidth), "NAME".padEnd(nameWidth), "STATUS"];
2424
+ if (agentCtx) {
2425
+ header.push(`AUTHORIZED FOR ${agentCtx.displayName}`);
2426
+ }
2427
+ console.log(source_default.dim(header.join(" ")));
2428
+ for (const connector of connectors) {
2429
+ const row = [
2430
+ connector.id.padEnd(idWidth),
2431
+ connector.displayName.padEnd(nameWidth),
2432
+ renderConnected(connector)
2433
+ ];
2434
+ if (agentCtx) {
2435
+ row.push(
2436
+ agentCtx.authorizedIds.has(connector.id) ? source_default.green("\u2713") : source_default.dim("-")
2437
+ );
2438
+ }
2439
+ console.log(row.join(" "));
2440
+ }
2441
+ })
2442
+ );
2443
+ var statusCommand3 = new Command().name("status").description("Show detailed status of a custom connector").argument("<connector-id>", "Custom connector id").option("--agent <id>", "Show authorization state for the given agent").action(
2444
+ withErrorHandler(
2445
+ async (connectorId, options) => {
2446
+ const [connector, agentCtx] = await Promise.all([
2447
+ getZeroCustomConnector(connectorId),
2448
+ resolveCustomAgentContext(options.agent)
2449
+ ]);
2450
+ if (!connector) {
2451
+ throw new Error(`Custom connector not found: ${connectorId}`);
2452
+ }
2453
+ console.log(`Custom connector: ${source_default.cyan(connector.displayName)}`);
2454
+ console.log();
2455
+ console.log(`${"ID:".padEnd(LABEL_WIDTH2)}${connector.id}`);
2456
+ console.log(
2457
+ `${"Status:".padEnd(LABEL_WIDTH2)}${renderConnected(connector)}`
2458
+ );
2459
+ console.log(
2460
+ `${"Prefixes:".padEnd(LABEL_WIDTH2)}${connector.prefixTemplates.join(", ")}`
2461
+ );
2462
+ console.log(
2463
+ `${"Fields:".padEnd(LABEL_WIDTH2)}${connector.fields.map((field) => {
2464
+ return `${field.kind}:${field.key}${field.required ? "" : "?"}`;
2465
+ }).join(", ")}`
2466
+ );
2467
+ if (connector.headerInjections.length > 0) {
2468
+ console.log(
2469
+ `${"Headers:".padEnd(LABEL_WIDTH2)}${connector.headerInjections.map((header) => {
2470
+ return header.name;
2471
+ }).join(", ")}`
2472
+ );
2473
+ }
2474
+ if (connector.queryInjections.length > 0) {
2475
+ console.log(
2476
+ `${"Query params:".padEnd(LABEL_WIDTH2)}${connector.queryInjections.map((query) => {
2477
+ return query.name;
2478
+ }).join(", ")}`
2479
+ );
2480
+ }
2481
+ if (agentCtx) {
2482
+ console.log(
2483
+ `${"Authorized:".padEnd(LABEL_WIDTH2)}${agentCtx.authorizedIds.has(connector.id) ? source_default.green("yes") : source_default.yellow("no")}`
2484
+ );
2485
+ }
2486
+ }
2487
+ )
2488
+ );
2489
+ var proposeCommand = new Command().name("propose").description("Create a browser save link for a custom connector proposal").requiredOption("--proposal-file <path>", "JSON proposal file").option("--connector-id <id>", "Override connector id for update proposals").option("--agent <id>", "Authorize this agent when the proposal is saved").action(
2490
+ withErrorHandler(
2491
+ async (options) => {
2492
+ const raw = await readFile(options.proposalFile, "utf8");
2493
+ const proposalJson = JSON.parse(raw);
2494
+ if (!isJsonObject(proposalJson)) {
2495
+ throw new Error("Proposal file must contain a JSON object");
2496
+ }
2497
+ const parsed = customConnectorProposalSchema.parse({
2498
+ ...proposalJson,
2499
+ ...options.connectorId ? { connectorId: options.connectorId } : {}
2500
+ });
2501
+ const origin = await getPlatformOrigin();
2502
+ const params = new URLSearchParams({
2503
+ p: encodeBase64UrlJson(parsed)
2504
+ });
2505
+ const agentId = options.agent ?? process.env.ZERO_AGENT_ID;
2506
+ if (agentId) {
2507
+ params.set("agentId", agentId);
2508
+ }
2509
+ const url = `${origin}/connectors/custom/proposal?${params.toString()}`;
2510
+ console.log(`[Configure ${parsed.displayName}](${url})`);
2511
+ console.log();
2512
+ console.log(
2513
+ "Open this link to review, save values, and authorize the connector."
2514
+ );
2515
+ }
2516
+ )
2517
+ );
2518
+ var customConnectorCommand = new Command().name("custom").description("Inspect and propose org custom connectors").addCommand(listCommand7).addCommand(statusCommand3).addCommand(proposeCommand);
2519
+
2373
2520
  // src/commands/zero/connector/index.ts
2374
- var zeroConnectorCommand = new Command().name("connector").description("Check third-party service connections (GitHub, Slack, etc.)").addCommand(connectCommand).addCommand(listCommand6).addCommand(searchCommand).addCommand(statusCommand2);
2521
+ var zeroConnectorCommand = new Command().name("connector").description("Check third-party service connections (GitHub, Slack, etc.)").addCommand(customConnectorCommand).addCommand(connectCommand).addCommand(listCommand6).addCommand(searchCommand).addCommand(statusCommand2);
2375
2522
 
2376
2523
  // src/commands/zero/credit.ts
2377
2524
  init_esm_shims();
@@ -3512,33 +3659,6 @@ function printTriggersTable(triggers) {
3512
3659
  );
3513
3660
  }
3514
3661
  }
3515
- function printTriggerDetails(trigger) {
3516
- const status = trigger.enabled ? source_default.green("enabled") : source_default.yellow("disabled");
3517
- console.log(`${"Kind:".padEnd(14)}${trigger.kind}`);
3518
- console.log(`${"ID:".padEnd(14)}${trigger.id}`);
3519
- console.log(`${"Automation:".padEnd(14)}${trigger.automationId}`);
3520
- console.log(`${"Status:".padEnd(14)}${status}`);
3521
- switch (trigger.kind) {
3522
- case "cron":
3523
- console.log(`${"Cron:".padEnd(14)}${trigger.cronExpression}`);
3524
- break;
3525
- case "once":
3526
- console.log(`${"At:".padEnd(14)}${trigger.atTime}`);
3527
- break;
3528
- case "loop":
3529
- console.log(
3530
- `${"Every:".padEnd(14)}${formatDurationSeconds(trigger.intervalSeconds)}`
3531
- );
3532
- break;
3533
- }
3534
- console.log(`${"Timezone:".padEnd(14)}${trigger.timezone}`);
3535
- console.log(
3536
- `${"Next run:".padEnd(14)}${trigger.nextRunAt ? formatRelativeTime(trigger.nextRunAt) : source_default.dim("-")}`
3537
- );
3538
- console.log(
3539
- `${"Last run:".padEnd(14)}${trigger.lastRunAt ? formatRelativeTime(trigger.lastRunAt) : source_default.dim("-")}`
3540
- );
3541
- }
3542
3662
 
3543
3663
  // src/commands/zero/automation/create.ts
3544
3664
  function buildInlineTrigger(options) {
@@ -3547,8 +3667,8 @@ function buildInlineTrigger(options) {
3547
3667
  return value !== void 0;
3548
3668
  }
3549
3669
  ).length;
3550
- if (sugarCount > 1) {
3551
- throw new Error("Use at most one of --cron, --once, --loop");
3670
+ if (sugarCount !== 1) {
3671
+ throw new Error("Use exactly one of --cron, --once, --loop");
3552
3672
  }
3553
3673
  if (options.timezone && !options.cron && !options.once) {
3554
3674
  throw new Error("--timezone requires --cron or --once");
@@ -3570,9 +3690,9 @@ function buildInlineTrigger(options) {
3570
3690
  intervalSeconds: parseDurationSeconds(options.loop)
3571
3691
  };
3572
3692
  }
3573
- return void 0;
3693
+ throw new Error("Use exactly one of --cron, --once, --loop");
3574
3694
  }
3575
- var createCommand2 = new Command().name("create").description("Create an automation (optionally with its first trigger)").requiredOption("-n, --name <name>", "Automation name").requiredOption("--agent <id>", "Agent ID or name to run").requiredOption(
3695
+ var createCommand2 = new Command().name("create").description("Create an automation with a schedule trigger").requiredOption("-n, --name <name>", "Automation name").requiredOption("--agent <id>", "Agent ID or name to run").requiredOption(
3576
3696
  "-p, --prompt <instruction>",
3577
3697
  "Instruction the agent runs when the automation fires"
3578
3698
  ).option("--description <text>", "Optional description").option("--cron <expression>", 'Add a cron trigger (e.g. "0 9 * * *")').option(
@@ -3582,13 +3702,12 @@ var createCommand2 = new Command().name("create").description("Create an automat
3582
3702
  "after",
3583
3703
  `
3584
3704
  Examples:
3585
- Triggerless: zero automation create -n alerts --agent my-agent -p "Summarize alerts"
3586
3705
  Daily at 9am: zero automation create -n alerts --agent my-agent -p "..." --cron "0 9 * * *"
3587
3706
  One-time: zero automation create -n alerts --agent my-agent -p "..." --once "2026-06-10T09:00" -z UTC
3588
3707
  Every 15 min: zero automation create -n alerts --agent my-agent -p "..." --loop 15m
3589
3708
 
3590
3709
  Notes:
3591
- - At most one of --cron, --once, --loop; add more triggers later with: zero automation trigger add`
3710
+ - Exactly one of --cron, --once, --loop is required`
3592
3711
  ).action(
3593
3712
  withErrorHandler(async (options) => {
3594
3713
  const trigger = buildInlineTrigger(options);
@@ -3617,11 +3736,6 @@ Notes:
3617
3736
  );
3618
3737
  }
3619
3738
  console.log();
3620
- if (!createdTrigger) {
3621
- console.log(
3622
- ` Add a trigger: ${source_default.cyan(`zero automation trigger add ${automation.name} cron --expr "0 9 * * *"`)}`
3623
- );
3624
- }
3625
3739
  console.log(
3626
3740
  ` Run manually: ${source_default.cyan(`zero automation run ${automation.name}`)}`
3627
3741
  );
@@ -3631,14 +3745,13 @@ Notes:
3631
3745
  // src/commands/zero/automation/list.ts
3632
3746
  init_esm_shims();
3633
3747
  function formatTriggerSummary(automation) {
3634
- if (automation.triggers.length === 0) {
3748
+ const [trigger] = automation.triggers;
3749
+ if (!trigger) {
3635
3750
  return source_default.dim("-");
3636
3751
  }
3637
- return automation.triggers.map((t) => {
3638
- return t.kind;
3639
- }).join(", ");
3752
+ return trigger.kind;
3640
3753
  }
3641
- var listCommand7 = new Command().name("list").alias("ls").description("List automations with their triggers").addHelpText(
3754
+ var listCommand8 = new Command().name("list").alias("ls").description("List automations with their schedule trigger").addHelpText(
3642
3755
  "after",
3643
3756
  `
3644
3757
  Examples:
@@ -3680,7 +3793,7 @@ Examples:
3680
3793
  "ID".padEnd(idWidth),
3681
3794
  "AGENT".padEnd(agentWidth),
3682
3795
  "STATUS".padEnd(8),
3683
- "TRIGGERS"
3796
+ "TRIGGER"
3684
3797
  ].join(" ")
3685
3798
  )
3686
3799
  );
@@ -3705,7 +3818,7 @@ Examples:
3705
3818
 
3706
3819
  // src/commands/zero/automation/show.ts
3707
3820
  init_esm_shims();
3708
- var showCommand = new Command().name("show").description("Show an automation and its triggers").argument("<automation>", "Automation ID or name").addHelpText(
3821
+ var showCommand = new Command().name("show").description("Show an automation and its schedule").argument("<automation>", "Automation ID or name").addHelpText(
3709
3822
  "after",
3710
3823
  `
3711
3824
  Examples:
@@ -3731,16 +3844,12 @@ Notes:
3731
3844
  );
3732
3845
  console.log(`${"Thread:".padEnd(14)}${automation.chatThreadId}`);
3733
3846
  console.log();
3734
- if (automation.triggers.length === 0) {
3735
- console.log(source_default.dim("No triggers"));
3736
- console.log(
3737
- source_default.dim(
3738
- ` Add one with: zero automation trigger add ${automation.name} cron --expr "0 9 * * *"`
3739
- )
3740
- );
3847
+ const [trigger] = automation.triggers;
3848
+ if (!trigger) {
3849
+ console.log(source_default.dim("No schedule trigger"));
3741
3850
  return;
3742
3851
  }
3743
- printTriggersTable(automation.triggers);
3852
+ printTriggersTable([trigger]);
3744
3853
  })
3745
3854
  );
3746
3855
 
@@ -3789,8 +3898,7 @@ Examples:
3789
3898
  zero automation update alerts --loop 10m
3790
3899
 
3791
3900
  Notes:
3792
- - At most one of --cron, --once, --loop; it replaces the automation's single time trigger in place (the kind may switch, run history is kept)
3793
- - With multiple time triggers, address one directly: zero automation trigger update <trigger-id>`
3901
+ - At most one of --cron, --once, --loop; it replaces the automation's schedule trigger in place (the kind may switch, run history is kept)`
3794
3902
  ).action(
3795
3903
  withErrorHandler(async (ref, options) => {
3796
3904
  const timing = buildTimingUpdate(options);
@@ -3806,17 +3914,7 @@ Notes:
3806
3914
  const timeTriggers = automation.triggers;
3807
3915
  const [first] = timeTriggers;
3808
3916
  if (!first) {
3809
- throw new Error(
3810
- `No time trigger to update; add one with: zero automation trigger add ${ref} cron --expr "0 9 * * *"`
3811
- );
3812
- }
3813
- if (timeTriggers.length > 1) {
3814
- const ids = timeTriggers.map((trigger) => {
3815
- return trigger.id;
3816
- }).join(", ");
3817
- throw new Error(
3818
- `Multiple time triggers; update one explicitly: zero automation trigger update <id> ... (ids: ${ids})`
3819
- );
3917
+ throw new Error(`Automation ${ref} has no schedule trigger`);
3820
3918
  }
3821
3919
  timeTriggerId = first.id;
3822
3920
  }
@@ -3858,7 +3956,7 @@ Notes:
3858
3956
  throw new Error("--yes flag is required in non-interactive mode");
3859
3957
  }
3860
3958
  const confirmed = await promptConfirm(
3861
- `Delete automation ${source_default.cyan(ref)} and all of its triggers?`,
3959
+ `Delete automation ${source_default.cyan(ref)} and its schedule trigger?`,
3862
3960
  false
3863
3961
  );
3864
3962
  if (!confirmed) {
@@ -3873,14 +3971,11 @@ Notes:
3873
3971
 
3874
3972
  // src/commands/zero/automation/enable.ts
3875
3973
  init_esm_shims();
3876
- var enableCommand = new Command().name("enable").description("Enable an automation (all of its triggers resume)").argument("<automation>", "Automation ID or name").addHelpText(
3974
+ var enableCommand = new Command().name("enable").description("Enable an automation").argument("<automation>", "Automation ID or name").addHelpText(
3877
3975
  "after",
3878
3976
  `
3879
3977
  Examples:
3880
- zero automation enable alerts
3881
-
3882
- Notes:
3883
- - To enable a single trigger instead: zero automation trigger enable <trigger-id>`
3978
+ zero automation enable alerts`
3884
3979
  ).action(
3885
3980
  withErrorHandler(async (ref) => {
3886
3981
  const automation = await enableAutomation(ref);
@@ -3890,14 +3985,11 @@ Notes:
3890
3985
 
3891
3986
  // src/commands/zero/automation/disable.ts
3892
3987
  init_esm_shims();
3893
- var disableCommand = new Command().name("disable").description("Disable an automation (suspends all of its triggers)").argument("<automation>", "Automation ID or name").addHelpText(
3988
+ var disableCommand = new Command().name("disable").description("Disable an automation").argument("<automation>", "Automation ID or name").addHelpText(
3894
3989
  "after",
3895
3990
  `
3896
3991
  Examples:
3897
- zero automation disable alerts
3898
-
3899
- Notes:
3900
- - To disable a single trigger instead: zero automation trigger disable <trigger-id>`
3992
+ zero automation disable alerts`
3901
3993
  ).action(
3902
3994
  withErrorHandler(async (ref) => {
3903
3995
  const automation = await disableAutomation(ref);
@@ -3920,206 +4012,8 @@ Examples:
3920
4012
  })
3921
4013
  );
3922
4014
 
3923
- // src/commands/zero/automation/trigger/index.ts
3924
- init_esm_shims();
3925
-
3926
- // src/commands/zero/automation/trigger/add.ts
3927
- init_esm_shims();
3928
- var TRIGGER_KINDS = ["cron", "once", "loop"];
3929
- function buildTrigger(kind, options) {
3930
- switch (kind) {
3931
- case "cron":
3932
- if (!options.expr) {
3933
- throw new Error(
3934
- 'cron triggers require --expr (e.g. --expr "0 9 * * *")'
3935
- );
3936
- }
3937
- return {
3938
- kind: "cron",
3939
- cronExpression: options.expr,
3940
- timezone: options.timezone
3941
- };
3942
- case "once":
3943
- if (!options.at) {
3944
- throw new Error(
3945
- 'once triggers require --at (e.g. --at "2026-06-10T09:00")'
3946
- );
3947
- }
3948
- requireTimezoneForLocalAtTime(options.at, options.timezone, "--at");
3949
- return { kind: "once", atTime: options.at, timezone: options.timezone };
3950
- case "loop":
3951
- if (!options.every) {
3952
- throw new Error("loop triggers require --every (e.g. --every 15m)");
3953
- }
3954
- return {
3955
- kind: "loop",
3956
- intervalSeconds: parseDurationSeconds(options.every)
3957
- };
3958
- default:
3959
- throw new Error(
3960
- `Unknown trigger kind: "${kind}". Use one of: ${TRIGGER_KINDS.join(", ")}`
3961
- );
3962
- }
3963
- }
3964
- var addCommand = new Command().name("add").description("Add a trigger (cron | once | loop) to an automation").argument("<automation>", "Automation ID or name").argument("<kind>", `Trigger kind: ${TRIGGER_KINDS.join(" | ")}`).option(
3965
- "--expr <expression>",
3966
- 'Cron expression for kind "cron" (e.g. "0 9 * * *")'
3967
- ).option(
3968
- "--at <iso-time>",
3969
- 'Fire time for kind "once" (e.g. "2026-06-10T09:00")'
3970
- ).option("--every <duration>", 'Interval for kind "loop" (e.g. 15m, 1h, 90s)').option("-z, --timezone <tz>", "IANA timezone for cron/once").addHelpText(
3971
- "after",
3972
- `
3973
- Trigger kinds:
3974
- cron Recurring schedule: zero automation trigger add alerts cron --expr "0 9 * * *" [--timezone Asia/Shanghai]
3975
- once One-time fire: zero automation trigger add alerts once --at "2026-06-10T09:00" [--timezone UTC]
3976
- loop Fixed interval: zero automation trigger add alerts loop --every 15m`
3977
- ).action(
3978
- withErrorHandler(async (ref, kind, options) => {
3979
- if (options.timezone && kind !== "cron" && kind !== "once") {
3980
- throw new Error("--timezone only applies to cron and once triggers");
3981
- }
3982
- const body = buildTrigger(kind, options);
3983
- const { trigger } = await addAutomationTrigger(ref, body);
3984
- console.log(source_default.green(`\u2713 Trigger added to automation "${ref}"`));
3985
- printTriggerDetails(trigger);
3986
- })
3987
- );
3988
-
3989
- // src/commands/zero/automation/trigger/update.ts
3990
- init_esm_shims();
3991
- var EXACTLY_ONE_FLAG_MESSAGE = "Provide exactly one of --expr (cron), --at (once), --every (loop)";
3992
- function buildUpdate(options) {
3993
- const flagCount = [options.expr, options.at, options.every].filter(
3994
- (value) => {
3995
- return value !== void 0;
3996
- }
3997
- ).length;
3998
- if (flagCount > 1) {
3999
- throw new Error(EXACTLY_ONE_FLAG_MESSAGE);
4000
- }
4001
- if (options.timezone && !options.expr && !options.at) {
4002
- throw new Error("--timezone only applies to --expr and --at");
4003
- }
4004
- if (options.expr) {
4005
- return {
4006
- kind: "cron",
4007
- cronExpression: options.expr,
4008
- timezone: options.timezone
4009
- };
4010
- }
4011
- if (options.at) {
4012
- requireTimezoneForLocalAtTime(options.at, options.timezone, "--at");
4013
- return { kind: "once", atTime: options.at, timezone: options.timezone };
4014
- }
4015
- if (options.every) {
4016
- return {
4017
- kind: "loop",
4018
- intervalSeconds: parseDurationSeconds(options.every)
4019
- };
4020
- }
4021
- throw new Error(EXACTLY_ONE_FLAG_MESSAGE);
4022
- }
4023
- var updateCommand2 = new Command().name("update").description(
4024
- "Replace a time trigger's schedule in place (kind may switch among cron/once/loop)"
4025
- ).argument("<trigger>", "Trigger ID").option("--expr <expression>", 'New cron schedule (e.g. "0 9 * * *")').option("--at <iso-time>", 'New one-time fire (e.g. "2026-06-10T09:00")').option("--every <duration>", "New loop interval (e.g. 15m, 1h, 90s)").option("-z, --timezone <tz>", "IANA timezone for --expr / --at").addHelpText(
4026
- "after",
4027
- `
4028
- Examples:
4029
- zero automation trigger update 22222222-2222-4222-8222-222222222222 --expr "0 9 * * *" -z Asia/Shanghai
4030
- zero automation trigger update 22222222-2222-4222-8222-222222222222 --at "2026-06-10T09:00"
4031
- zero automation trigger update 22222222-2222-4222-8222-222222222222 --every 10m
4032
-
4033
- Notes:
4034
- - Exactly one of --expr (cron), --at (once), --every (loop); the trigger's kind switches to match
4035
- - The trigger keeps its id, enabled flag, and run history; the next run is recomputed and the failure counter resets`
4036
- ).action(
4037
- withErrorHandler(async (id, options) => {
4038
- const body = buildUpdate(options);
4039
- const trigger = await updateAutomationTrigger(id, body);
4040
- console.log(source_default.green(`\u2713 Trigger ${trigger.id} updated`));
4041
- printTriggerDetails(trigger);
4042
- })
4043
- );
4044
-
4045
- // src/commands/zero/automation/trigger/index.ts
4046
- var listCommand8 = new Command().name("list").alias("ls").description("List an automation's triggers").argument("<automation>", "Automation ID or name").addHelpText(
4047
- "after",
4048
- `
4049
- Examples:
4050
- zero automation trigger list alerts`
4051
- ).action(
4052
- withErrorHandler(async (ref) => {
4053
- const { triggers } = await listAutomationTriggers(ref);
4054
- if (triggers.length === 0) {
4055
- console.log(source_default.dim("No triggers"));
4056
- console.log(
4057
- source_default.dim(
4058
- ` Add one with: zero automation trigger add ${ref} cron --expr "0 9 * * *"`
4059
- )
4060
- );
4061
- return;
4062
- }
4063
- printTriggersTable(triggers);
4064
- })
4065
- );
4066
- var showCommand2 = new Command().name("show").description("Show a trigger").argument("<trigger>", "Trigger ID").addHelpText(
4067
- "after",
4068
- `
4069
- Examples:
4070
- zero automation trigger show 22222222-2222-4222-8222-222222222222`
4071
- ).action(
4072
- withErrorHandler(async (id) => {
4073
- const trigger = await showAutomationTrigger(id);
4074
- printTriggerDetails(trigger);
4075
- })
4076
- );
4077
- var rmCommand = new Command().name("rm").alias("remove").description("Remove a trigger").argument("<trigger>", "Trigger ID").addHelpText(
4078
- "after",
4079
- `
4080
- Examples:
4081
- zero automation trigger rm 22222222-2222-4222-8222-222222222222`
4082
- ).action(
4083
- withErrorHandler(async (id) => {
4084
- await removeAutomationTrigger(id);
4085
- console.log(source_default.green(`\u2713 Trigger ${id} removed`));
4086
- })
4087
- );
4088
- var enableCommand2 = new Command().name("enable").description("Enable a single trigger").argument("<trigger>", "Trigger ID").addHelpText(
4089
- "after",
4090
- `
4091
- Examples:
4092
- zero automation trigger enable 22222222-2222-4222-8222-222222222222`
4093
- ).action(
4094
- withErrorHandler(async (id) => {
4095
- const trigger = await enableAutomationTrigger(id);
4096
- console.log(source_default.green(`\u2713 Trigger ${trigger.id} enabled`));
4097
- })
4098
- );
4099
- var disableCommand2 = new Command().name("disable").description("Disable a single trigger").argument("<trigger>", "Trigger ID").addHelpText(
4100
- "after",
4101
- `
4102
- Examples:
4103
- zero automation trigger disable 22222222-2222-4222-8222-222222222222`
4104
- ).action(
4105
- withErrorHandler(async (id) => {
4106
- const trigger = await disableAutomationTrigger(id);
4107
- console.log(source_default.green(`\u2713 Trigger ${trigger.id} disabled`));
4108
- })
4109
- );
4110
- var triggerCommand = new Command().name("trigger").description("Manage an automation's triggers").addCommand(addCommand).addCommand(updateCommand2).addCommand(listCommand8).addCommand(showCommand2).addCommand(rmCommand).addCommand(enableCommand2).addCommand(disableCommand2).addHelpText(
4111
- "after",
4112
- `
4113
- Examples:
4114
- Add a trigger: zero automation trigger add <automation> cron --expr "0 9 * * *"
4115
- Update a schedule: zero automation trigger update <trigger-id> --every 10m (kind switches to match the flag)
4116
- List triggers: zero automation trigger list <automation>
4117
- Inspect a trigger: zero automation trigger show <trigger-id>
4118
- Pause one trigger: zero automation trigger disable <trigger-id>`
4119
- );
4120
-
4121
4015
  // src/commands/zero/automation/index.ts
4122
- var zeroAutomationCommand = new Command().name("automation").description("Create or manage automations and their triggers").addCommand(createCommand2).addCommand(listCommand7).addCommand(showCommand).addCommand(updateCommand).addCommand(deleteCommand3).addCommand(enableCommand).addCommand(disableCommand).addCommand(runCommand).addCommand(triggerCommand).addHelpText(
4016
+ var zeroAutomationCommand = new Command().name("automation").description("Create or manage scheduled automations").addCommand(createCommand2).addCommand(listCommand8).addCommand(showCommand).addCommand(updateCommand).addCommand(deleteCommand3).addCommand(enableCommand).addCommand(disableCommand).addCommand(runCommand).addHelpText(
4123
4017
  "after",
4124
4018
  `
4125
4019
  Examples:
@@ -4127,7 +4021,6 @@ Examples:
4127
4021
  List automations: zero automation list
4128
4022
  Inspect one: zero automation show alerts
4129
4023
  Fire manually: zero automation run alerts
4130
- Manage triggers: zero automation trigger --help
4131
4024
  Pause an automation: zero automation disable alerts
4132
4025
  Resume an automation: zero automation enable alerts
4133
4026
  Delete an automation: zero automation delete alerts`
@@ -4432,7 +4325,7 @@ var createCommand3 = new Command().name("create").description("Create a GitHub l
4432
4325
  }
4433
4326
  )
4434
4327
  );
4435
- var updateCommand3 = new Command().name("update").alias("edit").description("Update a GitHub label listener").argument("<listener-id>", "GitHub label listener ID").option("--label <name>", "New GitHub label name").option("--agent-id <id>", "New agent ID").option("--prompt <text>", "New prompt").option(
4328
+ var updateCommand2 = new Command().name("update").alias("edit").description("Update a GitHub label listener").argument("<listener-id>", "GitHub label listener ID").option("--label <name>", "New GitHub label name").option("--agent-id <id>", "New agent ID").option("--prompt <text>", "New prompt").option(
4436
4329
  "--trigger-mode <mode>",
4437
4330
  "Who can trigger the listener: anyone | created_by_me"
4438
4331
  ).option("--enable", "Enable the listener").option("--disable", "Disable the listener").option("--json", "Print raw JSON").action(
@@ -4476,7 +4369,7 @@ var deleteCommand5 = new Command().name("delete").alias("rm").description("Delet
4476
4369
  }
4477
4370
  )
4478
4371
  );
4479
- var labelListenerCommand = new Command().name("label-listener").alias("label-listeners").alias("labels").description("Manage GitHub label listeners").addCommand(listCommand10).addCommand(createCommand3).addCommand(updateCommand3).addCommand(deleteCommand5).addHelpText(
4372
+ var labelListenerCommand = new Command().name("label-listener").alias("label-listeners").alias("labels").description("Manage GitHub label listeners").addCommand(listCommand10).addCommand(createCommand3).addCommand(updateCommand2).addCommand(deleteCommand5).addHelpText(
4480
4373
  "after",
4481
4374
  `
4482
4375
  Examples:
@@ -5876,9 +5769,57 @@ init_esm_shims();
5876
5769
 
5877
5770
  // src/commands/zero/workflow/trigger/display.ts
5878
5771
  init_esm_shims();
5772
+ var GMAIL_TEXT_FIELDS = [
5773
+ "from",
5774
+ "subject",
5775
+ "body",
5776
+ "to",
5777
+ "cc"
5778
+ ];
5779
+ function quote(value) {
5780
+ return `"${value}"`;
5781
+ }
5782
+ function quoteList(values) {
5783
+ return values.map(quote).join(", ");
5784
+ }
5785
+ function textMatcherParts(field, matcher) {
5786
+ const parts = [];
5787
+ if (matcher.contains) {
5788
+ parts.push(`${field} contains ${quote(matcher.contains)}`);
5789
+ }
5790
+ if (matcher.containsAny) {
5791
+ parts.push(`${field} contains any of ${quoteList(matcher.containsAny)}`);
5792
+ }
5793
+ if (matcher.doesNotContain) {
5794
+ parts.push(`${field} does not contain ${quote(matcher.doesNotContain)}`);
5795
+ }
5796
+ if (matcher.doesNotContainAny) {
5797
+ parts.push(
5798
+ `${field} does not contain any of ${quoteList(matcher.doesNotContainAny)}`
5799
+ );
5800
+ }
5801
+ return parts;
5802
+ }
5803
+ function formatGmailMatchSummary(config) {
5804
+ const match = config.match;
5805
+ if (!match) {
5806
+ return "all inbound messages";
5807
+ }
5808
+ const parts = [];
5809
+ for (const field of GMAIL_TEXT_FIELDS) {
5810
+ const matcher = match[field];
5811
+ if (matcher) {
5812
+ parts.push(...textMatcherParts(field, matcher));
5813
+ }
5814
+ }
5815
+ if (match.snippet || match.labels || match.hasAttachment !== void 0) {
5816
+ parts.push("custom match rules");
5817
+ }
5818
+ return parts.length > 0 ? parts.join("; ") : "all inbound messages";
5819
+ }
5879
5820
  function formatWorkflowTriggerEntry(trigger) {
5880
5821
  if (trigger.kind === "event") {
5881
- return "Gmail new message";
5822
+ return `Gmail new message: ${formatGmailMatchSummary(trigger.eventConfig)}`;
5882
5823
  }
5883
5824
  const { schedule } = trigger;
5884
5825
  switch (schedule.type) {
@@ -5936,7 +5877,14 @@ function printWorkflowTriggerDetails(trigger, options) {
5936
5877
  console.log(`${"Workflow:".padEnd(14)}${options.workflowRef}`);
5937
5878
  }
5938
5879
  console.log(`${"Status:".padEnd(14)}${status}`);
5939
- console.log(`${"Trigger:".padEnd(14)}${formatWorkflowTriggerEntry(trigger)}`);
5880
+ console.log(
5881
+ `${"Trigger:".padEnd(14)}${trigger.kind === "event" ? "Gmail new message" : formatWorkflowTriggerEntry(trigger)}`
5882
+ );
5883
+ if (trigger.kind === "event") {
5884
+ console.log(
5885
+ `${"Match:".padEnd(14)}${formatGmailMatchSummary(trigger.eventConfig)}`
5886
+ );
5887
+ }
5940
5888
  console.log(`${"Owner:".padEnd(14)}${trigger.ownerUserId}`);
5941
5889
  console.log(
5942
5890
  `${"Chat thread:".padEnd(14)}${trigger.chatThreadId ?? source_default.dim("-")}`
@@ -5945,10 +5893,209 @@ function printWorkflowTriggerDetails(trigger, options) {
5945
5893
  console.log(`${"Last run:".padEnd(14)}${formatRunTime(trigger.lastRunAt)}`);
5946
5894
  }
5947
5895
 
5896
+ // src/commands/zero/workflow/trigger/gmail-config.ts
5897
+ init_esm_shims();
5898
+ import { readFileSync as readFileSync13 } from "fs";
5899
+ var TEXT_FIELD_LABELS = {
5900
+ from: "from",
5901
+ subject: "subject",
5902
+ body: "body",
5903
+ to: "to",
5904
+ cc: "cc"
5905
+ };
5906
+ function isRecord(value) {
5907
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5908
+ }
5909
+ function textFieldFromKey(key) {
5910
+ switch (key) {
5911
+ case "from":
5912
+ case "subject":
5913
+ case "body":
5914
+ case "to":
5915
+ case "cc":
5916
+ return key;
5917
+ default:
5918
+ return null;
5919
+ }
5920
+ }
5921
+ function formatFieldName(field) {
5922
+ return TEXT_FIELD_LABELS[field];
5923
+ }
5924
+ function parseNonEmptyString(value, path2) {
5925
+ if (typeof value !== "string" || value.length === 0) {
5926
+ throw new Error(`${path2} must be a non-empty string`);
5927
+ }
5928
+ return value;
5929
+ }
5930
+ function parseNonEmptyStringArray(value, path2) {
5931
+ if (!Array.isArray(value) || value.length === 0) {
5932
+ throw new Error(`${path2} must be a non-empty string array`);
5933
+ }
5934
+ return value.map((item, index) => {
5935
+ return parseNonEmptyString(item, `${path2}[${index}]`);
5936
+ });
5937
+ }
5938
+ function parseTextMatcher(field, value) {
5939
+ if (!isRecord(value)) {
5940
+ throw new Error(`${formatFieldName(field)} must be an object`);
5941
+ }
5942
+ const matcher = {};
5943
+ for (const key of Object.keys(value)) {
5944
+ switch (key) {
5945
+ case "contains":
5946
+ matcher.contains = parseNonEmptyString(
5947
+ value[key],
5948
+ `${formatFieldName(field)}.contains`
5949
+ );
5950
+ break;
5951
+ case "containsAny":
5952
+ matcher.containsAny = parseNonEmptyStringArray(
5953
+ value[key],
5954
+ `${formatFieldName(field)}.containsAny`
5955
+ );
5956
+ break;
5957
+ case "doesNotContain":
5958
+ matcher.doesNotContain = parseNonEmptyString(
5959
+ value[key],
5960
+ `${formatFieldName(field)}.doesNotContain`
5961
+ );
5962
+ break;
5963
+ case "doesNotContainAny":
5964
+ matcher.doesNotContainAny = parseNonEmptyStringArray(
5965
+ value[key],
5966
+ `${formatFieldName(field)}.doesNotContainAny`
5967
+ );
5968
+ break;
5969
+ default:
5970
+ throw new Error(
5971
+ `Unsupported Gmail trigger text matcher "${key}" for ${formatFieldName(field)}`
5972
+ );
5973
+ }
5974
+ }
5975
+ if (matcher.contains === void 0 && matcher.containsAny === void 0 && matcher.doesNotContain === void 0 && matcher.doesNotContainAny === void 0) {
5976
+ throw new Error(
5977
+ `${formatFieldName(field)} must include at least one text matcher`
5978
+ );
5979
+ }
5980
+ return matcher;
5981
+ }
5982
+ function parseMatch(value) {
5983
+ if (value === void 0) {
5984
+ return void 0;
5985
+ }
5986
+ if (!isRecord(value)) {
5987
+ throw new Error("match must be an object");
5988
+ }
5989
+ const match = {};
5990
+ for (const key of Object.keys(value)) {
5991
+ const field = textFieldFromKey(key);
5992
+ if (field === null) {
5993
+ throw new Error(
5994
+ `Unsupported Gmail trigger match field "${key}". Supported fields: from, subject, body, to, cc`
5995
+ );
5996
+ }
5997
+ match[field] = parseTextMatcher(field, value[key]);
5998
+ }
5999
+ return Object.keys(match).length > 0 ? match : void 0;
6000
+ }
6001
+ function readConfigMatch(path2) {
6002
+ let parsed;
6003
+ try {
6004
+ parsed = JSON.parse(readFileSync13(path2, "utf-8"));
6005
+ } catch (error) {
6006
+ const message = error instanceof Error ? error.message : String(error);
6007
+ throw new Error(
6008
+ `Failed to read Gmail trigger config "${path2}": ${message}`
6009
+ );
6010
+ }
6011
+ if (!isRecord(parsed)) {
6012
+ throw new Error("Gmail trigger config must be a JSON object");
6013
+ }
6014
+ for (const key of Object.keys(parsed)) {
6015
+ if (key !== "match") {
6016
+ throw new Error(
6017
+ `Unsupported Gmail trigger config field "${key}". Use a top-level match object`
6018
+ );
6019
+ }
6020
+ }
6021
+ return parseMatch(parsed.match);
6022
+ }
6023
+ function textFlagSpecs(options) {
6024
+ return [
6025
+ {
6026
+ field: "from",
6027
+ contains: options.fromContains,
6028
+ doesNotContain: options.fromNotContains
6029
+ },
6030
+ {
6031
+ field: "subject",
6032
+ contains: options.subjectContains,
6033
+ doesNotContain: options.subjectNotContains
6034
+ },
6035
+ {
6036
+ field: "body",
6037
+ contains: options.bodyContains,
6038
+ doesNotContain: options.bodyNotContains
6039
+ },
6040
+ {
6041
+ field: "to",
6042
+ contains: options.toContains,
6043
+ doesNotContain: options.toNotContains
6044
+ },
6045
+ {
6046
+ field: "cc",
6047
+ contains: options.ccContains,
6048
+ doesNotContain: options.ccNotContains
6049
+ }
6050
+ ];
6051
+ }
6052
+ function hasGmailTriggerOptions(options) {
6053
+ return options.config !== void 0 || textFlagSpecs(options).some((spec) => {
6054
+ return spec.contains !== void 0 || spec.doesNotContain !== void 0;
6055
+ });
6056
+ }
6057
+ function buildMatchFromFlags(options) {
6058
+ const match = {};
6059
+ for (const spec of textFlagSpecs(options)) {
6060
+ const matcher = {};
6061
+ if (spec.contains !== void 0) {
6062
+ if (spec.contains.length === 0) {
6063
+ throw new Error(
6064
+ `${formatFieldName(spec.field)} contains must be non-empty`
6065
+ );
6066
+ }
6067
+ matcher.contains = spec.contains;
6068
+ }
6069
+ if (spec.doesNotContain !== void 0) {
6070
+ if (spec.doesNotContain.length === 0) {
6071
+ throw new Error(
6072
+ `${formatFieldName(spec.field)} doesNotContain must be non-empty`
6073
+ );
6074
+ }
6075
+ matcher.doesNotContain = spec.doesNotContain;
6076
+ }
6077
+ if (matcher.contains !== void 0 || matcher.doesNotContain !== void 0) {
6078
+ match[spec.field] = matcher;
6079
+ }
6080
+ }
6081
+ return Object.keys(match).length > 0 ? match : void 0;
6082
+ }
6083
+ function buildGmailNewMessageEventConfig(options) {
6084
+ if (options.config !== void 0 && textFlagSpecs(options).some((spec) => {
6085
+ return spec.contains !== void 0 || spec.doesNotContain !== void 0;
6086
+ })) {
6087
+ throw new Error("Use either --config or Gmail text match flags, not both");
6088
+ }
6089
+ const match = options.config !== void 0 ? readConfigMatch(options.config) : buildMatchFromFlags(options);
6090
+ return match ? { provider: "gmail", event: "new_message", match } : { provider: "gmail", event: "new_message" };
6091
+ }
6092
+
5948
6093
  // src/commands/zero/workflow/trigger/index.ts
5949
6094
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5950
6095
  var SCHEDULE_KINDS = ["cron", "once", "loop"];
5951
- var EXACTLY_ONE_FLAG_MESSAGE2 = "Provide exactly one of --expr (cron), --at (once), --every (loop)";
6096
+ var EVENT_KINDS = ["gmail-new-message"];
6097
+ var TRIGGER_KINDS = [...SCHEDULE_KINDS, ...EVENT_KINDS];
6098
+ var EXACTLY_ONE_FLAG_MESSAGE = "Provide exactly one of --expr (cron), --at (once), --every (loop)";
5952
6099
  function timezoneOrUtc(timezone) {
5953
6100
  return timezone ?? "UTC";
5954
6101
  }
@@ -6081,18 +6228,41 @@ function buildSchedule(kind, options) {
6081
6228
  };
6082
6229
  default:
6083
6230
  throw new Error(
6084
- `Unknown trigger kind: "${kind}". Use one of: ${SCHEDULE_KINDS.join(", ")}`
6231
+ `Unknown trigger kind: "${kind}". Use one of: ${TRIGGER_KINDS.join(", ")}`
6085
6232
  );
6086
6233
  }
6087
6234
  }
6088
- function buildUpdate2(options) {
6235
+ function hasScheduleAddOptions(options) {
6236
+ return options.expr !== void 0 || options.at !== void 0 || options.every !== void 0 || options.timezone !== void 0;
6237
+ }
6238
+ function buildCreateRequest(kind, options) {
6239
+ if (kind === "gmail-new-message") {
6240
+ if (hasScheduleAddOptions(options)) {
6241
+ throw new Error(
6242
+ "--expr, --at, --every, and --timezone only apply to schedule triggers"
6243
+ );
6244
+ }
6245
+ return {
6246
+ kind: "event",
6247
+ eventType: "gmail-new-message",
6248
+ eventConfig: buildGmailNewMessageEventConfig(options)
6249
+ };
6250
+ }
6251
+ if (hasGmailTriggerOptions(options)) {
6252
+ throw new Error(
6253
+ "Gmail match flags and --config only apply to gmail-new-message triggers"
6254
+ );
6255
+ }
6256
+ return { schedule: buildSchedule(kind, options) };
6257
+ }
6258
+ function buildUpdate(options) {
6089
6259
  const flagCount = [options.expr, options.at, options.every].filter(
6090
6260
  (value) => {
6091
6261
  return value !== void 0;
6092
6262
  }
6093
6263
  ).length;
6094
6264
  if (flagCount !== 1) {
6095
- throw new Error(EXACTLY_ONE_FLAG_MESSAGE2);
6265
+ throw new Error(EXACTLY_ONE_FLAG_MESSAGE);
6096
6266
  }
6097
6267
  if (options.timezone && !options.expr && !options.at) {
6098
6268
  throw new Error("--timezone only applies to --expr and --at");
@@ -6125,27 +6295,46 @@ async function resolveWorkflowId(ref, options) {
6125
6295
  }
6126
6296
  return matches[0].id;
6127
6297
  }
6128
- var addCommand2 = new Command().name("add").description("Add a schedule trigger to a workflow").argument("<workflow>", "Workflow ID or name").argument("<kind>", `Trigger kind: ${SCHEDULE_KINDS.join(" | ")}`).option("--expr <expression>", 'Cron expression for kind "cron"').option("--at <iso-time>", 'Fire time for kind "once"').option("--every <duration>", 'Interval for kind "loop" (e.g. 15m, 1h, 90s)').option("-z, --timezone <tz>", "IANA timezone for cron/once (default: UTC)").option("--agent <id>", "Agent ID for resolving a workflow name").addHelpText(
6298
+ var addCommand = new Command().name("add").description("Add a trigger to a workflow").argument("<workflow>", "Workflow ID or name").argument("<kind>", `Trigger type: ${TRIGGER_KINDS.join(" | ")}`).option("--expr <expression>", 'Cron expression for kind "cron"').option("--at <iso-time>", 'Fire time for kind "once"').option("--every <duration>", 'Interval for kind "loop" (e.g. 15m, 1h, 90s)').option("-z, --timezone <tz>", "IANA timezone for cron/once (default: UTC)").option("--config <path>", "Path to a Gmail new message trigger config JSON").option("--from-contains <text>", "Require the From header to contain text").option(
6299
+ "--from-not-contains <text>",
6300
+ "Require the From header not to contain text"
6301
+ ).option(
6302
+ "--subject-contains <text>",
6303
+ "Require the Subject header to contain text"
6304
+ ).option(
6305
+ "--subject-not-contains <text>",
6306
+ "Require the Subject header not to contain text"
6307
+ ).option("--body-contains <text>", "Require the message body to contain text").option(
6308
+ "--body-not-contains <text>",
6309
+ "Require the message body not to contain text"
6310
+ ).option("--to-contains <text>", "Require the To header to contain text").option(
6311
+ "--to-not-contains <text>",
6312
+ "Require the To header not to contain text"
6313
+ ).option("--cc-contains <text>", "Require the Cc header to contain text").option(
6314
+ "--cc-not-contains <text>",
6315
+ "Require the Cc header not to contain text"
6316
+ ).option("--agent <id>", "Agent ID for resolving a workflow name").addHelpText(
6129
6317
  "after",
6130
6318
  `
6131
6319
  Examples:
6132
6320
  zero workflow trigger add tell-a-joke cron --expr "0 9 * * *" -z Asia/Shanghai
6133
6321
  zero workflow trigger add tell-a-joke once --at "2026-06-10T09:00" -z Asia/Shanghai
6134
6322
  zero workflow trigger add tell-a-joke loop --every 15m
6323
+ zero workflow trigger add triage gmail-new-message --from-contains "@example.com"
6324
+ zero workflow trigger add triage gmail-new-message --config ./gmail-trigger.json
6135
6325
 
6136
6326
  Notes:
6137
6327
  - Workflow names resolve under --agent, then ZERO_AGENT_ID, then all visible workflows
6328
+ - Gmail triggers match all inbound messages when no text match rules are provided
6138
6329
  - Use the workflow ID when a name is ambiguous`
6139
6330
  ).action(
6140
6331
  withErrorHandler(
6141
6332
  async (workflowRef, kind, options) => {
6142
- if (options.timezone && kind !== "cron" && kind !== "once") {
6333
+ if (options.timezone && kind !== "cron" && kind !== "once" && kind !== "gmail-new-message") {
6143
6334
  throw new Error("--timezone only applies to cron and once triggers");
6144
6335
  }
6145
6336
  const workflowId = await resolveWorkflowId(workflowRef, options);
6146
- const body = {
6147
- schedule: buildSchedule(kind, options)
6148
- };
6337
+ const body = buildCreateRequest(kind, options);
6149
6338
  const trigger = await createWorkflowTrigger(workflowId, body);
6150
6339
  console.log(
6151
6340
  source_default.green(`\u2713 Trigger added to workflow "${workflowRef}"`)
@@ -6154,7 +6343,7 @@ Notes:
6154
6343
  }
6155
6344
  )
6156
6345
  );
6157
- var updateCommand4 = new Command().name("update").description("Replace a workflow trigger's schedule").argument("<trigger>", "Workflow trigger ID").option("--expr <expression>", 'New cron schedule (e.g. "0 9 * * *")').option("--at <iso-time>", 'New one-time fire (e.g. "2026-06-10T09:00")').option("--every <duration>", "New loop interval (e.g. 15m, 1h, 90s)").option("-z, --timezone <tz>", "IANA timezone for --expr / --at").addHelpText(
6346
+ var updateCommand3 = new Command().name("update").description("Replace a workflow trigger's schedule").argument("<trigger>", "Workflow trigger ID").option("--expr <expression>", 'New cron schedule (e.g. "0 9 * * *")').option("--at <iso-time>", 'New one-time fire (e.g. "2026-06-10T09:00")').option("--every <duration>", "New loop interval (e.g. 15m, 1h, 90s)").option("-z, --timezone <tz>", "IANA timezone for --expr / --at").addHelpText(
6158
6347
  "after",
6159
6348
  `
6160
6349
  Examples:
@@ -6163,12 +6352,12 @@ Examples:
6163
6352
  zero workflow trigger update 22222222-2222-4222-8222-222222222222 --every 10m`
6164
6353
  ).action(
6165
6354
  withErrorHandler(async (id, options) => {
6166
- const trigger = await updateWorkflowTrigger(id, buildUpdate2(options));
6355
+ const trigger = await updateWorkflowTrigger(id, buildUpdate(options));
6167
6356
  console.log(source_default.green(`\u2713 Trigger ${trigger.id} updated`));
6168
6357
  printWorkflowTriggerDetails(trigger);
6169
6358
  })
6170
6359
  );
6171
- var listCommand14 = new Command().name("list").alias("ls").description("List a workflow's schedule triggers").argument("<workflow>", "Workflow ID or name").option("--agent <id>", "Agent ID for resolving a workflow name").addHelpText(
6360
+ var listCommand14 = new Command().name("list").alias("ls").description("List a workflow's triggers").argument("<workflow>", "Workflow ID or name").option("--agent <id>", "Agent ID for resolving a workflow name").addHelpText(
6172
6361
  "after",
6173
6362
  `
6174
6363
  Examples:
@@ -6192,25 +6381,25 @@ Examples:
6192
6381
  }
6193
6382
  )
6194
6383
  );
6195
- var showCommand3 = new Command().name("show").description("Show a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6384
+ var showCommand2 = new Command().name("show").description("Show a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6196
6385
  withErrorHandler(async (id) => {
6197
6386
  const trigger = await getWorkflowTrigger(id);
6198
6387
  printWorkflowTriggerDetails(trigger);
6199
6388
  })
6200
6389
  );
6201
- var rmCommand2 = new Command().name("rm").alias("remove").description("Remove a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6390
+ var rmCommand = new Command().name("rm").alias("remove").description("Remove a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6202
6391
  withErrorHandler(async (id) => {
6203
6392
  await deleteWorkflowTrigger(id);
6204
6393
  console.log(source_default.green(`\u2713 Trigger ${id} removed`));
6205
6394
  })
6206
6395
  );
6207
- var enableCommand3 = new Command().name("enable").description("Enable a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6396
+ var enableCommand2 = new Command().name("enable").description("Enable a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6208
6397
  withErrorHandler(async (id) => {
6209
6398
  const trigger = await enableWorkflowTrigger(id);
6210
6399
  console.log(source_default.green(`\u2713 Trigger ${trigger.id} enabled`));
6211
6400
  })
6212
6401
  );
6213
- var disableCommand3 = new Command().name("disable").description("Disable a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6402
+ var disableCommand2 = new Command().name("disable").description("Disable a workflow trigger").argument("<trigger>", "Workflow trigger ID").action(
6214
6403
  withErrorHandler(async (id) => {
6215
6404
  const trigger = await disableWorkflowTrigger(id);
6216
6405
  console.log(source_default.green(`\u2713 Trigger ${trigger.id} disabled`));
@@ -6225,7 +6414,7 @@ var runCommand3 = new Command().name("run").description("Fire a workflow trigger
6225
6414
  console.log(`Stream logs: zero logs ${result.runId}`);
6226
6415
  })
6227
6416
  );
6228
- var triggerCommand2 = new Command().name("trigger").description("Manage a workflow's schedule triggers").addCommand(addCommand2).addCommand(updateCommand4).addCommand(listCommand14).addCommand(showCommand3).addCommand(rmCommand2).addCommand(enableCommand3).addCommand(disableCommand3).addCommand(runCommand3).addHelpText(
6417
+ var triggerCommand = new Command().name("trigger").description("Manage a workflow's triggers").addCommand(addCommand).addCommand(updateCommand3).addCommand(listCommand14).addCommand(showCommand2).addCommand(rmCommand).addCommand(enableCommand2).addCommand(disableCommand2).addCommand(runCommand3).addHelpText(
6229
6418
  "after",
6230
6419
  `
6231
6420
  Examples:
@@ -6238,7 +6427,7 @@ Examples:
6238
6427
  );
6239
6428
 
6240
6429
  // src/commands/zero/workflow/index.ts
6241
- var zeroWorkflowCommand = new Command("workflow").description("Manage workflows").addCommand(createCommand4).addCommand(editCommand2).addCommand(viewCommand2).addCommand(listCommand13).addCommand(deleteCommand7).addCommand(copyCommand).addCommand(runCommand2).addCommand(triggerCommand2).addHelpText(
6430
+ var zeroWorkflowCommand = new Command("workflow").description("Manage workflows").addCommand(createCommand4).addCommand(editCommand2).addCommand(viewCommand2).addCommand(listCommand13).addCommand(deleteCommand7).addCommand(copyCommand).addCommand(runCommand2).addCommand(triggerCommand).addHelpText(
6242
6431
  "after",
6243
6432
  `
6244
6433
  Examples:
@@ -6986,7 +7175,7 @@ function extensionForMimeType(mimeType) {
6986
7175
  const suffix = mimeType.startsWith("image/") ? mimeType.slice(6) : "bin";
6987
7176
  return sanitizeFilenamePart(suffix, "bin").toLowerCase();
6988
7177
  }
6989
- function isRecord(value) {
7178
+ function isRecord2(value) {
6990
7179
  return typeof value === "object" && value !== null && !Array.isArray(value);
6991
7180
  }
6992
7181
  function stringField(value, key) {
@@ -7083,7 +7272,7 @@ async function formatComputerUseResultForConsole(result, commandId) {
7083
7272
  }
7084
7273
  }
7085
7274
  const action = result.action;
7086
- if (isRecord(action)) {
7275
+ if (isRecord2(action)) {
7087
7276
  printable.action = compactActionResult(action);
7088
7277
  }
7089
7278
  return JSON.stringify(printable, null, 2);
@@ -7440,7 +7629,7 @@ function canonicalizeRegistryId(prefix, value) {
7440
7629
 
7441
7630
  // src/commands/zero/generate/lib/dispatch.ts
7442
7631
  init_esm_shims();
7443
- import { readFileSync as readFileSync13 } from "fs";
7632
+ import { readFileSync as readFileSync14 } from "fs";
7444
7633
 
7445
7634
  // src/commands/zero/generate/lib/connector-guidance.ts
7446
7635
  init_esm_shims();
@@ -7995,7 +8184,7 @@ function resolvePrompt(prompt) {
7995
8184
  }
7996
8185
  if (!process.stdin.isTTY) {
7997
8186
  try {
7998
- const piped = readFileSync13("/dev/stdin", "utf8").trim();
8187
+ const piped = readFileSync14("/dev/stdin", "utf8").trim();
7999
8188
  if (piped.length > 0) {
8000
8189
  return piped;
8001
8190
  }
@@ -10018,12 +10207,12 @@ init_esm_shims();
10018
10207
 
10019
10208
  // src/lib/host/publish-static-site.ts
10020
10209
  init_esm_shims();
10021
- import { readFile as readFile2 } from "fs/promises";
10210
+ import { readFile as readFile3 } from "fs/promises";
10022
10211
 
10023
10212
  // src/lib/host/static-site.ts
10024
10213
  init_esm_shims();
10025
10214
  import { createHash } from "crypto";
10026
- import { readdir, readFile, stat } from "fs/promises";
10215
+ import { readdir, readFile as readFile2, stat } from "fs/promises";
10027
10216
  import { extname as extname4, relative, resolve, sep, dirname, posix } from "path";
10028
10217
  var MIME_BY_EXTENSION4 = {
10029
10218
  ".html": "text/html; charset=utf-8",
@@ -10147,7 +10336,7 @@ function collectReferences(ext, text) {
10147
10336
  return isHtmlExtension(ext) ? collectHtmlReferences(text) : collectCssReferences(text);
10148
10337
  }
10149
10338
  async function hashFile(path2) {
10150
- const bytes = await readFile(path2);
10339
+ const bytes = await readFile2(path2);
10151
10340
  return createHash("sha256").update(bytes).digest("hex");
10152
10341
  }
10153
10342
  async function walk(root, dir, files) {
@@ -10187,7 +10376,7 @@ async function assertReferencesExist(files) {
10187
10376
  if (!shouldValidateReferences(ext)) {
10188
10377
  continue;
10189
10378
  }
10190
- const text = await readFile(file.absolutePath, "utf8");
10379
+ const text = await readFile2(file.absolutePath, "utf8");
10191
10380
  const references = collectReferences(ext, text);
10192
10381
  for (const reference of references) {
10193
10382
  const normalized = normalizeReference(file.path, reference);
@@ -10263,7 +10452,7 @@ async function publishStaticSite(options) {
10263
10452
  throw new Error(`Missing upload URL for ${file.path}`);
10264
10453
  }
10265
10454
  options.onProgress?.({ phase: "uploading", path: file.path });
10266
- const bytes = await readFile2(file.absolutePath);
10455
+ const bytes = await readFile3(file.absolutePath);
10267
10456
  const response = await fetch(uploadUrl, {
10268
10457
  method: "PUT",
10269
10458
  headers: { "Content-Type": file.contentType },
@@ -11372,7 +11561,7 @@ function registerZeroCommands(prog, commands) {
11372
11561
  var program = new Command();
11373
11562
  program.name("zero").description(
11374
11563
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
11375
- ).version("9.202.2").addHelpText("after", () => {
11564
+ ).version("9.204.0").addHelpText("after", () => {
11376
11565
  return buildZeroHelpText();
11377
11566
  });
11378
11567
  if (process.argv[1]?.endsWith("zero.js") || process.argv[1]?.endsWith("zero.ts") || process.argv[1]?.endsWith("zero")) {