@workser/cli 0.6.16 → 0.6.18

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
@@ -3663,6 +3663,158 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
3663
3663
 
3664
3664
  // src/help-content.ts
3665
3665
  var HELP_TOPICS = [
3666
+ {
3667
+ topic: "agent-cloud",
3668
+ title: "Ship an agent inside the app",
3669
+ summary: "Create an AI agent that runs on Workser and can be called from this project's apps.",
3670
+ commands: ["agent-cloud"],
3671
+ source: "skills/workser/reference/agent-cloud.md",
3672
+ body: `# Ship an agent inside the app
3673
+
3674
+ \`workser agent-cloud\` creates an AI agent that runs on **Workser's**
3675
+ infrastructure, keeps its own memory and tools, and can be called from the web,
3676
+ mobile, API or Python apps in this project.
3677
+
3678
+ **This is not \`workser agent\`.** That one hands a subtask to a coding agent on
3679
+ this machine \u2014 a teammate helping you build. This one is a thing the project
3680
+ *ships*: it works for the user after you are gone.
3681
+
3682
+ \`\`\`
3683
+ workser agent-cloud list
3684
+ workser agent-cloud create "Order desk" --instructions "..."
3685
+ workser agent-cloud show <agentId>
3686
+ workser agent-cloud run <agentId> "<what to do>"
3687
+ workser agent-cloud runs <agentId> # recent runs
3688
+ workser agent-cloud runs <runId> # one run, with what it cost
3689
+ \`\`\`
3690
+
3691
+ Every call is scoped to the project this folder belongs to.
3692
+
3693
+ ## Creating one is not finishing one
3694
+
3695
+ An agent created with a name and a sentence knows nothing about the business.
3696
+ Teaching it is the actual work, and it is all here:
3697
+
3698
+ \`\`\`
3699
+ workser agent-cloud set <id> system_prompt="..." handle="orderdesk"
3700
+ workser agent-cloud add <id> skill name="Refunds" instructions_md="..."
3701
+ workser agent-cloud add <id> knowledge name="Price list" content_text="..."
3702
+ workser agent-cloud add <id> tool display_name="Send email" provider="gmail" \\
3703
+ provider_tool_id="GMAIL_SEND_EMAIL"
3704
+ workser agent-cloud add <id> secret key="STRIPE_KEY" value="..."
3705
+ workser agent-cloud add <id> subagent subagent_id=<otherId> name="researcher"
3706
+ workser agent-cloud get <id> skill # what it has
3707
+ workser agent-cloud remove <id> skill <itemId>
3708
+ \`\`\`
3709
+
3710
+ \`add\` takes \`key=value\` pairs and REFUSES a field it does not know, rather than
3711
+ sending it. That matters: the API silently drops unknown fields, so a typo
3712
+ would otherwise be accepted, dropped, and reported as success \u2014 leaving an
3713
+ agent that had been told nothing.
3714
+
3715
+ ## Nothing takes effect until you publish
3716
+
3717
+ **This is the step to not forget.** The runtime resolves the PUBLISHED version
3718
+ of an agent and never the draft, so every \`set\` and \`add\` above is inert until:
3719
+
3720
+ \`\`\`
3721
+ workser agent-cloud publish <id> --note "taught it refunds"
3722
+ \`\`\`
3723
+
3724
+ Before publishing, try the setup without putting it live:
3725
+
3726
+ \`\`\`
3727
+ workser agent-cloud try <id> "a customer wants a refund on order 1042"
3728
+ \`\`\`
3729
+
3730
+ A \`try\` runs the draft, costs the same as a real run, and changes nothing that
3731
+ customers can reach.
3732
+
3733
+ ## Choosing how it thinks and what it runs on
3734
+
3735
+ \`\`\`
3736
+ workser agent-cloud models # cheapest first, on Workser credit
3737
+ workser agent-cloud models --all # includes ones needing your own key
3738
+ workser agent-cloud set <id> default_provider=openrouter default_model=...
3739
+
3740
+ workser agent-cloud machines # video, data analysis, design, ...
3741
+ \`\`\`
3742
+
3743
+ A model marked "needs your own key" will make \`publish\` FAIL unless a matching
3744
+ secret is stored first. Add the key with \`add <id> secret\` before setting it.
3745
+
3746
+ ## Agents need a paid plan \u2014 the trial does not include them
3747
+
3748
+ \`create\` and \`run\` both refuse with **402** on a free or trialling
3749
+ organisation. That is a rule, not a fault: every run buys model tokens and
3750
+ holds a machine, so it costs real money the moment it happens.
3751
+
3752
+ **When you hit it, hand the plan over \u2014 do not just describe it.** Workser
3753
+ files the refusal on the conversation as a step and an artifact the user can
3754
+ click, so the person you are helping gets something actionable rather than a
3755
+ paragraph. Say plainly what you were doing, that agents need a plan, and stop.
3756
+
3757
+ Do NOT:
3758
+
3759
+ - retry the call, or try a different agent \u2014 the answer is the same
3760
+ - try to work around it by running the job yourself as a coding agent; that is
3761
+ a different product and it will not be there when the user closes the window
3762
+ - guess at prices, discounts or trial extensions
3763
+
3764
+ If the user asks how much: it is per minute of run time plus the model, and the
3765
+ plan page has the current figures. You do not have them.
3766
+
3767
+ ## When to reach for this
3768
+
3769
+ When the user describes a job that **keeps happening** and needs judgement:
3770
+ "check every order for stock and email me the problems", "read the LINE
3771
+ messages and file them", "reconcile these invoices". That is an agent.
3772
+
3773
+ A one-off transformation is not an agent \u2014 write the code. A fixed sequence of
3774
+ steps with no judgement in it is not an agent either \u2014 that is \`workser
3775
+ workflow\`.
3776
+
3777
+ ## Calling it from the app you are building
3778
+
3779
+ Do NOT shell out to the CLI from app code. Use the SDK, which streams:
3780
+
3781
+ \`\`\`ts
3782
+ import { workser } from '@workser/app';
3783
+
3784
+ const run = await workser.agents.run(agentId, { message }, {
3785
+ referenceUserId: user.id, // who it is acting for
3786
+ });
3787
+
3788
+ for await (const event of workser.agents.stream(run.id)) {
3789
+ // event.type, event.data \u2014 forward these to the browser
3790
+ }
3791
+ \`\`\`
3792
+
3793
+ \`stream()\` reconnects itself through dropped connections, so the person
3794
+ watching sees the agent think. See the \`workser-sdk\` skill, \`reference/agents.md\`.
3795
+
3796
+ ## Things that will bite you
3797
+
3798
+ 1. **A run costs money by the minute.** It is metered \u2014 runtime, workspace, and
3799
+ a per-run fee \u2014 so a loop that starts agents is a loop that spends. Cancel
3800
+ what you abandon: \`workser agent-cloud runs <runId>\` shows the cost.
3801
+
3802
+ 2. **Instructions are the product.** The agent does what its instructions say,
3803
+ in the user's own words. Write them the way you would brief a new colleague:
3804
+ what to do, what to leave alone, when to ask. Vague instructions are the
3805
+ single biggest cause of an agent that "doesn't work".
3806
+
3807
+ 3. **Free plans cannot run agents at all**, and a trial has a small allowance.
3808
+ A \`402\` with \`spend_limit_reached\` is not a bug \u2014 tell the user what it says
3809
+ and point them at their plan.
3810
+
3811
+ 4. **Say who it is for.** An agent acting for one of the app's customers needs
3812
+ \`referenceUserId\`, or its memory and audit trail belong to nobody.
3813
+
3814
+ 5. **Do not invent an agent the user did not ask for.** Creating one is cheap;
3815
+ an agent nobody wanted, quietly costing money per run, is not.
3816
+ `
3817
+ },
3666
3818
  {
3667
3819
  topic: "analysis",
3668
3820
  title: "Analysis \u2014 running Python on this project's data",
@@ -7459,8 +7611,366 @@ function formatRole(r) {
7459
7611
  return `${label} ${agent} ${enabled} ${ready}${tail}`;
7460
7612
  }
7461
7613
 
7462
- // src/commands/verify.ts
7614
+ // src/commands/agent-cloud.ts
7463
7615
  var import_picocolors17 = __toESM(require_picocolors(), 1);
7616
+ function registerAgentCloud(program3) {
7617
+ const cloud = program3.command("agent-cloud").description(
7618
+ "Agents that run in the cloud and can be called from this project's apps (not the local coding agents \u2014 that's `workser agent`)"
7619
+ );
7620
+ cloud.command("list").description("List this project's cloud agents").action(
7621
+ action(async ({ ctx }) => {
7622
+ const res = await api(ctx, "/v1/agent-cloud");
7623
+ const agents = res?.agents ?? res ?? [];
7624
+ ok(res, () => {
7625
+ if (!agents.length) {
7626
+ line(import_picocolors17.default.dim("No cloud agents yet."));
7627
+ line(
7628
+ import_picocolors17.default.dim("Create one: ") + import_picocolors17.default.bold('workser agent-cloud create "Order desk" --instructions "..."')
7629
+ );
7630
+ return;
7631
+ }
7632
+ for (const a of agents) {
7633
+ line(
7634
+ `${import_picocolors17.default.bold(a.name ?? a.id)} ${import_picocolors17.default.dim(a.id)}` + (a.status ? ` ${import_picocolors17.default.dim(a.status)}` : "")
7635
+ );
7636
+ if (a.description) line(" " + import_picocolors17.default.dim(a.description));
7637
+ }
7638
+ });
7639
+ })
7640
+ );
7641
+ cloud.command("create <name>").description("Create a cloud agent in this project").option("--description <text>", "What it is for, in the owner's words").option(
7642
+ "--instructions <text>",
7643
+ "The agent's standing instructions \u2014 what it should always do"
7644
+ ).action(
7645
+ action(async ({ ctx, args, opts }) => {
7646
+ const res = await api(ctx, "/v1/agent-cloud", {
7647
+ method: "POST",
7648
+ body: {
7649
+ name: args[0],
7650
+ description: opts.description,
7651
+ instructions: opts.instructions
7652
+ }
7653
+ });
7654
+ ok(res, () => {
7655
+ line(import_picocolors17.default.green("Created ") + import_picocolors17.default.bold(res?.name ?? args[0]));
7656
+ if (res?.id) line(import_picocolors17.default.dim(res.id));
7657
+ line("");
7658
+ line(
7659
+ import_picocolors17.default.dim("Give it work: ") + import_picocolors17.default.bold(`workser agent-cloud run ${res?.id ?? "<id>"} "..."`)
7660
+ );
7661
+ });
7662
+ })
7663
+ );
7664
+ cloud.command("show <agentId>").description("One cloud agent, with its configuration").action(
7665
+ action(async ({ ctx, args }) => {
7666
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}`);
7667
+ ok(res, () => {
7668
+ line(import_picocolors17.default.bold(res?.name ?? args[0]));
7669
+ if (res?.description) line(import_picocolors17.default.dim(res.description));
7670
+ if (res?.instructions) {
7671
+ line("");
7672
+ line(import_picocolors17.default.bold("instructions:"));
7673
+ line(res.instructions);
7674
+ }
7675
+ });
7676
+ })
7677
+ );
7678
+ cloud.command("run <agentId> <message>").description("Give a cloud agent something to do").action(
7679
+ action(async ({ ctx, args }) => {
7680
+ const res = await api(
7681
+ ctx,
7682
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/runs`,
7683
+ { method: "POST", body: { input: { message: args[1] } } }
7684
+ );
7685
+ ok(res, () => {
7686
+ line(import_picocolors17.default.green("Started run ") + import_picocolors17.default.bold(res?.id ?? ""));
7687
+ line(
7688
+ import_picocolors17.default.dim("Follow it: ") + import_picocolors17.default.bold(`workser agent-cloud runs ${res?.id ?? "<runId>"}`)
7689
+ );
7690
+ });
7691
+ })
7692
+ );
7693
+ cloud.command("runs [agentIdOrRunId]").description("Recent runs for an agent, or one run in detail").option("--limit <n>", "How many to list", "10").action(
7694
+ action(async ({ ctx, args, opts }) => {
7695
+ const id = args[0];
7696
+ if (!id) {
7697
+ warn("Give an agent id to list its runs, or a run id to inspect one.");
7698
+ return;
7699
+ }
7700
+ try {
7701
+ const run = await api(
7702
+ ctx,
7703
+ `/v1/agent-cloud/runs/${encodeURIComponent(id)}`
7704
+ );
7705
+ ok(run, () => printRun(run));
7706
+ return;
7707
+ } catch {
7708
+ }
7709
+ const res = await api(
7710
+ ctx,
7711
+ `/v1/agent-cloud/${encodeURIComponent(id)}/runs`,
7712
+ { query: { limit: opts.limit } }
7713
+ );
7714
+ const runs = res?.runs ?? res ?? [];
7715
+ ok(res, () => {
7716
+ if (!runs.length) {
7717
+ line(import_picocolors17.default.dim("No runs yet."));
7718
+ return;
7719
+ }
7720
+ for (const r of runs) printRun(r, true);
7721
+ });
7722
+ })
7723
+ );
7724
+ const COLLECTIONS = {
7725
+ skill: {
7726
+ path: "skills",
7727
+ key: "skills",
7728
+ label: "skill",
7729
+ fields: ["name", "description", "instructions_md"],
7730
+ required: ["name"]
7731
+ },
7732
+ tool: {
7733
+ path: "tools",
7734
+ key: "tools",
7735
+ label: "action",
7736
+ fields: ["display_name", "provider", "provider_tool_id", "description"],
7737
+ required: ["display_name"]
7738
+ },
7739
+ mcp: {
7740
+ path: "mcp-servers",
7741
+ key: "servers",
7742
+ label: "MCP server",
7743
+ fields: ["name", "url"],
7744
+ required: ["name", "url"]
7745
+ },
7746
+ knowledge: {
7747
+ path: "resources",
7748
+ key: "resources",
7749
+ label: "reference material",
7750
+ fields: ["name", "content_text"],
7751
+ required: ["name"]
7752
+ },
7753
+ secret: {
7754
+ path: "secrets",
7755
+ key: "secrets",
7756
+ label: "stored key",
7757
+ fields: ["key", "value", "description"],
7758
+ required: ["key", "value"]
7759
+ },
7760
+ subagent: {
7761
+ path: "subagents",
7762
+ key: "subagents",
7763
+ label: "team member",
7764
+ fields: ["subagent_id", "name", "description"],
7765
+ required: ["subagent_id", "name"]
7766
+ },
7767
+ workflow: {
7768
+ path: "workflows",
7769
+ key: "workflows",
7770
+ label: "workflow",
7771
+ fields: ["workflow_id", "description"],
7772
+ required: ["workflow_id"]
7773
+ }
7774
+ };
7775
+ const kinds = Object.keys(COLLECTIONS).join(" | ");
7776
+ cloud.command("get <agentId> <kind>").description(`What an agent has been given (${kinds})`).action(
7777
+ action(async ({ ctx, args }) => {
7778
+ const spec = COLLECTIONS[args[1]];
7779
+ if (!spec) throw new Error(`Unknown kind "${args[1]}". One of: ${kinds}`);
7780
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}/${spec.path}`);
7781
+ const items = res?.[spec.key] ?? [];
7782
+ ok(res, () => {
7783
+ if (!items.length) {
7784
+ line(import_picocolors17.default.dim(`No ${spec.label} yet.`));
7785
+ return;
7786
+ }
7787
+ for (const i of items) {
7788
+ const title = i.name ?? i.display_name ?? i.key ?? i.workflow_id ?? i.id;
7789
+ line(`${import_picocolors17.default.bold(title)} ${import_picocolors17.default.dim(i.id)}`);
7790
+ if (i.description) line(" " + import_picocolors17.default.dim(i.description));
7791
+ if (i.is_enabled === false) line(" " + import_picocolors17.default.yellow("turned off"));
7792
+ }
7793
+ });
7794
+ })
7795
+ );
7796
+ cloud.command("add <agentId> <kind> [pairs...]").description(
7797
+ `Give an agent something (${kinds}). Pairs are key=value, e.g. name="Refunds"`
7798
+ ).action(
7799
+ action(async ({ ctx, args }) => {
7800
+ const spec = COLLECTIONS[args[1]];
7801
+ if (!spec) throw new Error(`Unknown kind "${args[1]}". One of: ${kinds}`);
7802
+ const body = parsePairs(args[2] ?? [], spec.fields);
7803
+ const missing = spec.required.filter((f) => !body[f]);
7804
+ if (missing.length) {
7805
+ throw new Error(
7806
+ `A ${spec.label} needs ${missing.join(" and ")}. Accepted: ${spec.fields.join(", ")}`
7807
+ );
7808
+ }
7809
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}/${spec.path}`, {
7810
+ method: "POST",
7811
+ body
7812
+ });
7813
+ ok(res, () => {
7814
+ line(import_picocolors17.default.green(`Added the ${spec.label}.`) + " " + import_picocolors17.default.dim(res?.id ?? ""));
7815
+ line(
7816
+ import_picocolors17.default.dim("Not live yet \u2014 run ") + import_picocolors17.default.bold(`workser agent-cloud publish ${args[0]}`) + import_picocolors17.default.dim(" when the setup is ready.")
7817
+ );
7818
+ });
7819
+ })
7820
+ );
7821
+ cloud.command("remove <agentId> <kind> <itemId>").description(`Take something away from an agent (${kinds})`).action(
7822
+ action(async ({ ctx, args }) => {
7823
+ const spec = COLLECTIONS[args[1]];
7824
+ if (!spec) throw new Error(`Unknown kind "${args[1]}". One of: ${kinds}`);
7825
+ const res = await api(
7826
+ ctx,
7827
+ `/v1/agent-cloud/${encodeURIComponent(args[0])}/${spec.path}/${encodeURIComponent(args[2])}`,
7828
+ { method: "DELETE" }
7829
+ );
7830
+ ok(res, () => line(import_picocolors17.default.green(`Removed the ${spec.label}.`)));
7831
+ })
7832
+ );
7833
+ cloud.command("set <agentId> [pairs...]").description(
7834
+ "Change the brief or the model. Pairs: name, description, system_prompt, handle, default_provider, default_model"
7835
+ ).action(
7836
+ action(async ({ ctx, args }) => {
7837
+ const body = parsePairs(args[1] ?? [], [
7838
+ "name",
7839
+ "description",
7840
+ "system_prompt",
7841
+ "handle",
7842
+ "default_provider",
7843
+ "default_model"
7844
+ ]);
7845
+ if (!Object.keys(body).length) {
7846
+ throw new Error(
7847
+ 'Nothing to change. Example: workser agent-cloud set <id> system_prompt="Always check stock first"'
7848
+ );
7849
+ }
7850
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}`, {
7851
+ method: "PATCH",
7852
+ body
7853
+ });
7854
+ ok(res, () => {
7855
+ line(import_picocolors17.default.green("Saved."));
7856
+ line(
7857
+ import_picocolors17.default.dim("Not live yet \u2014 run ") + import_picocolors17.default.bold(`workser agent-cloud publish ${args[0]}`) + import_picocolors17.default.dim(".")
7858
+ );
7859
+ });
7860
+ })
7861
+ );
7862
+ 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(
7863
+ action(async ({ ctx, args, opts }) => {
7864
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}/publish`, {
7865
+ method: "POST",
7866
+ body: { changelog: opts.note }
7867
+ });
7868
+ ok(
7869
+ res,
7870
+ () => line(import_picocolors17.default.green(`Live${res?.version ? ` \u2014 version ${res.version}` : ""}.`))
7871
+ );
7872
+ })
7873
+ );
7874
+ cloud.command("try <agentId> <message>").description("Run the current setup WITHOUT publishing it").action(
7875
+ action(async ({ ctx, args }) => {
7876
+ const res = await api(ctx, `/v1/agent-cloud/${encodeURIComponent(args[0])}/test-runs`, {
7877
+ method: "POST",
7878
+ body: { input: { message: args[1] } }
7879
+ });
7880
+ ok(res, () => {
7881
+ line(import_picocolors17.default.green("Trying it \u2014 nothing has gone live."));
7882
+ if (res?.run_id) {
7883
+ line(
7884
+ import_picocolors17.default.dim("Watch it: ") + import_picocolors17.default.bold(`workser agent-cloud runs ${res.run_id}`)
7885
+ );
7886
+ }
7887
+ });
7888
+ })
7889
+ );
7890
+ cloud.command("machines").description("The pre-built machines an agent can run on").action(
7891
+ action(async ({ ctx }) => {
7892
+ const res = await api(ctx, "/v1/agent-cloud/catalog/machines");
7893
+ ok(res, () => {
7894
+ for (const m of res?.machines ?? []) {
7895
+ line(`${import_picocolors17.default.bold(m.label)} ${import_picocolors17.default.dim(m.alias)}`);
7896
+ line(" " + import_picocolors17.default.dim(m.description));
7897
+ }
7898
+ });
7899
+ })
7900
+ );
7901
+ cloud.command("models").description("Models this organisation can run an agent on, cheapest first").option("--all", "Include models that need your own provider key").action(
7902
+ action(async ({ ctx, opts }) => {
7903
+ const res = await api(ctx, "/v1/agent-cloud/catalog/models/live");
7904
+ const models = (res?.models ?? []).filter(
7905
+ (m) => opts.all || m.credit_tier === "PLATFORM_CREDITS"
7906
+ );
7907
+ ok(res, () => {
7908
+ if (!models.length) {
7909
+ warn("The live model list could not be read.");
7910
+ return;
7911
+ }
7912
+ for (const m of models.slice(0, 40)) {
7913
+ const price = typeof m.input_price_per_million_usd === "number" ? `$${m.input_price_per_million_usd.toFixed(2)}/M in` : "price unknown";
7914
+ const byok = m.credit_tier === "PLATFORM_CREDITS" ? "" : import_picocolors17.default.yellow(" needs your own key");
7915
+ line(`${import_picocolors17.default.bold(m.id)} ${import_picocolors17.default.dim(price)}${byok}`);
7916
+ }
7917
+ });
7918
+ })
7919
+ );
7920
+ }
7921
+ function printRun(run, compact = false) {
7922
+ const status = String(run?.status ?? "").toLowerCase();
7923
+ const colour2 = status === "completed" ? import_picocolors17.default.green : status === "failed" ? import_picocolors17.default.red : import_picocolors17.default.yellow;
7924
+ line(
7925
+ `${colour2(status || "unknown")} ${import_picocolors17.default.bold(run?.id ?? "")}` + (run?.duration_ms ? ` ${import_picocolors17.default.dim(formatDuration(run.duration_ms))}` : "")
7926
+ );
7927
+ const breakdown = run?.cost_breakdown;
7928
+ if (breakdown) {
7929
+ if (!breakdown.settled) {
7930
+ line(" " + import_picocolors17.default.dim("cost: still being worked out"));
7931
+ } else {
7932
+ line(" " + import_picocolors17.default.dim(`cost: $${Number(breakdown.total_usd).toFixed(4)}`));
7933
+ line(
7934
+ " " + import_picocolors17.default.dim(
7935
+ `model $${Number(breakdown.model_usd).toFixed(4)} \xB7 infrastructure $${Number(breakdown.infrastructure_usd).toFixed(4)}`
7936
+ )
7937
+ );
7938
+ }
7939
+ } else if (run?.cost_usd !== void 0 && run?.cost_usd !== null) {
7940
+ line(" " + import_picocolors17.default.dim(`model cost: $${Number(run.cost_usd).toFixed(4)}`));
7941
+ }
7942
+ if (!compact && run?.output) {
7943
+ line("");
7944
+ line(typeof run.output === "string" ? run.output : JSON.stringify(run.output, null, 2));
7945
+ }
7946
+ if (run?.error?.message) line(" " + import_picocolors17.default.red(run.error.message));
7947
+ }
7948
+ function formatDuration(ms) {
7949
+ if (ms < 1e3) return `${ms}ms`;
7950
+ const seconds = Math.round(ms / 1e3);
7951
+ if (seconds < 60) return `${seconds}s`;
7952
+ const minutes = Math.floor(seconds / 60);
7953
+ return `${minutes}m ${seconds % 60}s`;
7954
+ }
7955
+ function parsePairs(pairs, allowed) {
7956
+ const out = {};
7957
+ for (const raw of pairs) {
7958
+ const at = raw.indexOf("=");
7959
+ if (at < 1) {
7960
+ throw new Error(`"${raw}" is not a key=value pair.`);
7961
+ }
7962
+ const key = raw.slice(0, at).trim();
7963
+ const value = raw.slice(at + 1);
7964
+ if (!allowed.includes(key)) {
7965
+ throw new Error(`"${key}" is not a field here. Accepted: ${allowed.join(", ")}`);
7966
+ }
7967
+ if (value) out[key] = value;
7968
+ }
7969
+ return out;
7970
+ }
7971
+
7972
+ // src/commands/verify.ts
7973
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
7464
7974
  function registerVerify(program3) {
7465
7975
  program3.command("verify").description(
7466
7976
  "Run the project's checks (typecheck/lint/build) \u2014 use before declaring a task done"
@@ -7482,23 +7992,23 @@ function registerVerify(program3) {
7482
7992
  function printVerify(res) {
7483
7993
  if (!res) return;
7484
7994
  if (!res.checks?.length) {
7485
- line(import_picocolors17.default.dim(res.note ?? "No checks detected."));
7995
+ line(import_picocolors18.default.dim(res.note ?? "No checks detected."));
7486
7996
  return;
7487
7997
  }
7488
7998
  for (const c of res.checks) {
7489
7999
  line(
7490
- ` ${c.ok ? import_picocolors17.default.green("\u2713") : import_picocolors17.default.red("\u2717")} ${c.name}${c.ok ? "" : import_picocolors17.default.dim(` (exit ${c.exitCode})`)}`
8000
+ ` ${c.ok ? import_picocolors18.default.green("\u2713") : import_picocolors18.default.red("\u2717")} ${c.name}${c.ok ? "" : import_picocolors18.default.dim(` (exit ${c.exitCode})`)}`
7491
8001
  );
7492
8002
  }
7493
8003
  if (res.ok) success("All checks passed");
7494
8004
  else
7495
8005
  line(
7496
- import_picocolors17.default.red("Some checks failed \u2014 fix the errors above and re-run ") + import_picocolors17.default.bold("workser verify") + import_picocolors17.default.red(".")
8006
+ import_picocolors18.default.red("Some checks failed \u2014 fix the errors above and re-run ") + import_picocolors18.default.bold("workser verify") + import_picocolors18.default.red(".")
7497
8007
  );
7498
8008
  }
7499
8009
 
7500
8010
  // src/commands/checkpoint.ts
7501
- var import_picocolors18 = __toESM(require_picocolors(), 1);
8011
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
7502
8012
  function registerCheckpoint(program3) {
7503
8013
  program3.command("checkpoint [label]").description(
7504
8014
  "Save the current state of this folder so you can come back to it"
@@ -7515,8 +8025,8 @@ function registerCheckpoint(program3) {
7515
8025
  ok(res, () => {
7516
8026
  const p = res?.point;
7517
8027
  success(`Saved a checkpoint${p?.label ? `: ${p.label}` : ""}`);
7518
- if (p?.ref) line(import_picocolors18.default.dim(` ${p.ref.slice(0, 7)}`));
7519
- line(import_picocolors18.default.dim(" Come back to it with `workser restore`."));
8028
+ if (p?.ref) line(import_picocolors19.default.dim(` ${p.ref.slice(0, 7)}`));
8029
+ line(import_picocolors19.default.dim(" Come back to it with `workser restore`."));
7520
8030
  });
7521
8031
  })
7522
8032
  );
@@ -7542,12 +8052,12 @@ function registerCheckpoint(program3) {
7542
8052
  );
7543
8053
  if (res?.filesChanged) {
7544
8054
  line(
7545
- import_picocolors18.default.dim(
8055
+ import_picocolors19.default.dim(
7546
8056
  ` ${res.filesChanged} file${res.filesChanged === 1 ? "" : "s"} changed`
7547
8057
  )
7548
8058
  );
7549
8059
  }
7550
- line(import_picocolors18.default.dim(" This is reversible: `workser restore` again."));
8060
+ line(import_picocolors19.default.dim(" This is reversible: `workser restore` again."));
7551
8061
  });
7552
8062
  })
