@workser/cli 0.6.27 → 0.6.29

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/dist/index.js CHANGED
@@ -3810,6 +3810,12 @@ sending it. That matters: the API silently drops unknown fields, so a typo
3810
3810
  would otherwise be accepted, dropped, and reported as success \u2014 leaving an
3811
3811
  agent that had been told nothing.
3812
3812
 
3813
+ ## Nothing starts it until you give it a trigger
3814
+
3815
+ A published agent runs when asked and at no other time. A time, a chat message
3816
+ or an app event is a **trigger**, and it works the moment it is saved \u2014 it is
3817
+ not part of the agent's version. \`workser help agent-triggers\`.
3818
+
3813
3819
  ## Nothing takes effect until you publish
3814
3820
 
3815
3821
  **This is the step to not forget.** The runtime resolves the PUBLISHED version
@@ -3857,26 +3863,28 @@ publishes an agent that never answers anybody. Pictures, video, voice and
3857
3863
  transcription are abilities, not models you set, and one call from the app's
3858
3864
  own code needs no agent at all: \`workser help agent-cloud-media\`.
3859
3865
 
3860
- ## Agents need a paid plan \u2014 the trial does not include them
3866
+ ## Every plan can use Agent Cloud \u2014 runs use credits
3861
3867
 
3862
- \`create\` and \`run\` both refuse with **402** on a free or trialling
3863
- organisation. That is a rule, not a fault: every run buys model tokens and
3864
- holds a machine, so it costs real money the moment it happens.
3868
+ Creating, teaching and publishing an agent are available on free, trial and paid
3869
+ plans. A \`try\` or live \`run\` spends real money on model usage and hosted runtime,
3870
+ so it starts only when the organisation's shared AI-credit wallet clears the
3871
+ minimum balance shown in the refusal.
3865
3872
 
3866
- **When you hit it, hand the plan over \u2014 do not just describe it.** Workser
3873
+ **When you hit a 402, hand the credit top-up over \u2014 do not just describe it.** Workser
3867
3874
  files the refusal on the conversation as a clickable step and artifact, so the
3868
- person gets something actionable. Say what you were doing, that agents need a
3869
- plan, and stop.
3875
+ person gets something actionable. Say what you were doing, that the wallet needs
3876
+ credits, and stop. The same rule applies when the owner supplies their own model
3877
+ key because the hosted runtime and fallback path still cost money.
3870
3878
 
3871
3879
  Do NOT:
3872
3880
 
3873
- - retry the call, or try a different agent \u2014 the answer is the same
3881
+ - retry the call, or try a different agent \u2014 the wallet is shared
3874
3882
  - try to work around it by running the job yourself as a coding agent; that is
3875
3883
  a different product and it will not be there when the user closes the window
3876
- - guess at prices, discounts or trial extensions
3884
+ - guess at prices, discounts or free usage
3877
3885
 
3878
3886
  If the user asks how much: it is per minute of run time plus the model, and the
3879
- plan page has the current figures. You do not have them.
3887
+ Agent Cloud usage view has the current figures. You do not have them.
3880
3888
 
3881
3889
  ## When to reach for this
3882
3890
 
@@ -3897,7 +3905,7 @@ workflow\`.
3897
3905
  ## If Agent Cloud is ruled out, there is an order
3898
3906
 
3899
3907
  Propose Agent Cloud first, every time. If it genuinely does not fit \u2014 the owner
3900
- has said no to the plan, or wants their own code in their own framework \u2014 go
3908
+ does not want a managed runtime, or wants their own code in their own framework \u2014 go
3901
3909
  down this list in order, and say which rung you took and why you skipped the
3902
3910
  ones above it:
3903
3911
 
@@ -3952,15 +3960,86 @@ watching sees the agent think. See the \`workser-sdk\` skill, \`reference/agents
3952
3960
  what to do, what to leave alone, when to ask. Vague instructions are the
3953
3961
  single biggest cause of an agent that "doesn't work".
3954
3962
 
3955
- 3. **Free plans cannot run agents at all**, and a trial has a small allowance.
3956
- A \`402\` with \`spend_limit_reached\` is not a bug \u2014 tell the user what it says
3957
- and point them at their plan.
3963
+ 3. **Every plan can run agents once the shared wallet has enough credits.** A
3964
+ \`402\` with \`insufficient_credits\` needs a top-up; \`spend_limit_reached\` means
3965
+ the owner reached the guard rail they set. Tell them exactly what it says and
3966
+ hand over the matching action; never suggest a subscription upgrade.
3958
3967
 
3959
3968
  4. **Say who it is for.** An agent acting for one of the app's customers needs
3960
3969
  \`referenceUserId\`, or its memory and audit trail belong to nobody.
3961
3970
 
3962
3971
  5. **Do not invent an agent the user did not ask for.** Creating one is cheap;
3963
3972
  an agent nobody wanted, quietly costing money per run, is not.
3973
+ `
3974
+ },
3975
+ {
3976
+ topic: "agent-triggers",
3977
+ title: "What starts an Agent Cloud agent",
3978
+ summary: "Schedules, chat channels and app events \u2014 a trigger works the moment it is saved, unlike everything else on an agent.",
3979
+ commands: [],
3980
+ source: "skills/workser/reference/agent-triggers.md",
3981
+ body: `# What starts an Agent Cloud agent
3982
+
3983
+ A published agent runs when somebody asks it to and at no other time. A trigger
3984
+ is what makes it start on its own \u2014 a time, a chat message, or an event in a
3985
+ connected app.
3986
+
3987
+ \`\`\`
3988
+ workser agent-cloud triggers <id> # what starts it today
3989
+ workser agent-cloud trigger-add <id> schedule cron="0 9 * * 1-5" name="Morning report"
3990
+ workser agent-cloud trigger-add <id> chat app_type=line
3991
+ workser agent-cloud trigger-add <id> app_event event_type=GMAIL_NEW_GMAIL_MESSAGE connected_account_id=<id>
3992
+ workser agent-cloud trigger-setup <id> <triggerId> # the webhook URL to paste
3993
+ workser agent-cloud trigger-events <id> # what fired, and what it started
3994
+ workser agent-cloud trigger-remove <id> <triggerId>
3995
+ \`\`\`
3996
+
3997
+ **A trigger works the moment it is saved** \u2014 unlike everything above, it is not
3998
+ part of the agent's version and does not wait for a publish.
3999
+
4000
+ \`rule=\` is optional and is the FIRING rule, not the agent's instructions:
4001
+ \`rule="only when the message mentions an order"\`. Leave it empty on a chat
4002
+ channel and every message reaches the agent, which is what a support agent
4003
+ wants \u2014 the agent decides what to do with each one.
4004
+
4005
+ For a chat trigger, **\`trigger-setup\` is not optional**: it returns the webhook
4006
+ URL to paste into LINE's or Slack's console, and it says whether anything has
4007
+ arrived yet. Without that step the trigger sits there looking finished and
4008
+ never receives anything.
4009
+
4010
+ To let the agent ANSWER on that channel, the project also needs the account
4011
+ connected \u2014 \`workser line connect --token \u2026\`. See \`workser help chat-channels\`.
4012
+
4013
+ ## The two halves of a chat agent
4014
+
4015
+ Receiving and answering are separate, and each has its own setup:
4016
+
4017
+ | | What it does | How |
4018
+ | --- | --- | --- |
4019
+ | The trigger | a message STARTS the agent | \`trigger-add <id> chat app_type=line\`, then \`trigger-setup\` |
4020
+ | The connection | the agent can ANSWER | \`workser line connect --token \u2026\` |
4021
+
4022
+ Do only the first and the agent listens and never speaks. Do only the second and
4023
+ nothing ever wakes it up.
4024
+
4025
+ With both in place, a run started by a message is handed the reply token and the
4026
+ sender, and gets every operation of that account as a tool \u2014 \`line_reply\`,
4027
+ \`slack_send_message\` and the rest. Sends go through the agent's approval gate;
4028
+ reads do not.
4029
+
4030
+ ## Schedules, in plain words
4031
+
4032
+ \`cron=\` takes a standard five-field expression. The four people ask for:
4033
+
4034
+ \`\`\`
4035
+ cron="0 9 * * 1-5" every weekday at 9am
4036
+ cron="0 9 * * *" every day at 9am
4037
+ cron="0 * * * *" every hour
4038
+ cron="0 9 * * 1" every Monday at 9am
4039
+ \`\`\`
4040
+
4041
+ \`timezone=\` defaults to UTC, which is almost never what somebody means by "9am".
4042
+ Set it: \`timezone="Asia/Bangkok"\`.
3964
4043
  `
3965
4044
  },
3966
4045
  {
@@ -4378,6 +4457,113 @@ copy is the current one.
4378
4457
  \`workser business\` is how **you** inspect and fix data while building. The app reads
4379
4458
  the same records at runtime through \`workser.business\` in \`@workser/app\` \u2014 see the
4380
4459
  \`workser-sdk\` skill. An app shelling out to this CLI per request is wrong.
4460
+ `
4461
+ },
4462
+ {
4463
+ topic: "chat-channels",
4464
+ title: "LINE, Telegram, Discord, Slack",
4465
+ summary: "Connect a chat account once, then send, reply and read from the CLI or from an agent \u2014 LINE gets its whole Messaging API.",
4466
+ commands: ["line", "telegram", "discord", "slack"],
4467
+ source: "skills/workser/reference/chat-channels.md",
4468
+ body: `# LINE, Telegram, Discord, Slack
4469
+
4470
+ Connect the business's chat account to this project **once**. After that it is
4471
+ reachable from here, from Workser Code, and from any Agent Cloud agent in this
4472
+ project \u2014 including the one a chat message starts.
4473
+
4474
+ Every provider takes the same six verbs:
4475
+
4476
+ \`\`\`
4477
+ workser <provider> status # is anything connected?
4478
+ workser <provider> connect --token <token>
4479
+ workser <provider> verify # does the credential still work?
4480
+ workser <provider> disconnect
4481
+ workser <provider> ops [--group <name>] # everything it can do
4482
+ workser <provider> call <operation> --params '<json>'
4483
+ \`\`\`
4484
+
4485
+ where \`<provider>\` is \`line\`, \`telegram\`, \`discord\` or \`slack\`.
4486
+
4487
+ ## Getting the token
4488
+
4489
+ | Provider | Where | Flag |
4490
+ | --- | --- | --- |
4491
+ | LINE | Developers console \u2192 Messaging API \u2192 Channel access token | \`--token\`, plus \`--secret\` for the channel secret |
4492
+ | Telegram | @BotFather \u2192 \`/newbot\` or \`/token\` | \`--token\` |
4493
+ | Discord | Developer Portal \u2192 your app \u2192 Bot \u2192 Reset Token | \`--token\` (the bot must also be invited to the server) |
4494
+ | Slack | Your app \u2192 OAuth & Permissions \u2192 Bot User OAuth Token (\`xoxb-\`) | \`--token\`, plus \`--signing-secret\` |
4495
+
4496
+ \`connect\` calls the provider immediately to prove the credential works, so a
4497
+ mistyped token fails right there instead of an hour later as a customer message
4498
+ nobody answered.
4499
+
4500
+ ## \`call\` is the whole API
4501
+
4502
+ \`ops\` prints every operation with a one-line summary. The \`\u25CF\` marks the ones
4503
+ that send something, spend quota, or change the account.
4504
+
4505
+ \`\`\`
4506
+ workser line call push --params '{"to":"U4af\u2026","messages":[{"type":"text","text":"Your order shipped"}]}'
4507
+ workser line call richmenu_list
4508
+ workser telegram call send_chat_action --params '{"chat_id":123,"action":"typing"}'
4509
+ workser slack call list_conversations --params '{"types":"public_channel"}'
4510
+ workser discord call create_thread --params '{"channel_id":"\u2026","message_id":"\u2026","name":"Order #4021"}'
4511
+ \`\`\`
4512
+
4513
+ Parameters are flat \u2014 path, query and body fields all go in \`--params\` together
4514
+ and the server sorts them. Fields it has never heard of are **forwarded
4515
+ untouched**, so a flex component or a Slack block the vendor shipped last week
4516
+ works today.
4517
+
4518
+ ## Shortcuts
4519
+
4520
+ \`\`\`
4521
+ workser line send <to> "<text>" | reply <replyToken> "<text>" | broadcast "<text>"
4522
+ workser line quota | profile <userId>
4523
+ workser telegram send <chatId> "<text>"
4524
+ workser discord send <channelId> "<text>"
4525
+ workser slack send <channel> "<text>"
4526
+ \`\`\`
4527
+
4528
+ ## Reply beats push, on LINE
4529
+
4530
+ \`reply\` uses the token that came with the incoming message. It is **free** and
4531
+ does not touch the monthly quota; \`push\` costs one message per recipient. The
4532
+ token is single-use and expires within a minute or so, so reply first and fall
4533
+ back to push.
4534
+
4535
+ \`broadcast\` sends to every follower and spends one message each. On a large
4536
+ account that is the most expensive call available \u2014 say what it will cost
4537
+ before running it.
4538
+
4539
+ ## How complete each one is
4540
+
4541
+ **LINE is the deep one** \u2014 its entire Messaging API, about seventy operations:
4542
+ messaging, rich menus, audiences and narrowcast, insight, quota, content,
4543
+ groups and rooms, and the webhook endpoint itself.
4544
+
4545
+ **Telegram, Discord and Slack carry what a chat agent needs** \u2014 send, reply,
4546
+ edit, delete, react, typing indicator, threads and DMs, plus reading people and
4547
+ conversations. Not their whole APIs. \`ops\` is the truth; ask it rather than
4548
+ assuming an operation exists.
4549
+
4550
+ ## Rich menus, in order (LINE)
4551
+
4552
+ A rich menu does nothing until it has both a picture and a place to appear:
4553
+
4554
+ 1. \`richmenu_create\` \u2014 the layout and tappable areas. Returns an id.
4555
+ 2. \`richmenu_upload_image\` \u2014 \`content\` is the file **base64-encoded**, with
4556
+ \`content_type\`. The image must match the declared size exactly.
4557
+ 3. \`richmenu_set_default\` (everybody) or \`richmenu_link_user\` (one person).
4558
+
4559
+ \`richmenu_validate\` checks a layout without creating it.
4560
+
4561
+ ## When it is an agent doing this
4562
+
4563
+ An Agent Cloud agent in a project with a connected account gets these as tools
4564
+ automatically \u2014 there is nothing to bind, and no capability to switch on. Sends
4565
+ go through the agent's approval gate; reads do not. See
4566
+ \`workser help agent-cloud\`.
4381
4567
  `
4382
4568
  },
