@workser/cli 0.6.17 → 0.6.19

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,88 @@ 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-media",
3668
+ title: "Pictures, voice, video \u2014 and calling a model without an agent",
3669
+ summary: "What an Agent Cloud agent can make and read, plus one-shot model calls from the app's own code.",
3670
+ commands: ["agent-cloud"],
3671
+ source: "skills/workser/reference/agent-cloud-media.md",
3672
+ body: `# Pictures, voice, video \u2014 and calling a model without an agent
3673
+
3674
+ ### Models that are not chat models
3675
+
3676
+ \`models\` used to list chat models and nothing else \u2014 not by choice, but
3677
+ because the catalogue threw every other kind away before anyone could ask for
3678
+ one. It no longer does:
3679
+
3680
+ \`\`\`
3681
+ workser agent-cloud models --kind image # models that draw
3682
+ workser agent-cloud models --kind video
3683
+ workser agent-cloud models --kind speech # text in, a voice out
3684
+ workser agent-cloud models --kind transcription # a voice in, text out
3685
+ workser agent-cloud models --kind embedding # for search by meaning
3686
+ workser agent-cloud models --accepts image # chat models that see photos
3687
+ \`\`\`
3688
+
3689
+ Prices are shown in the unit the model is SOLD in \u2014 \`4.6 credits each\` for a
3690
+ picture, \`credits/1M\` for words. A picture quoted per million would be out by
3691
+ six orders of magnitude.
3692
+
3693
+ \`default_model\` must stay a **chat model**. It is what the agent thinks with;
3694
+ setting it to an image model publishes an agent that never answers anybody.
3695
+ The other kinds are reached through abilities, below.
3696
+
3697
+ ## What the agent can make, and what it can read
3698
+
3699
+ These are abilities on the agent, not models you set:
3700
+
3701
+ | Ability | The agent gains |
3702
+ | --- | --- |
3703
+ | \`media_generation.image\` | \`generate_image\` \u2014 answer with a picture, art for a post |
3704
+ | \`media_generation.audio.speech\` | \`generate_speech\` \u2014 reply in voice |
3705
+ | \`media_generation.audio.sound_effects\` | \`generate_sound_effect\` |
3706
+ | \`media_generation.audio.music\` | \`generate_music\` \u2014 a background track |
3707
+ | \`media_generation.video\` | \`generate_video\` \u2014 short clips |
3708
+ | \`perception.image\` | \`describe_image\` \u2014 read a photo a customer sent |
3709
+ | \`perception.video\` | \`describe_video\` |
3710
+ | \`perception.audio\` | \`transcribe_audio\` \u2014 a voice note becomes text |
3711
+
3712
+ Turn on only what the job needs. **Video costs an order of magnitude more than
3713
+ everything else here**, and it is refused outright on a deployment where an
3714
+ operator has not priced it \u2014 that refusal is a configuration fact, not the
3715
+ user's mistake, so relay it rather than retrying.
3716
+
3717
+ Everything these produce comes back as a **URL in the project's storage**, so
3718
+ it can go straight into a reply, a post, or the next tool.
3719
+
3720
+ ## When the app itself needs a model, not an agent
3721
+
3722
+ An agent is a sandbox, a tool loop, and minutes of metered runtime. Most of
3723
+ what an app needs a model for is one call that takes a second: a product
3724
+ description, a thumbnail, a spoken confirmation, search that understands
3725
+ meaning. Starting an agent for those is the wrong shape and the wrong price.
3726
+
3727
+ Use \`@workser/app\` in the app's own code:
3728
+
3729
+ \`\`\`ts
3730
+ import { workser } from '@workser/app';
3731
+
3732
+ const blurb = await workser.ai.text('Write a 40-word description of ' + name);
3733
+ const art = await workser.ai.image('product photo of ' + name);
3734
+ const audio = await workser.ai.speech('Your order is confirmed.');
3735
+ const vector = await workser.ai.embed(descriptions); // many in one call
3736
+ for await (const word of workser.ai.stream(question)) process.stdout.write(word);
3737
+ \`\`\`
3738
+
3739
+ Nobody holds a provider key: Workser's own credential is used and the
3740
+ organisation's credit ledger is charged, the same wallet an agent run draws on.
3741
+ It needs no setup in a Workser-deployed app \u2014 the two environment variables are
3742
+ injected at provisioning.
3743
+
3744
+ **The rule:** one answer goes to \`workser.ai\`; work that takes minutes and uses
3745
+ tools goes to an agent.
3746
+ `
3747
+ },
3666
3748
  {
3667
3749
  topic: "agent-cloud",
3668
3750
  title: "Ship an agent inside the app",
@@ -3743,6 +3825,11 @@ workser agent-cloud machines # video, data analysis, design, ...
3743
3825
  A model marked "needs your own key" will make \`publish\` FAIL unless a matching
3744
3826
  secret is stored first. Add the key with \`add <id> secret\` before setting it.
3745
3827
 
3828
+ \`default_model\` must stay a **chat model** \u2014 setting it to an image model
3829
+ publishes an agent that never answers anybody. Pictures, video, voice and
3830
+ transcription are abilities, not models you set, and one call from the app's
3831
+ own code needs no agent at all: \`workser help agent-cloud-media\`.
3832
+
3746
3833
  ## Agents need a paid plan \u2014 the trial does not include them
3747
3834
 
3748
3835
  \`create\` and \`run\` both refuse with **402** on a free or trialling
@@ -3750,9 +3837,9 @@ organisation. That is a rule, not a fault: every run buys model tokens and
3750
3837
  holds a machine, so it costs real money the moment it happens.
3751
3838
 
3752
3839
  **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.
3840
+ files the refusal on the conversation as a clickable step and artifact, so the
3841
+ person gets something actionable. Say what you were doing, that agents need a
3842
+ plan, and stop.
3756
3843
 
3757
3844
  Do NOT:
3758
3845
 
@@ -7898,21 +7985,37 @@ function registerAgentCloud(program3) {
7898
7985
  });
7899
7986
  })
7900
7987
  );
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(
7988
+ cloud.command("models").description("Models this organisation can run an agent on, cheapest first").option("--all", "Include models that need your own provider key").option(
7989
+ "--kind <kind>",
7990
+ "text | image | video | speech | transcription | embedding (comma-separated)"
7991
+ ).option("--accepts <inputs>", "Only models that can read this: image, audio, video, file").action(
7902
7992
  action(async ({ ctx, opts }) => {
7903
- const res = await api(ctx, "/v1/agent-cloud/catalog/models/live");
7993
+ const q = new URLSearchParams();
7994
+ if (opts.kind) q.set("kind", String(opts.kind));
7995
+ if (opts.accepts) q.set("accepts", String(opts.accepts));
7996
+ if (opts.kind && !String(opts.kind).split(",").includes("text")) {
7997
+ q.set("tools_only", "false");
7998
+ }
7999
+ const suffix = q.toString() ? `?${q.toString()}` : "";
8000
+ const res = await api(
8001
+ ctx,
8002
+ `/v1/agent-cloud/catalog/models/gateways${suffix}`
8003
+ );
7904
8004
  const models = (res?.models ?? []).filter(
7905
8005
  (m) => opts.all || m.credit_tier === "PLATFORM_CREDITS"
7906
8006
  );
7907
8007
  ok(res, () => {
7908
8008
  if (!models.length) {
7909
- warn("The live model list could not be read.");
8009
+ warn(
8010
+ opts.kind ? `No ${opts.kind} models are available on your credits. Try --all.` : "The live model list could not be read."
8011
+ );
7910
8012
  return;
7911
8013
  }
7912
8014
  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";
8015
+ const price = m.price_unit === "each" ? typeof m.credits_each === "number" ? `${m.credits_each} credits each` : "price unknown" : typeof m.input_price_per_million_usd === "number" ? `$${m.input_price_per_million_usd.toFixed(2)}/M in` : "price unknown";
7914
8016
  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}`);