7553
8063
  );
@@ -7564,25 +8074,25 @@ function registerCheckpoint(program3) {
7564
8074
  function printPoints(points) {
7565
8075
  if (!points.length) {
7566
8076
  info("No checkpoints yet for this folder.");
7567
- line(import_picocolors18.default.dim(" Take one with `workser checkpoint`."));
8077
+ line(import_picocolors19.default.dim(" Take one with `workser checkpoint`."));
7568
8078
  return;
7569
8079
  }
7570
- line(import_picocolors18.default.bold("Checkpoints"));
8080
+ line(import_picocolors19.default.bold("Checkpoints"));
7571
8081
  for (const p of points) {
7572
8082
  const when = p.at ? new Date(p.at).toLocaleString() : "";
7573
8083
  line(
7574
- ` ${import_picocolors18.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors18.default.dim(` ${when}`) : ""}`
8084
+ ` ${import_picocolors19.default.dim(p.ref.slice(0, 7))} ${p.label}${when ? import_picocolors19.default.dim(` ${when}`) : ""}`
7575
8085
  );
7576
8086
  }
7577
8087
  line(
7578
- import_picocolors18.default.dim(
8088
+ import_picocolors19.default.dim(
7579
8089
  "\nGo back with `workser restore <ref>`, or just `workser restore` for the newest."
7580
8090
  )
7581
8091
  );
7582
8092
  }
7583
8093
 
7584
8094
  // src/commands/sync.ts
7585
- var import_picocolors19 = __toESM(require_picocolors(), 1);
8095
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
7586
8096
  function registerSync(program3) {
7587
8097
  program3.command("sync").description(
7588
8098
  "Reconcile this folder with the copy Workser holds (pull, then push)"
@@ -7609,7 +8119,7 @@ function registerSync(program3) {
7609
8119
  warn(res?.message ?? "Couldn't sync this folder.");
7610
8120
  if (res?.state === "diverged") {
7611
8121
  line(
7612
- import_picocolors19.default.dim(
8122
+ import_picocolors20.default.dim(
7613
8123
  " This folder and Workser's copy have both changed. Open Workser to resolve it."
7614
8124
  )
7615
8125
  );
@@ -7621,7 +8131,7 @@ function registerSync(program3) {
7621
8131
  return;
7622
8132
  }
7623
8133
  success("Synced");
7624
- if (res?.ref) line(import_picocolors19.default.dim(` ${String(res.ref).slice(0, 7)}`));
8134
+ if (res?.ref) line(import_picocolors20.default.dim(` ${String(res.ref).slice(0, 7)}`));
7625
8135
  });
7626
8136
  if (refused) process.exitCode = 1;
7627
8137
  })
@@ -7629,7 +8139,7 @@ function registerSync(program3) {
7629
8139
  }
7630
8140
 
7631
8141
  // src/commands/workflow.ts
7632
- var import_picocolors20 = __toESM(require_picocolors(), 1);
8142
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
7633
8143
  function registerWorkflow(program3) {
7634
8144
  const wf = program3.command("workflow").description("Create, run, and inspect workflow automations for the project");
7635
8145
  wf.command("list").description("List the project's workflows").action(
@@ -7637,10 +8147,10 @@ function registerWorkflow(program3) {
7637
8147
  const projectId = requireProject(ctx);
7638
8148
  const items = await api(ctx, `/v1/projects/${projectId}/workflows`);
7639
8149
  ok(items, () => {
7640
- if (!items?.length) return line(import_picocolors20.default.dim("No workflows yet. `workser workflow create`."));
8150
+ if (!items?.length) return line(import_picocolors21.default.dim("No workflows yet. `workser workflow create`."));
7641
8151
  for (const w of items) {
7642
- const status = w.is_active ? import_picocolors20.default.green("active") : import_picocolors20.default.dim("inactive");
7643
- line(`${w.id} ${import_picocolors20.default.bold(w.name ?? "Untitled")} ${status}`);
8152
+ const status = w.is_active ? import_picocolors21.default.green("active") : import_picocolors21.default.dim("inactive");
8153
+ line(`${w.id} ${import_picocolors21.default.bold(w.name ?? "Untitled")} ${status}`);
7644
8154
  }
7645
8155
  });
7646
8156
  })
@@ -7652,7 +8162,7 @@ function registerWorkflow(program3) {
7652
8162
  const res = await api(ctx, `/v1/projects/${projectId}/workflows`, {
7653
8163
  body: { name: args[0], ...extra }
7654
8164
  });
7655
- ok(res, () => line(`Created workflow ${import_picocolors20.default.bold(res.id)}.`));
8165
+ ok(res, () => line(`Created workflow ${import_picocolors21.default.bold(res.id)}.`));
7656
8166
  })
7657
8167
  );
7658
8168
  wf.command("get <id>").description("Show a workflow's full definition").action(
@@ -7687,8 +8197,8 @@ function registerWorkflow(program3) {
7687
8197
  action(async ({ ctx, args }) => {
7688
8198
  const items = await api(ctx, `/v1/workflows/${args[0]}/executions`);
7689
8199
  ok(items, () => {
7690
- if (!items?.length) return line(import_picocolors20.default.dim("No runs yet."));
7691
- for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors20.default.dim(e.started_at ?? "")}`);
8200
+ if (!items?.length) return line(import_picocolors21.default.dim("No runs yet."));
8201
+ for (const e of items) line(`${e.id} ${e.status ?? ""} ${import_picocolors21.default.dim(e.started_at ?? "")}`);
7692
8202
  });
7693
8203
  })
7694
8204
  );
@@ -7696,15 +8206,15 @@ function registerWorkflow(program3) {
7696
8206
  action(async ({ ctx, args }) => {
7697
8207
  const items = await api(ctx, `/v1/node-types`, { query: { q: args[0] } });
7698
8208
  ok(items, () => {
7699
- if (!items?.length) return line(import_picocolors20.default.dim("No matching node types."));
7700
- for (const n of items) line(`${n.name ?? n.type} ${import_picocolors20.default.dim(n.category ?? "")}`);
8209
+ if (!items?.length) return line(import_picocolors21.default.dim("No matching node types."));
8210
+ for (const n of items) line(`${n.name ?? n.type} ${import_picocolors21.default.dim(n.category ?? "")}`);
7701
8211
  });
7702
8212
  })
7703
8213
  );
7704
8214
  }
7705
8215
 
7706
8216
  // src/commands/connection.ts
7707
- var import_picocolors21 = __toESM(require_picocolors(), 1);
8217
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
7708
8218
  function registerConnection(program3) {
7709
8219
  const connection = program3.command("connection").description("Connect and use third-party app connections (Gmail, Slack, Stripe, ...)");
7710
8220
  connection.command("list").description("List connectable toolkits and this project's existing connections").option("--toolkit <slug>", "filter connections to one toolkit").action(
@@ -7717,8 +8227,8 @@ function registerConnection(program3) {
7717
8227
  ok({ catalog, connections }, () => {
7718
8228
  const connected = new Set((connections ?? []).map((c) => c.toolkit ?? c.composio_app));
7719
8229
  for (const t of catalog ?? []) {
7720
- const status = connected.has(t.slug) ? import_picocolors21.default.green("connected") : import_picocolors21.default.dim("not connected");
7721
- line(`${t.slug} ${import_picocolors21.default.bold(t.name ?? t.slug)} ${status}`);
8230
+ const status = connected.has(t.slug) ? import_picocolors22.default.green("connected") : import_picocolors22.default.dim("not connected");
8231
+ line(`${t.slug} ${import_picocolors22.default.bold(t.name ?? t.slug)} ${status}`);
7722
8232
  }
7723
8233
  });
7724
8234
  })
@@ -7730,9 +8240,9 @@ function registerConnection(program3) {
7730
8240
  query: { q: args[0], toolkit: opts.toolkit, limit: opts.limit }
7731
8241
  });
7732
8242
  ok(items, () => {
7733
- if (!items?.length) return line(import_picocolors21.default.dim("No matching actions."));
8243
+ if (!items?.length) return line(import_picocolors22.default.dim("No matching actions."));
7734
8244
  for (const t of items) {
7735
- line(`${t.slug} ${import_picocolors21.default.dim(`[${t.toolkit}]`)} ${t.description ?? ""}`);
8245
+ line(`${t.slug} ${import_picocolors22.default.dim(`[${t.toolkit}]`)} ${t.description ?? ""}`);
7736
8246
  }
7737
8247
  });
7738
8248
  })
@@ -7749,7 +8259,7 @@ function registerConnection(program3) {
7749
8259
  });
7750
8260
  ok(
7751
8261
  res,
7752
- () => res.oauth_url ? line(`Open this URL to finish connecting: ${import_picocolors21.default.underline(res.oauth_url)}`) : line(`Connection ${res.connection_id} is ${res.status}.`)
8262
+ () => res.oauth_url ? line(`Open this URL to finish connecting: ${import_picocolors22.default.underline(res.oauth_url)}`) : line(`Connection ${res.connection_id} is ${res.status}.`)
7753
8263
  );
7754
8264
  })
7755
8265
  );
@@ -7767,8 +8277,8 @@ function registerConnection(program3) {
7767
8277
  const projectId = requireProject(ctx);
7768
8278
  const items = await api(ctx, `/v1/projects/${projectId}/integrations/${args[0]}/tools`);
7769
8279
  ok(items, () => {
7770
- if (!items?.length) return line(import_picocolors21.default.dim("No tools found."));
7771
- for (const t of items) line(`${t.slug} ${import_picocolors21.default.dim(t.description ?? "")}`);
8280
+ if (!items?.length) return line(import_picocolors22.default.dim("No tools found."));
8281
+ for (const t of items) line(`${t.slug} ${import_picocolors22.default.dim(t.description ?? "")}`);
7772
8282
  });
7773
8283
  })
7774
8284
  );
@@ -7784,7 +8294,7 @@ function registerConnection(program3) {
7784
8294
  }
7785
8295
 
7786
8296
  // src/commands/tool.ts
7787
- var import_picocolors22 = __toESM(require_picocolors(), 1);
8297
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
7788
8298
  function registerTool(program3) {
7789
8299
  const tool = program3.command("tool").description(
7790
8300
  "Computer-use tools: filesystem, shell, screenshot, input control, clipboard, notifications, basic browser"
@@ -7793,7 +8303,7 @@ function registerTool(program3) {
7793
8303
  action(async ({ ctx }) => {
7794
8304
  const tools = await api(ctx, "/v1/tool/list");
7795
8305
  ok(tools, () => {
7796
- if (!tools?.length) return line(import_picocolors22.default.dim("No tools available."));
8306
+ if (!tools?.length) return line(import_picocolors23.default.dim("No tools available."));
7797
8307
  const byCategory = /* @__PURE__ */ new Map();
7798
8308
  for (const t of tools) {
7799
8309
  const list = byCategory.get(t.category) ?? [];
@@ -7801,9 +8311,9 @@ function registerTool(program3) {
7801
8311
  byCategory.set(t.category, list);
7802
8312
  }
7803
8313
  for (const [category, items] of byCategory) {
7804
- line(import_picocolors22.default.bold(category) + ":");
8314
+ line(import_picocolors23.default.bold(category) + ":");
7805
8315
  for (const t of items) {
7806
- line(` ${t.name} ${import_picocolors22.default.dim(t.description ?? "")}`);
8316
+ line(` ${t.name} ${import_picocolors23.default.dim(t.description ?? "")}`);
7807
8317
  }
7808
8318
  }
7809
8319
  });
@@ -7821,7 +8331,7 @@ function registerTool(program3) {
7821
8331
  }
7822
8332
 
7823
8333
  // src/commands/memory.ts
7824
- var import_picocolors23 = __toESM(require_picocolors(), 1);
8334
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
7825
8335
  function registerMemory(program3) {
7826
8336
  const memory = program3.command("memory").description("Durable, cross-conversation project memory (shared with cloud agents on the same project)");
7827
8337
  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(
@@ -7845,9 +8355,9 @@ function registerMemory(program3) {
7845
8355
  });
7846
8356
  ok(res, () => {
7847
8357
  const results = res?.results ?? res ?? [];
7848
- if (!results?.length) return line(import_picocolors23.default.dim("No matching memories."));
8358
+ if (!results?.length) return line(import_picocolors24.default.dim("No matching memories."));
7849
8359
  for (const r of results) {
7850
- line(`${import_picocolors23.default.dim(r.id ?? "?")} ${memoryText(r)}`);
8360
+ line(`${import_picocolors24.default.dim(r.id ?? "?")} ${memoryText(r)}`);
7851
8361
  }
7852
8362
  });
7853
8363
  })
@@ -7867,11 +8377,11 @@ function memoryText(r) {
7867
8377
  if (typeof text === "string" && text.trim()) return text;
7868
8378
  const title = r?.documents?.[0]?.title;
7869
8379
  if (typeof title === "string" && title.trim()) return title;
7870
- return import_picocolors23.default.dim("(no readable text on this row)");
8380
+ return import_picocolors24.default.dim("(no readable text on this row)");
7871
8381
  }
7872
8382
 
7873
8383
  // src/commands/note.ts
7874
- var import_picocolors24 = __toESM(require_picocolors(), 1);
8384
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
7875
8385
  function registerNote(program3) {
7876
8386
  program3.command("note <text>").description("Leave a fact the rest of the team will need").addHelpText(
7877
8387
  "after",
@@ -7908,14 +8418,14 @@ function registerNote(program3) {
7908
8418
  }
7909
8419
  ok(res, () => {
7910
8420
  success("Noted for the team.");
7911
- line(import_picocolors24.default.dim(` ${text}`));
8421
+ line(import_picocolors25.default.dim(` ${text}`));
7912
8422
  });
7913
8423
  })
7914
8424
  );
7915
8425
  }
7916
8426
 
7917
8427
  // src/commands/business.ts
7918
- var import_picocolors25 = __toESM(require_picocolors(), 1);
8428
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
7919
8429
  var RESOURCE_PATHS = {
7920
8430
  "business-config": "business-config",
7921
8431
  "business-settings": "business-settings",
@@ -7975,7 +8485,7 @@ function registerBusiness(program3) {
7975
8485
  const projectId = requireProject(ctx);
7976
8486
  const [resource] = args;
7977
8487
  const res = await api(ctx, businessPath(projectId, resource), { body: JSON.parse(opts.body) });
7978
- ok(res, () => line(`Created ${resource} ${import_picocolors25.default.bold(res?.id ?? "")}.`));
8488
+ ok(res, () => line(`Created ${resource} ${import_picocolors26.default.bold(res?.id ?? "")}.`));
7979
8489
  })
7980
8490
  );
7981
8491
  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(
@@ -8017,7 +8527,7 @@ function businessPath(projectId, resource, subpath) {
8017
8527
  }
8018
8528
 
8019
8529
  // src/commands/artifact.ts
8020
- var import_picocolors26 = __toESM(require_picocolors(), 1);
8530
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
8021
8531
  import { existsSync as existsSync2, statSync } from "fs";
8022
8532
  import { resolve as resolve3, basename as basename3 } from "path";
8023
8533
  var KINDS = [
@@ -8114,7 +8624,7 @@ function registerArtifact(program3) {
8114
8624
  ok(
8115
8625
  res,
8116
8626
  () => success(
8117
- `Recorded ${import_picocolors26.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors26.default.dim(` (${res.kind})`) : ""}`
8627
+ `Recorded ${import_picocolors27.default.bold(res?.title ?? "artifact")}${res?.kind ? import_picocolors27.default.dim(` (${res.kind})`) : ""}`
8118
8628
  )
8119
8629
  );
8120
8630
  })
@@ -8146,13 +8656,13 @@ function registerArtifact(program3) {
8146
8656
  artifact.command("run").description("Show the task/conversation this agent run is attached to").action(
8147
8657
  action(async ({ ctx }) => {
8148
8658
  const res = await api(ctx, `/v1/runs/${runTarget(ctx)}`);
8149
- ok(res, () => printRun(res));
8659
+ ok(res, () => printRun2(res));
8150
8660
  })
8151
8661
  );
8152
8662
  }
8153
8663
  function printArtifacts(rows) {
8154
8664
  if (!rows.length) {
8155
- line(import_picocolors26.default.dim("Nothing produced yet."));
8665
+ line(import_picocolors27.default.dim("Nothing produced yet."));
8156
8666
  return;
8157
8667
  }
8158
8668
  const byStep = /* @__PURE__ */ new Map();
@@ -8162,26 +8672,26 @@ function printArtifacts(rows) {
8162
8672
  byStep.set(r.subtask_id, list);
8163
8673
  }
8164
8674
  for (const [stepId, items] of byStep) {
8165
- line(import_picocolors26.default.bold(`step ${stepId}`));
8675
+ line(import_picocolors27.default.bold(`step ${stepId}`));
8166
8676
  for (const a of items) {
8167
- const flag = a.promoted_at ? import_picocolors26.default.green(" *") : " ";
8677
+ const flag = a.promoted_at ? import_picocolors27.default.green(" *") : " ";
8168
8678
  const where = a.local_path || a.cloud_url || "";
8169
8679
  line(
8170
- `${flag} ${import_picocolors26.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors26.default.dim(` ${where}`) : "")
8680
+ `${flag} ${import_picocolors27.default.dim(`[${a.kind}]`)} ${a.title ?? "(untitled)"}` + (where ? import_picocolors27.default.dim(` ${where}`) : "")
8171
8681
  );
8172
- if (a.description) line(import_picocolors26.default.dim(` ${a.description}`));
8682
+ if (a.description) line(import_picocolors27.default.dim(` ${a.description}`));
8173
8683
  }
8174
8684
  line("");
8175
8685
  }
8176
- line(import_picocolors26.default.dim("* = handed over as a deliverable; the rest is working material."));
8686
+ line(import_picocolors27.default.dim("* = handed over as a deliverable; the rest is working material."));
8177
8687
  }
8178
- function printRun(run) {
8688
+ function printRun2(run) {
8179
8689
  if (!run) return;
8180
- line(` run ${import_picocolors26.default.bold(run.runId)}`);
8690
+ line(` run ${import_picocolors27.default.bold(run.runId)}`);
8181
8691
  if (run.taskId) line(` task ${run.taskId}`);
8182
8692
  if (run.conversationId) line(` chat ${run.conversationId}`);
8183
8693
  if (run.projectId) line(` project ${run.projectId}`);
8184
- if (run.cwd) line(` folder ${import_picocolors26.default.dim(run.cwd)}`);
8694
+ if (run.cwd) line(` folder ${import_picocolors27.default.dim(run.cwd)}`);
8185
8695
  }
8186
8696
 
8187
8697
  // src/commands/image.ts
@@ -8259,7 +8769,7 @@ function registerImage(program3) {
8259
8769
  );
8260
8770
  const res = await api(
8261
8771
  ctx,
8262
- `/projects/${projectId}/images/generate`,
8772
+ `/v1/projects/${projectId}/images/generate`,
8263
8773
  {
8264
8774
  method: "POST",
8265
8775
  body: {
@@ -8299,7 +8809,7 @@ function registerImage(program3) {
8299
8809
  action(async ({ ctx, opts, args }) => {
8300
8810
  const projectId = requireProject(ctx);
8301
8811
  const source = await resolveMediaSource({ url: opts.url, file: opts.file });
8302
- const res = await api(ctx, `/projects/${projectId}/images/understand`, {
8812
+ const res = await api(ctx, `/v1/projects/${projectId}/images/understand`, {
8303
8813
  method: "POST",
8304
8814
  body: { source, query: args[0], task: opts.task }
8305
8815
  });
@@ -8332,7 +8842,7 @@ function registerVideo(program3) {
8332
8842
  action(async ({ ctx, opts, args }) => {
8333
8843
  const projectId = requireProject(ctx);
8334
8844
  const source = await resolveMediaSource({ url: opts.url, file: opts.file });
8335
- const res = await api(ctx, `/projects/${projectId}/video/understand`, {
8845
+ const res = await api(ctx, `/v1/projects/${projectId}/video/understand`, {
8336
8846
  method: "POST",
8337
8847
  body: { source, query: args[0], task: opts.task }
8338
8848
  });
@@ -8352,7 +8862,7 @@ function registerAudio(program3) {
8352
8862
  action(async ({ ctx, opts, args }) => {
8353
8863
  const projectId = requireProject(ctx);
8354
8864
  const source = await resolveMediaSource({ url: opts.url, file: opts.file });
8355
- const res = await api(ctx, `/projects/${projectId}/audio/understand`, {
8865
+ const res = await api(ctx, `/v1/projects/${projectId}/audio/understand`, {
8356
8866
  method: "POST",
8357
8867
  body: { source, query: args[0], task: opts.task }
8358
8868
  });
@@ -8362,7 +8872,7 @@ function registerAudio(program3) {
8362
8872
  }
8363
8873
 
8364
8874
  // src/commands/ask.ts
8365
- var import_picocolors27 = __toESM(require_picocolors(), 1);
8875
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
8366
8876
  var TYPES = [
8367
8877
  "input",
8368
8878
  "choice",
@@ -8440,7 +8950,7 @@ function registerAsk(program3) {
8440
8950
  code: "bad_request"
8441
8951
  });
8442
8952
  }
8443
- info(import_picocolors27.default.dim("Waiting for the user to answer\u2026"));
8953
+ info(import_picocolors28.default.dim("Waiting for the user to answer\u2026"));
8444
8954
  const res = await api(ctx, `/v1/runs/${runTarget(ctx)}/ask`, {
8445
8955
  body: {
8446
8956
  type,
@@ -8468,12 +8978,12 @@ function deriveTitle(message) {
8468
8978
  function printAnswer(res) {
8469
8979
  if (!res) return;
8470
8980
  if (res.status === "answered") {
8471
- line(` ${import_picocolors27.default.green("answered")}`);
8981
+ line(` ${import_picocolors28.default.green("answered")}`);
8472
8982
  const value = extract(res.response);
8473
8983
  if (value) line(` ${value}`);
8474
8984
  return;
8475
8985
  }
8476
- line(` ${import_picocolors27.default.yellow(res.status)} ${import_picocolors27.default.dim(res.reason ?? "")}`);
8986
+ line(` ${import_picocolors28.default.yellow(res.status)} ${import_picocolors28.default.dim(res.reason ?? "")}`);
8477
8987
  }
8478
8988
  function extract(response) {
8479
8989
  if (response == null) return "";
@@ -8492,7 +9002,7 @@ function extract(response) {
8492
9002
  }
8493
9003
 
8494
9004
  // src/commands/search.ts
8495
- var import_picocolors28 = __toESM(require_picocolors(), 1);
9005
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
8496
9006
  function registerSearch(program3) {
8497
9007
  program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
8498
9008
  action(async ({ ctx, args, opts }) => {
@@ -8505,9 +9015,9 @@ function registerSearch(program3) {
8505
9015
  line("");
8506
9016
  }
8507
9017
  const results = res?.results ?? [];
8508
- if (!results.length) return line(import_picocolors28.default.dim("No results."));
9018
+ if (!results.length) return line(import_picocolors29.default.dim("No results."));
8509
9019
  for (const r of results) {
8510
- line(`${r.title || import_picocolors28.default.dim("(untitled)")} ${import_picocolors28.default.dim(r.url)}`);
9020
+ line(`${r.title || import_picocolors29.default.dim("(untitled)")} ${import_picocolors29.default.dim(r.url)}`);
8511
9021
  }
8512
9022
  });
8513
9023
  })
@@ -8515,7 +9025,7 @@ function registerSearch(program3) {
8515
9025
  }
8516
9026
 
8517
9027
  // src/commands/board.ts
8518
- var import_picocolors29 = __toESM(require_picocolors(), 1);
9028
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
8519
9029
 
8520
9030
  // src/commands/record-step.ts
8521
9031
  async function recordEntityStep(ctx, opts) {
@@ -8554,7 +9064,7 @@ function registerBoard(program3) {
8554
9064
  }
8555
9065
  ok(rows, () => {
8556
9066
  if (!rows.length) {
8557
- line(import_picocolors29.default.dim("No cards on the Board yet."));
9067
+ line(import_picocolors30.default.dim("No cards on the Board yet."));
8558
9068
  return;
8559
9069
  }
8560
9070
  for (const r of rows) line(formatRow(r));
@@ -8569,7 +9079,7 @@ function registerBoard(program3) {
8569
9079
  `/v1/projects/${projectId}/work-items/${args[0]}`
8570
9080
  );
8571
9081
  ok(row, () => {
8572
- line(`${import_picocolors29.default.bold(row.title)} ${import_picocolors29.default.dim(row.id)}`);
9082
+ line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.id)}`);
8573
9083
  line(`${statusTag(row.status)} priority ${row.priority}`);
8574
9084
  if (row.ownerHuman) line(`owner: ${row.ownerHuman}`);
8575
9085
  if (row.labels?.length) line(`labels: ${row.labels.join(", ")}`);
@@ -8610,7 +9120,7 @@ ${row.description}`);
8610
9120
  refId: row?.id,
8611
9121
  output: { workItem: row }
8612
9122
  });
8613
- ok(row, () => line(`Created work item ${import_picocolors29.default.bold(row?.id ?? "")} \u2014 ${title}`));
9123
+ ok(row, () => line(`Created work item ${import_picocolors30.default.bold(row?.id ?? "")} \u2014 ${title}`));
8614
9124
  })
8615
9125
  );
8616
9126
  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(
@@ -8644,7 +9154,7 @@ ${row.description}`);
8644
9154
  );
8645
9155
  }
8646
9156
  const row = await patchItem(ctx, projectId, String(args[0]), body);
8647
- ok(row, () => line(`Updated ${import_picocolors29.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
9157
+ ok(row, () => line(`Updated ${import_picocolors30.default.bold(row.id)} \u2014 ${row.title} ${statusTag(row.status)}`));
8648
9158
  })
8649
9159
  );
8650
9160
  board.command("move <id> <status>").description(
@@ -8655,14 +9165,14 @@ ${row.description}`);
8655
9165
  const status = String(args[1]);
8656
9166
  assertStatus(status);
8657
9167
  const row = await patchItem(ctx, projectId, String(args[0]), { status });
8658
- ok(row, () => line(`Moved ${import_picocolors29.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
9168
+ ok(row, () => line(`Moved ${import_picocolors30.default.bold(row.title)} \u2192 ${statusTag(row.status)}`));
8659
9169
  })
8660
9170
  );
8661
9171
  board.command("close <id>").description("Shorthand for `board move <id> done`").action(
8662
9172
  action(async ({ ctx, args }) => {
8663
9173
  const projectId = requireProject(ctx);
8664
9174
  const row = await patchItem(ctx, projectId, String(args[0]), { status: "done" });
8665
- ok(row, () => line(`Closed ${import_picocolors29.default.bold(row.title)} ${statusTag(row.status)}`));
9175
+ ok(row, () => line(`Closed ${import_picocolors30.default.bold(row.title)} ${statusTag(row.status)}`));
8666
9176
  })
8667
9177
  );
8668
9178
  }
@@ -8695,23 +9205,23 @@ function assertPriority(value) {
8695
9205
  }
8696
9206
  }
8697
9207
  function formatRow(r) {
8698
- const labels = r.labels?.length ? import_picocolors29.default.dim(` [${r.labels.join(", ")}]`) : "";
8699
- const owner = r.ownerHuman ? import_picocolors29.default.dim(` @${r.ownerHuman}`) : "";
8700
- return `${import_picocolors29.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
9208
+ const labels = r.labels?.length ? import_picocolors30.default.dim(` [${r.labels.join(", ")}]`) : "";
9209
+ const owner = r.ownerHuman ? import_picocolors30.default.dim(` @${r.ownerHuman}`) : "";
9210
+ return `${import_picocolors30.default.dim(r.id)} ${statusTag(r.status)} ${r.title}${labels}${owner}`;
8701
9211
  }
8702
9212
  function statusTag(status) {
8703
9213
  const label = status.padEnd(11);
8704
- if (status === "done") return import_picocolors29.default.green(label);
8705
- if (status === "in-progress") return import_picocolors29.default.yellow(label);
8706
- if (status === "in-review") return import_picocolors29.default.cyan(label);
8707
- return import_picocolors29.default.dim(label);
9214
+ if (status === "done") return import_picocolors30.default.green(label);
9215
+ if (status === "in-progress") return import_picocolors30.default.yellow(label);
9216
+ if (status === "in-review") return import_picocolors30.default.cyan(label);
9217
+ return import_picocolors30.default.dim(label);
8708
9218
  }
8709
9219
  function collect2(value, previous) {
8710
9220
  return [...previous, value];
8711
9221
  }
8712
9222
 
8713
9223
  // src/commands/task.ts
8714
- var import_picocolors30 = __toESM(require_picocolors(), 1);
9224
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
8715
9225
 
8716
9226
  // src/subtask-attachments.ts
8717
9227
  var MAX_REF_NOTE = 500;
@@ -8773,6 +9283,7 @@ var STATUSES2 = [
8773
9283
  ];
8774
9284
  var PRIORITIES2 = ["urgent", "high", "normal", "low"];
8775
9285
  var SIZES = ["small", "standard"];
9286
+ var ESTIMATES = ["10m", "30m", "45m", "1h", "1h+"];
8776
9287
  var ROLES = [
8777
9288
  "pm",
8778
9289
  "architect",
@@ -8825,7 +9336,7 @@ function registerTask(program3) {
8825
9336
  }) ?? [];
8826
9337
  ok(rows, () => {
8827
9338
  if (!rows.length) {
8828
- line(import_picocolors30.default.dim("No tasks on the board yet."));
9339
+ line(import_picocolors31.default.dim("No tasks on the board yet."));
8829
9340
  return;
8830
9341
  }
8831
9342
  for (const r of rows) line(formatRow2(r));
@@ -8855,6 +9366,9 @@ function registerTask(program3) {
8855
9366
  ).option("--priority <value>", `how urgent (${PRIORITIES2.join(" | ")})`).option(
8856
9367
  "--size <value>",
8857
9368
  `small lets the channel start it without asking; standard asks (${SIZES.join(" | ")})`
9369
+ ).option(
9370
+ "--estimate <value>",
9371
+ `roughly how long this should take, shown on the plan (${ESTIMATES.join(" | ")})`
8858
9372
  ).option(
8859
9373
  "--status <value>",
8860
9374
  `start it somewhere other than 'todo' \u2014 use 'backlog' for a later phase's work (${STATUSES2.join(" | ")})`
@@ -8865,6 +9379,8 @@ function registerTask(program3) {
8865
9379
  if (opts.priority !== void 0)
8866
9380
  assertOneOf("--priority", opts.priority, PRIORITIES2);
8867
9381
  if (opts.size !== void 0) assertOneOf("--size", opts.size, SIZES);
9382
+ if (opts.estimate !== void 0)
9383
+ assertOneOf("--estimate", opts.estimate, ESTIMATES);
8868
9384
  if (opts.status !== void 0)
8869
9385
  assertOneOf("--status", opts.status, STATUSES2);
8870
9386
  const hasChannelOrigin = Boolean(
@@ -8882,6 +9398,7 @@ function registerTask(program3) {
8882
9398
  phase: opts.phase,
8883
9399
  priority: opts.priority,
8884
9400
  size: opts.size,
9401
+ estimate: opts.estimate,
8885
9402
  // `backlog` for a later phase's work: written down so the owner can
8886
9403
  // read the whole plan, with nothing for the runner to pick up.
8887
9404
  status: opts.status,
@@ -8934,10 +9451,10 @@ function registerTask(program3) {
8934
9451
  };
8935
9452
  ok(result, () => {
8936
9453
  line(
8937
- `${import_picocolors30.default.green("opened")} ${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.key ?? row.id)}`
9454
+ `${import_picocolors31.default.green("opened")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
8938
9455
  );
8939
9456
  if (channelMessage)
8940
- line(import_picocolors30.default.dim("posted to the channel as Project Manager"));
9457
+ line(import_picocolors31.default.dim("posted to the channel as Project Manager"));
8941
9458
  if (channelMessageError) {
8942
9459
  warn(
8943
9460
  `Task opened, but its Project Manager card could not be posted: ${channelMessageError.message}`
@@ -9016,12 +9533,12 @@ function registerTask(program3) {
9016
9533
  ) : null;
9017
9534
  ok(runner ?? row, () => {
9018
9535
  line(
9019
- `${import_picocolors30.default.green("added")} ${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.key ?? row.id)}`
9536
+ `${import_picocolors31.default.green("added")} ${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`
9020
9537
  );
9021
- if (row.role) line(import_picocolors30.default.dim(`role: ${row.role}`));
9538
+ if (row.role) line(import_picocolors31.default.dim(`role: ${row.role}`));
9022
9539
  if (runner) {
9023
9540
  line(
9024
- import_picocolors30.default.dim(
9541
+ import_picocolors31.default.dim(
9025
9542
  `runs on: ${[opts.agent, opts.model, opts.effort].filter(Boolean).join(" \xB7 ")}`
9026
9543
  )
9027
9544
  );
@@ -9039,7 +9556,7 @@ function registerTask(program3) {
9039
9556
  const rows = row.subtasks ?? [];
9040
9557
  ok(rows, () => {
9041
9558
  if (!rows.length) {
9042
- line(import_picocolors30.default.dim("No subtasks yet."));
9559
+ line(import_picocolors31.default.dim("No subtasks yet."));
9043
9560
  return;
9044
9561
  }
9045
9562
  rows.forEach((r, i) => line(formatSubtask(r, i + 1)));
@@ -9087,7 +9604,7 @@ function registerTask(program3) {
9087
9604
  }
9088
9605
  }
9089
9606
  );
9090
- ok(row, () => line(`${import_picocolors30.default.green("updated")} ${import_picocolors30.default.bold(row.title)}`));
9607
+ ok(row, () => line(`${import_picocolors31.default.green("updated")} ${import_picocolors31.default.bold(row.title)}`));
9091
9608
  })
9092
9609
  );
9093
9610
  subtask.command("remove <id>").description("Remove a subtask (only before the work starts)").action(
@@ -9095,7 +9612,7 @@ function registerTask(program3) {
9095
9612
  await api(ctx, `/v1/project-tasks/${encodeURIComponent(args[0])}`, {
9096
9613
  method: "DELETE"
9097
9614
  });
9098
- ok({ removed: args[0] }, () => line(import_picocolors30.default.green("removed")));
9615
+ ok({ removed: args[0] }, () => line(import_picocolors31.default.green("removed")));
9099
9616
  })
9100
9617
  );
9101
9618
  task.command("move <id> <status>").description(
@@ -9110,7 +9627,7 @@ function registerTask(program3) {
9110
9627
  );
9111
9628
  ok(
9112
9629
  row,
9113
- () => line(`${import_picocolors30.default.green("moved")} ${import_picocolors30.default.bold(row.title)} \u2192 ${args[1]}`)
9630
+ () => line(`${import_picocolors31.default.green("moved")} ${import_picocolors31.default.bold(row.title)} \u2192 ${args[1]}`)
9114
9631
  );
9115
9632
  })
9116
9633
  );
@@ -9126,18 +9643,18 @@ function registerTask(program3) {
9126
9643
  );
9127
9644
  ok(row, () => {
9128
9645
  if (!row?.queued) {
9129
- line(import_picocolors30.default.yellow("could not resume it \u2014 check the step id"));
9646
+ line(import_picocolors31.default.yellow("could not resume it \u2014 check the step id"));
9130
9647
  return;
9131
9648
  }
9132
9649
  const started = row.started ?? 0;
9133
9650
  if (started > 0) {
9134
9651
  line(
9135
- `${import_picocolors30.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
9652
+ `${import_picocolors31.default.green("resumed")} \u2014 ${started} step${started === 1 ? "" : "s"} started`
9136
9653
  );
9137
9654
  return;
9138
9655
  }
9139
9656
  line(
9140
- `${import_picocolors30.default.green("resumed")} \u2014 it is queued, but nothing started yet. Check the plan is approved and that no step is blocking the rest.`
9657
+ `${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.`
9141
9658
  );
9142
9659
  });
9143
9660
  })
@@ -9151,11 +9668,11 @@ function registerTask(program3) {
9151
9668
  );
9152
9669
  ok(row, () => {
9153
9670
  if (row?.reopened) {
9154
- line(`${import_picocolors30.default.green("sent back")} \u2014 it will be picked up again`);
9671
+ line(`${import_picocolors31.default.green("sent back")} \u2014 it will be picked up again`);
9155
9672
  return;
9156
9673
  }
9157
9674
  line(
9158
- import_picocolors30.default.yellow(
9675
+ import_picocolors31.default.yellow(
9159
9676
  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"
9160
9677
  )
9161
9678
  );
@@ -9172,7 +9689,7 @@ function registerTask(program3) {
9172
9689
  `/v1/project-tasks/${encodeURIComponent(id)}/dispatch-check`,
9173
9690
  { method: "POST" }
9174
9691
  );
9175
- ok(row, () => line(import_picocolors30.default.green("approved \u2014 you may start")));
9692
+ ok(row, () => line(import_picocolors31.default.green("approved \u2014 you may start")));
9176
9693
  })
9177
9694
  );
9178
9695
  task.command("start [id]").description(
@@ -9187,13 +9704,13 @@ function registerTask(program3) {
9187
9704
  const started = row?.started ?? null;
9188
9705
  if (started && started > 0) {
9189
9706
  line(
9190
- import_picocolors30.default.green(
9707
+ import_picocolors31.default.green(
9191
9708
  `started ${started} step${started === 1 ? "" : "s"} \u2014 they are running now`
9192
9709
  )
9193
9710
  );
9194
9711
  return;
9195
9712
  }
9196
- line(import_picocolors30.default.yellow(row?.note ?? "Nothing started."));
9713
+ line(import_picocolors31.default.yellow(row?.note ?? "Nothing started."));
9197
9714
  });
9198
9715
  })
9199
9716
  );
@@ -9208,7 +9725,7 @@ function registerTask(program3) {
9208
9725
  );
9209
9726
  ok({ awaiting: row2.approval_state === "awaiting", task: row2 }, () => {
9210
9727
  line(
9211
- row2.approval_state === "awaiting" ? import_picocolors30.default.yellow(
9728
+ row2.approval_state === "awaiting" ? import_picocolors31.default.yellow(
9212
9729
  "The plan is waiting on the owner. They see it in the task."
9213
9730
  ) : `Already ${row2.approval_state}.`
9214
9731
  );
@@ -9233,7 +9750,7 @@ function registerTask(program3) {
9233
9750
  );
9234
9751
  ok(
9235
9752
  row,
9236
- () => line(`${import_picocolors30.default.green(row.approval_state)} ${import_picocolors30.default.bold(row.title)}`)
9753
+ () => line(`${import_picocolors31.default.green(row.approval_state)} ${import_picocolors31.default.bold(row.title)}`)
9237
9754
  );
9238
9755
  })
9239
9756
  );
@@ -9249,7 +9766,7 @@ function registerTask(program3) {
9249
9766
  `/v1/project-tasks/${encodeURIComponent(id)}/move`,
9250
9767
  { body: { status: "ready" } }
9251
9768
  );
9252
- ok(row, () => line(`${import_picocolors30.default.green("ready")} ${import_picocolors30.default.bold(row.title)}`));
9769
+ ok(row, () => line(`${import_picocolors31.default.green("ready")} ${import_picocolors31.default.bold(row.title)}`));
9253
9770
  })
9254
9771
  );
9255
9772
  }
@@ -9294,24 +9811,24 @@ function assertOneOf(flag, value, allowed) {
9294
9811
  }
9295
9812
  }
9296
9813
  function formatRow2(r) {
9297
- const key = import_picocolors30.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
9298
- const steps = r.subtaskTotal ? import_picocolors30.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
9299
- const gate = r.approval_state === "awaiting" ? import_picocolors30.default.yellow(" awaiting approval") : "";
9814
+ const key = import_picocolors31.default.dim((r.key ?? r.id.slice(0, 8)).padEnd(10));
9815
+ const steps = r.subtaskTotal ? import_picocolors31.default.dim(` ${r.subtaskDone}/${r.subtaskTotal}`) : "";
9816
+ const gate = r.approval_state === "awaiting" ? import_picocolors31.default.yellow(" awaiting approval") : "";
9300
9817
  return `${key} ${statusTag2(r.status)} ${r.title}${steps}${gate}`;
9301
9818
  }
9302
9819
  function formatSubtask(r, index) {
9303
- const n = import_picocolors30.default.dim(String(index).padStart(2, "0"));
9304
- const role = r.role ? import_picocolors30.default.dim(` [${r.role}]`) : "";
9305
- const scope = r.scope_paths?.length ? import_picocolors30.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
9820
+ const n = import_picocolors31.default.dim(String(index).padStart(2, "0"));
9821
+ const role = r.role ? import_picocolors31.default.dim(` [${r.role}]`) : "";
9822
+ const scope = r.scope_paths?.length ? import_picocolors31.default.dim(` owns: ${r.scope_paths.join(", ")}`) : "";
9306
9823
  const head = `${n} ${statusTag2(r.status)} ${r.title}${role}${scope}`;
9307
9824
  const summary = (r.result_summary ?? "").trim();
9308
9825
  if (!summary) return head;
9309
9826
  const wrapped = summary.split("\n").map((l) => ` ${l}`).join("\n");
9310
9827
  return `${head}
9311
- ${import_picocolors30.default.dim(wrapped)}`;
9828
+ ${import_picocolors31.default.dim(wrapped)}`;
9312
9829
  }
9313
9830
  function printTask(row, goal) {
9314
- line(`${import_picocolors30.default.bold(row.title)} ${import_picocolors30.default.dim(row.key ?? row.id)}`);
9831
+ line(`${import_picocolors31.default.bold(row.title)} ${import_picocolors31.default.dim(row.key ?? row.id)}`);
9315
9832
  line(`${statusTag2(row.status)} approval: ${row.approval_state}`);
9316
9833
  if (row.summary) line(`
9317
9834
  ${row.summary}`);
@@ -9324,53 +9841,53 @@ touches: ${row.targets.map((t) => t.appName ?? t.ref ?? t.kind).join(", ")}`
9324
9841
  }
9325
9842
  if (row.subtasks?.length) {
9326
9843
  line(`
9327
- ${import_picocolors30.default.bold("subtasks")}`);
9844
+ ${import_picocolors31.default.bold("subtasks")}`);
9328
9845
  row.subtasks.forEach((s, i) => line(formatSubtask(s, i + 1)));
9329
9846
  line(
9330
- import_picocolors30.default.dim(
9847
+ import_picocolors31.default.dim(
9331
9848
  `
9332
9849
  Run \`workser artifact list\` to see what these subtasks produced,`
9333
9850
  )
9334
9851
  );
9335
9852
  line(
9336
- import_picocolors30.default.dim(
9853
+ import_picocolors31.default.dim(
9337
9854
  `or \`workser artifact list --step <id>\` for one subtask's output alone.`
9338
9855
  )
9339
9856
  );
9340
9857
  } else {
9341
- line(import_picocolors30.default.dim("\nNo subtasks yet."));
9858
+ line(import_picocolors31.default.dim("\nNo subtasks yet."));
9342
9859
  }
9343
9860
  }
9344
9861
  function printGoalContext(row, goal) {
9345
9862
  const mine = row.phase ?? null;
9346
9863
  line(`
9347
- ${import_picocolors30.default.bold("part of")}: ${goal.title}`);
9348
- if (goal.outcome) line(import_picocolors30.default.dim(`done means: ${goal.outcome}`));
9864
+ ${import_picocolors31.default.bold("part of")}: ${goal.title}`);
9865
+ if (goal.outcome) line(import_picocolors31.default.dim(`done means: ${goal.outcome}`));
9349
9866
  const progress = goal.progress ?? [];
9350
9867
  for (const p of progress) {
9351
9868
  const where = p.total === 0 ? "not started" : `${p.done} of ${p.total} done`;
9352
- const here = mine && p.name === mine ? import_picocolors30.default.cyan(" <- this task") : "";
9353
- const mark = p.state === "done" ? import_picocolors30.default.green("*") : import_picocolors30.default.dim("-");
9869
+ const here = mine && p.name === mine ? import_picocolors31.default.cyan(" <- this task") : "";
9870
+ const mark = p.state === "done" ? import_picocolors31.default.green("*") : import_picocolors31.default.dim("-");
9354
9871
  line(` ${mark} ${p.name} \u2014 ${where}${here}`);
9355
9872
  }
9356
9873
  const next = progress.find((p) => p.state !== "done");
9357
9874
  const running = progress.some((p) => p.state === "working");
9358
9875
  if (next && !running) {
9359
9876
  line(
9360
- import_picocolors30.default.dim(
9877
+ import_picocolors31.default.dim(
9361
9878
  `
9362
9879
  Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to them in a sentence rather than starting it unasked.`
9363
9880
  )
9364
9881
  );
9365
9882
  } else if (!next) {
9366
9883
  line(
9367
- import_picocolors30.default.dim(
9884
+ import_picocolors31.default.dim(
9368
9885
  "\nEvery part of this plan is done. Say so, and offer to wrap it up."
9369
9886
  )
9370
9887
  );
9371
9888
  }
9372
9889
  line(
9373
- import_picocolors30.default.dim(
9890
+ import_picocolors31.default.dim(
9374
9891
  "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."
9375
9892
  )
9376
9893
  );
@@ -9378,23 +9895,24 @@ Nothing is running. The next part waiting is "${next.name}" \u2014 offer it to t
9378
9895
  function statusTag2(status) {
9379
9896
  switch (status) {
9380
9897
  case "ready":
9381
- return import_picocolors30.default.green("[ready]");
9898
+ return import_picocolors31.default.green("[ready]");
9382
9899
  case "working":
9383
- return import_picocolors30.default.blue("[working]");
9900
+ return import_picocolors31.default.blue("[working]");
9384
9901
  case "checking":
9385
- return import_picocolors30.default.cyan("[checking]");
9902
+ return import_picocolors31.default.cyan("[checking]");
9386
9903
  case "accepted":
9387
- return import_picocolors30.default.green("[accepted]");
9904
+ return import_picocolors31.default.green("[accepted]");
9388
9905
  case "archived":
9389
- return import_picocolors30.default.dim("[archived]");
9906
+ return import_picocolors31.default.dim("[archived]");
9390
9907
  default:
9391
- return import_picocolors30.default.dim("[todo]");
9908
+ return import_picocolors31.default.dim("[todo]");
9392
9909
  }
9393
9910
  }
9394
9911
 
9395
9912
  // src/commands/goal.ts
9396
- var import_picocolors31 = __toESM(require_picocolors(), 1);
9913
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
9397
9914
  var STATUSES3 = ["proposed", "agreed", "working", "delivered", "abandoned"];
9915
+ var PACES = ["slice", "phase", "all"];
9398
9916
  function registerGoal(program3) {
9399
9917
  const goal = program3.command("goal").description("Business goals and the phases that deliver them");
9400
9918
  goal.command("list").description("What this project is working towards").action(
@@ -9405,7 +9923,7 @@ function registerGoal(program3) {
9405
9923
  }) ?? [];
9406
9924
  ok(rows, () => {
9407
9925
  if (!rows.length) {
9408
- line(import_picocolors31.default.dim("No goals yet \u2014 every task here stands on its own."));
9926
+ line(import_picocolors32.default.dim("No goals yet \u2014 every task here stands on its own."));
9409
9927
  return;
9410
9928
  }
9411
9929
  for (const g of rows) line(formatGoal(g));
@@ -9432,6 +9950,9 @@ function registerGoal(program3) {
9432
9950
  ).option(
9433
9951
  "--criteria <json>",
9434
9952
  `what "done" means per phase, in the OWNER's words: '{"Checkout":["A customer can pay by card and gets a receipt"]}'`
9953
+ ).option(
9954
+ "--pace <value>",
9955
+ `how much to do before checking in \u2014 slice (one task, then show), phase (the current phase, the default), all (${PACES.join(" | ")})`
9435
9956
  ).action(
9436
9957
  action(async ({ ctx, args, opts }) => {
9437
9958
  requireProject(ctx);
@@ -9464,11 +9985,17 @@ function registerGoal(program3) {
9464
9985
  );
9465
9986
  }
9466
9987
  }
9988
+ if (opts.pace !== void 0 && !PACES.includes(opts.pace)) {
9989
+ throw new WorkserError(
9990
+ `Unknown --pace "${opts.pace}". Use one of: ${PACES.join(", ")}.`
9991
+ );
9992
+ }
9467
9993
  const row = await api(ctx, "/v1/project-goals", {
9468
9994
  body: {
9469
9995
  projectId: ctx.projectId,
9470
9996
  title: args[0],
9471
9997
  outcome: opts.outcome,
9998
+ pace: opts.pace,
9472
9999
  phases: phases.map((name) => ({
9473
10000
  name,
9474
10001
  criteria: (criteria[name] ?? []).map((text, i) => ({
@@ -9482,10 +10009,10 @@ function registerGoal(program3) {
9482
10009
  }
9483
10010
  });
9484
10011
  ok(row, () => {
9485
- success(`Proposed ${import_picocolors31.default.bold(row?.title ?? "goal")}`);
10012
+ success(`Proposed ${import_picocolors32.default.bold(row?.title ?? "goal")}`);
9486
10013
  printGoal(row);
9487
10014
  line(
9488
- import_picocolors31.default.dim(
10015
+ import_picocolors32.default.dim(
9489
10016
  "\nNothing has been created yet. The owner agrees the shape first."
9490
10017
  )
9491
10018
  );
@@ -9514,7 +10041,7 @@ function registerGoal(program3) {
9514
10041
  ok(
9515
10042
  row,
9516
10043
  () => line(
9517
- row?.recorded ? met === true ? import_picocolors31.default.green("recorded \u2014 met") : met === false ? import_picocolors31.default.yellow("recorded \u2014 not met") : import_picocolors31.default.dim("recorded \u2014 back to unchecked") : import_picocolors31.default.yellow(
10044
+ 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(
9518
10045
  "couldn't record it \u2014 check the goal id, the phase name and the criterion id"
9519
10046
  )
9520
10047
  )
@@ -9557,18 +10084,23 @@ function registerGoal(program3) {
9557
10084
  }
9558
10085
  ok({ goalId, phase, filed }, () => {
9559
10086
  success(
9560
- `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors31.default.bold(phase)}`
10087
+ `Filed ${filed.length} ${filed.length === 1 ? "task" : "tasks"} under ${import_picocolors32.default.bold(phase)}`
9561
10088
  );
9562
10089
  });
9563
10090
  })
9564
10091
  );
9565
- goal.command("update <id>").description("Change the shape, or move the goal along").option("--title <text>", "the owner's words for what they want").option("--outcome <text>", "what 'done' means").option("--phase <name...>", "replace the ordered phase list").option("--status <value>", `one of: ${STATUSES3.join(" | ")}`).action(
10092
+ goal.command("update <id>").description("Change the shape, or move the goal along").option("--title <text>", "the owner's words for what they want").option("--outcome <text>", "what 'done' means").option("--phase <name...>", "replace the ordered phase list").option("--status <value>", `one of: ${STATUSES3.join(" | ")}`).option("--pace <value>", `how much to do before checking in (${PACES.join(" | ")})`).action(
9566
10093
  action(async ({ ctx, args, opts }) => {
9567
10094
  if (opts.status && !STATUSES3.includes(opts.status)) {
9568
10095
  throw new WorkserError(
9569
10096
  `Unknown status "${opts.status}". Use one of: ${STATUSES3.join(", ")}.`
9570
10097
  );
9571
10098
  }
10099
+ if (opts.pace && !PACES.includes(opts.pace)) {
10100
+ throw new WorkserError(
10101
+ `Unknown --pace "${opts.pace}". Use one of: ${PACES.join(", ")}.`
10102
+ );
10103
+ }
9572
10104
  const row = await api(
9573
10105
  ctx,
9574
10106
  `/v1/project-goals/${encodeURIComponent(String(args[0]))}`,
@@ -9578,7 +10110,8 @@ function registerGoal(program3) {
9578
10110
  title: opts.title,
9579
10111
  outcome: opts.outcome,
9580
10112
  phases: opts.phase,
9581
- status: opts.status
10113
+ status: opts.status,
10114
+ pace: opts.pace
9582
10115
  }
9583
10116
  }
9584
10117
  );
@@ -9589,42 +10122,43 @@ function registerGoal(program3) {
9589
10122
  function appScope(g) {
9590
10123
  const n = g.appIds?.length ?? 0;
9591
10124
  if (!n) return "";
9592
- return import_picocolors31.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
10125
+ return import_picocolors32.default.dim(` ${n} part${n === 1 ? "" : "s"} of the system`);
9593
10126
  }
9594
10127
  function formatGoal(g) {
9595
- const where = g.currentPhase && g.status !== "delivered" ? import_picocolors31.default.dim(` now: ${g.currentPhase}`) : "";
9596
- const count = g.taskTotal != null ? import_picocolors31.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
9597
- return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors31.default.dim(g.id)}`;
10128
+ const where = g.currentPhase && g.status !== "delivered" ? import_picocolors32.default.dim(` now: ${g.currentPhase}`) : "";
10129
+ const count = g.taskTotal != null ? import_picocolors32.default.dim(` ${g.taskDone}/${g.taskTotal} done`) : "";
10130
+ return `${statusTag3(g.status)} ${g.title}${appScope(g)}${where}${count} ${import_picocolors32.default.dim(g.id)}`;
9598
10131
  }
9599
10132
  function printGoal(g) {
9600
10133
  if (!g) return;
9601
- line(`${import_picocolors31.default.bold(g.title)} ${import_picocolors31.default.dim(g.id)}`);
10134
+ line(`${import_picocolors32.default.bold(g.title)} ${import_picocolors32.default.dim(g.id)}`);
9602
10135
  line(statusTag3(g.status));
9603
10136
  if (g.outcome) line(`
9604
10137
  done means: ${g.outcome}`);
10138
+ if (g.pace) line(import_picocolors32.default.dim(`pace: ${paceLine(g.pace)}`));
9605
10139
  const progress = g.progress ?? [];
9606
10140
  if (!progress.length) {
9607
- line(import_picocolors31.default.dim("\nNo phases agreed yet."));
10141
+ line(import_picocolors32.default.dim("\nNo phases agreed yet."));
9608
10142
  return;
9609
10143
  }
9610
10144
  line(`
9611
- ${import_picocolors31.default.bold("phases")}`);
10145
+ ${import_picocolors32.default.bold("phases")}`);
9612
10146
  for (const p of progress) {
9613
- const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors31.default.dim("nothing filed yet");
9614
- const mark = p.state === "done" ? import_picocolors31.default.green("done") : p.state === "working" ? import_picocolors31.default.blue("working") : import_picocolors31.default.dim("waiting");
10147
+ const bar2 = p.total > 0 ? `${p.done}/${p.total} done` : import_picocolors32.default.dim("nothing filed yet");
10148
+ const mark = p.state === "done" ? import_picocolors32.default.green("done") : p.state === "working" ? import_picocolors32.default.blue("working") : import_picocolors32.default.dim("waiting");
9615
10149
  line(
9616
- ` ${import_picocolors31.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors31.default.dim(bar2)}`
10150
+ ` ${import_picocolors32.default.dim(String(p.index).padStart(2, "0"))} ${p.name} ${mark} ${import_picocolors32.default.dim(bar2)}`
9617
10151
  );
9618
10152
  for (const c of p.criteria ?? []) {
9619
- const tick = c.met === true ? import_picocolors31.default.green("\u2713") : c.met === false ? import_picocolors31.default.red("\u2717") : import_picocolors31.default.dim("\xB7");
9620
- line(` ${tick} ${c.text} ${import_picocolors31.default.dim(c.id)}`);
9621
- if (c.note) line(import_picocolors31.default.dim(` ${c.note}`));
10153
+ const tick = c.met === true ? import_picocolors32.default.green("\u2713") : c.met === false ? import_picocolors32.default.red("\u2717") : import_picocolors32.default.dim("\xB7");
10154
+ line(` ${tick} ${c.text} ${import_picocolors32.default.dim(c.id)}`);
10155
+ if (c.note) line(import_picocolors32.default.dim(` ${c.note}`));
9622
10156
  }
9623
10157
  }
9624
10158
  const current = progress.find((p) => p.name === g.currentPhase);
9625
10159
  if (current) {
9626
10160
  line(
9627
- import_picocolors31.default.dim(
10161
+ import_picocolors32.default.dim(
9628
10162
  `
9629
10163
  ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current.index} of ${progress.length}).`
9630
10164
  )
@@ -9634,17 +10168,22 @@ ${current.name} \u2014 ${current.done} of ${current.total} done (phase ${current
9634
10168
  function statusTag3(status) {
9635
10169
  switch (status) {
9636
10170
  case "agreed":
9637
- return import_picocolors31.default.cyan("[agreed]");
10171
+ return import_picocolors32.default.cyan("[agreed]");
9638
10172
  case "working":
9639
- return import_picocolors31.default.blue("[working]");
10173
+ return import_picocolors32.default.blue("[working]");
9640
10174
  case "delivered":
9641
- return import_picocolors31.default.green("[delivered]");
10175
+ return import_picocolors32.default.green("[delivered]");
9642
10176
  case "abandoned":
9643
- return import_picocolors31.default.dim("[abandoned]");
10177
+ return import_picocolors32.default.dim("[abandoned]");
9644
10178
  default:
9645
- return import_picocolors31.default.yellow("[proposed]");
10179
+ return import_picocolors32.default.yellow("[proposed]");
9646
10180
  }
9647
10181
  }
10182
+ function paceLine(pace) {
10183
+ if (pace === "slice") return "slice \u2014 do ONE task, then stop and show the owner";
10184
+ if (pace === "all") return "all \u2014 work through the phases; report at the end";
10185
+ return "phase \u2014 the current phase, task by task";
10186
+ }
9648
10187
 
9649
10188
  // src/role-guard.ts
9650
10189
  var READS = [
@@ -9826,7 +10365,7 @@ function stripLeadingGlobalOptions(argv) {
9826
10365
  }
9827
10366
 
9828
10367
  // src/commands/decision.ts
9829
- var import_picocolors32 = __toESM(require_picocolors(), 1);
10368
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
9830
10369
  function registerDecision(program3) {
9831
10370
  const decision = program3.command("decision").description("Read and record the project's architecture decisions");
9832
10371
  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)").action(
@@ -9839,11 +10378,11 @@ function registerDecision(program3) {
9839
10378
  rows = applyLimit(rows, opts.limit);
9840
10379
  ok(rows, () => {
9841
10380
  if (!rows.length) {
9842
- line(import_picocolors32.default.dim("No decisions recorded yet."));
10381
+ line(import_picocolors33.default.dim("No decisions recorded yet."));
9843
10382
  return;
9844
10383
  }
9845
10384
  for (const r of rows) {
9846
- line(`${import_picocolors32.default.dim(r.id)} ${import_picocolors32.default.dim(shortDate(r.createdAt))} ${r.title}`);
10385
+ line(`${import_picocolors33.default.dim(r.id)} ${import_picocolors33.default.dim(shortDate(r.createdAt))} ${r.title}`);
9847
10386
  line(` ${truncate(r.decision, 100)}`);
9848
10387
  }
9849
10388
  });
@@ -9857,16 +10396,16 @@ function registerDecision(program3) {
9857
10396
  `/v1/projects/${projectId}/architecture-decisions/${args[0]}`
9858
10397
  );
9859
10398
  ok(row, () => {
9860
- line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.id)}`);
9861
- line(import_picocolors32.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10399
+ line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10400
+ line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9862
10401
  line(`
9863
- ${import_picocolors32.default.bold("Context")}
10402
+ ${import_picocolors33.default.bold("Context")}
9864
10403
  ${row.context}`);
9865
10404
  line(`
9866
- ${import_picocolors32.default.bold("Decision")}
10405
+ ${import_picocolors33.default.bold("Decision")}
9867
10406
  ${row.decision}`);
9868
10407
  if (row.consequences) line(`
9869
- ${import_picocolors32.default.bold("Consequences")}
10408
+ ${import_picocolors33.default.bold("Consequences")}
9870
10409
  ${row.consequences}`);
9871
10410
  });
9872
10411
  })
@@ -9895,7 +10434,7 @@ ${row.consequences}`);
9895
10434
  refId: row?.id,
9896
10435
  output: { decision: row }
9897
10436
  });
9898
- ok(row, () => line(`Recorded decision ${import_picocolors32.default.bold(row?.id ?? "")} \u2014 ${title}`));
10437
+ ok(row, () => line(`Recorded decision ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
9899
10438
  })
9900
10439
  );
9901
10440
  const requirement = program3.command("requirement").description("Read and record the project's requirements");
@@ -9907,11 +10446,11 @@ ${row.consequences}`);
9907
10446
  rows = applyLimit(rows, opts.limit);
9908
10447
  ok(rows, () => {
9909
10448
  if (!rows.length) {
9910
- line(import_picocolors32.default.dim("No requirements recorded yet."));
10449
+ line(import_picocolors33.default.dim("No requirements recorded yet."));
9911
10450
  return;
9912
10451
  }
9913
10452
  for (const r of rows) {
9914
- line(`${import_picocolors32.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
10453
+ line(`${import_picocolors33.default.dim(r.id)} ${r.status.padEnd(9)} ${r.title}`);
9915
10454
  }
9916
10455
  });
9917
10456
  })
@@ -9924,8 +10463,8 @@ ${row.consequences}`);
9924
10463
  `/v1/projects/${projectId}/requirements/${args[0]}`
9925
10464
  );
9926
10465
  ok(row, () => {
9927
- line(`${import_picocolors32.default.bold(row.title)} ${import_picocolors32.default.dim(row.id)}`);
9928
- line(import_picocolors32.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
10466
+ line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10467
+ line(import_picocolors33.default.dim(`${row.status} \xB7 ${shortDate(row.createdAt)}`));
9929
10468
  line(`
9930
10469
  ${row.body}`);
9931
10470
  });
@@ -9951,7 +10490,7 @@ ${row.body}`);
9951
10490
  refId: row?.id,
9952
10491
  output: { requirement: row }
9953
10492
  });
9954
- ok(row, () => line(`Recorded requirement ${import_picocolors32.default.bold(row?.id ?? "")} \u2014 ${title}`));
10493
+ ok(row, () => line(`Recorded requirement ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
9955
10494
  })
9956
10495
  );
9957
10496
  requirement.command("update <id>").description(
@@ -9980,7 +10519,7 @@ ${row.body}`);
9980
10519
  code: "bad_request"
9981
10520
  });
9982
10521
  }
9983
- ok(row, () => line(`Updated requirement ${import_picocolors32.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
10522
+ ok(row, () => line(`Updated requirement ${import_picocolors33.default.bold(row.id)} \u2014 ${row.title} (${row.status})`));
9984
10523
  })
9985
10524
  );
9986
10525
  }
@@ -10003,7 +10542,7 @@ function shortDate(iso) {
10003
10542
  }
10004
10543
 
10005
10544
  // src/commands/doc.ts
10006
- var import_picocolors33 = __toESM(require_picocolors(), 1);
10545
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
10007
10546
 
10008
10547
  // src/mermaid-fences.ts
10009
10548
  function hasDiagram(code) {
@@ -10048,13 +10587,13 @@ function registerDoc(program3) {
10048
10587
  }) ?? [];
10049
10588
  ok(rows, () => {
10050
10589
  if (!rows.length) {
10051
- line(import_picocolors33.default.dim("No documents yet."));
10590
+ line(import_picocolors34.default.dim("No documents yet."));
10052
10591
  return;
10053
10592
  }
10054
10593
  for (const r of rows) {
10055
- const link = r.workItemId ? import_picocolors33.default.dim(` \u21B3 ${r.workItemId}`) : "";
10056
- const file = r.filePath ? import_picocolors33.default.dim(` ${r.filePath}`) : "";
10057
- line(`${import_picocolors33.default.dim(r.id)} ${r.title}${link}${file}`);
10594
+ const link = r.workItemId ? import_picocolors34.default.dim(` \u21B3 ${r.workItemId}`) : "";
10595
+ const file = r.filePath ? import_picocolors34.default.dim(` ${r.filePath}`) : "";
10596
+ line(`${import_picocolors34.default.dim(r.id)} ${r.title}${link}${file}`);
10058
10597
  }
10059
10598
  });
10060
10599
  })
@@ -10068,17 +10607,17 @@ function registerDoc(program3) {
10068
10607
  );
10069
10608
  if (opts.markdown) {
10070
10609
  ok({ id: row.id, title: row.title, filePath: row.filePath }, () => {
10071
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10610
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
10072
10611
  line(
10073
- row.filePath ? `Read it at ${import_picocolors33.default.bold(row.filePath)} (relative to the project folder).` : import_picocolors33.default.dim("This document has no markdown mirror on disk yet.")
10612
+ 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.")
10074
10613
  );
10075
10614
  });
10076
10615
  return;
10077
10616
  }
10078
10617
  ok(row, () => {
10079
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10080
- if (row.workItemId) line(import_picocolors33.default.dim(`linked to work item ${row.workItemId}`));
10081
- if (row.filePath) line(import_picocolors33.default.dim(`markdown mirror: ${row.filePath}`));
10618
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
10619
+ if (row.workItemId) line(import_picocolors34.default.dim(`linked to work item ${row.workItemId}`));
10620
+ if (row.filePath) line(import_picocolors34.default.dim(`markdown mirror: ${row.filePath}`));
10082
10621
  line("");
10083
10622
  line(row.contentJson);
10084
10623
  });
@@ -10107,7 +10646,7 @@ function registerDoc(program3) {
10107
10646
  refId: row?.id,
10108
10647
  output: { document: row }
10109
10648
  });
10110
- ok(row, () => line(`Created document ${import_picocolors33.default.bold(row?.id ?? "")} \u2014 ${title}`));
10649
+ ok(row, () => line(`Created document ${import_picocolors34.default.bold(row?.id ?? "")} \u2014 ${title}`));
10111
10650
  })
10112
10651
  );
10113
10652
  doc.command("diagram <id>").description(
@@ -10133,19 +10672,19 @@ function registerDoc(program3) {
10133
10672
  diagrams: diagrams.map((code) => ({ kind: diagramKind(code), code }))
10134
10673
  };
10135
10674
  ok(payload, () => {
10136
- line(`${import_picocolors33.default.bold(row.title)} ${import_picocolors33.default.dim(row.id)}`);
10675
+ line(`${import_picocolors34.default.bold(row.title)} ${import_picocolors34.default.dim(row.id)}`);
10137
10676
  if (markdown === null) {
10138
10677
  line(
10139
- import_picocolors33.default.dim(
10678
+ import_picocolors34.default.dim(
10140
10679
  row.filePath ? `Could not read ${row.filePath} from this folder.` : "This document has no markdown mirror on disk yet."
10141
10680
  )
10142
10681
  );
10143
10682
  } else if (!diagrams.length) {
10144
- line(import_picocolors33.default.dim("No diagrams in this document."));
10683
+ line(import_picocolors34.default.dim("No diagrams in this document."));
10145
10684
  } else {
10146
10685
  for (const [i, code] of diagrams.entries()) {
10147
10686
  const kind = diagramKind(code) ?? "diagram";
10148
- line(`${import_picocolors33.default.dim(String(i + 1))} ${kind} ${import_picocolors33.default.dim(`${code.split("\n").length} lines`)}`);
10687
+ line(`${import_picocolors34.default.dim(String(i + 1))} ${kind} ${import_picocolors34.default.dim(`${code.split("\n").length} lines`)}`);
10149
10688
  }
10150
10689
  }
10151
10690
  });
@@ -10181,7 +10720,7 @@ function registerDoc(program3) {
10181
10720
  code: "bad_request"
10182
10721
  });
10183
10722
  }
10184
- ok(row, () => line(`Updated document ${import_picocolors33.default.bold(row.id)} \u2014 ${row.title}`));
10723
+ ok(row, () => line(`Updated document ${import_picocolors34.default.bold(row.id)} \u2014 ${row.title}`));
10185
10724
  })
10186
10725
  );
10187
10726
  }
@@ -10195,9 +10734,9 @@ function readMirror(cwd, filePath) {
10195
10734
  }
10196
10735
 
10197
10736
  // src/commands/design.ts
10198
- var import_picocolors34 = __toESM(require_picocolors(), 1);
10737
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
10199
10738
  function registerDesign(program3) {
10200
- const design = program3.command("design").description("Read the project's brand (colours, fonts, logo)");
10739
+ const design = program3.command("design").description("The project's brand, and pictures of the screens you draw");
10201
10740
  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(
10202
10741
  action(async ({ ctx, opts }) => {
10203
10742
  const projectId = requireProject(ctx);
@@ -10209,11 +10748,11 @@ function registerDesign(program3) {
10209
10748
  if (opts.raw) {
10210
10749
  ok(files, () => {
10211
10750
  if (!files.length) {
10212
- line(import_picocolors34.default.dim("No brand set for this project."));
10751
+ line(import_picocolors35.default.dim("No brand set for this project."));
10213
10752
  return;
10214
10753
  }
10215
10754
  for (const f of files) {
10216
- line(import_picocolors34.default.bold(f.path));
10755
+ line(import_picocolors35.default.bold(f.path));
10217
10756
  line(f.contents);
10218
10757
  line("");
10219
10758
  }
@@ -10230,27 +10769,62 @@ function registerDesign(program3) {
10230
10769
  } : { hasBrand: false, colors: {}, fonts: {}, brand: {}, files: [] };
10231
10770
  ok(summary, () => {
10232
10771
  if (!tokens) {
10233
- line(import_picocolors34.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
10772
+ line(import_picocolors35.default.dim("No brand set for this project \u2014 choose sensible styling yourself."));
10234
10773
  return;
10235
10774
  }
10236
10775
  for (const [name, value] of Object.entries(tokens.brand)) {
10237
- line(`${import_picocolors34.default.dim(name.padEnd(12))} ${value}`);
10776
+ line(`${import_picocolors35.default.dim(name.padEnd(12))} ${value}`);
10238
10777
  }
10239
10778
  for (const [name, value] of Object.entries(tokens.color)) {
10240
- line(`${import_picocolors34.default.dim(`color.${name}`.padEnd(12))} ${value}`);
10779
+ line(`${import_picocolors35.default.dim(`color.${name}`.padEnd(12))} ${value}`);
10241
10780
  }
10242
10781
  for (const [name, value] of Object.entries(tokens.font)) {
10243
- line(`${import_picocolors34.default.dim(`font.${name}`.padEnd(12))} ${value}`);
10782
+ line(`${import_picocolors35.default.dim(`font.${name}`.padEnd(12))} ${value}`);
10244
10783
  }
10245
10784
  line("");
10246
10785
  line(
10247
- import_picocolors34.default.dim(
10786
+ import_picocolors35.default.dim(
10248
10787
  `Generated into the working tree as ${files.map((f) => f.path).join(", ")} \u2014 wire those in, never edit them.`
10249
10788
  )
10250
10789
  );
10251
10790
  });
10252
10791
  })
10253
10792
  );
10793
+ design.command("shot").argument("<files...>", "the HTML screens to photograph, relative to the project folder").description("Render a design screen to a PNG beside it, for surfaces that can't run a page").option("--width <px>", "artboard width (default 1440)", (v) => Number(v)).option("--height <px>", "artboard height (default 900)", (v) => Number(v)).action(
10794
+ action(async ({ ctx, args, opts }) => {
10795
+ const projectId = requireProject(ctx);
10796
+ const res = await api(ctx, "/v1/design-shot", {
10797
+ method: "POST",
10798
+ body: {
10799
+ projectId,
10800
+ files: args,
10801
+ width: opts.width,
10802
+ height: opts.height
10803
+ }
10804
+ });
10805
+ ok(res, () => {
10806
+ for (const shot of res.shots ?? []) {
10807
+ if (shot.out) {
10808
+ line(`${import_picocolors35.default.green("\u2713")} ${shot.file} ${import_picocolors35.default.dim("\u2192")} ${shot.out}`);
10809
+ } else {
10810
+ line(`${import_picocolors35.default.red("\u2717")} ${shot.file} ${import_picocolors35.default.dim(shot.error ?? "no image")}`);
10811
+ }
10812
+ }
10813
+ for (const path of res.refused ?? []) {
10814
+ line(import_picocolors35.default.dim(`skipped ${path} \u2014 not a page inside this project`));
10815
+ }
10816
+ const made = (res.shots ?? []).filter((s) => s.out).length;
10817
+ if (made) {
10818
+ line("");
10819
+ line(
10820
+ import_picocolors35.default.dim(
10821
+ `Record each one so it shows up: workser artifact add <file>.png --kind design -d "<what this screen is>"`
10822
+ )
10823
+ );
10824
+ }
10825
+ });
10826
+ })
10827
+ );
10254
10828
  }
10255
10829
  function parseTokens(files) {
10256
10830
  const doc = files.find((f) => f.path.endsWith("tokens.json"));
@@ -10275,7 +10849,7 @@ function unwrap(group) {
10275
10849
  }
10276
10850
 
10277
10851
  // src/commands/api.ts
10278
- var import_picocolors35 = __toESM(require_picocolors(), 1);
10852
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
10279
10853
  import { readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
10280
10854
  import { join as join3, relative, sep } from "path";
10281
10855
 
@@ -10389,10 +10963,10 @@ function registerApi(program3) {
10389
10963
  const appId = requireApp2(opts.app);
10390
10964
  const res = await api(ctx, `/v1/apps/${encodeURIComponent(appId)}/api/requests`);
10391
10965
  ok(res, () => {
10392
- for (const note of res?.notes ?? []) line(import_picocolors35.default.dim(note));
10966
+ for (const note of res?.notes ?? []) line(import_picocolors36.default.dim(note));
10393
10967
  for (const r of res?.requests ?? []) {
10394
10968
  line(
10395
- `${import_picocolors35.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors35.default.dim(` ${r.note}`) : ""}`
10969
+ `${import_picocolors36.default.dim(r.method.padEnd(6))}${r.path}${r.note ? import_picocolors36.default.dim(` ${r.note}`) : ""}`
10396
10970
  );
10397
10971
  }
10398
10972
  });
@@ -10426,14 +11000,14 @@ function registerApi(program3) {
10426
11000
  );
10427
11001
  ok(res, () => {
10428
11002
  if (!res?.ok) {
10429
- line(import_picocolors35.default.red(res?.error ?? "The service did not answer."));
11003
+ line(import_picocolors36.default.red(res?.error ?? "The service did not answer."));
10430
11004
  return;
10431
11005
  }
10432
11006
  const code = `${res.status}${res.statusText ? ` ${res.statusText}` : ""}`;
10433
- const colour2 = res.status && res.status < 300 ? import_picocolors35.default.green : res.status && res.status < 500 ? import_picocolors35.default.yellow : import_picocolors35.default.red;
10434
- line(`${colour2(code)} ${import_picocolors35.default.dim(`${res.durationMs}ms ${res.url}`)}`);
11007
+ const colour2 = res.status && res.status < 300 ? import_picocolors36.default.green : res.status && res.status < 500 ? import_picocolors36.default.yellow : import_picocolors36.default.red;
11008
+ line(`${colour2(code)} ${import_picocolors36.default.dim(`${res.durationMs}ms ${res.url}`)}`);
10435
11009
  if (res.body) line(res.body);
10436
- if (res.truncated) line(import_picocolors35.default.dim("(answer truncated)"));
11010
+ if (res.truncated) line(import_picocolors36.default.dim("(answer truncated)"));
10437
11011
  });
10438
11012
  if (!res?.ok) process.exitCode = 1;
10439
11013
  })
@@ -10453,15 +11027,15 @@ function registerApi(program3) {
10453
11027
  for (const r of report.routes) {
10454
11028
  const known = report.missing.some((m) => m.path === r.path);
10455
11029
  line(
10456
- `${known ? import_picocolors35.default.yellow("undocumented") : import_picocolors35.default.green("documented ")} ${r.path}${import_picocolors35.default.dim(` ${r.file}`)}`
11030
+ `${known ? import_picocolors36.default.yellow("undocumented") : import_picocolors36.default.green("documented ")} ${r.path}${import_picocolors36.default.dim(` ${r.file}`)}`
10457
11031
  );
10458
11032
  }
10459
11033
  for (const p of report.stale) {
10460
- line(`${import_picocolors35.default.dim("in spec only ")} ${p}`);
11034
+ line(`${import_picocolors36.default.dim("in spec only ")} ${p}`);
10461
11035
  }
10462
11036
  line("");
10463
11037
  if (report.ok && specFile) success(summary);
10464
- else line(import_picocolors35.default.yellow(summary));
11038
+ else line(import_picocolors36.default.yellow(summary));
10465
11039
  });
10466
11040
  if (opts.check && !report.ok) {
10467
11041
  throw new WorkserError(summary, { code: "bad_request" });
@@ -10532,7 +11106,7 @@ function listRepoFiles(root, maxDepth = 8) {
10532
11106
  }
10533
11107
 
10534
11108
  // src/commands/analysis.ts
10535
- var import_picocolors36 = __toESM(require_picocolors(), 1);
11109
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
10536
11110
  import { readFileSync as readFileSync4 } from "fs";
10537
11111
  function registerAnalysis(program3) {
10538
11112
  const cmd = program3.command("analysis").description("Run Python analysis locally, recorded in the task");
@@ -10542,14 +11116,14 @@ function registerAnalysis(program3) {
10542
11116
  const res = await api(ctx, path);
10543
11117
  ok(res, () => {
10544
11118
  line(
10545
- `${res?.available ? import_picocolors36.default.green("python") : import_picocolors36.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors36.default.dim(res?.python ?? "")}`
11119
+ `${res?.available ? import_picocolors37.default.green("python") : import_picocolors37.default.red("python")} ${res?.version ?? "not found"} ${import_picocolors37.default.dim(res?.python ?? "")}`
10546
11120
  );
10547
11121
  for (const lib of res?.libraries ?? []) {
10548
11122
  line(
10549
- `${lib.present ? import_picocolors36.default.green(lib.name) : import_picocolors36.default.yellow(lib.name)}${import_picocolors36.default.dim(lib.present ? "" : " missing")}`
11123
+ `${lib.present ? import_picocolors37.default.green(lib.name) : import_picocolors37.default.yellow(lib.name)}${import_picocolors37.default.dim(lib.present ? "" : " missing")}`
10550
11124
  );
10551
11125
  }
10552
- for (const note of res?.notes ?? []) line(import_picocolors36.default.dim(note));
11126
+ for (const note of res?.notes ?? []) line(import_picocolors37.default.dim(note));
10553
11127
  });
10554
11128
  if (!res?.available) process.exitCode = 1;
10555
11129
  })
@@ -10571,15 +11145,15 @@ function registerAnalysis(program3) {
10571
11145
  );
10572
11146
  ok(res, () => {
10573
11147
  if (res?.stdout) line(res.stdout.replace(/\n$/, ""));
10574
- if (res?.stderr) line(import_picocolors36.default.dim(res.stderr.replace(/\n$/, "")));
10575
- if (res?.truncated) line(import_picocolors36.default.dim("(output truncated)"));
11148
+ if (res?.stderr) line(import_picocolors37.default.dim(res.stderr.replace(/\n$/, "")));
11149
+ if (res?.truncated) line(import_picocolors37.default.dim("(output truncated)"));
10576
11150
  const took = `${Math.round((res?.durationMs ?? 0) / 100) / 10}s`;
10577
11151
  line(
10578
- res?.ok ? import_picocolors36.default.green(`\u2713 ${res.summary ?? "It finished."}`) + import_picocolors36.default.dim(` ${took}`) : import_picocolors36.default.yellow(res?.summary ?? "It did not finish.") + import_picocolors36.default.dim(` ${took}`)
11152
+ 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}`)
10579
11153
  );
10580
11154
  if (res && !res.sandboxed) {
10581
11155
  line(
10582
- import_picocolors36.default.dim(
11156
+ import_picocolors37.default.dim(
10583
11157
  "This platform has no OS sandbox, so the script ran with your own file access."
10584
11158
  )
10585
11159
  );
@@ -10607,7 +11181,7 @@ function readCode(file, inline) {
10607
11181
  }
10608
11182
 
10609
11183
  // src/commands/scan.ts
10610
- var import_picocolors37 = __toESM(require_picocolors(), 1);
11184
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
10611
11185
  import { spawnSync as spawnSync2 } from "child_process";
10612
11186
  import { existsSync as existsSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
10613
11187
  import { join as join4, relative as relative2, sep as sep2 } from "path";
@@ -10891,22 +11465,22 @@ function runPermissions(cwd, findings, checked, skipped) {
10891
11465
  }
10892
11466
  function print2(report, summary) {
10893
11467
  for (const s of report.skipped) {
10894
- line(`${import_picocolors37.default.yellow("not checked")} ${s.check}${import_picocolors37.default.dim(` \u2014 ${s.reason}`)}`);
11468
+ line(`${import_picocolors38.default.yellow("not checked")} ${s.check}${import_picocolors38.default.dim(` \u2014 ${s.reason}`)}`);
10895
11469
  }
10896
11470
  for (const f of report.findings) {
10897
- const where = f.file ? import_picocolors37.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
11471
+ const where = f.file ? import_picocolors38.default.dim(` ${f.file}${f.line ? `:${f.line}` : ""}`) : "";
10898
11472
  line(`${severityTag(f.severity)} ${f.title}${where}`);
10899
- line(` ${import_picocolors37.default.dim(f.fix)}`);
11473
+ line(` ${import_picocolors38.default.dim(f.fix)}`);
10900
11474
  }
10901
11475
  if (report.findings.length || report.skipped.length) line("");
10902
11476
  if (report.ok && !report.skipped.length) success(summary);
10903
- else if (report.ok) line(import_picocolors37.default.yellow(summary));
10904
- else line(import_picocolors37.default.red(summary));
11477
+ else if (report.ok) line(import_picocolors38.default.yellow(summary));
11478
+ else line(import_picocolors38.default.red(summary));
10905
11479
  }
10906
11480
  function severityTag(severity) {
10907
- if (severity === "high") return import_picocolors37.default.red("serious ");
10908
- if (severity === "medium") return import_picocolors37.default.yellow("worth fixing");
10909
- return import_picocolors37.default.dim("minor ");
11481
+ if (severity === "high") return import_picocolors38.default.red("serious ");
11482
+ if (severity === "medium") return import_picocolors38.default.yellow("worth fixing");
11483
+ return import_picocolors38.default.dim("minor ");
10910
11484
  }
10911
11485
  function git(cwd, args) {
10912
11486
  try {
@@ -10967,7 +11541,7 @@ function listRepoFiles2(root, maxDepth = 8) {
10967
11541
  }
10968
11542
 
10969
11543
  // src/commands/health.ts
10970
- var import_picocolors38 = __toESM(require_picocolors(), 1);
11544
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
10971
11545
  function registerHealth(program3) {
10972
11546
  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(
10973
11547
  action(async ({ ctx, opts }) => {
@@ -10981,16 +11555,16 @@ function registerHealth(program3) {
10981
11555
  }
10982
11556
  function print3(res) {
10983
11557
  if (!res?.checks?.length) {
10984
- line(import_picocolors38.default.dim(res?.note ?? "Nothing to check."));
11558
+ line(import_picocolors39.default.dim(res?.note ?? "Nothing to check."));
10985
11559
  return;
10986
11560
  }
10987
11561
  for (const c of res.checks) {
10988
- const mark = c.ok ? import_picocolors38.default.green("up ") : import_picocolors38.default.red("down");
10989
- const timing = import_picocolors38.default.dim(`${c.ms}ms`);
10990
- const detail = c.ok ? timing : import_picocolors38.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
10991
- line(` ${mark} ${c.appName} ${import_picocolors38.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
11562
+ const mark = c.ok ? import_picocolors39.default.green("up ") : import_picocolors39.default.red("down");
11563
+ const timing = import_picocolors39.default.dim(`${c.ms}ms`);
11564
+ const detail = c.ok ? timing : import_picocolors39.default.dim(`${c.error ?? "no answer"}${c.failures > 1 ? ` \xB7 ${c.failures} in a row` : ""}`);
11565
+ line(` ${mark} ${c.appName} ${import_picocolors39.default.dim(`(${c.environment})`)} ${c.url} ${detail}`);
10992
11566
  if (c.incidentOpened) {
10993
- line(import_picocolors38.default.yellow(` An incident has been opened on the board for this.`));
11567
+ line(import_picocolors39.default.yellow(` An incident has been opened on the board for this.`));
10994
11568
  }
10995
11569
  }
10996
11570
  const down = res.checks.filter((c) => !c.ok);
@@ -11003,14 +11577,14 @@ function print3(res) {
11003
11577
  }
11004
11578
  const production = down.filter((c) => c.environment === "production").length;
11005
11579
  line(
11006
- import_picocolors38.default.red(
11580
+ import_picocolors39.default.red(
11007
11581
  `${down.length} of ${res.checks.length} not answering` + (production ? ` \u2014 ${production} customer-facing.` : " (preview only).")
11008
11582
  )
11009
11583
  );
11010
11584
  }
11011
11585
 
11012
11586
  // src/commands/urls.ts
11013
- var import_picocolors39 = __toESM(require_picocolors(), 1);
11587
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
11014
11588
  function registerUrls(program3) {
11015
11589
  program3.command("urls").description("The stable preview and production addresses of every app in this project").option("--app <webAppId>", "just one app").action(
11016
11590
  action(async ({ ctx, opts }) => {
@@ -11024,20 +11598,20 @@ function registerUrls(program3) {
11024
11598
  const summary = urlsSummary(rows);
11025
11599
  ok({ rows, summary }, () => {
11026
11600
  for (const row of rows) {
11027
- const label = import_picocolors39.default.dim(row.environment.padEnd(10));
11028
- const value = row.url ? import_picocolors39.default.cyan(row.url) : import_picocolors39.default.dim(row.note ?? "not published");
11601
+ const label = import_picocolors40.default.dim(row.environment.padEnd(10));
11602
+ const value = row.url ? import_picocolors40.default.cyan(row.url) : import_picocolors40.default.dim(row.note ?? "not published");
11029
11603
  line(` ${row.appName.padEnd(22)} ${label} ${value}`);
11030
11604
  }
11031
11605
  line("");
11032
11606
  if (rows.some((r) => r.url)) success(summary);
11033
- else line(import_picocolors39.default.yellow(summary));
11607
+ else line(import_picocolors40.default.yellow(summary));
11034
11608
  });
11035
11609
  })
11036
11610
  );
11037
11611
  }
11038
11612
 
11039
11613
  // src/commands/deployments.ts
11040
- var import_picocolors40 = __toESM(require_picocolors(), 1);
11614
+ var import_picocolors41 = __toESM(require_picocolors(), 1);
11041
11615
  function registerDeployments(program3) {
11042
11616
  const cmd = program3.command("deployments").description("Deployment history, and putting a build in front of customers");
11043
11617
  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(
@@ -11055,7 +11629,7 @@ function registerDeployments(program3) {
11055
11629
  ok(res, () => {
11056
11630
  if (!items.length) {
11057
11631
  return line(
11058
- import_picocolors40.default.dim(
11632
+ import_picocolors41.default.dim(
11059
11633
  environment ? `Nothing has been deployed to ${environment} yet.` : "Nothing has been deployed yet. `workser deploy` builds the first one."
11060
11634
  )
11061
11635
  );
@@ -11075,13 +11649,13 @@ function registerDeployments(program3) {
11075
11649
  ).catch(() => null) : null;
11076
11650
  ok({ ...dep, logs }, () => {
11077
11651
  line(formatDeployment(dep));
11078
- if (dep?.error_message) line(import_picocolors40.default.red(` ${dep.error_message}`));
11652
+ if (dep?.error_message) line(import_picocolors41.default.red(` ${dep.error_message}`));
11079
11653
  const events = logs?.events ?? [];
11080
11654
  for (const e of events) {
11081
- line(` ${import_picocolors40.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
11655
+ line(` ${import_picocolors41.default.dim(String(e.type ?? "log"))} ${e.text ?? ""}`);
11082
11656
  }
11083
11657
  if (opts.logs && !events.length) {
11084
- line(import_picocolors40.default.dim(" That build produced no output."));
11658
+ line(import_picocolors41.default.dim(" That build produced no output."));
11085
11659
  }
11086
11660
  });
11087
11661
  })
@@ -11125,16 +11699,16 @@ function printPromoted(res, version) {
11125
11699
  const what = version === null ? "the latest build" : `version ${version}`;
11126
11700
  const url = res.url ?? res.vercel_url;
11127
11701
  success(`Production is being rebuilt from ${what}.`);
11128
- if (url) line(import_picocolors40.default.dim(`It will be at ${url}`));
11129
- line(import_picocolors40.default.dim("`workser deploy status` follows it."));
11702
+ if (url) line(import_picocolors41.default.dim(`It will be at ${url}`));
11703
+ line(import_picocolors41.default.dim("`workser deploy status` follows it."));
11130
11704
  }
11131
11705
  function formatDeployment(d) {
11132
11706
  if (!d) return "";
11133
- const version = d.version !== void 0 ? import_picocolors40.default.yellow(`v${d.version}`) : import_picocolors40.default.dim("v?");
11134
- const env = import_picocolors40.default.dim((d.environment ?? "?").padEnd(10));
11707
+ const version = d.version !== void 0 ? import_picocolors41.default.yellow(`v${d.version}`) : import_picocolors41.default.dim("v?");
11708
+ const env = import_picocolors41.default.dim((d.environment ?? "?").padEnd(10));
11135
11709
  const app = d.webAppName ? `${d.webAppName} ` : "";
11136
- const when = import_picocolors40.default.dim(formatTime2(d.created_at));
11137
- const url = d.url ? " " + import_picocolors40.default.cyan(d.url) : "";
11710
+ const when = import_picocolors41.default.dim(formatTime2(d.created_at));
11711
+ const url = d.url ? " " + import_picocolors41.default.cyan(d.url) : "";
11138
11712
  return `${version} ${env} ${colorStatus(d.status ?? "")} ${app}${when}${url}`;
11139
11713
  }
11140
11714
  function formatTime2(t) {
@@ -11144,7 +11718,7 @@ function formatTime2(t) {
11144
11718
  }
11145
11719
 
11146
11720
  // src/commands/usage.ts
11147
- var import_picocolors41 = __toESM(require_picocolors(), 1);
11721
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
11148
11722
 
11149
11723
  // src/usage.ts
11150
11724
  var NEAR_LIMIT_FRACTION = 0.8;
@@ -11239,7 +11813,7 @@ function registerUsage(program3) {
11239
11813
  function print4(report) {
11240
11814
  const dims = report.dimensions ?? [];
11241
11815
  if (!dims.length) {
11242
- return line(import_picocolors41.default.dim("Nothing to measure for this project yet."));
11816
+ return line(import_picocolors42.default.dim("Nothing to measure for this project yet."));
11243
11817
  }
11244
11818
  const width = Math.max(...dims.map((d) => d.label.length));
11245
11819
  for (const d of dims) {
@@ -11248,23 +11822,23 @@ function print4(report) {
11248
11822
  line("");
11249
11823
  const summary = usageSummary(report);
11250
11824
  const worst = dims.map(usageState);
11251
- if (worst.includes("over")) line(import_picocolors41.default.red(summary));
11825
+ if (worst.includes("over")) line(import_picocolors42.default.red(summary));
11252
11826
  else if (worst.includes("near") || worst.includes("unknown"))
11253
- line(import_picocolors41.default.yellow(summary));
11827
+ line(import_picocolors42.default.yellow(summary));
11254
11828
  else success(summary);
11255
11829
  }
11256
11830
  function gauge(d, _labelWidth) {
11257
11831
  const drawn = bar(d);
11258
- return drawn ? ` ${import_picocolors41.default.dim(drawn)}` : "";
11832
+ return drawn ? ` ${import_picocolors42.default.dim(drawn)}` : "";
11259
11833
  }
11260
11834
  function colour(d) {
11261
11835
  switch (usageState(d)) {
11262
11836
  case "over":
11263
- return d.kind === "hard" ? import_picocolors41.default.red : import_picocolors41.default.yellow;
11837
+ return d.kind === "hard" ? import_picocolors42.default.red : import_picocolors42.default.yellow;
11264
11838
  case "near":
11265
- return import_picocolors41.default.yellow;
11839
+ return import_picocolors42.default.yellow;
11266
11840
  case "unknown":
11267
- return import_picocolors41.default.dim;
11841
+ return import_picocolors42.default.dim;
11268
11842
  default:
11269
11843
  return (s) => s;
11270
11844
  }
@@ -11272,7 +11846,7 @@ function colour(d) {
11272
11846
 
11273
11847
  // src/index.ts
11274
11848
  var pkg = {
11275
- version: true ? "0.6.16" : "0.0.0-dev"
11849
+ version: true ? "0.6.18" : "0.0.0-dev"
11276
11850
  };
11277
11851
  var program2 = new Command();
11278
11852
  program2.name("workser").description(
@@ -11305,6 +11879,7 @@ registerDomain(program2);
11305
11879
  registerOpen(program2);
11306
11880
  registerDoctor(program2);
11307
11881
  registerAgent(program2);
11882
+ registerAgentCloud(program2);
11308
11883
  registerVerify(program2);
11309
11884
  registerApi(program2);
11310
11885
  registerAnalysis(program2);