4383
4569
  {
@@ -5045,7 +5231,8 @@ that URL directly in the app.
5045
5231
 
5046
5232
  \`\`\`bash
5047
5233
  workser image generate "flat illustration of a farm delivery van, brand colors" --json
5048
- workser image gen "same van, from the side" -r https://\u2026 -o ./public/van.png --json
5234
+ workser image gen "same van, from the side" -r https://\u2026 --json # use .data.url
5235
+ workser image gen "van, rear view" -o /tmp/van.png --json # only if you must
5049
5236
  \`\`\`
5050
5237
 
5051
5238
  ## Notes that matter (generation)
@@ -5056,6 +5243,16 @@ workser image gen "same van, from the side" -r https://\u2026 -o ./public/van.pn
5056
5243
  question comes back as text rather than an image. Check that you actually got an
5057
5244
  image before wiring the URL into a page; an empty result is not a transport error
5058
5245
  to retry.
5246
+ - **The URL is the deliverable \u2014 don't download it into the app folder.** This
5247
+ example used to write to \`./public/van.png\`, which is the single most common way
5248
+ generated art ends up committed to the user's repository: in every deploy bundle
5249
+ for ever, unreplaceable without a redeploy, against a 25MB publish cap. The
5250
+ returned URL is already public and already served. Reference it.
5251
+
5252
+ When a file genuinely has to exist \u2014 an asset the build reads, something to hand
5253
+ the user \u2014 \`-o\` into a temp path and \`workser storage put\` it into the bucket
5254
+ (or whatever store the owner chose; the repo is never it). See
5255
+ \`reference/storage.md\`.
5059
5256
  - **\`--output\` writes only the first image.** If you asked for several, the rest
5060
5257
  exist only as URLs.
5061
5258
  - **Placeholder art is not a deliverable.** Generating a hero image to unblock a
@@ -5533,6 +5730,40 @@ workser storage put <local> <key> # upload a file into the bucket
5533
5730
  workser storage get <key> [dest] # download an object (or print its URL)
5534
5731
  \`\`\`
5535
5732
 
5733
+ ## This is where media goes \u2014 the folder is for code
5734
+
5735
+ Every project has this bucket, provisioned and paid for, served over a CDN, and
5736
+ visible to the owner on the Files screen. It is the **default** home for generated
5737
+ art, product photography, PDFs, exports, avatars and anything a user uploads.
5738
+
5739
+ **Default, not mandate.** An owner who wants Cloudinary, their own S3, or anything
5740
+ else is entitled to it \u2014 build it and don't argue. What is not yours to do is pick
5741
+ the alternative for them, or drift into one because it was quicker. If you think
5742
+ there is a real reason to go outside, say so and let them answer, then
5743
+ \`workser decision create\` it so the next agent doesn't quietly reverse it.
5744
+
5745
+ **The repo is not one of the options.** Whichever provider the owner chose, files
5746
+ under \`public/\`, \`assets/\` or \`static/\` cost them four things, none recoverable
5747
+ later:
5748
+
5749
+ - **They are committed.** Publishing runs \`git add -A\` over the app folder, so
5750
+ every image enters the repository's history. Deleting it afterwards does not
5751
+ remove it.
5752
+ - **They ride in every deploy.** The source bundle has a 25MB cap; enough media
5753
+ and publishing stops working outright, with an error about the bundle rather
5754
+ than about the images.
5755
+ - **They cannot change without a redeploy.** A photo the owner wants swapped
5756
+ becomes a code change and a build. From the bucket it is one \`storage put\`.
5757
+ - **They are invisible.** The Files screen lists the bucket. Nothing there shows
5758
+ what is sitting in the repo, so the owner cannot find, replace or delete it.
5759
+
5760
+ Small build-time assets \u2014 a logo, a favicon, an icon, an SVG the bundler inlines \u2014
5761
+ are the exception. Anything that is content, or that a user produced, is not.
5762
+
5763
+ \`\`\`bash
5764
+ workser storage put ./out/hero.png products/hero.png --json # \u2192 .data.url
5765
+ \`\`\`
5766
+
5536
5767
  ## Notes that matter
5537
5768
 
5538
5769
  - **One bucket per project, shared by its apps.** Namespace your keys by app or
@@ -5542,7 +5773,9 @@ workser storage get <key> [dest] # download an object (or print its URL)
5542
5773
  - **This is not where app uploads should go through you.** At runtime the app uses
5543
5774
  \`workser.storage\` from \`@workser/app\`, and for anything large it should request a
5544
5775
  presigned upload URL so the bytes never pass through Workser. See the
5545
- \`workser-sdk\` skill.
5776
+ \`workser-sdk\` skill. Writing an upload handler that saves into the app's own
5777
+ filesystem is the same mistake as above, plus one more: on a serverless host the
5778
+ file is gone at the end of the request.
5546
5779
 
5547
5780
  ## Not the same as \`workser neon storage\`
5548
5781
 
@@ -7537,7 +7770,12 @@ function registerVersions(program3) {
7537
7770
  }
7538
7771
  const items = await api(ctx, `/v1/projects/${projectId}/versions`, {
7539
7772
  query: {
7540
- ...opts.app ? { webAppId: String(opts.app) } : {},
7773
+ // `--app` wins; otherwise the app this folder is linked to (`ctx.appId`,
7774
+ // from `.workser-app`). Standing in an app folder and being told
7775
+ // "needs a webAppId" was the CLI refusing to read the marker it had
7776
+ // already read for `status`. Outside any app folder this stays unset
7777
+ // and the server's own refusal (never the primary-app guess) applies.
7778
+ ...opts.app || ctx.appId ? { webAppId: String(opts.app ?? ctx.appId) } : {},
7541
7779
  ...parsed.value ? { environment: parsed.value } : {}
7542
7780
  }
7543
7781
  });
@@ -7579,7 +7817,12 @@ function registerLogs(program3) {
7579
7817
  throw new WorkserError(parsed.error, { code: "bad_input" });
7580
7818
  }
7581
7819
  const scope = {
7582
- ...opts.app ? { webAppId: String(opts.app) } : {},
7820
+ // `--app` wins; otherwise the app this folder is linked to (`ctx.appId`,
7821
+ // from `.workser-app`). Standing in an app folder and being told
7822
+ // "needs a webAppId" was the CLI refusing to read the marker it had
7823
+ // already read for `status`. Outside any app folder this stays unset
7824
+ // and the server's own refusal (never the primary-app guess) applies.
7825
+ ...opts.app || ctx.appId ? { webAppId: String(opts.app ?? ctx.appId) } : {},
7583
7826
  ...parsed.value ? { environment: parsed.value } : {}
7584
7827
  };
7585
7828
  const first = await api(ctx, `/v1/projects/${projectId}/logs`, {
@@ -7750,7 +7993,7 @@ function registerDoctor(program3) {
7750
7993
  const endpointSource = opts.endpoint ? "--endpoint" : process.env.WORKSER_DAEMON_URL ? "$WORKSER_DAEMON_URL" : session.endpoint ? "session" : process.env.WORKSER_API_URL ? "$WORKSER_API_URL" : `cloud-default: ${env}`;
7751
7994
  const projectSource = opts.project ? "--project" : link?.projectId ? ".workser link" : session.defaultProjectId ? "session" : void 0;
7752
7995
  const git2 = gitVersion();
7753
- const report = {
7996
+ const report2 = {
7754
7997
  endpoint: ctx.endpoint,
7755
7998
  endpointSource,
7756
7999
  env,
@@ -7770,7 +8013,7 @@ function registerDoctor(program3) {
7770
8013
  cwd: ctx.cwd,
7771
8014
  workspace: session.workspaceName ?? null
7772
8015
  };
7773
- ok(report, () => {
8016
+ ok(report2, () => {
7774
8017
  line(import_picocolors15.default.bold("workser doctor"));
7775
8018
  line(` endpoint: ${ctx.endpoint} ${import_picocolors15.default.dim(`(${endpointSource})`)}`);
7776
8019
  line(
@@ -8231,6 +8474,118 @@ function registerAgentCloud(program3) {
8231
8474
  });
8232
8475
  })
8233
8476
  );
8477
+ cloud.command("triggers <agentId>").description("What starts this agent \u2014 schedules, chat channels, app events").action(
8478
+ action(async ({ ctx, args }) => {
8479
+ const res = await api(
8480
+ ctx,
8481
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/triggers`
8482
+ );
8483
+ const items = res?.triggers ?? [];
8484
+ ok(res, () => {
8485
+ if (!items.length) {
8486
+ return line(
8487
+ import_picocolors17.default.dim("Nothing starts this agent yet \u2014 it only runs when asked.")
8488
+ );
8489
+ }
8490
+ for (const t of items) {
8491
+ const state = t.status === "active" ? import_picocolors17.default.green("on") : import_picocolors17.default.yellow(t.status);
8492
+ const when = t.schedule_config?.cron_expression ?? t.app_event_config?.event_source ?? t.app_event_config?.event_type ?? "";
8493
+ line(
8494
+ `${import_picocolors17.default.bold(t.name ?? t.trigger_type)} ${state} ${import_picocolors17.default.dim(when)}`
8495
+ );
8496
+ line(" " + import_picocolors17.default.dim(t.id));
8497
+ }
8498
+ });
8499
+ })
8500
+ );
8501
+ cloud.command("trigger-add <agentId> <kind> [pairs...]").description(
8502
+ "Start an agent on a schedule, a chat channel or an app event. kind is schedule | chat | app_event. e.g. trigger-add <id> chat app_type=line"
8503
+ ).action(
8504
+ action(async ({ ctx, args }) => {
8505
+ const kind = String(args[1]);
8506
+ const pairs = parsePairs(
8507
+ args[2] ?? [],
8508
+ [
8509
+ "name",
8510
+ "rule",
8511
+ "cron",
8512
+ "timezone",
8513
+ "app_type",
8514
+ "event_type",
8515
+ "connected_account_id"
8516
+ ]
8517
+ );
8518
+ const body = buildTriggerBody(kind, pairs);
8519
+ const res = await api(
8520
+ ctx,
8521
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/triggers`,
8522
+ { method: "POST", body }
8523
+ );
8524
+ ok(res, () => {
8525
+ line(import_picocolors17.default.green("Added.") + " " + import_picocolors17.default.dim(res?.trigger?.id ?? ""));
8526
+ if (kind === "chat") {
8527
+ line(
8528
+ import_picocolors17.default.dim("Now get the webhook URL to paste into the platform: ") + import_picocolors17.default.bold(
8529
+ `workser agent-cloud trigger-setup ${args[0]} ${res?.trigger?.id ?? "<triggerId>"}`
8530
+ )
8531
+ );
8532
+ }
8533
+ });
8534
+ })
8535
+ );
8536
+ cloud.command("trigger-setup <agentId> <triggerId>").description("The webhook URL and console steps for a chat trigger").action(
8537
+ action(async ({ ctx, args }) => {
8538
+ const res = await api(
8539
+ ctx,
8540
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/triggers/${encodeURIComponent(args[1])}/setup`
8541
+ );
8542
+ ok(res, () => {
8543
+ line(import_picocolors17.default.bold(`Connect ${res?.label ?? "this channel"}`));
8544
+ line("");
8545
+ line(import_picocolors17.default.bold("Webhook URL"));
8546
+ line(" " + (res?.webhook_url ?? import_picocolors17.default.dim("\u2014")));
8547
+ if (res?.verify_token) {
8548
+ line(import_picocolors17.default.bold("Verify token"));
8549
+ line(" " + res.verify_token);
8550
+ }
8551
+ line("");
8552
+ for (const step of res?.setup_steps ?? []) line(" " + import_picocolors17.default.dim(step));
8553
+ line("");
8554
+ line(
8555
+ res?.is_verified ? import_picocolors17.default.green(`Receiving \u2014 ${res.event_count ?? 0} events so far.`) : import_picocolors17.default.yellow("Nothing has arrived yet.")
8556
+ );
8557
+ });
8558
+ })
8559
+ );
8560
+ cloud.command("trigger-remove <agentId> <triggerId>").description("Stop something from starting this agent").action(
8561
+ action(async ({ ctx, args }) => {
8562
+ const res = await api(
8563
+ ctx,
8564
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/triggers/${encodeURIComponent(args[1])}`,
8565
+ { method: "DELETE" }
8566
+ );
8567
+ ok(res, () => line("Removed."));
8568
+ })
8569
+ );
8570
+ cloud.command("trigger-events <agentId>").description("What has fired lately, and whether it started the agent").option("--limit <n>", "how many to show").action(
8571
+ action(async ({ ctx, args, opts }) => {
8572
+ const res = await api(
8573
+ ctx,
8574
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/trigger-events`,
8575
+ { query: { limit: opts.limit } }
8576
+ );
8577
+ const items = res?.events ?? [];
8578
+ ok(res, () => {
8579
+ if (!items.length) return line(import_picocolors17.default.dim("Nothing has fired yet."));
8580
+ for (const e of items) {
8581
+ const state = e.status === "matched" ? import_picocolors17.default.green("started the agent") : e.status === "failed" ? import_picocolors17.default.red("failed") : import_picocolors17.default.yellow(e.status);
8582
+ line(`${state} ${e.summary ?? e.trigger_name ?? ""}`);
8583
+ if (e.error_message) line(" " + import_picocolors17.default.red(e.error_message));
8584
+ else if (e.reasoning) line(" " + import_picocolors17.default.dim(e.reasoning));
8585
+ }
8586
+ });
8587
+ })
8588
+ );
8234
8589
  cloud.command("publish <agentId>").description("Put the current setup live \u2014 nothing takes effect until this runs").option("--note <text>", "What changed, for the version history").action(
8235
8590
  action(async ({ ctx, args, opts }) => {
8236
8591
  const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}/publish`, {
@@ -8413,6 +8768,60 @@ function parsePairs(pairs, allowed) {
8413
8768
  }
8414
8769
  return out;
8415
8770
  }
8771
+ function buildTriggerBody(kind, pairs) {
8772
+ const base = {
8773
+ name: pairs.name,
8774
+ rule: pairs.rule
8775
+ };
8776
+ if (kind === "schedule") {
8777
+ if (!pairs.cron) {
8778
+ throw new Error(
8779
+ 'A schedule needs cron=, e.g. cron="0 9 * * 1-5" for every weekday at 9am.'
8780
+ );
8781
+ }
8782
+ return {
8783
+ ...base,
8784
+ trigger_type: "schedule",
8785
+ name: pairs.name ?? "Scheduled run",
8786
+ schedule_config: {
8787
+ cron_expression: pairs.cron,
8788
+ timezone: pairs.timezone ?? "UTC"
8789
+ }
8790
+ };
8791
+ }
8792
+ if (kind === "chat") {
8793
+ if (!pairs.app_type) {
8794
+ throw new Error(
8795
+ "A chat trigger needs app_type=, one of: line, telegram, discord, slack, facebook_messenger, instagram, whatsapp, twitter, custom."
8796
+ );
8797
+ }
8798
+ return {
8799
+ ...base,
8800
+ trigger_type: "chat_webhook",
8801
+ chat_webhook_config: {
8802
+ app_type: pairs.app_type,
8803
+ event_type: pairs.event_type ?? "message"
8804
+ }
8805
+ };
8806
+ }
8807
+ if (kind === "app_event") {
8808
+ if (!pairs.event_type || !pairs.connected_account_id) {
8809
+ throw new Error(
8810
+ "An app-event trigger needs event_type= and connected_account_id= (see `workser connection list`)."
8811
+ );
8812
+ }
8813
+ return {
8814
+ ...base,
8815
+ trigger_type: "app_event",
8816
+ name: pairs.name ?? pairs.event_type,
8817
+ app_event_config: {
8818
+ event_type: pairs.event_type,
8819
+ connected_account_id: pairs.connected_account_id
8820
+ }
8821
+ };
8822
+ }
8823
+ throw new Error(`Unknown kind "${kind}". One of: schedule | chat | app_event`);
8824
+ }
8416
8825
 
8417
8826
  // src/commands/verify.ts
8418
8827
  var import_picocolors18 = __toESM(require_picocolors(), 1);
@@ -8738,8 +9147,273 @@ function registerConnection(program3) {
8738
9147
  );
8739
9148
  }
8740
9149
 
8741
- // src/commands/tool.ts
9150
+ // src/commands/channel.ts
8742
9151
  var import_picocolors23 = __toESM(require_picocolors(), 1);
9152
+ var SHAPES = [
9153
+ {
9154
+ id: "line",
9155
+ label: "LINE",
9156
+ description: "Connect a LINE Official Account and use the LINE Messaging API",
9157
+ tokenField: "channel_access_token",
9158
+ tokenHelp: "Messaging API tab \u2192 Channel access token",
9159
+ extra: {
9160
+ flag: "--secret <channelSecret>",
9161
+ field: "channel_secret",
9162
+ help: "Basic settings \u2192 Channel secret (for webhook signatures)"
9163
+ },
9164
+ send: {
9165
+ operation: "push",
9166
+ build: (to, text) => ({ to, messages: [{ type: "text", text }] })
9167
+ },
9168
+ reply: {
9169
+ operation: "reply",
9170
+ build: (replyToken, text) => ({
9171
+ replyToken,
9172
+ messages: [{ type: "text", text }]
9173
+ })
9174
+ }
9175
+ },
9176
+ {
9177
+ id: "telegram",
9178
+ label: "Telegram",
9179
+ description: "Connect a Telegram bot and message chats, groups and channels",
9180
+ tokenField: "bot_token",
9181
+ tokenHelp: "The token @BotFather gave you",
9182
+ send: {
9183
+ operation: "send_message",
9184
+ build: (chat_id, text) => ({ chat_id, text })
9185
+ }
9186
+ },
9187
+ {
9188
+ id: "discord",
9189
+ label: "Discord",
9190
+ description: "Connect a Discord bot and post in servers and DMs",
9191
+ tokenField: "bot_token",
9192
+ tokenHelp: "Developer Portal \u2192 your application \u2192 Bot \u2192 Reset Token",
9193
+ send: {
9194
+ operation: "send_message",
9195
+ build: (channel_id, content) => ({ channel_id, content })
9196
+ }
9197
+ },
9198
+ {
9199
+ id: "slack",
9200
+ label: "Slack",
9201
+ description: "Connect a Slack app and post in channels and DMs",
9202
+ tokenField: "bot_token",
9203
+ tokenHelp: "OAuth & Permissions \u2192 Bot User OAuth Token (starts xoxb-)",
9204
+ extra: {
9205
+ flag: "--signing-secret <secret>",
9206
+ field: "signing_secret",
9207
+ help: "Basic Information \u2192 Signing Secret (for event signatures)"
9208
+ },
9209
+ send: {
9210
+ operation: "send_message",
9211
+ build: (channel, text) => ({ channel, text })
9212
+ }
9213
+ }
9214
+ ];
9215
+ function registerChannels(program3) {
9216
+ for (const shape of SHAPES) register(program3, shape);
9217
+ }
9218
+ function register(program3, shape) {
9219
+ const cmd = program3.command(shape.id).description(shape.description);
9220
+ const path = (suffix = "") => (projectId) => `/v1/projects/${projectId}/channels/${shape.id}${suffix}`;
9221
+ cmd.command("status").description(`Show this project's ${shape.label} connection`).action(
9222
+ action(async ({ ctx }) => {
9223
+ const projectId = requireProject(ctx);
9224
+ const res = await api(ctx, path()(projectId));
9225
+ ok(res, () => {
9226
+ const c = res?.connection;
9227
+ if (!c) {
9228
+ return line(
9229
+ import_picocolors23.default.dim(
9230
+ `No ${shape.label} account connected. Run \`workser ${shape.id} connect\`.`
9231
+ )
9232
+ );
9233
+ }
9234
+ const state = c.status === "connected" ? import_picocolors23.default.green(c.status) : import_picocolors23.default.yellow(c.status);
9235
+ line(`${import_picocolors23.default.bold(c.display_name ?? shape.label)} ${state}`);
9236
+ for (const [k, v] of Object.entries(c.public_config ?? {})) {
9237
+ if (v) line(import_picocolors23.default.dim(` ${k}: ${v}`));
9238
+ }
9239
+ if (c.last_error) line(import_picocolors23.default.red(` ${c.last_error}`));
9240
+ });
9241
+ })
9242
+ );
9243
+ const connect = cmd.command("connect").description(`Connect a ${shape.label} account to this project`).requiredOption("--token <token>", shape.tokenHelp);
9244
+ if (shape.extra) connect.option(shape.extra.flag, shape.extra.help);
9245
+ connect.action(
9246
+ action(async ({ ctx, opts }) => {
9247
+ const projectId = requireProject(ctx);
9248
+ const body = {};
9249
+ body[shape.tokenField] = String(opts.token);
9250
+ if (shape.extra) {
9251
+ const key = shape.extra.flag.split(" ")[0].replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
9252
+ if (opts[key]) body[shape.extra.field] = opts[key];
9253
+ }
9254
+ const res = await api(ctx, path("/connect")(projectId), { body });
9255
+ ok(
9256
+ res,
9257
+ () => line(
9258
+ `Connected ${import_picocolors23.default.bold(res?.connection?.display_name ?? shape.label)}.`
9259
+ )
9260
+ );
9261
+ })
9262
+ );
9263
+ cmd.command("verify").description(`Ask ${shape.label} whether the stored credential still works`).action(
9264
+ action(async ({ ctx }) => {
9265
+ const projectId = requireProject(ctx);
9266
+ const res = await api(ctx, path("/verify")(projectId), { body: {} });
9267
+ ok(res, () => {
9268
+ const c = res?.connection;
9269
+ line(
9270
+ c?.status === "connected" ? import_picocolors23.default.green("The credential still works.") : import_picocolors23.default.red(c?.last_error ?? `${shape.label} refused it.`)
9271
+ );
9272
+ });
9273
+ })
9274
+ );
9275
+ cmd.command("disconnect").description(`Disconnect ${shape.label} from this project`).action(
9276
+ action(async ({ ctx }) => {
9277
+ const projectId = requireProject(ctx);
9278
+ const res = await api(ctx, path()(projectId), { method: "DELETE" });
9279
+ ok(res, () => line("Disconnected."));
9280
+ })
9281
+ );
9282
+ cmd.command("ops").description(`List every ${shape.label} operation`).option("--group <name>", "filter to one group").action(
9283
+ action(async ({ ctx, opts }) => {
9284
+ const projectId = requireProject(ctx);
9285
+ const res = await api(ctx, path("/operations")(projectId));
9286
+ const all = res?.operations ?? [];
9287
+ const items = opts.group ? all.filter((o) => o.group === opts.group) : all;
9288
+ ok(items, () => {
9289
+ if (!items.length) return line(import_picocolors23.default.dim("No operations matched."));
9290
+ let group = "";
9291
+ for (const o of items) {
9292
+ if (o.group !== group) {
9293
+ group = o.group;
9294
+ line(import_picocolors23.default.dim(`
9295
+ ${group}`));
9296
+ }
9297
+ const mark = o.writes ? import_picocolors23.default.yellow(" \u25CF") : " ";
9298
+ line(`${mark} ${import_picocolors23.default.bold(o.id.padEnd(28))} ${o.summary}`);
9299
+ }
9300
+ line(
9301
+ import_picocolors23.default.dim(
9302
+ "\n\u25CF sends something, spends quota, or changes the account."
9303
+ )
9304
+ );
9305
+ for (const note of res?.excludes ?? []) {
9306
+ line(import_picocolors23.default.dim(` not included \u2014 ${note}`));
9307
+ }
9308
+ });
9309
+ })
9310
+ );
9311
+ cmd.command("call <operation>").description(`Run any ${shape.label} operation \u2014 see \`workser ${shape.id} ops\``).option("--params <json>", "the operation's parameters as a JSON string", "{}").action(
9312
+ action(async ({ ctx, args, opts }) => {
9313
+ const projectId = requireProject(ctx);
9314
+ const res = await call(ctx, projectId, shape, args[0], parseParams(opts.params));
9315
+ ok(res, () => line(JSON.stringify(res?.data ?? res, null, 2)));
9316
+ })
9317
+ );
9318
+ if (shape.send) {
9319
+ const send = shape.send;
9320
+ cmd.command("send <to> <text>").description(
9321
+ shape.id === "discord" ? "Post a text message in a Discord channel id" : shape.id === "slack" ? "Post a text message in a Slack channel or DM id" : "Send a text message to a chat, user, group or room id"
9322
+ ).action(
9323
+ action(async ({ ctx, args }) => {
9324
+ const projectId = requireProject(ctx);
9325
+ const res = await call(
9326
+ ctx,
9327
+ projectId,
9328
+ shape,
9329
+ send.operation,
9330
+ send.build(args[0], args[1])
9331
+ );
9332
+ ok(res, () => report(res, "Sent."));
9333
+ })
9334
+ );
9335
+ }
9336
+ if (shape.reply) {
9337
+ const reply = shape.reply;
9338
+ cmd.command("reply <token> <text>").description("Answer a message using its reply token \u2014 free, and single-use").action(
9339
+ action(async ({ ctx, args }) => {
9340
+ const projectId = requireProject(ctx);
9341
+ const res = await call(
9342
+ ctx,
9343
+ projectId,
9344
+ shape,
9345
+ reply.operation,
9346
+ reply.build(args[0], args[1])
9347
+ );
9348
+ ok(res, () => report(res, "Replied."));
9349
+ })
9350
+ );
9351
+ }
9352
+ if (shape.id === "line") {
9353
+ cmd.command("broadcast <text>").description("Send a text message to EVERY follower \u2014 spends one message each").action(
9354
+ action(async ({ ctx, args }) => {
9355
+ const projectId = requireProject(ctx);
9356
+ const res = await call(ctx, projectId, shape, "broadcast", {
9357
+ messages: [{ type: "text", text: args[0] }]
9358
+ });
9359
+ ok(res, () => report(res, "Broadcast sent."));
9360
+ })
9361
+ );
9362
+ cmd.command("quota").description("How much of this month's message allowance is left").action(
9363
+ action(async ({ ctx }) => {
9364
+ const projectId = requireProject(ctx);
9365
+ const [quota, used] = await Promise.all([
9366
+ call(ctx, projectId, shape, "quota", {}),
9367
+ call(ctx, projectId, shape, "quota_consumption", {})
9368
+ ]);
9369
+ ok({ quota: quota?.data, consumption: used?.data }, () => {
9370
+ const limit = quota?.data?.value;
9371
+ const spent = used?.data?.totalUsage;
9372
+ if (limit === void 0) {
9373
+ return line(JSON.stringify(quota?.data ?? quota, null, 2));
9374
+ }
9375
+ line(`${spent ?? "?"} of ${limit} messages used this month.`);
9376
+ });
9377
+ })
9378
+ );
9379
+ cmd.command("profile <userId>").description("Look up one follower").action(
9380
+ action(async ({ ctx, args }) => {
9381
+ const projectId = requireProject(ctx);
9382
+ const res = await call(ctx, projectId, shape, "profile", {
9383
+ userId: args[0]
9384
+ });
9385
+ ok(res, () => line(JSON.stringify(res?.data ?? res, null, 2)));
9386
+ })
9387
+ );
9388
+ }
9389
+ }
9390
+ function parseParams(raw) {
9391
+ try {
9392
+ const parsed = JSON.parse(raw || "{}");
9393
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
9394
+ return parsed;
9395
+ }
9396
+ } catch {
9397
+ }
9398
+ throw new Error(
9399
+ `--params must be a JSON object, e.g. --params '{"chat_id":123,"text":"hi"}'`
9400
+ );
9401
+ }
9402
+ async function call(ctx, projectId, shape, operation, params) {
9403
+ return api(ctx, `/v1/projects/${projectId}/channels/${shape.id}/call`, {
9404
+ body: { operation, params }
9405
+ });
9406
+ }
9407
+ function report(res, success2) {
9408
+ if (res?.ok === false) {
9409
+ line(import_picocolors23.default.red(res.message ?? "That was refused."));
9410
+ return;
9411
+ }
9412
+ line(success2);
9413
+ }
9414
+
9415
+ // src/commands/tool.ts
9416
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
8743
9417
  function registerTool(program3) {
8744
9418
  const tool = program3.command("tool").description(
8745
9419
  "Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
@@ -8748,7 +9422,7 @@ function registerTool(program3) {
8748
9422
  action(async ({ ctx }) => {
8749
9423
  const tools = await api(ctx, "/v1/tool/list");
8750
9424
  ok(tools, () => {
8751
- if (!tools?.length) return line(import_picocolors23.default.dim("No tools available."));
9425
+ if (!tools?.length) return line(import_picocolors24.default.dim("No tools available."));
8752
9426
  const byCategory = /* @__PURE__ */ new Map();
8753
9427
  for (const t of tools) {
8754
9428
  const list = byCategory.get(t.category) ?? [];
@@ -8756,9 +9430,9 @@ function registerTool(program3) {
8756
9430
  byCategory.set(t.category, list);
8757
9431
  }
8758
9432
  for (const [category, items] of byCategory) {
8759
- line(import_picocolors23.default.bold(category) + ":");
9433
+ line(import_picocolors24.default.bold(category) + ":");
8760
9434
  for (const t of items) {
8761
- line(` ${t.name} ${import_picocolors23.default.dim(t.description ?? "")}`);
9435
+ line(` ${t.name} ${import_picocolors24.default.dim(t.description ?? "")}`);
8762
9436
  }
8763
9437
  }
8764
9438
  });
@@ -8776,7 +9450,7 @@ function registerTool(program3) {
8776
9450
  }
8777
9451
 
8778
9452
  // src/commands/memory.ts
8779
- var import_picocolors24 = __toESM(require_picocolors(), 1);
9453
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
8780
9454
  function registerMemory(program3) {
8781
9455
  const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
8782
9456
  memory.command("add <content>").description("Store something worth remembering across future conversations").option("--metadata <json>", "extra metadata for filtering, as a JSON string").option("--id <customId>", "custom id for dedup/updates").action(
@@ -8800,9 +9474,9 @@ function registerMemory(program3) {
8800
9474
  });
8801
9475
  ok(res, () => {
8802
9476
  const results = res?.results ?? res ?? [];
8803
- if (!results?.length) return line(import_picocolors24.default.dim("No matching memories."));
9477
+ if (!results?.length) return line(import_picocolors25.default.dim("No matching memories."));
8804
9478
  for (const r of results) {
8805
- line(`${import_picocolors24.default.dim(r.id ?? "?")} ${memoryText(r)}`);
9479
+ line(`${import_picocolors25.default.dim(r.id ?? "?")} ${memoryText(r)}`);
8806
9480
  }
8807
9481
  });
8808
9482
  })
@@ -8822,11 +9496,11 @@ function memoryText(r) {
8822
9496
  if (typeof text === "string" && text.trim()) return text;
8823
9497
  const title = r?.documents?.[0]?.title;
8824
9498
  if (typeof title === "string" && title.trim()) return title;
8825
- return import_picocolors24.default.dim("(no readable text on this row)");
9499
+ return import_picocolors25.default.dim("(no readable text on this row)");
8826
9500
  }
8827
9501
 
8828
9502
  // src/commands/note.ts
8829
- var import_picocolors25 = __toESM(require_picocolors(), 1);
9503
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
8830
9504
  function registerNote(program3) {
8831
9505
  program3.command("note <text>").description("Leave a fact the rest of the team will need").addHelpText(
8832
9506
  "after",
@@ -8863,14 +9537,14 @@ function registerNote(program3) {
8863
9537
  }
8864
9538
  ok(res, () => {
8865
9539
  success("Noted for the team.");
8866
- line(import_picocolors25.default.dim(` ${text}`));
9540
+ line(import_picocolors26.default.dim(` ${text}`));
8867
9541
  });
8868
9542
  })
8869
9543
  );
8870
9544
  }
8871
9545
 
8872
9546
  // src/commands/business.ts
8873
- var import_picocolors26 = __toESM(require_picocolors(), 1);
9547
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
8874
9548
  var RESOURCE_PATHS = {
8875
9549
  "business-config": "business-config",
8876
9550
  "business-settings": "business-settings",
@@ -8930,7 +9604,7 @@ function registerBusiness(program3) {
8930
9604
  const projectId = requireProject(ctx);
8931
9605
  const [resource] = args;
8932
9606
  const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
8933
- ok(res, () => line(`Created ${resource} ${import_picocolors26.default.bold(res?.id ?? "")}.`));
9607
+ ok(res, () => line(`Created ${resource} ${import_picocolors27.default.bold(res?.id ?? "")}.`));
8934
9608
  })
8935
9609
  );
8936
9610
  biz.command("update <resource> <id>").description("Update a record by id (PATCH/PUT \u2014 matches the underlying route)").option("--body <json>", "changed fields as a JSON object string", "{}").action(
@@ -8972,7 +9646,7 @@ function businessPath(projectId, resource, subpath) {
8972
9646
  }
8973
9647
 
8974
9648
  // src/commands/artifact.ts
8975
- var import_picocolors27 = __toESM(require_picocolors(), 1);
9649
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
8976
9650
  import { existsSync as existsSync2, statSync } from "fs";
8977
9651
  import { resolve as resolve3, basename as basename3 } from "path";
8978
9652
  var KINDS = [
@@ -9069,7 +9743,7 @@ function registerArtifact(program3) {
9069
9743
  ok(
9070
9744
  res,
9071
9745
  () => success(
9072
- `Recorded ${import_picocolors27.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors27.default.dim(` (${res.kind})`) : ""}`
9746
+ `Recorded ${import_picocolors28.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors28.default.dim(` (${res.kind})`) : ""}`
9073
9747
  )
9074
9748
  );
9075
9749
  })
@@ -9107,7 +9781,7 @@ function registerArtifact(program3) {
9107
9781
  }
9108
9782
  function printArtifacts(rows) {
9109
9783
  if (!rows.length) {
9110
- line(import_picocolors27.default.dim("Nothing produced yet."));
9784
+ line(import_picocolors28.default.dim("Nothing produced yet."));
9111
9785
  return;
9112
9786
  }
9113
9787
  const byStep = /* @__PURE__ */ new Map();
@@ -9117,26 +9791,26 @@ function printArtifacts(rows) {
9117
9791
  byStep.set(r.subtask_id, list);
9118
9792
  }
9119
9793
  for (const [stepId, items] of byStep) {
9120
- line(import_picocolors27.default.bold(`step ${stepId}`));
9794
+ line(import_picocolors28.default.bold(`step ${stepId}`));
9121
9795
  for (const a of items) {
9122
- const flag = a.promoted_at ? import_picocolors27.default.green(" *") : " ";
9796
+ const flag = a.promoted_at ? import_picocolors28.default.green(" *") : " ";
9123
9797
  const where = a.local_path || a.cloud_url || "";
9124
9798
  line(
9125
- `${flag} ${import_picocolors27.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors27.default.dim(` ${where}`) : "")
9799
+ `${flag} ${import_picocolors28.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors28.default.dim(` ${where}`) : "")
9126
9800
  );
9127
- if (a.description) line(import_picocolors27.default.dim(` ${a.description}`));
9801
+ if (a.description) line(import_picocolors28.default.dim(` ${a.description}`));
9128
9802
  }
9129
9803
  line("");
9130
9804
  }
9131
- line(import_picocolors27.default.dim("* = handed over as a deliverable; the rest is working material."));
9805
+ line(import_picocolors28.default.dim("* = handed over as a deliverable; the rest is working material."));
9132
9806
  }
9133
9807
  function printRun2(run) {
9134
9808
  if (!run) return;
9135
- line(` run ${import_picocolors27.default.bold(run.runId)}`);
9809
+ line(` run ${import_picocolors28.default.bold(run.runId)}`);
9136
9810
  if (run.taskId) line(` task ${run.taskId}`);
9137
9811
  if (run.conversationId) line(` chat ${run.conversationId}`);
9138
9812
  if (run.projectId) line(` project ${run.projectId}`);
9139
- if (run.cwd) line(` folder ${import_picocolors27.default.dim(run.cwd)}`);
9813
+ if (run.cwd) line(` folder ${import_picocolors28.default.dim(run.cwd)}`);
9140
9814
  }
9141
9815
 
9142
9816
  // src/commands/image.ts
@@ -9343,7 +10017,7 @@ function registerAudio(program3) {
9343
10017
  }
9344
10018
 
9345
10019
  // src/commands/ask.ts
9346
- var import_picocolors28 = __toESM(require_picocolors(), 1);
10020
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
9347
10021
  var TYPES = [
9348
10022
  "input",
9349
10023
  "choice",
@@ -9440,7 +10114,7 @@ function registerAsk(program3) {
9440
10114
  code: "bad_request"
9441
10115
  });
9442
10116
  }
9443
- info(import_picocolors28.default.dim("Waiting for the user to answer\u2026"));
10117
+ info(import_picocolors29.default.dim("Waiting for the user to answer\u2026"));
9444
10118
  const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
9445
10119
  body: {
9446
10120
  type,
@@ -9468,12 +10142,12 @@ function deriveTitle(message) {
9468
10142
  function printAnswer(res) {
9469
10143
  if (!res) return;
9470
10144
  if (res.status === "answered") {
9471
- line(` ${import_picocolors28.default.green("answered")}`);
10145
+ line(` ${import_picocolors29.default.green("answered")}`);
9472
10146
  const value = extract(res.response);
9473
10147
  if (value) line(` ${value}`);
9474
10148
  return;
9475
10149
  }
9476
- line(` ${import_picocolors28.default.yellow(res.status)} ${import_picocolors28.default.dim(res.reason ?? "")}`);
10150
+ line(` ${import_picocolors29.default.yellow(res.status)} ${import_picocolors29.default.dim(res.reason ?? "")}`);
9477
10151
  }
9478
10152
  function extract(response) {
9479
10153
  if (response == null) return "";
@@ -9492,7 +10166,7 @@ function extract(response) {
9492
10166
  }
9493
10167
 
9494
10168
  // src/commands/search.ts
9495
- var import_picocolors29 = __toESM(require_picocolors(), 1);
10169
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
9496
10170
  function registerSearch(program3) {
9497
10171
  program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
9498
10172
  action(async ({ ctx, args, opts }) => {
@@ -9505,9 +10179,9 @@ function registerSearch(program3) {
9505
10179
  line("");
9506
10180
  }
9507
10181
  const results = res?.results ?? [];
9508
- if (!results.length) return line(import_picocolors29.default.dim("No results."));
10182
+ if (!results.length) return line(import_picocolors30.default.dim("No results."));
9509
10183
  for (const r of results) {
9510
- line(`${r.title || import_picocolors29.default.dim("(untitled)")} ${import_picocolors29.default.dim(r.url)}`);
10184
+ line(`${r.title || import_picocolors30.default.dim("(untitled)")} ${import_picocolors30.default.dim(r.url)}`);
9511
10185
  }
9512
10186
  });
9513
10187
  })
@@ -9515,7 +10189,7 @@ function registerSearch(program3) {
9515
10189
  }
9516
10190
 
9517
10191
  // src/commands/board.ts
9518
- var import_picocolors30 = __toESM(require_picocolors(), 1);
10192
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
9519
10193
 
9520
10194
  // src/commands/record-step.ts
9521
10195
  async function recordEntityStep(ctx, opts) {
@@ -9554,7 +10228,7 @@ function registerBoard(program3) {
9554
10228
  }
9555
10229
  ok(rows, () => {
9556
10230
  if (!rows.length) {
9557
- line(import_picocolors30.default.dim("No cards on the Board yet."));
10231
+ line(import_picocolors31.default.dim("No cards on the Board yet."));
9558
10232
  return;
9559
10233
  }
9560
10234
  for (const r of rows) line(formatRow(r));
@@ -9569,7 +10243,7 @@ function registerBoard(program3) {
9569
10243
  `/v1/projects/${projectId}/work-items/${args[0]}`
9570
10244
  );
9571
10245
  ok(row, () => {
9572
- line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.id)}`);
10246
+ line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.id)}`);
9573
10247
  line(`${statusTag(row.status)} priority ${row.priority}`);
9574
10248
  if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
9575
10249
  if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
@@ -9610,7 +10284,7 @@ ${row.description}`);
9610
10284
  refId: row?.id,
9611
10285
  output: { workItem: row }
9612
10286
  });
9613
- ok(row, () => line(`Created work item ${import_picocolors30.default.bold(row?.id ?? "")} \u2014 ${title}`));
10287
+ ok(row, () => line(`Created work item ${import_picocolors31.default.bold(row?.id ?? "")} \u2014 ${title}`));
9614
10288
  })
9615
10289
  );
9616
10290
  board.command("update <id>").description("Change fields on a card \u2014 pass only what changes").option("--title <text>", "new title").option("--description <text>", "new description").option("--status <value>", STATUSES.join(" | ")).option("--priority <value>", PRIORITIES.join(" | ")).option(
@@ -9644,7 +10318,7 @@ ${row.description}`);
9644
10318
  );
9645
10319
  }
9646
10320
  const row = await patchItem(ctx, projectId, String(args[0]), body);
9647
- ok(row, () => line(`Updated ${import_picocolors30.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
10321
+ ok(row, () => line(`Updated ${import_picocolors31.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
9648
10322
  })
9649
10323
  );
9650
10324
  board.command("move <id> <status>").description(
@@ -9655,14 +10329,14 @@ ${row.description}`);
9655
10329
  const status = String(args[1]);
9656
10330
  assertStatus(status);
9657
10331
  const row = await patchItem(ctx, projectId, String(args[0]), { status });
9658
- ok(row, () => line(`Moved ${import_picocolors30.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
10332
+ ok(row, () => line(`Moved ${import_picocolors31.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
9659
10333
  })
9660
10334
  );
9661
10335
  board.command("close <id>").description("Shorthand for `board move <id> done`").action(
9662
10336
  action(async ({ ctx, args }) => {
9663
10337
  const projectId = requireProject(ctx);
9664
10338
  const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
9665
- ok(row, () => line(`Closed ${import_picocolors30.default.bold(row.title)} ${statusTag(row.status)}`));
10339
+ ok(row, () => line(`Closed ${import_picocolors31.default.bold(row.title)} ${statusTag(row.status)}`));
9666
10340
  })
9667
10341
  );
9668
10342
  }
@@ -9695,23 +10369,23 @@ function assertPriority(value) {
9695
10369
  }
9696
10370
  }
9697
10371
  function formatRow(r) {
9698
- const labels = r.labels?.length ? import_picocolors30.default.dim(` [${r.labels.join(", ")}]`) : "";
9699
- const owner = r.ownerHuman ? import_picocolors30.default.dim(` @${r.ownerHuman}`) : "";
9700
- return `${import_picocolors30.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
10372
+ const labels = r.labels?.length ? import_picocolors31.default.dim(` [${r.labels.join(", ")}]`) : "";
10373
+ const owner = r.ownerHuman ? import_picocolors31.default.dim(` @${r.ownerHuman}`) : "";
10374
+ return `${import_picocolors31.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
9701
10375
  }
9702
10376
  function statusTag(status) {
9703
10377
  const label = status.padEnd(11);
9704
- if (status === "done") return import_picocolors30.default.green(label);
9705
- if (status === "in-progress") return import_picocolors30.default.yellow(label);
9706
- if (status === "in-review") return import_picocolors30.default.cyan(label);
9707
- return import_picocolors30.default.dim(label);
10378
+ if (status === "done") return import_picocolors31.default.green(label);
10379
+ if (status === "in-progress") return import_picocolors31.default.yellow(label);
10380
+ if (status === "in-review") return import_picocolors31.default.cyan(label);
10381
+ return import_picocolors31.default.dim(label);
9708
10382
  }
9709
10383
  function collect2(value, previous) {
9710
10384
  return [...previous, value];
9711
10385
  }
9712
10386
 
9713
10387
  // src/commands/task.ts
9714
- var import_picocolors31 = __toESM(require_picocolors(), 1);
10388
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
9715
10389
 
9716
10390
  // src/subtask-attachments.ts
9717
10391
  var MAX_REF_NOTE = 500;
@@ -9826,7 +10500,7 @@ function registerTask(program3) {
9826
10500
  }) ?? [];
9827
10501
  ok(rows, () => {
9828
10502
  if (!rows.length) {
9829
- line(import_picocolors31.default.dim("No tasks on the board yet."));
10503
+ line(import_picocolors32.default.dim("No tasks on the board yet."));
9830
10504
  return;
9831
10505
  }
9832
10506
  for (const r of rows) line(formatRow2(r));
@@ -9941,10 +10615,10 @@ function registerTask(program3) {
9941
10615
  };
9942
10616
  ok(result, () => {
9943
10617
  line(
9944
- `${import_picocolors31.default.green("opened")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
10618
+ `${import_picocolors32.default.green("opened")} ${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`
9945
10619
  );
9946
10620
  if (channelMessage)
9947
- line(import_picocolors31.default.dim("posted to the channel as Project Manager"));
10621
+ line(import_picocolors32.default.dim("posted to the channel as Project Manager"));
9948
10622
  if (channelMessageError) {
9949
10623
  warn(
9950
10624
  `Task opened, but its Project Manager card could not be posted: ${channelMessageError.message}`
@@ -10023,12 +10697,12 @@ function registerTask(program3) {
10023
10697
  ) : null;
10024
10698
  ok(runner ?? row, () => {
10025
10699
  line(
10026
- `${import_picocolors31.default.green("added")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
10700
+ `${import_picocolors32.default.green("added")} ${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`
10027
10701
  );
10028
- if (row.role) line(import_picocolors31.default.dim(`role: ${row.role}`));
10702
+ if (row.role) line(import_picocolors32.default.dim(`role: ${row.role}`));
10029
10703
  if (runner) {
10030
10704
  line(
10031
- import_picocolors31.default.dim(
10705
+ import_picocolors32.default.dim(
10032
10706
  `runs on: ${[opts.agent, opts.model, opts.effort].filter(Boolean).join(" \xB7 ")}`
10033
10707
  )
10034
10708
  );
@@ -10046,7 +10720,7 @@ function registerTask(program3) {
10046
10720
  const rows = row.subtasks ?? [];
10047
10721
  ok(rows, () => {
10048
10722
  if (!rows.length) {
10049
- line(import_picocolors31.default.dim("No subtasks yet."));
10723
+ line(import_picocolors32.default.dim("No subtasks yet."));
10050
10724
  return;
10051
10725
  }
10052
10726
  rows.forEach((r, i) => line(formatSubtask(r, i + 1)));
@@ -10094,7 +10768,7 @@ function registerTask(program3) {
10094
10768
  }
10095
10769
  }
10096
10770
  );
10097
- ok(row, () => line(`${import_picocolors31.default.green("updated")} ${import_picocolors31.default.bold(row.title)}`));
10771
+ ok(row, () => line(`${import_picocolors32.default.green("updated")} ${import_picocolors32.default.bold(row.title)}`));
10098
10772
  })
10099
10773
  );
10100
10774
  subtask.command("remove <id>").description("Remove a subtask (only before the work starts)").action(
@@ -10102,7 +10776,7 @@ function registerTask(program3) {
10102
10776
  await api(ctx, `/v1/project-tasks/${encodeURIComponent(args[0])}`, {
10103
10777
  method: "DELETE"
10104
10778
  });
10105
- ok({ removed: args[0] }, () => line(import_picocolors31.default.green("removed")));
10779
+ ok({ removed: args[0] }, () => line(import_picocolors32.default.green("removed")));
10106
10780
  })
10107
10781
  );
10108
10782
  task.command("move <id> <status>").description(
@@ -10117,7 +10791,7 @@ function registerTask(program3) {
10117
10791
  );
10118
10792
  ok(
10119
10793
  row,
10120
- () => line(`${import_picocolors31.default.green("moved")} ${import_picocolors31.default.bold(row.title)} \u2192 ${args[1]}`)
10794
+ () => line(`${import_picocolors32.default.green("moved")} ${import_picocolors32.default.bold(row.title)} \u2192 ${args[1]}`)
10121
10795
  );
10122
10796
  })
10123
10797
  );
@@ -10133,18 +10807,18 @@ function registerTask(program3) {
10133
10807
  );
10134
10808
  ok(row, () => {
10135
10809
  if (!row?.queued) {
10136
- line(import_picocolors31.default.yellow("could not resume it \u2014 check the step id"));
10810
+ line(import_picocolors32.default.yellow("could not resume it \u2014 check the step id"));
10137
10811
  return;
10138
10812
  }
10139
10813
  const started = row.started ?? 0;
10140
10814
  if (started > 0) {
10141
10815
  line(
10142
- `${import_picocolors31.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
10816
+ `${import_picocolors32.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
10143
10817
  );
10144
10818
  return;
10145
10819
  }
10146
10820
  line(
10147
- `${import_picocolors31.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
10821
+ `${import_picocolors32.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
10148
10822
  );
10149
10823
  });
10150
10824
  })
@@ -10158,11 +10832,11 @@ function registerTask(program3) {
10158
10832
  );
10159
10833
  ok(row, () => {
10160
10834
  if (row?.reopened) {
10161
- line(`${import_picocolors31.default.green("sent back")} \u2014 it will be picked up again`);
10835
+ line(`${import_picocolors32.default.green("sent back")} \u2014 it will be picked up again`);
10162
10836
  return;
10163
10837
  }
10164
10838
  line(
10165
- import_picocolors31.default.yellow(
10839
+ import_picocolors32.default.yellow(
10166
10840
  row?.reason === "already_open" ? "already waiting to be picked up \u2014 nothing to send back" : row?.reason === "still_working" ? "still working \u2014 let it finish before sending it back" : "could not send that step back"
10167
10841
  )
10168
10842
  );
@@ -10179,7 +10853,7 @@ function registerTask(program3) {
10179
10853
  `/v1/project-tasks/${encodeURIComponent(id)}/dispatch-check`,
10180
10854
  { method: "POST" }
10181
10855
  );
10182
- ok(row, () => line(import_picocolors31.default.green("approved \u2014 you may start")));
10856
+ ok(row, () => line(import_picocolors32.default.green("approved \u2014 you may start")));
10183
10857
  })
10184
10858
  );
10185
10859
  task.command("start [id]").description(
@@ -10194,13 +10868,13 @@ function registerTask(program3) {
10194
10868
  const started = row?.started ?? null;
10195
10869
  if (started && started > 0) {
10196
10870
  line(
10197
- import_picocolors31.default.green(
10871
+ import_picocolors32.default.green(
10198
10872
  `started ${started} step${started === 1 ? "" : "s"} \u2014 they are running now`
10199
10873
  )
10200
10874
  );
10201
10875
  return;
10202
10876
  }
10203
- line(import_picocolors31.default.yellow(row?.note ?? "Nothing started."));
10877
+ line(import_picocolors32.default.yellow(row?.note ?? "Nothing started."));
10204
10878
  });
10205
10879
  })
10206
10880
  );
@@ -10215,7 +10889,7 @@ function registerTask(program3) {
10215
10889
  );
10216
10890
  ok({ awaiting: row2.approval_state === "awaiting", task: row2 }, () => {
10217
10891
  line(
10218
- row2.approval_state === "awaiting" ? import_picocolors31.default.yellow(
10892
+ row2.approval_state === "awaiting" ? import_picocolors32.default.yellow(
10219
10893
  "The plan is waiting on the owner. They see it in the task."
10220
10894
  ) : `Already ${row2.approval_state}.`
10221
10895
  );
@@ -10240,7 +10914,7 @@ function registerTask(program3) {
10240
10914
  );
10241
10915
  ok(
10242
10916
  row,
10243
- () => line(`${import_picocolors31.default.green(row.approval_state)} ${import_picocolors31.default.bold(row.title)}`)
10917
+ () => line(`${import_picocolors32.default.green(row.approval_state)} ${import_picocolors32.default.bold(row.title)}`)
10244
10918
  );
10245
10919
  })
10246
10920
  );
@@ -10256,7 +10930,7 @@ function registerTask(program3) {
10256
10930
  `/v1/project-tasks/${encodeURIComponent(id)}/move`,
10257
10931
  { body: { status: "ready" } }
10258
10932
  );
10259
- ok(row, () => line(`${import_picocolors31.default.green("ready")} ${import_picocolors31.default.bold(row.title)}`));
10933
+ ok(row, () => line(`${import_picocolors32.default.green("ready")} ${import_picocolors32.default.bold(row.title)}`));
10260
10934
  })
10261
10935
  );
10262
10936
  }
@@ -10301,24 +10975,24 @@ function assertOneOf(flag, value, allowed) {
10301
10975
  }
10302
10976
  }
10303
10977
  function formatRow2(r) {
10304
- const key = import_picocolors31.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
10305
- const steps = r.subtaskTotal ? import_picocolors31.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
10306
- const gate = r.approval_state === "awaiting" ? import_picocolors31.default.yellow(" awaiting approval") : "";
10978
+ const key = import_picocolors32.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
10979
+ const steps = r.subtaskTotal ? import_picocolors32.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
10980
+ const gate = r.approval_state === "awaiting" ? import_picocolors32.default.yellow(" awaiting approval") : "";
10307
10981
  return `${key} ${statusTag2(r.status)} ${r.title}${steps}${gate}`;
10308
10982
  }
10309
10983
  function formatSubtask(r, index) {
10310
- const n = import_picocolors31.default.dim(String(index).padStart(2, "0"));
10311
- const role = r.role ? import_picocolors31.default.dim(` [${r.role}]`) : "";
10312
- const scope = r.scope_paths?.length ? import_picocolors31.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
10984
+ const n = import_picocolors32.default.dim(String(index).padStart(2, "0"));
10985
+ const role = r.role ? import_picocolors32.default.dim(` [${r.role}]`) : "";
10986
+ const scope = r.scope_paths?.length ? import_picocolors32.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
10313
10987
  const head = `${n} ${statusTag2(r.status)} ${r.title}${role}${scope}`;
10314
10988
  const summary = (r.result_summary ?? "").trim();
10315
10989
  if (!summary) return head;
10316
10990
  const wrapped = summary.split("\n").map((l) => ` ${l}`).join("\n");
10317
10991
  return `${head}
10318
- ${import_picocolors31.default.dim(wrapped)}`;
10992
+ ${import_picocolors32.default.dim(wrapped)}`;
10319
10993
  }
10320
10994
  function printTask(row, goal) {
10321
- line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`);
10995
+ line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.key ?? row.id)}`);
10322
10996
  line(`${statusTag2(row.status)} approval: ${row.approval_state}`);
10323
10997
  if (row.summary) line(`
10324
10998
  ${row.summary}`);
@@ -10331,53 +11005,53 @@ touches: ${row.targets.map((t) => t.appName ?? t.ref ?? t.kind).join(", ")}`
10331
11005
  }
10332
11006
  if (row.subtasks?.length) {
10333
11007
  line(`
10334
- ${import_picocolors31.default.bold("subtasks")}`);
11008
+ ${import_picocolors32.default.bold("subtasks")}`);
10335
11009
  row.subtasks.forEach((s, i) => line(formatSubtask(s, i + 1)));
10336
11010
  line(
10337
- import_picocolors31.default.dim(
11011
+ import_picocolors32.default.dim(
10338
11012
  `
10339
11013
  Run \`workser artifact list\` to see what these subtasks produced,`
10340
11014
  )
10341
11015
  );
10342
11016
  line(
10343
- import_picocolors31.default.dim(
11017
+ import_picocolors32.default.dim(
10344
11018
  `or \`workser artifact list --step <id>\` for one subtask's output alone.`
10345
11019
  )
10346
11020
  );
10347
11021
  } else {
10348
- line(import_picocolors31.default.dim("\nNo subtasks yet."));
11022
+ line(import_picocolors32.default.dim("\nNo subtasks yet."));
10349
11023
  }
10350
11024
  }
10351
11025
  function printGoalContext(row, goal) {
10352
11026
  const mine = row.phase ?? null;
10353
11027
  line(`
10354
- ${import_picocolors31.default.bold("part of")}: ${goal.title}`);
10355
- if (goal.outcome) line(import_picocolors31.default.dim(`done means: ${goal.outcome}`));
11028
+ ${import_picocolors32.default.bold("part of")}: ${goal.title}`);
11029
+ if (goal.outcome) line(import_picocolors32.default.dim(`done means: ${goal.outcome}`));
10356
11030
  const progress = goal.progress ?? [];
10357
11031
  for (const p of progress) {
10358
11032
  const where = p.total === 0 ? "not started" : `${p.done} of ${p.total} done`;
10359
- const here = mine && p.name === mine ? import_picocolors31.default.cyan(" <- this task") : "";
10360
- const mark = p.state === "done" ? import_picocolors31.default.green("*") : import_picocolors31.default.dim("-");
11033
+ const here = mine && p.name === mine ? import_picocolors32.default.cyan(" <- this task") : "";
11034
+ const mark = p.state === "done" ? import_picocolors32.default.green("*") : import_picocolors32.default.dim("-");
10361
11035
  line(` ${mark} ${p.name} \u2014 ${where}${here}`);
10362
11036
  }
10363
11037
  const next = progress.find((p) => p.state !== "done");
10364
11038
  const running = progress.some((p) => p.state === "working");
10365
11039
  if (next && !running) {
10366
11040
  line(
10367
- import_picocolors31.default.dim(
11041
+ import_picocolors32.default.dim(
10368
11042
  `
10369
11043
  Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to them in a sentence rather than starting it unasked.`
10370
11044
  )
10371
11045
  );
10372
11046
  } else if (!next) {
10373
11047
  line(
10374
- import_picocolors31.default.dim(
11048
+ import_picocolors32.default.dim(
10375
11049
  "\nEvery part of this plan is done. Say so, and offer to wrap it up."
10376
11050
  )
10377
11051
  );
10378
11052
  }
10379
11053
  line(
10380
- import_picocolors31.default.dim(
11054
+ import_picocolors32.default.dim(
10381
11055
  "The plan is a reference for what was agreed, not a rule about what may run. Work can start on any part at any time if they ask for it."
10382
11056
  )
10383
11057
  );
@@ -10385,22 +11059,22 @@ Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to t
10385
11059
  function statusTag2(status) {
10386
11060
  switch (status) {
10387
11061
  case "ready":
10388
- return import_picocolors31.default.green("[ready]");
11062
+ return import_picocolors32.default.green("[ready]");
10389
11063
  case "working":
10390
- return import_picocolors31.default.blue("[working]");
11064
+ return import_picocolors32.default.blue("[working]");
10391
11065
  case "checking":
10392
- return import_picocolors31.default.cyan("[checking]");
11066
+ return import_picocolors32.default.cyan("[checking]");
10393
11067
  case "accepted":
10394
- return import_picocolors31.default.green("[accepted]");
11068
+ return import_picocolors32.default.green("[accepted]");
10395
11069
  case "archived":
10396
- return import_picocolors31.default.dim("[archived]");
11070
+ return import_picocolors32.default.dim("[archived]");
10397
11071
  default:
10398
- return import_picocolors31.default.dim("[todo]");
11072
+ return import_picocolors32.default.dim("[todo]");
10399
11073
  }
10400
11074
  }
10401
11075
 
10402
11076
  // src/commands/goal.ts
10403
- var import_picocolors32 = __toESM(require_picocolors(), 1);
11077
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
10404
11078
  var STATUSES3 = ["proposed", "agreed", "working", "delivered", "abandoned"];
10405
11079
  var PACES = ["slice", "phase", "all"];
10406
11080
  function registerGoal(program3) {
@@ -10413,7 +11087,7 @@ function registerGoal(program3) {
10413
11087
  }) ?? [];
10414
11088
  ok(rows, () => {
10415
11089
  if (!rows.length) {
10416
- line(import_picocolors32.default.dim("No goals yet \u2014 every task here stands on its own."));
11090
+ line(import_picocolors33.default.dim("No goals yet \u2014 every task here stands on its own."));
10417
11091
  return;
10418
11092
  }
10419
11093
  for (const g of rows) line(formatGoal(g));
@@ -10499,10 +11173,10 @@ function registerGoal(program3) {
10499
11173
  }
10500
11174
  });
10501
11175
  ok(row, () => {
10502
- success(`Proposed ${import_picocolors32.default.bold(row?.title ?? "goal")}`);
11176
+ success(`Proposed ${import_picocolors33.default.bold(row?.title ?? "goal")}`);
10503
11177
  printGoal(row);
10504
11178
  line(
10505
- import_picocolors32.default.dim(
11179
+ import_picocolors33.default.dim(
10506
11180
  "\nNothing has been created yet. The owner agrees the shape first."
10507
11181
  )
10508
11182
  );
@@ -10531,7 +11205,7 @@ function registerGoal(program3) {
10531
11205
  ok(
10532
11206
  row,
10533
11207
  () => line(
10534
- row?.recorded ? met === true ? import_picocolors32.default.green("recorded \u2014 met") : met === false ? import_picocolors32.default.yellow("recorded \u2014 not met") : import_picocolors32.default.dim("recorded \u2014 back to unchecked") : import_picocolors32.default.yellow(
11208
+ row?.recorded ? met === true ? import_picocolors33.default.green("recorded \u2014 met") : met === false ? import_picocolors33.default.yellow("recorded \u2014 not met") : import_picocolors33.default.dim("recorded \u2014 back to unchecked") : import_picocolors33.default.yellow(
10535
11209
  "couldn't record it \u2014 check the goal id, the phase name and the criterion id"
10536
11210
  )
10537
11211
  )
@@ -10574,7 +11248,7 @@ function registerGoal(program3) {
10574
11248
  }
10575
11249
  ok({ goalId, phase, filed }, () => {
10576
11250
  success(
10577
- `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors32.default.bold(phase)}`
11251
+ `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors33.default.bold(phase)}`
10578
11252
  );
10579
11253
  });
10580
11254
  })
@@ -10612,43 +11286,43 @@ function registerGoal(program3) {
10612
11286
  function appScope(g) {
10613
11287
  const n = g.appIds?.length ?? 0;
10614
11288
  if (!n) return "";
10615
- return import_picocolors32.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
11289
+ return import_picocolors33.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
10616
11290
  }
10617
11291
  function formatGoal(g) {
10618
- const where = g.currentPhase && g.status !== "delivered" ? import_picocolors32.default.dim(` now: ${g.currentPhase}`) : "";
10619
- const count = g.taskTotal != null ? import_picocolors32.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
10620
- return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors32.default.dim(g.id)}`;
11292
+ const where = g.currentPhase && g.status !== "delivered" ? import_picocolors33.default.dim(` now: ${g.currentPhase}`) : "";
11293
+ const count = g.taskTotal != null ? import_picocolors33.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
11294
+ return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors33.default.dim(g.id)}`;
10621
11295
  }
10622
11296
  function printGoal(g) {
10623
11297
  if (!g) return;
10624
- line(`${import_picocolors32.default.bold(g.title)} ${import_picocolors32.default.dim(g.id)}`);
11298
+ line(`${import_picocolors33.default.bold(g.title)} ${import_picocolors33.default.dim(g.id)}`);
10625
11299
  line(statusTag3(g.status));
10626
11300
  if (g.outcome) line(`
10627
11301
  done means: ${g.outcome}`);
10628
- if (g.pace) line(import_picocolors32.default.dim(`pace: ${paceLine(g.pace)}`));
11302
+ if (g.pace) line(import_picocolors33.default.dim(`pace: ${paceLine(g.pace)}`));
10629
11303
  const progress = g.progress ?? [];
10630
11304
  if (!progress.length) {
10631
- line(import_picocolors32.default.dim("\nNo phases agreed yet."));
11305
+ line(import_picocolors33.default.dim("\nNo phases agreed yet."));
10632
11306
  return;
10633
11307
  }
10634
11308
  line(`
10635
- ${import_picocolors32.default.bold("phases")}`);
11309
+ ${import_picocolors33.default.bold("phases")}`);
10636
11310
  for (const p of progress) {
10637
- const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors32.default.dim("nothing filed yet");
10638
- const mark = p.state === "done" ? import_picocolors32.default.green("done") : p.state === "working" ? import_picocolors32.default.blue("working") : import_picocolors32.default.dim("waiting");
11311
+ const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors33.default.dim("nothing filed yet");
11312
+ const mark = p.state === "done" ? import_picocolors33.default.green("done") : p.state === "working" ? import_picocolors33.default.blue("working") : import_picocolors33.default.dim("waiting");
10639
11313
  line(
10640
- ` ${import_picocolors32.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors32.default.dim(bar2)}`
11314
+ ` ${import_picocolors33.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors33.default.dim(bar2)}`
10641
11315
  );
10642
11316
  for (const c of p.criteria ?? []) {
10643
- const tick = c.met === true ? import_picocolors32.default.green("\u2713") : c.met === false ? import_picocolors32.default.red("\u2717") : import_picocolors32.default.dim("\xB7");
10644
- line(` ${tick} ${c.text} ${import_picocolors32.default.dim(c.id)}`);
10645
- if (c.note) line(import_picocolors32.default.dim(` ${c.note}`));
11317
+ const tick = c.met === true ? import_picocolors33.default.green("\u2713") : c.met === false ? import_picocolors33.default.red("\u2717") : import_picocolors33.default.dim("\xB7");
11318
+ line(` ${tick} ${c.text} ${import_picocolors33.default.dim(c.id)}`);
11319
+ if (c.note) line(import_picocolors33.default.dim(` ${c.note}`));
10646
11320
  }
10647
11321
  }
10648
11322
  const current = progress.find((p) => p.name === g.currentPhase);
10649
11323
  if (current) {
10650
11324
  line(
10651
- import_picocolors32.default.dim(
11325
+ import_picocolors33.default.dim(
10652
11326
  `
10653
11327
  ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current.index} of ${progress.length}).`
10654
11328
  )
@@ -10658,15 +11332,15 @@ ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current
10658
11332
  function statusTag3(status) {
10659
11333
  switch (status) {
10660
11334
  case "agreed":
10661
- return import_picocolors32.default.cyan("[agreed]");
11335
+ return import_picocolors33.default.cyan("[agreed]");
10662
11336
  case "working":
10663
- return import_picocolors32.default.blue("[working]");
11337
+ return import_picocolors33.default.blue("[working]");
10664
11338
  case "delivered":
10665
- return import_picocolors32.default.green("[delivered]");
11339
+ return import_picocolors33.default.green("[delivered]");
10666
11340
  case "abandoned":
10667
- return import_picocolors32.default.dim("[abandoned]");
11341
+ return import_picocolors33.default.dim("[abandoned]");
10668
11342
  default:
10669
- return import_picocolors32.default.yellow("[proposed]");
11343
+ return import_picocolors33.default.yellow("[proposed]");
10670
11344
  }
10671
11345
  }
10672
11346
  function paceLine(pace) {
@@ -10720,7 +11394,7 @@ function stripLeadingGlobalOptions(argv) {
10720
11394
  }
10721
11395
 
10722
11396
  // src/commands/decision.ts
10723
- var import_picocolors33 = __toESM(require_picocolors(), 1);
11397
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
10724
11398
  function registerDecision(program3) {
10725
11399
  const decision = program3.command("decision").description("Read and record the project's architecture decisions");
10726
11400
  decision.command("list").description("Every decision on record \u2014 read this before changing how something works").option("--limit <n>", "cap the number returned (newest first)").option("--search <text>", "match the title, case-insensitively").option("--label <name>", "only decisions carrying this label").option("--app <id>", "only decisions about this app").option("--infra <name>", "only decisions touching this part of the infrastructure").option("--status <name>", "proposed | accepted | superseded").action(
@@ -10743,13 +11417,13 @@ function registerDecision(program3) {
10743
11417
  rows = applyLimit(rows, opts.limit);
10744
11418
  ok(rows, () => {
10745
11419
  if (!rows.length) {
10746
- line(import_picocolors33.default.dim("No decisions recorded yet."));
11420
+ line(import_picocolors34.default.dim("No decisions recorded yet."));
10747
11421
  return;
10748
11422
  }
10749
11423
  for (const r of rows) {
10750
11424
  const tags = [...r.labels ?? [], ...r.infraRefs ?? []];
10751
- const meta = tags.length ? import_picocolors33.default.dim(` [${tags.join(", ")}]`) : "";
10752
- line(`${import_picocolors33.default.dim(r.id)} ${import_picocolors33.default.dim(shortDate(r.createdAt))} ${r.title}${meta}`);
11425
+ const meta = tags.length ? import_picocolors34.default.dim(` [${tags.join(", ")}]`) : "";
11426
+ line(`${import_picocolors34.default.dim(r.id)} ${import_picocolors34.default.dim(shortDate(r.createdAt))} ${r.title}${meta}`);
10753
11427
  line(` ${truncate(r.decision, 100)}`);
10754
11428
  }
10755
11429
  });
@@ -10763,16 +11437,16 @@ function registerDecision(program3) {
10763
11437
  `/v1/projects/${projectId}/architecture-decisions/${args[0]}`
10764
11438
  );
10765
11439
  ok(row, () => {
10766
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10767
- line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
11440
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11441
+ line(import_picocolors34.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10768
11442
  line(`
10769
- ${import_picocolors33.default.bold("Context")}
11443
+ ${import_picocolors34.default.bold("Context")}
10770
11444
  ${row.context}`);
10771
11445
  line(`
10772
- ${import_picocolors33.default.bold("Decision")}
11446
+ ${import_picocolors34.default.bold("Decision")}
10773
11447
  ${row.decision}`);
10774
11448
  if (row.consequences) line(`
10775
- ${import_picocolors33.default.bold("Consequences")}
11449
+ ${import_picocolors34.default.bold("Consequences")}
10776
11450
  ${row.consequences}`);
10777
11451
  });
10778
11452
  })
@@ -10789,7 +11463,7 @@ ${row.consequences}`);
10789
11463
  const path = `/v1/projects/${projectId}/architecture-decisions/${id}`;
10790
11464
  if (opts.delete) {
10791
11465
  await api(ctx, path, { method: "DELETE" });
10792
- ok({ deleted: true, id }, () => line(`Deleted decision ${import_picocolors33.default.bold(id)}.`));
11466
+ ok({ deleted: true, id }, () => line(`Deleted decision ${import_picocolors34.default.bold(id)}.`));
10793
11467
  return;
10794
11468
  }
10795
11469
  const row = await api(ctx, path, {
@@ -10799,7 +11473,7 @@ ${row.consequences}`);
10799
11473
  ok(
10800
11474
  row,
10801
11475
  () => line(
10802
- `Marked ${import_picocolors33.default.bold(row?.title ?? id)} superseded.` + (opts.reason ? ` Record the replacement with \`decision create\`.` : "")
11476
+ `Marked ${import_picocolors34.default.bold(row?.title ?? id)} superseded.` + (opts.reason ? ` Record the replacement with \`decision create\`.` : "")
10803
11477
  )
10804
11478
  );
10805
11479
  })
@@ -10820,7 +11494,7 @@ ${row.consequences}`);
10820
11494
  `/v1/projects/${projectId}/architecture-decisions/${id}`,
10821
11495
  { method: "PATCH", body }
10822
11496
  );
10823
- ok(row, () => line(`Filed decision ${import_picocolors33.default.bold(row?.title ?? id)}.`));
11497
+ ok(row, () => line(`Filed decision ${import_picocolors34.default.bold(row?.title ?? id)}.`));
10824
11498
  })
10825
11499
  );
10826
11500
  decision.command("create <title>").description(
@@ -10851,7 +11525,7 @@ ${row.consequences}`);
10851
11525
  refId: row?.id,
10852
11526
  output: { decision: row }
10853
11527
  });
10854
- ok(row, () => line(`Recorded decision ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
11528
+ ok(row, () => line(`Recorded decision ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
10855
11529
  })
10856
11530
  );
10857
11531
  const requirement = program3.command("requirement").description("Read and record the project's requirements");
@@ -10863,11 +11537,11 @@ ${row.consequences}`);
10863
11537
  rows = applyLimit(rows, opts.limit);
10864
11538
  ok(rows, () => {
10865
11539
  if (!rows.length) {
10866
- line(import_picocolors33.default.dim("No requirements recorded yet."));
11540
+ line(import_picocolors34.default.dim("No requirements recorded yet."));
10867
11541
  return;
10868
11542
  }
10869
11543
  for (const r of rows) {
10870
- line(`${import_picocolors33.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
11544
+ line(`${import_picocolors34.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
10871
11545
  }
10872
11546
  });
10873
11547
  })
@@ -10880,8 +11554,8 @@ ${row.consequences}`);
10880
11554
  `/v1/projects/${projectId}/requirements/${args[0]}`
10881
11555
  );
10882
11556
  ok(row, () => {
10883
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10884
- line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
11557
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11558
+ line(import_picocolors34.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10885
11559
  line(`
10886
11560
  ${row.body}`);
10887
11561
  });
@@ -10907,7 +11581,7 @@ ${row.body}`);
10907
11581
  refId: row?.id,
10908
11582
  output: { requirement: row }
10909
11583
  });
10910
- ok(row, () => line(`Recorded requirement ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
11584
+ ok(row, () => line(`Recorded requirement ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
10911
11585
  })
10912
11586
  );
10913
11587
  requirement.command("update <id>").description(
@@ -10936,7 +11610,7 @@ ${row.body}`);
10936
11610
  code: "bad_request"
10937
11611
  });
10938
11612
  }
10939
- ok(row, () => line(`Updated requirement ${import_picocolors33.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
11613
+ ok(row, () => line(`Updated requirement ${import_picocolors34.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
10940
11614
  })
10941
11615
  );
10942
11616
  }
@@ -10975,7 +11649,7 @@ function toList(value) {
10975
11649
  }
10976
11650
 
10977
11651
  // src/commands/doc.ts
10978
- var import_picocolors34 = __toESM(require_picocolors(), 1);
11652
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
10979
11653
 
10980
11654
  // src/mermaid-fences.ts
10981
11655
  function hasDiagram(code) {
@@ -11062,16 +11736,16 @@ function registerDoc(program3) {
11062
11736
  }) ?? [];
11063
11737
  ok(rows, () => {
11064
11738
  if (!rows.length) {
11065
- line(import_picocolors34.default.dim("No documents yet."));
11739
+ line(import_picocolors35.default.dim("No documents yet."));
11066
11740
  return;
11067
11741
  }
11068
11742
  for (const r of rows) {
11069
- const link = r.workItemId ? import_picocolors34.default.dim(` \u21B3 ${r.workItemId}`) : "";
11070
- const file = r.filePath ? import_picocolors34.default.dim(` ${r.filePath}`) : "";
11743
+ const link = r.workItemId ? import_picocolors35.default.dim(` \u21B3 ${r.workItemId}`) : "";
11744
+ const file = r.filePath ? import_picocolors35.default.dim(` ${r.filePath}`) : "";
11071
11745
  const tags = [...r.labels ?? [], ...r.infraRefs ?? []];
11072
- const meta = tags.length ? import_picocolors34.default.dim(` [${tags.join(", ")}]`) : "";
11073
- const kind = r.docKind ? import_picocolors34.default.dim(`${r.docKind} `) : "";
11074
- line(`${import_picocolors34.default.dim(r.id)} ${kind}${r.title}${meta}${link}${file}`);
11746
+ const meta = tags.length ? import_picocolors35.default.dim(` [${tags.join(", ")}]`) : "";
11747
+ const kind = r.docKind ? import_picocolors35.default.dim(`${r.docKind} `) : "";
11748
+ line(`${import_picocolors35.default.dim(r.id)} ${kind}${r.title}${meta}${link}${file}`);
11075
11749
  }
11076
11750
  });
11077
11751
  })
@@ -11085,17 +11759,17 @@ function registerDoc(program3) {
11085
11759
  );
11086
11760
  if (opts.markdown) {
11087
11761
  ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
11088
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11762
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11089
11763
  line(
11090
- row.filePath ? `Read it at ${import_picocolors34.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors34.default.dim("This document has no markdown mirror on disk yet.")
11764
+ row.filePath ? `Read it at ${import_picocolors35.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors35.default.dim("This document has no markdown mirror on disk yet.")
11091
11765
  );
11092
11766
  });
11093
11767
  return;
11094
11768
  }
11095
11769
  ok(row, () => {
11096
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11097
- if (row.workItemId) line(import_picocolors34.default.dim(`linked to work item ${row.workItemId}`));
11098
- if (row.filePath) line(import_picocolors34.default.dim(`markdown mirror: ${row.filePath}`));
11770
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11771
+ if (row.workItemId) line(import_picocolors35.default.dim(`linked to work item ${row.workItemId}`));
11772
+ if (row.filePath) line(import_picocolors35.default.dim(`markdown mirror: ${row.filePath}`));
11099
11773
  line("");
11100
11774
  line(row.contentJson);
11101
11775
  });
@@ -11128,7 +11802,7 @@ function registerDoc(program3) {
11128
11802
  refId: row?.id,
11129
11803
  output: { document: row }
11130
11804
  });
11131
- ok(row, () => line(`Created document ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
11805
+ ok(row, () => line(`Created document ${import_picocolors35.default.bold(row?.id ?? "")} \u2014 ${title}`));
11132
11806
  })
11133
11807
  );
11134
11808
  doc.command("file <id>").description("File an existing document \u2014 its kind, labels, app, infrastructure").option("--kind <kind>", `what this document IS \u2014 ${KIND_HELP2}`).option("--label <name...>", "tag it \u2014 shares the project's label vocabulary").option("--app <id>", "which app this document is about").option("--infra <name...>", "what it touches: database, storage, auth, \u2026").action(
@@ -11147,7 +11821,7 @@ function registerDoc(program3) {
11147
11821
  `/v1/projects/${projectId}/documents/${id}`,
11148
11822
  { method: "PATCH", body }
11149
11823
  );
11150
- ok(row, () => line(`Filed ${import_picocolors34.default.bold(row?.title ?? id)}.`));
11824
+ ok(row, () => line(`Filed ${import_picocolors35.default.bold(row?.title ?? id)}.`));
11151
11825
  })
11152
11826
  );
11153
11827
  doc.command("diagram <id>").description(
@@ -11173,19 +11847,19 @@ function registerDoc(program3) {
11173
11847
  diagrams: diagrams.map((code) => ({ kind: diagramKind(code), code }))
11174
11848
  };
11175
11849
  ok(payload, () => {
11176
- line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
11850
+ line(`${import_picocolors35.default.bold(row.title)} ${import_picocolors35.default.dim(row.id)}`);
11177
11851
  if (markdown === null) {
11178
11852
  line(
11179
- import_picocolors34.default.dim(
11853
+ import_picocolors35.default.dim(
11180
11854
  row.filePath ? `Could not read ${row.filePath} from this folder.` : "This document has no markdown mirror on disk yet."
11181
11855
  )
11182
11856
  );
11183
11857
  } else if (!diagrams.length) {
11184
- line(import_picocolors34.default.dim("No diagrams in this document."));
11858
+ line(import_picocolors35.default.dim("No diagrams in this document."));
11185
11859
  } else {
11186
11860
  for (const [i, code] of diagrams.entries()) {
11187
11861
  const kind = diagramKind(code) ?? "diagram";
11188
- line(`${import_picocolors34.default.dim(String(i + 1))} ${kind} ${import_picocolors34.default.dim(`${code.split("\n").length} lines`)}`);
11862
+ line(`${import_picocolors35.default.dim(String(i + 1))} ${kind} ${import_picocolors35.default.dim(`${code.split("\n").length} lines`)}`);
11189
11863
  }
11190
11864
  }
11191
11865
  });
@@ -11221,7 +11895,7 @@ function registerDoc(program3) {
11221
11895
  code: "bad_request"
11222
11896
  });
11223
11897
  }
11224
- ok(row, () => line(`Updated document ${import_picocolors34.default.bold(row.id)} \u2014 ${row.title}`));
11898
+ ok(row, () => line(`Updated document ${import_picocolors35.default.bold(row.id)} \u2014 ${row.title}`));
11225
11899
  })
11226
11900
  );
11227
11901
  }
@@ -11235,7 +11909,7 @@ function readMirror(cwd, filePath) {
11235
11909
  }
11236
11910
 
11237
11911
  // src/commands/design.ts
11238
- var import_picocolors35 = __toESM(require_picocolors(), 1);
11912
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
11239
11913
  function registerDesign(program3) {
11240
11914
  const design = program3.command("design").description("The project's brand, and pictures of the screens you draw");
11241
11915
  design.command("show").description("Show this project's brand \u2014 read it before writing any UI").option("--raw", "print the generated token files verbatim instead of a summary").action(
@@ -11249,11 +11923,11 @@ function registerDesign(program3) {
11249
11923
  if (opts.raw) {
11250
11924
  ok(files, () => {
11251
11925
  if (!files.length) {
11252
- line(import_picocolors35.default.dim("No brand set for this project."));
11926
+ line(import_picocolors36.default.dim("No brand set for this project."));
11253
11927
  return;
11254
11928
  }
11255
11929
  for (const f of files) {
11256
- line(import_picocolors35.default.bold(f.path));
11930
+ line(import_picocolors36.default.bold(f.path));
11257
11931
  line(f.contents);
11258
11932
  line("");
11259
11933
  }
@@ -11270,21 +11944,21 @@ function registerDesign(program3) {
11270
11944
  } : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
11271
11945
  ok(summary, () => {
11272
11946
  if (!tokens) {
11273
- line(import_picocolors35.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
11947
+ line(import_picocolors36.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
11274
11948
  return;
11275
11949
  }
11276
11950
  for (const [name, value] of Object.entries(tokens.brand)) {
11277
- line(`${import_picocolors35.default.dim(name.padEnd(12))} ${value}`);
11951
+ line(`${import_picocolors36.default.dim(name.padEnd(12))} ${value}`);
11278
11952
  }
11279
11953
  for (const [name, value] of Object.entries(tokens.color)) {
11280
- line(`${import_picocolors35.default.dim(`color.${name}`.padEnd(12))} ${value}`);
11954
+ line(`${import_picocolors36.default.dim(`color.${name}`.padEnd(12))} ${value}`);
11281
11955
  }
11282
11956
  for (const [name, value] of Object.entries(tokens.font)) {
11283
- line(`${import_picocolors35.default.dim(`font.${name}`.padEnd(12))} ${value}`);
11957
+ line(`${import_picocolors36.default.dim(`font.${name}`.padEnd(12))} ${value}`);
11284
11958
  }
11285
11959
  line("");
11286
11960
  line(
11287
- import_picocolors35.default.dim(
11961
+ import_picocolors36.default.dim(
11288
11962
  `Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
11289
11963
  )
11290
11964
  );
@@ -11306,19 +11980,19 @@ function registerDesign(program3) {
11306
11980
  ok(res, () => {
11307
11981
  for (const shot of res.shots ?? []) {
11308
11982
  if (shot.out) {
11309
- line(`${import_picocolors35.default.green("\u2713")} ${shot.file} ${import_picocolors35.default.dim("\u2192")} ${shot.out}`);
11983
+ line(`${import_picocolors36.default.green("\u2713")} ${shot.file} ${import_picocolors36.default.dim("\u2192")} ${shot.out}`);
11310
11984
  } else {
11311
- line(`${import_picocolors35.default.red("\u2717")} ${shot.file} ${import_picocolors35.default.dim(shot.error ?? "no image")}`);
11985
+ line(`${import_picocolors36.default.red("\u2717")} ${shot.file} ${import_picocolors36.default.dim(shot.error ?? "no image")}`);
11312
11986
  }
11313
11987
  }
11314
11988
  for (const path of res.refused ?? []) {
11315
- line(import_picocolors35.default.dim(`skipped ${path} \u2014 not a page inside this project`));
11989
+ line(import_picocolors36.default.dim(`skipped ${path} \u2014 not a page inside this project`));
11316
11990
  }
11317
11991
  const made = (res.shots ?? []).filter((s) => s.out).length;
11318
11992
  if (made) {
11319
11993
  line("");
11320
11994
  line(
11321
- import_picocolors35.default.dim(
11995
+ import_picocolors36.default.dim(
11322
11996
  `Record each one so it shows up: workser artifact add <file>.png --kind design -d "<what this screen is>"`
11323
11997
  )
11324
11998
  );
@@ -11350,7 +12024,7 @@ function unwrap(group) {
11350
12024
  }
11351
12025
 
11352
12026
  // src/commands/api.ts
11353
- var import_picocolors36 = __toESM(require_picocolors(), 1);
12027
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
11354
12028
  import { readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
11355
12029
  import { join as join3, relative, sep } from "path";
11356
12030
 
@@ -11444,16 +12118,16 @@ function specReport(routes, documented) {
11444
12118
  ok: missing.length === 0
11445
12119
  };
11446
12120
  }
11447
- function specSummary(report, specFile) {
12121
+ function specSummary(report2, specFile) {
11448
12122
  if (!specFile) {
11449
12123
  return `This service has no API description. Write one at ${SPEC_FILES[0]} listing the routes it serves.`;
11450
12124
  }
11451
- if (report.ok) {
11452
- const n = report.routes.length;
12125
+ if (report2.ok) {
12126
+ const n = report2.routes.length;
11453
12127
  return n === 1 ? `The one route this service has is described in ${specFile}.` : `All ${n} routes are described in ${specFile}.`;
11454
12128
  }
11455
- const names = report.missing.map((m) => m.path).join(", ");
11456
- return `${report.missing.length} route${report.missing.length === 1 ? " is" : "s are"} not described in ${specFile}: ${names}`;
12129
+ const names = report2.missing.map((m) => m.path).join(", ");
12130
+ return `${report2.missing.length} route${report2.missing.length === 1 ? " is" : "s are"} not described in ${specFile}: ${names}`;
11457
12131
  }
11458
12132
 
11459
12133
  // src/commands/api.ts
@@ -11464,10 +12138,10 @@ function registerApi(program3) {
11464
12138
  const appId = requireApp2(opts.app);
11465
12139
  const res = await api(ctx, `/v1/apps/${encodeURIComponent(appId)}/api/requests`);
11466
12140
  ok(res, () => {
11467
- for (const note of res?.notes ?? []) line(import_picocolors36.default.dim(note));
12141
+ for (const note of res?.notes ?? []) line(import_picocolors37.default.dim(note));
11468
12142
  for (const r of res?.requests ?? []) {
11469
12143
  line(
11470
- `${import_picocolors36.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors36.default.dim(` ${r.note}`) : ""}`
12144
+ `${import_picocolors37.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors37.default.dim(` ${r.note}`) : ""}`
11471
12145
  );
11472
12146
  }
11473
12147
  });
@@ -11501,14 +12175,14 @@ function registerApi(program3) {
11501
12175
  );
11502
12176
  ok(res, () => {
11503
12177
  if (!res?.ok) {
11504
- line(import_picocolors36.default.red(res?.error ?? "The service did not answer."));
12178
+ line(import_picocolors37.default.red(res?.error ?? "The service did not answer."));
11505
12179
  return;
11506
12180
  }
11507
12181
  const code = `${res.status}${res.statusText ? ` ${res.statusText}` : ""}`;
11508
- const colour2 = res.status && res.status < 300 ? import_picocolors36.default.green : res.status && res.status < 500 ? import_picocolors36.default.yellow : import_picocolors36.default.red;
11509
- line(`${colour2(code)} ${import_picocolors36.default.dim(`${res.durationMs}ms ${res.url}`)}`);
12182
+ const colour2 = res.status && res.status < 300 ? import_picocolors37.default.green : res.status && res.status < 500 ? import_picocolors37.default.yellow : import_picocolors37.default.red;
12183
+ line(`${colour2(code)} ${import_picocolors37.default.dim(`${res.durationMs}ms ${res.url}`)}`);
11510
12184
  if (res.body) line(res.body);
11511
- if (res.truncated) line(import_picocolors36.default.dim("(answer truncated)"));
12185
+ if (res.truncated) line(import_picocolors37.default.dim("(answer truncated)"));
11512
12186
  });
11513
12187
  if (!res?.ok) process.exitCode = 1;
11514
12188
  })
@@ -11522,23 +12196,23 @@ function registerApi(program3) {
11522
12196
  const routes = discoverRoutes(files);
11523
12197
  const specFile = SPEC_FILES.find((f) => files.includes(f)) ?? null;
11524
12198
  const documented = specFile ? specPaths(readIfPresent(join3(ctx.cwd, specFile))) : [];
11525
- const report = specReport(routes, documented);
11526
- const summary = specSummary(report, specFile);
11527
- ok({ ...report, specFile, summary }, () => {
11528
- for (const r of report.routes) {
11529
- const known = report.missing.some((m) => m.path === r.path);
12199
+ const report2 = specReport(routes, documented);
12200
+ const summary = specSummary(report2, specFile);
12201
+ ok({ ...report2, specFile, summary }, () => {
12202
+ for (const r of report2.routes) {
12203
+ const known = report2.missing.some((m) => m.path === r.path);
11530
12204
  line(
11531
- `${known ? import_picocolors36.default.yellow("undocumented") : import_picocolors36.default.green("documented ")} ${r.path}${import_picocolors36.default.dim(` ${r.file}`)}`
12205
+ `${known ? import_picocolors37.default.yellow("undocumented") : import_picocolors37.default.green("documented ")} ${r.path}${import_picocolors37.default.dim(` ${r.file}`)}`
11532
12206
  );
11533
12207
  }
11534
- for (const p of report.stale) {
11535
- line(`${import_picocolors36.default.dim("in spec only ")} ${p}`);
12208
+ for (const p of report2.stale) {
12209
+ line(`${import_picocolors37.default.dim("in spec only ")} ${p}`);
11536
12210
  }
11537
12211
  line("");
11538
- if (report.ok && specFile) success(summary);
11539
- else line(import_picocolors36.default.yellow(summary));
12212
+ if (report2.ok && specFile) success(summary);
12213
+ else line(import_picocolors37.default.yellow(summary));
11540
12214
  });
11541
- if (opts.check && !report.ok) {
12215
+ if (opts.check && !report2.ok) {
11542
12216
  throw new WorkserError(summary, { code: "bad_request" });
11543
12217
  }
11544
12218
  if (opts.check && !specFile) {
@@ -11607,7 +12281,7 @@ function listRepoFiles(root, maxDepth = 8) {
11607
12281
  }
11608
12282
 
11609
12283
  // src/commands/analysis.ts
11610
- var import_picocolors37 = __toESM(require_picocolors(), 1);
12284
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
11611
12285
  import { readFileSync as readFileSync4 } from "fs";
11612
12286
  function registerAnalysis(program3) {
11613
12287
  const cmd = program3.command("analysis").description("Run Python analysis locally, recorded in the task");
@@ -11617,14 +12291,14 @@ function registerAnalysis(program3) {
11617
12291
  const res = await api(ctx, path);
11618
12292
  ok(res, () => {
11619
12293
  line(
11620
- `${res?.available ? import_picocolors37.default.green("python") : import_picocolors37.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors37.default.dim(res?.python ?? "")}`
12294
+ `${res?.available ? import_picocolors38.default.green("python") : import_picocolors38.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors38.default.dim(res?.python ?? "")}`
11621
12295
  );
11622
12296
  for (const lib of res?.libraries ?? []) {
11623
12297
  line(
11624
- `${lib.present ? import_picocolors37.default.green(lib.name) : import_picocolors37.default.yellow(lib.name)}${import_picocolors37.default.dim(lib.present ? "" : " missing")}`
12298
+ `${lib.present ? import_picocolors38.default.green(lib.name) : import_picocolors38.default.yellow(lib.name)}${import_picocolors38.default.dim(lib.present ? "" : " missing")}`
11625
12299
  );
11626
12300
  }
11627
- for (const note of res?.notes ?? []) line(import_picocolors37.default.dim(note));
12301
+ for (const note of res?.notes ?? []) line(import_picocolors38.default.dim(note));
11628
12302
  });
11629
12303
  if (!res?.available) process.exitCode = 1;
11630
12304
  })
@@ -11646,15 +12320,15 @@ function registerAnalysis(program3) {
11646
12320
  );
11647
12321
  ok(res, () => {
11648
12322
  if (res?.stdout) line(res.stdout.replace(/\n$/, ""));
11649
- if (res?.stderr) line(import_picocolors37.default.dim(res.stderr.replace(/\n$/, "")));
11650
- if (res?.truncated) line(import_picocolors37.default.dim("(output truncated)"));
12323
+ if (res?.stderr) line(import_picocolors38.default.dim(res.stderr.replace(/\n$/, "")));
12324
+ if (res?.truncated) line(import_picocolors38.default.dim("(output truncated)"));
11651
12325
  const took = `${Math.round((res?.durationMs ?? 0) / 100) / 10}s`;
11652
12326
  line(
11653
- res?.ok ? import_picocolors37.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors37.default.dim(` ${took}`) : import_picocolors37.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors37.default.dim(` ${took}`)
12327
+ res?.ok ? import_picocolors38.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors38.default.dim(` ${took}`) : import_picocolors38.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors38.default.dim(` ${took}`)
11654
12328
  );
11655
12329
  if (res && !res.sandboxed) {
11656
12330
  line(
11657
- import_picocolors37.default.dim(
12331
+ import_picocolors38.default.dim(
11658
12332
  "This platform has no OS sandbox, so the script ran with your own file access."
11659
12333
  )
11660
12334
  );
@@ -11682,7 +12356,7 @@ function readCode(file, inline) {
11682
12356
  }
11683
12357
 
11684
12358
  // src/commands/scan.ts
11685
- var import_picocolors38 = __toESM(require_picocolors(), 1);
12359
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
11686
12360
  import { spawnSync as spawnSync2 } from "child_process";
11687
12361
  import { existsSync as existsSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
11688
12362
  import { join as join4, relative as relative2, sep as sep2 } from "path";
@@ -11840,15 +12514,15 @@ function buildReport(input) {
11840
12514
  function rank(s) {
11841
12515
  return s === "high" ? 3 : s === "medium" ? 2 : 1;
11842
12516
  }
11843
- function scanSummary(report) {
11844
- const ran = report.checked.length;
12517
+ function scanSummary(report2) {
12518
+ const ran = report2.checked.length;
11845
12519
  if (!ran) return "Nothing could be checked \u2014 see the reasons above.";
11846
- const what = report.checked.join(", ");
11847
- const total = report.findings.length;
12520
+ const what = report2.checked.join(", ");
12521
+ const total = report2.findings.length;
11848
12522
  if (!total) {
11849
12523
  return `Checked ${what} \u2014 nothing found.`;
11850
12524
  }
11851
- const high = report.counts.high;
12525
+ const high = report2.counts.high;
11852
12526
  return `Checked ${what} \u2014 ${total} ${total === 1 ? "thing" : "things"} to look at` + (high ? `, ${high} of them serious.` : ".");
11853
12527
  }
11854
12528
 
@@ -11870,10 +12544,10 @@ function registerScan(program3) {
11870
12544
  if (only.has("secrets")) runSecrets(ctx.cwd, !!opts.staged, findings, checked, skipped);
11871
12545
  if (only.has("deps")) runDeps(ctx.cwd, findings, checked, skipped);
11872
12546
  if (only.has("permissions")) runPermissions(ctx.cwd, findings, checked, skipped);
11873
- const report = buildReport({ findings, checked, skipped });
11874
- const summary = scanSummary(report);
11875
- ok({ ...report, summary }, () => print2(report, summary));
11876
- if (opts.check && !report.ok) {
12547
+ const report2 = buildReport({ findings, checked, skipped });
12548
+ const summary = scanSummary(report2);
12549
+ ok({ ...report2, summary }, () => print2(report2, summary));
12550
+ if (opts.check && !report2.ok) {
11877
12551
  throw new WorkserError(summary, { code: "bad_request" });
11878
12552
  }
11879
12553
  })
@@ -11964,24 +12638,24 @@ function runPermissions(cwd, findings, checked, skipped) {
11964
12638
  });
11965
12639
  }
11966
12640
  }
11967
- function print2(report, summary) {
11968
- for (const s of report.skipped) {
11969
- line(`${import_picocolors38.default.yellow("not checked")} ${s.check}${import_picocolors38.default.dim(` \u2014 ${s.reason}`)}`);
12641
+ function print2(report2, summary) {
12642
+ for (const s of report2.skipped) {
12643
+ line(`${import_picocolors39.default.yellow("not checked")} ${s.check}${import_picocolors39.default.dim(` \u2014 ${s.reason}`)}`);
11970
12644
  }
11971
- for (const f of report.findings) {
11972
- const where = f.file ? import_picocolors38.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
12645
+ for (const f of report2.findings) {
12646
+ const where = f.file ? import_picocolors39.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
11973
12647
  line(`${severityTag(f.severity)} ${f.title}${where}`);
11974
- line(` ${import_picocolors38.default.dim(f.fix)}`);
12648
+ line(` ${import_picocolors39.default.dim(f.fix)}`);
11975
12649
  }
11976
- if (report.findings.length || report.skipped.length) line("");
11977
- if (report.ok && !report.skipped.length) success(summary);
11978
- else if (report.ok) line(import_picocolors38.default.yellow(summary));
11979
- else line(import_picocolors38.default.red(summary));
12650
+ if (report2.findings.length || report2.skipped.length) line("");
12651
+ if (report2.ok && !report2.skipped.length) success(summary);
12652
+ else if (report2.ok) line(import_picocolors39.default.yellow(summary));
12653
+ else line(import_picocolors39.default.red(summary));
11980
12654
  }
11981
12655
  function severityTag(severity) {
11982
- if (severity === "high") return import_picocolors38.default.red("serious ");
11983
- if (severity === "medium") return import_picocolors38.default.yellow("worth fixing");
11984
- return import_picocolors38.default.dim("minor ");
12656
+ if (severity === "high") return import_picocolors39.default.red("serious ");
12657
+ if (severity === "medium") return import_picocolors39.default.yellow("worth fixing");
12658
+ return import_picocolors39.default.dim("minor ");
11985
12659
  }
11986
12660
  function git(cwd, args) {
11987
12661
  try {
@@ -12042,7 +12716,7 @@ function listRepoFiles2(root, maxDepth = 8) {
12042
12716
  }
12043
12717
 
12044
12718
  // src/commands/health.ts
12045
- var import_picocolors39 = __toESM(require_picocolors(), 1);
12719
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
12046
12720
  function registerHealth(program3) {
12047
12721
  program3.command("health").description("Check that the published apps in this project are still answering").option("--app <webAppId>", "check one app rather than all of them").action(
12048
12722
  action(async ({ ctx, opts }) => {
@@ -12056,16 +12730,16 @@ function registerHealth(program3) {
12056
12730
  }
12057
12731
  function print3(res) {
12058
12732
  if (!res?.checks?.length) {
12059
- line(import_picocolors39.default.dim(res?.note ?? "Nothing to check."));
12733
+ line(import_picocolors40.default.dim(res?.note ?? "Nothing to check."));
12060
12734
  return;
12061
12735
  }
12062
12736
  for (const c of res.checks) {
12063
- const mark = c.ok ? import_picocolors39.default.green("up ") : import_picocolors39.default.red("down");
12064
- const timing = import_picocolors39.default.dim(`${c.ms}ms`);
12065
- const detail = c.ok ? timing : import_picocolors39.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
12066
- line(` ${mark} ${c.appName} ${import_picocolors39.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
12737
+ const mark = c.ok ? import_picocolors40.default.green("up ") : import_picocolors40.default.red("down");
12738
+ const timing = import_picocolors40.default.dim(`${c.ms}ms`);
12739
+ const detail = c.ok ? timing : import_picocolors40.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
12740
+ line(` ${mark} ${c.appName} ${import_picocolors40.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
12067
12741
  if (c.incidentOpened) {
12068
- line(import_picocolors39.default.yellow(` An incident has been opened on the board for this.`));
12742
+ line(import_picocolors40.default.yellow(` An incident has been opened on the board for this.`));
12069
12743
  }
12070
12744
  }
12071
12745
  const down = res.checks.filter((c) => !c.ok);
@@ -12078,14 +12752,14 @@ function print3(res) {
12078
12752
  }
12079
12753
  const production = down.filter((c) => c.environment === "production").length;
12080
12754
  line(
12081
- import_picocolors39.default.red(
12755
+ import_picocolors40.default.red(
12082
12756
  `${down.length} of ${res.checks.length} not answering` + (production ? ` \u2014 ${production} customer-facing.` : " (preview only).")
12083
12757
  )
12084
12758
  );
12085
12759
  }
12086
12760
 
12087
12761
  // src/commands/urls.ts
12088
- var import_picocolors40 = __toESM(require_picocolors(), 1);
12762
+ var import_picocolors41 = __toESM(require_picocolors(), 1);
12089
12763
  function registerUrls(program3) {
12090
12764
  program3.command("urls").description("The stable preview and production addresses of every app in this project").option("--app <webAppId>", "just one app").action(
12091
12765
  action(async ({ ctx, opts }) => {
@@ -12099,20 +12773,20 @@ function registerUrls(program3) {
12099
12773
  const summary = urlsSummary(rows);
12100
12774
  ok({ rows, summary }, () => {
12101
12775
  for (const row of rows) {
12102
- const label = import_picocolors40.default.dim(row.environment.padEnd(10));
12103
- const value = row.url ? import_picocolors40.default.cyan(row.url) : import_picocolors40.default.dim(row.note ?? "not published");
12776
+ const label = import_picocolors41.default.dim(row.environment.padEnd(10));
12777
+ const value = row.url ? import_picocolors41.default.cyan(row.url) : import_picocolors41.default.dim(row.note ?? "not published");
12104
12778
  line(` ${row.appName.padEnd(22)} ${label} ${value}`);
12105
12779
  }
12106
12780
  line("");
12107
12781
  if (rows.some((r) => r.url)) success(summary);
12108
- else line(import_picocolors40.default.yellow(summary));
12782
+ else line(import_picocolors41.default.yellow(summary));
12109
12783
  });
12110
12784
  })
12111
12785
  );
12112
12786
  }
12113
12787
 
12114
12788
  // src/commands/deployments.ts
12115
- var import_picocolors41 = __toESM(require_picocolors(), 1);
12789
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
12116
12790
  function registerDeployments(program3) {
12117
12791
  const cmd = program3.command("deployments").description("Deployment history, and putting a build in front of customers");
12118
12792
  cmd.command("list").description("What has been built, newest first").option("--app <webAppId>", "just one app (default: every app in the project)").option("--env <environment>", "preview or production").option("--limit <n>", "how many to show", "20").action(
@@ -12130,7 +12804,7 @@ function registerDeployments(program3) {
12130
12804
  ok(res, () => {
12131
12805
  if (!items.length) {
12132
12806
  return line(
12133
- import_picocolors41.default.dim(
12807
+ import_picocolors42.default.dim(
12134
12808
  environment ? `Nothing has been deployed to ${environment} yet.` : "Nothing has been deployed yet. `workser deploy` builds the first one."
12135
12809
  )
12136
12810
  );
@@ -12150,13 +12824,13 @@ function registerDeployments(program3) {
12150
12824
  ).catch(() => null) : null;
12151
12825
  ok({ ...dep, logs }, () => {
12152
12826
  line(formatDeployment(dep));
12153
- if (dep?.error_message) line(import_picocolors41.default.red(` ${dep.error_message}`));
12827
+ if (dep?.error_message) line(import_picocolors42.default.red(` ${dep.error_message}`));
12154
12828
  const events = logs?.events ?? [];
12155
12829
  for (const e of events) {
12156
- line(` ${import_picocolors41.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
12830
+ line(` ${import_picocolors42.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
12157
12831
  }
12158
12832
  if (opts.logs && !events.length) {
12159
- line(import_picocolors41.default.dim(" That build produced no output."));
12833
+ line(import_picocolors42.default.dim(" That build produced no output."));
12160
12834
  }
12161
12835
  });
12162
12836
  })
@@ -12200,16 +12874,16 @@ function printPromoted(res, version) {
12200
12874
  const what = version === null ? "the latest build" : `version ${version}`;
12201
12875
  const url = res.url ?? res.vercel_url;
12202
12876
  success(`Production is being rebuilt from ${what}.`);
12203
- if (url) line(import_picocolors41.default.dim(`It will be at ${url}`));
12204
- line(import_picocolors41.default.dim("`workser deploy status` follows it."));
12877
+ if (url) line(import_picocolors42.default.dim(`It will be at ${url}`));
12878
+ line(import_picocolors42.default.dim("`workser deploy status` follows it."));
12205
12879
  }
12206
12880
  function formatDeployment(d) {
12207
12881
  if (!d) return "";
12208
- const version = d.version !== void 0 ? import_picocolors41.default.yellow(`v${d.version}`) : import_picocolors41.default.dim("v?");
12209
- const env = import_picocolors41.default.dim((d.environment ?? "?").padEnd(10));
12882
+ const version = d.version !== void 0 ? import_picocolors42.default.yellow(`v${d.version}`) : import_picocolors42.default.dim("v?");
12883
+ const env = import_picocolors42.default.dim((d.environment ?? "?").padEnd(10));
12210
12884
  const app = d.webAppName ? `${d.webAppName} ` : "";
12211
- const when = import_picocolors41.default.dim(formatTime2(d.created_at));
12212
- const url = d.url ? " " + import_picocolors41.default.cyan(d.url) : "";
12885
+ const when = import_picocolors42.default.dim(formatTime2(d.created_at));
12886
+ const url = d.url ? " " + import_picocolors42.default.cyan(d.url) : "";
12213
12887
  return `${version} ${env} ${colorStatus(d.status ?? "")} ${app}${when}${url}`;
12214
12888
  }
12215
12889
  function formatTime2(t) {
@@ -12219,7 +12893,7 @@ function formatTime2(t) {
12219
12893
  }
12220
12894
 
12221
12895
  // src/commands/usage.ts
12222
- var import_picocolors42 = __toESM(require_picocolors(), 1);
12896
+ var import_picocolors43 = __toESM(require_picocolors(), 1);
12223
12897
 
12224
12898
  // src/usage.ts
12225
12899
  var NEAR_LIMIT_FRACTION = 0.8;
@@ -12266,22 +12940,22 @@ function dimensionLine(d) {
12266
12940
  }
12267
12941
  return `${d.label}: ${used} of ${limit}`;
12268
12942
  }
12269
- function usageSummary(report) {
12270
- const dims = report.dimensions ?? [];
12943
+ function usageSummary(report2) {
12944
+ const dims = report2.dimensions ?? [];
12271
12945
  const unknown = dims.filter((d) => d.used === null);
12272
12946
  const over = dims.filter((d) => usageState(d) === "over");
12273
12947
  const near = dims.filter((d) => usageState(d) === "near");
12274
12948
  const parts = [];
12275
12949
  if (over.length) {
12276
12950
  parts.push(
12277
- `${over.length === 1 ? "One thing is" : `${over.length} things are`} over the ${report.tier} plan's limit`
12951
+ `${over.length === 1 ? "One thing is" : `${over.length} things are`} over the ${report2.tier} plan's limit`
12278
12952
  );
12279
12953
  } else if (near.length) {
12280
12954
  parts.push(
12281
- `${near.length === 1 ? "One thing is" : `${near.length} things are`} close to the ${report.tier} plan's limit`
12955
+ `${near.length === 1 ? "One thing is" : `${near.length} things are`} close to the ${report2.tier} plan's limit`
12282
12956
  );
12283
12957
  } else if (dims.some((d) => d.used !== null)) {
12284
- parts.push(`Everything is comfortably inside the ${report.tier} plan`);
12958
+ parts.push(`Everything is comfortably inside the ${report2.tier} plan`);
12285
12959
  }
12286
12960
  if (unknown.length) {
12287
12961
  parts.push(
@@ -12291,8 +12965,8 @@ function usageSummary(report) {
12291
12965
  if (!parts.length) return "Nothing could be measured.";
12292
12966
  return `${parts.join("; ")}.`;
12293
12967
  }
12294
- function shouldFail(report) {
12295
- return (report.dimensions ?? []).some(
12968
+ function shouldFail(report2) {
12969
+ return (report2.dimensions ?? []).some(
12296
12970
  (d) => d.kind === "hard" && usageState(d) === "over"
12297
12971
  );
12298
12972
  }
@@ -12302,12 +12976,12 @@ function registerUsage(program3) {
12302
12976
  const usage = program3.command("usage").description("What this project and your plan are using \u2014 storage, projects, apps").action(
12303
12977
  action(async ({ ctx }) => {
12304
12978
  const projectId = requireProject(ctx);
12305
- const report = await api(
12979
+ const report2 = await api(
12306
12980
  ctx,
12307
12981
  `/v1/projects/${projectId}/usage`
12308
12982
  );
12309
- ok(report, () => print4(report));
12310
- if (shouldFail(report)) process.exitCode = 1;
12983
+ ok(report2, () => print4(report2));
12984
+ if (shouldFail(report2)) process.exitCode = 1;
12311
12985
  })
12312
12986
  );
12313
12987
  usage.command("infra").description(
@@ -12332,15 +13006,15 @@ function printInfra(usage) {
12332
13006
  ];
12333
13007
  const width = Math.max(...rows.map(([label]) => label.length));
12334
13008
  for (const [label, dim, unit] of rows) {
12335
- const value = dim.value === null ? import_picocolors42.default.dim("not measured") : `${dim.value}${unit ? ` ${unit}` : ""}`;
12336
- const when = dim.live ? import_picocolors42.default.dim("now") : import_picocolors42.default.dim(`through ${shortDay(dim.asOf)}`);
13009
+ const value = dim.value === null ? import_picocolors43.default.dim("not measured") : `${dim.value}${unit ? ` ${unit}` : ""}`;
13010
+ const when = dim.live ? import_picocolors43.default.dim("now") : import_picocolors43.default.dim(`through ${shortDay(dim.asOf)}`);
12337
13011
  line(` ${label.padEnd(width)} ${value} ${when}`);
12338
13012
  }
12339
13013
  const stale = rows.find(([, dim]) => !dim.live && dim.nextUpdate);
12340
13014
  if (stale) {
12341
13015
  line("");
12342
13016
  line(
12343
- import_picocolors42.default.dim(
13017
+ import_picocolors43.default.dim(
12344
13018
  `Bandwidth is reported by the host in closed days, so it updates once a day \u2014 next around ${shortTime(
12345
13019
  stale[1].nextUpdate
12346
13020
  )}.`
@@ -12356,35 +13030,35 @@ function shortTime(iso) {
12356
13030
  const d = new Date(iso);
12357
13031
  return Number.isFinite(d.getTime()) ? `${d.toISOString().slice(11, 16)} UTC` : "\u2014";
12358
13032
  }
12359
- function print4(report) {
12360
- const dims = report.dimensions ?? [];
13033
+ function print4(report2) {
13034
+ const dims = report2.dimensions ?? [];
12361
13035
  if (!dims.length) {
12362
- return line(import_picocolors42.default.dim("Nothing to measure for this project yet."));
13036
+ return line(import_picocolors43.default.dim("Nothing to measure for this project yet."));
12363
13037
  }
12364
13038
  const width = Math.max(...dims.map((d) => d.label.length));
12365
13039
  for (const d of dims) {
12366
13040
  line(` ${colour(d)(dimensionLine(d).padEnd(0))}${gauge(d, width)}`);
12367
13041
  }
12368
13042
  line("");
12369
- const summary = usageSummary(report);
13043
+ const summary = usageSummary(report2);
12370
13044
  const worst = dims.map(usageState);
12371
- if (worst.includes("over")) line(import_picocolors42.default.red(summary));
13045
+ if (worst.includes("over")) line(import_picocolors43.default.red(summary));
12372
13046
  else if (worst.includes("near") || worst.includes("unknown"))
12373
- line(import_picocolors42.default.yellow(summary));
13047
+ line(import_picocolors43.default.yellow(summary));
12374
13048
  else success(summary);
12375
13049
  }
12376
13050
  function gauge(d, _labelWidth) {
12377
13051
  const drawn = bar(d);
12378
- return drawn ? ` ${import_picocolors42.default.dim(drawn)}` : "";
13052
+ return drawn ? ` ${import_picocolors43.default.dim(drawn)}` : "";
12379
13053
  }
12380
13054
  function colour(d) {
12381
13055
  switch (usageState(d)) {
12382
13056
  case "over":
12383
- return d.kind === "hard" ? import_picocolors42.default.red : import_picocolors42.default.yellow;
13057
+ return d.kind === "hard" ? import_picocolors43.default.red : import_picocolors43.default.yellow;
12384
13058
  case "near":
12385
- return import_picocolors42.default.yellow;
13059
+ return import_picocolors43.default.yellow;
12386
13060
  case "unknown":
12387
- return import_picocolors42.default.dim;
13061
+ return import_picocolors43.default.dim;
12388
13062
  default:
12389
13063
  return (s) => s;
12390
13064
  }
@@ -12392,7 +13066,7 @@ function colour(d) {
12392
13066
 
12393
13067
  // src/index.ts
12394
13068
  var pkg = {
12395
- version: true ? "0.6.27" : "0.0.0-dev"
13069
+ version: true ? "0.6.29" : "0.0.0-dev"
12396
13070
  };
12397
13071
  var program2 = new Command();
12398
13072
  program2.name("workser").description(
@@ -12438,6 +13112,7 @@ registerCheckpoint(program2);
12438
13112
  registerSync(program2);
12439
13113
  registerWorkflow(program2);
12440
13114
  registerConnection(program2);
13115
+ registerChannels(program2);
12441
13116
  registerTool(program2);
12442
13117
  registerMemory(program2);
12443
13118
  registerNote(program2);