8017
+ const kind = m.kind && m.kind !== "text" ? import_picocolors17.default.dim(` [${m.kind}]`) : "";
8018
+ line(`${import_picocolors17.default.bold(m.model ?? m.id)}${kind} ${import_picocolors17.default.dim(price)}${byok}`);
7916
8019
  }
7917
8020
  });
7918
8021
  })
@@ -9912,6 +10015,7 @@ function statusTag2(status) {
9912
10015
  // src/commands/goal.ts
9913
10016
  var import_picocolors32 = __toESM(require_picocolors(), 1);
9914
10017
  var STATUSES3 = ["proposed", "agreed", "working", "delivered", "abandoned"];
10018
+ var PACES = ["slice", "phase", "all"];
9915
10019
  function registerGoal(program3) {
9916
10020
  const goal = program3.command("goal").description("Business goals and the phases that deliver them");
9917
10021
  goal.command("list").description("What this project is working towards").action(
@@ -9949,6 +10053,9 @@ function registerGoal(program3) {
9949
10053
  ).option(
9950
10054
  "--criteria <json>",
9951
10055
  `what "done" means per phase, in the OWNER's words: '{"Checkout":["A customer can pay by card and gets a receipt"]}'`
10056
+ ).option(
10057
+ "--pace <value>",
10058
+ `how much to do before checking in \u2014 slice (one task, then show), phase (the current phase, the default), all (${PACES.join(" | ")})`
9952
10059
  ).action(
9953
10060
  action(async ({ ctx, args, opts }) => {
9954
10061
  requireProject(ctx);
@@ -9981,11 +10088,17 @@ function registerGoal(program3) {
9981
10088
  );
9982
10089
  }
9983
10090
  }
10091
+ if (opts.pace !== void 0 && !PACES.includes(opts.pace)) {
10092
+ throw new WorkserError(
10093
+ `Unknown --pace "${opts.pace}". Use one of: ${PACES.join(", ")}.`
10094
+ );
10095
+ }
9984
10096
  const row = await api(ctx, "/v1/project-goals", {
9985
10097
  body: {
9986
10098
  projectId: ctx.projectId,
9987
10099
  title: args[0],
9988
10100
  outcome: opts.outcome,
10101
+ pace: opts.pace,
9989
10102
  phases: phases.map((name) => ({
9990
10103
  name,
9991
10104
  criteria: (criteria[name] ?? []).map((text, i) => ({
@@ -10079,13 +10192,18 @@ function registerGoal(program3) {
10079
10192
  });
10080
10193
  })
10081
10194
  );
10082
- 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(
10195
+ 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(
10083
10196
  action(async ({ ctx, args, opts }) => {
10084
10197
  if (opts.status && !STATUSES3.includes(opts.status)) {
10085
10198
  throw new WorkserError(
10086
10199
  `Unknown status "${opts.status}". Use one of: ${STATUSES3.join(", ")}.`
10087
10200
  );
10088
10201
  }
10202
+ if (opts.pace && !PACES.includes(opts.pace)) {
10203
+ throw new WorkserError(
10204
+ `Unknown --pace "${opts.pace}". Use one of: ${PACES.join(", ")}.`
10205
+ );
10206
+ }
10089
10207
  const row = await api(
10090
10208
  ctx,
10091
10209
  `/v1/project-goals/${encodeURIComponent(String(args[0]))}`,
@@ -10095,7 +10213,8 @@ function registerGoal(program3) {
10095
10213
  title: opts.title,
10096
10214
  outcome: opts.outcome,
10097
10215
  phases: opts.phase,
10098
- status: opts.status
10216
+ status: opts.status,
10217
+ pace: opts.pace
10099
10218
  }
10100
10219
  }
10101
10220
  );
@@ -10119,6 +10238,7 @@ function printGoal(g) {
10119
10238
  line(statusTag3(g.status));
10120
10239
  if (g.outcome) line(`
10121
10240
  done means: ${g.outcome}`);
10241
+ if (g.pace) line(import_picocolors32.default.dim(`pace: ${paceLine(g.pace)}`));
10122
10242
  const progress = g.progress ?? [];
10123
10243
  if (!progress.length) {
10124
10244
  line(import_picocolors32.default.dim("\nNo phases agreed yet."));
@@ -10162,6 +10282,11 @@ function statusTag3(status) {
10162
10282
  return import_picocolors32.default.yellow("[proposed]");
10163
10283
  }
10164
10284
  }
10285
+ function paceLine(pace) {
10286
+ if (pace === "slice") return "slice \u2014 do ONE task, then stop and show the owner";
10287
+ if (pace === "all") return "all \u2014 work through the phases; report at the end";
10288
+ return "phase \u2014 the current phase, task by task";
10289
+ }
10165
10290
 
10166
10291
  // src/role-guard.ts
10167
10292
  var READS = [
@@ -11824,7 +11949,7 @@ function colour(d) {
11824
11949
 
11825
11950
  // src/index.ts
11826
11951
  var pkg = {
11827
- version: true ? "0.6.17" : "0.0.0-dev"
11952
+ version: true ? "0.6.19" : "0.0.0-dev"
11828
11953
  };
11829
11954
  var program2 = new Command();
11830
11955
  program2.name("workser").description(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workser/cli",
3
- "version": "0.6.17",
3
+ "version": "0.6.19",
4
4
  "description": "Workser CLI — give your local AI agent native DevOps & infrastructure on Workser. The agent runs `workser …` to provision, deploy, and manage real apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,81 @@
1
+ ---
2
+ topic: agent-cloud-media
3
+ title: Pictures, voice, video — and calling a model without an agent
4
+ summary: What an Agent Cloud agent can make and read, plus one-shot model calls from the app's own code.
5
+ commands: [agent-cloud]
6
+ ---
7
+
8
+ # Pictures, voice, video — and calling a model without an agent
9
+
10
+ ### Models that are not chat models
11
+
12
+ `models` used to list chat models and nothing else — not by choice, but
13
+ because the catalogue threw every other kind away before anyone could ask for
14
+ one. It no longer does:
15
+
16
+ ```
17
+ workser agent-cloud models --kind image # models that draw
18
+ workser agent-cloud models --kind video
19
+ workser agent-cloud models --kind speech # text in, a voice out
20
+ workser agent-cloud models --kind transcription # a voice in, text out
21
+ workser agent-cloud models --kind embedding # for search by meaning
22
+ workser agent-cloud models --accepts image # chat models that see photos
23
+ ```
24
+
25
+ Prices are shown in the unit the model is SOLD in — `4.6 credits each` for a
26
+ picture, `credits/1M` for words. A picture quoted per million would be out by
27
+ six orders of magnitude.
28
+
29
+ `default_model` must stay a **chat model**. It is what the agent thinks with;
30
+ setting it to an image model publishes an agent that never answers anybody.
31
+ The other kinds are reached through abilities, below.
32
+
33
+ ## What the agent can make, and what it can read
34
+
35
+ These are abilities on the agent, not models you set:
36
+
37
+ | Ability | The agent gains |
38
+ | --- | --- |
39
+ | `media_generation.image` | `generate_image` — answer with a picture, art for a post |
40
+ | `media_generation.audio.speech` | `generate_speech` — reply in voice |
41
+ | `media_generation.audio.sound_effects` | `generate_sound_effect` |
42
+ | `media_generation.audio.music` | `generate_music` — a background track |
43
+ | `media_generation.video` | `generate_video` — short clips |
44
+ | `perception.image` | `describe_image` — read a photo a customer sent |
45
+ | `perception.video` | `describe_video` |
46
+ | `perception.audio` | `transcribe_audio` — a voice note becomes text |
47
+
48
+ Turn on only what the job needs. **Video costs an order of magnitude more than
49
+ everything else here**, and it is refused outright on a deployment where an
50
+ operator has not priced it — that refusal is a configuration fact, not the
51
+ user's mistake, so relay it rather than retrying.
52
+
53
+ Everything these produce comes back as a **URL in the project's storage**, so
54
+ it can go straight into a reply, a post, or the next tool.
55
+
56
+ ## When the app itself needs a model, not an agent
57
+
58
+ An agent is a sandbox, a tool loop, and minutes of metered runtime. Most of
59
+ what an app needs a model for is one call that takes a second: a product
60
+ description, a thumbnail, a spoken confirmation, search that understands
61
+ meaning. Starting an agent for those is the wrong shape and the wrong price.
62
+
63
+ Use `@workser/app` in the app's own code:
64
+
65
+ ```ts
66
+ import { workser } from '@workser/app';
67
+
68
+ const blurb = await workser.ai.text('Write a 40-word description of ' + name);
69
+ const art = await workser.ai.image('product photo of ' + name);
70
+ const audio = await workser.ai.speech('Your order is confirmed.');
71
+ const vector = await workser.ai.embed(descriptions); // many in one call
72
+ for await (const word of workser.ai.stream(question)) process.stdout.write(word);
73
+ ```
74
+
75
+ Nobody holds a provider key: Workser's own credential is used and the
76
+ organisation's credit ledger is charged, the same wallet an agent run draws on.
77
+ It needs no setup in a Workser-deployed app — the two environment variables are
78
+ injected at provisioning.
79
+
80
+ **The rule:** one answer goes to `workser.ai`; work that takes minutes and uses
81
+ tools goes to an agent.
@@ -79,6 +79,11 @@ workser agent-cloud machines # video, data analysis, design, ...
79
79
  A model marked "needs your own key" will make `publish` FAIL unless a matching
80
80
  secret is stored first. Add the key with `add <id> secret` before setting it.
81
81
 
82
+ `default_model` must stay a **chat model** — setting it to an image model
83
+ publishes an agent that never answers anybody. Pictures, video, voice and
84
+ transcription are abilities, not models you set, and one call from the app's
85
+ own code needs no agent at all: `workser help agent-cloud-media`.
86
+
82
87
  ## Agents need a paid plan — the trial does not include them
83
88
 
84
89
  `create` and `run` both refuse with **402** on a free or trialling
@@ -86,9 +91,9 @@ organisation. That is a rule, not a fault: every run buys model tokens and
86
91
  holds a machine, so it costs real money the moment it happens.
87
92
 
88
93
  **When you hit it, hand the plan over — do not just describe it.** Workser
89
- files the refusal on the conversation as a step and an artifact the user can
90
- click, so the person you are helping gets something actionable rather than a
91
- paragraph. Say plainly what you were doing, that agents need a plan, and stop.
94
+ files the refusal on the conversation as a clickable step and artifact, so the
95
+ person gets something actionable. Say what you were doing, that agents need a
96
+ plan, and stop.
92
97
 
93
98
  Do NOT:
94
99