@dench.com/cli 2.0.1 → 2.0.3

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.
Files changed (4) hide show
  1. package/README.md +33 -27
  2. package/dench.ts +37 -411
  3. package/host.ts +17 -2
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -4,17 +4,21 @@ Agent-facing CLI for the Dench agent workspace.
4
4
 
5
5
  ## Usage
6
6
 
7
- Run without installing:
7
+ Run without installing. The npm package is `@dench.com/cli`, but the binary
8
+ it installs is named `dench`, so always invoke it as
9
+ `npx -y -p @dench.com/cli dench <command>`. A bare
10
+ `npx -y @dench.com/cli <command>` will fail because `npx` looks for a bin
11
+ matching the package's last path segment (`cli`) and there isn't one.
8
12
 
9
13
  ```bash
10
- npx -y @dench.com/cli login --name "AI Agent - Billing Repo"
14
+ npx -y -p @dench.com/cli dench signin --name "AI Agent - Billing Repo"
11
15
  ```
12
16
 
13
17
  Or install globally:
14
18
 
15
19
  ```bash
16
20
  npm install -g @dench.com/cli
17
- dench login --name "AI Agent - Billing Repo"
21
+ dench signin --name "AI Agent - Billing Repo"
18
22
  ```
19
23
 
20
24
  `--kind` accepts any string. Suggested values: `claude_code`, `codex`, `cursor`,
@@ -47,11 +51,13 @@ DENCH_HOST=https://example.com dench <cmd>
47
51
 
48
52
  The smooth setup flow is:
49
53
 
50
- 1. Run `dench login --name "AI Agent - Billing Repo"` once.
54
+ 1. Run `dench signin --name "AI Agent - Billing Repo"` once.
51
55
  2. Let the CLI open the approval link. If it cannot, ask the human to open the
52
56
  printed link and confirm the email/workspace. Use `--no-open` to print the
53
57
  link without opening it.
54
- 3. Run `dench status --mine --json` and `dench tasks --json` to verify setup.
58
+ 3. Run `dench status --mine --json` and `dench crm entries list task --json`
59
+ to verify setup. Tasks live as CRM `task` entries — there is no separate
60
+ `dench tasks` command.
55
61
 
56
62
  The CLI selects the approved session for later commands automatically.
57
63
  Setup is complete after approval, both verification commands work, and you know
@@ -71,11 +77,11 @@ dench sessions
71
77
  dench use <session-key-or-workspace-slug>
72
78
  ```
73
79
 
74
- Use a human-readable `DENCH_SESSION_KEY` at login time only for long-lived
80
+ Use a human-readable `DENCH_SESSION_KEY` at sign-in time only for long-lived
75
81
  agents or when the human asks for a stable identity:
76
82
 
77
83
  ```bash
78
- DENCH_SESSION_KEY=billing-repo-agent dench login --name "AI Agent - Billing Repo"
84
+ DENCH_SESSION_KEY=billing-repo-agent dench signin --name "AI Agent - Billing Repo"
79
85
  DENCH_SESSION_KEY=billing-repo-agent dench status --mine --json
80
86
  ```
81
87
 
@@ -88,18 +94,18 @@ context. Other saved sessions can still be reused by future chats. To remove the
88
94
  wrong local session, run `dench logout --session <session-key>`. Use
89
95
  `dench logout --all` only when you want to clear every local Dench session.
90
96
 
91
- Do not create or edit local agent memory files during setup. Use Dench logs for
92
- meaningful progress, and use Dench memory only when the human asks or when
93
- saving a stable workspace fact.
97
+ Do not create or edit local agent memory files during setup. Use Dench
98
+ memory only when the human asks or when saving a stable workspace fact.
94
99
 
95
- Do not create, claim, or log a setup task by default. Only create or claim a
96
- task if the human assigns work, an open matching task exists, or coordination
97
- benefits from creating one. For ad-hoc requests, work directly and only log
98
- meaningful work.
100
+ Tasks live as CRM `task` entries there is no separate `dench tasks`,
101
+ `dench task`, `dench claim`, or `dench log` command. Create one via
102
+ `dench crm entries create task --cells '{"Name":"…","Status":"Open"}'`
103
+ only when the human assigns durable work or coordination benefits from
104
+ a tracked entry. For ad-hoc requests, work directly.
99
105
 
100
- Use `dench context` when you need to know who you are, which workspace you are
101
- using, assigned tasks, pending approvals you requested, connected apps, and the
102
- best next commands. Add `--json` for structured output.
106
+ Use `dench context` when you need to know who you are, which workspace you
107
+ are using, pending approvals you requested, connected apps, and the best
108
+ next commands. Add `--json` for structured output.
103
109
 
104
110
  ## Past chat threads
105
111
 
@@ -123,7 +129,7 @@ text — pick concrete tokens that the prior chat would have used (file
123
129
  paths, product names, error strings) rather than paraphrases.
124
130
 
125
131
  Inside a sandbox, `dench chat` uses `DENCH_API_KEY` + `DENCH_RUN_ID`
126
- automatically; locally, run `dench login` first.
132
+ automatically; locally, run `dench signin` first.
127
133
 
128
134
  ## Billing
129
135
 
@@ -190,16 +196,16 @@ Run `dench tool --help` or `dench apps --help` for command help.
190
196
  For staging:
191
197
 
192
198
  ```bash
193
- dench login --staging
194
- dench login --host https://workspace-staging.dench.com
199
+ dench signin --staging
200
+ dench signin --host https://workspace-staging.dench.com
195
201
  ```
196
202
 
197
- ## Sandbox / API key auth (no `dench login` required)
203
+ ## Sandbox / API key auth (no `dench signin` required)
198
204
 
199
205
  Inside a Daytona sandbox the unified Dench API key is baked into envVars
200
206
  at create time (see `src/lib/secrets/sandbox-tokens.ts`). When
201
207
  `DENCH_API_KEY` and `CONVEX_URL` are both present, the CLI skips the
202
- `dench login` flow entirely and uses the API key as a bearer for every
208
+ `dench signin` flow entirely and uses the API key as a bearer for every
203
209
  org-scoped command — `dench crm`, `dench files`, `dench fs sync`, etc.
204
210
 
205
211
  ```bash
@@ -217,7 +223,7 @@ Auth precedence:
217
223
 
218
224
  1. Explicit `--dev` flag → use the dev workspace env (`NEXT_PUBLIC_CONVEX_URL`,
219
225
  `DENCH_WORKSPACE`, `DENCH_DEV_AGENT_KEY`).
220
- 2. A locally-stored `dench login` session for the current backend (set via
226
+ 2. A locally-stored `dench signin` session for the current backend (set via
221
227
  `dench backend`, `--host`, `--prod`, or `--staging`).
222
228
  3. `DENCH_API_KEY` + `CONVEX_URL` (sandbox / automation path).
223
229
 
@@ -243,7 +249,7 @@ Auto-load now turns on only when one of:
243
249
  Explicit shell exports (`GATEWAY_URL=… ./dench …`) always win regardless.
244
250
 
245
251
  Commands that need to impersonate a specific agent (`dench memory`,
246
- `dench task`, `dench claim`, `dench log`, `dench approval request`,
247
- etc.) still require an agent session (`dench login`) — the unified API
248
- key is org-scoped and cannot speak as a single agent. Those commands
249
- return a clear "agent session required" error in API-key mode.
252
+ `dench approval request`, etc.) still require an agent session
253
+ (`dench signin`) — the unified API key is org-scoped and cannot speak
254
+ as a single agent. Those commands return a clear "agent session
255
+ required" error in API-key mode.
package/dench.ts CHANGED
@@ -16,11 +16,11 @@ import { ConvexHttpClient } from "convex/browser";
16
16
  import { makeFunctionReference } from "convex/server";
17
17
  import { agentKindLabel, normalizeAgentKind } from "./agentKind";
18
18
  import {
19
+ apiHostForDenchHost,
19
20
  DEFAULT_HOST,
20
21
  isLocalHost,
21
22
  LOCAL_HOST,
22
23
  normalizeHost,
23
- PRODUCTION_API_URL,
24
24
  PRODUCTION_CONVEX_URL,
25
25
  PRODUCTION_HOST,
26
26
  resolveBackendAlias,
@@ -97,7 +97,6 @@ type ConfigFile = {
97
97
  type WorkspaceOverview = {
98
98
  agents: Array<{ _id: string } & JsonRecord>;
99
99
  projects: JsonRecord[];
100
- tasks: JsonRecord[];
101
100
  approvals: Array<{ status?: string } & JsonRecord>;
102
101
  rules: JsonRecord[];
103
102
  recentArtifacts?: JsonRecord[];
@@ -119,33 +118,12 @@ type AgentStatus = {
119
118
  const api = {
120
119
  functions: {
121
120
  agentWorkspace: {
122
- agentAppendLog: makeFunctionReference<"mutation">(
123
- "functions/agentWorkspace:agentAppendLog",
124
- ),
125
- agentClaimTask: makeFunctionReference<"mutation">(
126
- "functions/agentWorkspace:agentClaimTask",
127
- ),
128
- agentCreateTask: makeFunctionReference<"mutation">(
129
- "functions/agentWorkspace:agentCreateTask",
130
- ),
131
- agentAddTaskComment: makeFunctionReference<"mutation">(
132
- "functions/agentWorkspace:agentAddTaskComment",
133
- ),
134
- agentAddTaskDependency: makeFunctionReference<"mutation">(
135
- "functions/agentWorkspace:agentAddTaskDependency",
136
- ),
137
- agentConvertSuggestionToTask: makeFunctionReference<"mutation">(
138
- "functions/agentWorkspace:agentConvertSuggestionToTask",
139
- ),
140
121
  agentDecideApproval: makeFunctionReference<"mutation">(
141
122
  "functions/agentWorkspace:agentDecideApproval",
142
123
  ),
143
124
  agentListArtifacts: makeFunctionReference<"query">(
144
125
  "functions/agentWorkspace:agentListArtifacts",
145
126
  ),
146
- agentListTasks: makeFunctionReference<"query">(
147
- "functions/agentWorkspace:agentListTasks",
148
- ),
149
127
  agentRequestApproval: makeFunctionReference<"mutation">(
150
128
  "functions/agentWorkspace:agentRequestApproval",
151
129
  ),
@@ -161,24 +139,12 @@ const api = {
161
139
  getAgentBillingStatus: makeFunctionReference<"query">(
162
140
  "functions/agentWorkspace:getAgentBillingStatus",
163
141
  ),
164
- agentUpdateTaskStatus: makeFunctionReference<"mutation">(
165
- "functions/agentWorkspace:agentUpdateTaskStatus",
166
- ),
167
142
  createSignInRequest: makeFunctionReference<"mutation">(
168
143
  "functions/agentWorkspace:createSignInRequest",
169
144
  ),
170
- devAppendLog: makeFunctionReference<"mutation">(
171
- "functions/agentWorkspace:devAppendLog",
172
- ),
173
- devClaimTask: makeFunctionReference<"mutation">(
174
- "functions/agentWorkspace:devClaimTask",
175
- ),
176
145
  devCreateProject: makeFunctionReference<"mutation">(
177
146
  "functions/agentWorkspace:devCreateProject",
178
147
  ),
179
- devCreateTask: makeFunctionReference<"mutation">(
180
- "functions/agentWorkspace:devCreateTask",
181
- ),
182
148
  devListWorkspaceOverview: makeFunctionReference<"query">(
183
149
  "functions/agentWorkspace:devListWorkspaceOverview",
184
150
  ),
@@ -431,20 +397,11 @@ Usage:
431
397
  dench backend [show|set|clear] [<production|staging|local|<url>>] [--json]
432
398
  dench context [--json]
433
399
  dench status [--mine|--self] [--json]
434
- dench tasks [--json]
435
- dench task create "title" [--description "..."] [--priority low|medium|high] [--risk low|medium|high] [--json]
436
- dench task status <taskId> <open|claimed|in_progress|waiting_approval|completed|canceled> [--note "..."] [--json]
437
- dench task comment <taskId> "message" [--json]
438
- dench task handoff <taskId> "message" --agent <agentId> [--json]
439
- dench task block <taskId> "message" [--json]
440
- dench claim <taskId> [--json]
441
- dench log "message" [--task <taskId>] [--json]
442
400
  dench memory search "query" [--limit 8] [--json]
443
401
  dench memory save <key> "text" [--kind fact|decision|preference|goal|tool_note|other] [--tags a,b] [--sensitivity normal|broad|sensitive] [--json]
444
402
  dench artifacts [--limit 25] [--json]
445
403
  dench suggested-work [--json]
446
- dench suggested-work task <artifactId> [--json]
447
- dench approval request "message" [--task <taskId>] [--json]
404
+ dench approval request "message" [--json]
448
405
  dench approval approve <approvalId> [--evidence "User said yes in chat"] [--json]
449
406
  dench approval reject <approvalId> [--evidence "User said no in chat"] [--json]
450
407
  dench billing status [--json]
@@ -483,7 +440,7 @@ Workspace members (for CRM \`user\`-type fields):
483
440
  cells assign to real members instead of being stored as unlinked
484
441
  display-name strings. See \`dench members help\`.
485
442
 
486
- Live web search (Exa via the Dench Cloud Gateway, auths with DENCH_API_KEY):
443
+ Live web search (Exa via the Dench Cloud Gateway, auths with the active \`dench signin\` agent session or DENCH_API_KEY):
487
444
  dench search "<query>" [--num-results N] [--type auto|fast|deep|deep-reasoning|neural] [--category news|company|people|...] [--include-domains a.com,b.com] [--exclude-domains c.com] [--max-chars 800] [--json]
488
445
  dench search contents <url> [<url>...] [--max-chars 4000] [--summary "..."] [--json]
489
446
  dench search answer "<query>" [--json]
@@ -579,8 +536,9 @@ Agent setup:
579
536
  Defaults to "other" when omitted. Values are normalized to lowercase
580
537
  snake_case before send (e.g. "Claude Code" -> claude_code).
581
538
  After sign-in, run dench context --json and dench crm objects list --json
582
- to verify the workspace + CRM are wired up.
583
- Do not create, claim, or log a setup task by default.
539
+ to verify the workspace + CRM are wired up. Tasks live as CRM \`task\`
540
+ entries there is no separate \`dench tasks\` / \`dench task\` /
541
+ \`dench claim\` / \`dench log\` command.
584
542
 
585
543
  Backends and hosts:
586
544
  Default backend: ${DEFAULT_HOST} (overridden by \`dench backend set\`)
@@ -622,10 +580,11 @@ Usage:
622
580
  dench tool disconnect <connectionId> [--json]
623
581
 
624
582
  Auth:
625
- All commands authenticate with DENCH_API_KEY (Bearer) directly to the
626
- Dench Cloud Gateway. No \`dench signin\` agent session is required —
627
- inside a Dench sandbox the key is baked into the env automatically;
628
- outside, export DENCH_API_KEY=<key> first.
583
+ All commands authenticate with the active \`dench signin\` agent
584
+ session OR with DENCH_API_KEY (Bearer), whichever is set. Inside a
585
+ Dench sandbox DENCH_API_KEY is baked into the env automatically; on
586
+ a developer machine \`dench signin\` to a paid workspace works the
587
+ same way (the CLI exchanges the session for a gateway key on demand).
629
588
 
630
589
  Notes:
631
590
  dench apps is an alias for dench tool status.
@@ -664,10 +623,8 @@ function artifactsHelp() {
664
623
  Usage:
665
624
  dench artifacts [--limit 25] [--json]
666
625
  dench suggested-work [--json]
667
- dench suggested-work task <artifactId> [--json]
668
626
 
669
- Artifacts are durable outputs for human or agent review. Suggested work can
670
- be converted into a workspace task.
627
+ Artifacts are durable outputs for human or agent review.
671
628
  `);
672
629
  }
673
630
 
@@ -727,20 +684,6 @@ Notes:
727
684
  `);
728
685
  }
729
686
 
730
- function tasksHelp() {
731
- console.log(`Dench tasks
732
-
733
- Usage:
734
- dench tasks [--json]
735
-
736
- Notes:
737
- List workspace tasks before creating new ones.
738
- Do not create, claim, or log a setup task by default.
739
- Only create or claim a task if the human assigns work, an open matching task
740
- exists, or coordination benefits from creating one.
741
- `);
742
- }
743
-
744
687
  function logoutHelp() {
745
688
  console.log(`Dench logout
746
689
 
@@ -761,9 +704,9 @@ Usage:
761
704
  dench apps [--json]
762
705
 
763
706
  Lists all external apps connected to this organization (Slack, Gmail,
764
- GitHub, etc.). Authenticates with DENCH_API_KEY (Bearer) directly to the
765
- Dench Cloud Gateway no \`dench signin\` agent session required. Alias
766
- for \`dench tool status\`.
707
+ GitHub, etc.). Authenticates with the active \`dench signin\` agent
708
+ session or DENCH_API_KEY (Bearer), whichever is set. Alias for
709
+ \`dench tool status\`.
767
710
  `);
768
711
  }
769
712
 
@@ -2050,16 +1993,6 @@ async function resolveOrgChoiceForOtp(input: {
2050
1993
  return { newOrganizationName: newName.trim() };
2051
1994
  }
2052
1995
 
2053
- function normalizeApiBase(input: string) {
2054
- const trimmed = input.trim();
2055
- const withProtocol = /^https?:\/\//.test(trimmed)
2056
- ? trimmed
2057
- : /^(localhost|127\.0\.0\.1|\[::1\])(?::|$)/.test(trimmed)
2058
- ? `http://${trimmed}`
2059
- : `https://${trimmed}`;
2060
- return new URL(withProtocol).origin;
2061
- }
2062
-
2063
1996
  async function parseResponseBody(response: Response) {
2064
1997
  const text = await response.text();
2065
1998
  if (!text) return {};
@@ -2313,16 +2246,14 @@ function resolveApiKeyConvexUrl(): string | undefined {
2313
2246
  /**
2314
2247
  * Resolve the Next.js API host the CLI should hit for HTTP calls (e.g.
2315
2248
  * Stripe billing topup, the gateway proxy). Same defense-in-depth
2316
- * pattern as `resolveApiKeyConvexUrl`: env wins, then the resolved
2317
- * Dench host the runtime is targeting, and finally
2318
- * `PRODUCTION_API_URL` as the last resort.
2249
+ * pattern as `resolveApiKeyConvexUrl`: env wins, then the canonical API
2250
+ * origin for the Dench host the runtime is targeting.
2319
2251
  */
2320
2252
  function resolveApiHost(fallbackHost: string): string {
2321
2253
  return (
2322
2254
  process.env.DENCH_API_URL?.trim() ||
2323
2255
  process.env.DENCH_INTERNAL_BASE?.trim() ||
2324
- fallbackHost ||
2325
- PRODUCTION_API_URL
2256
+ apiHostForDenchHost(fallbackHost)
2326
2257
  );
2327
2258
  }
2328
2259
 
@@ -2362,7 +2293,7 @@ async function getRuntime() {
2362
2293
  if (stored.status === "found") {
2363
2294
  return {
2364
2295
  mode: "session" as const,
2365
- host,
2296
+ host: resolveApiHost(host),
2366
2297
  client: new ConvexHttpClient(stored.session.convexUrl),
2367
2298
  sessionToken: stored.session.sessionToken,
2368
2299
  organization: stored.session.organization,
@@ -2543,7 +2474,17 @@ function gatewayAuthError(
2543
2474
  });
2544
2475
  }
2545
2476
  if (response.status === 401) {
2546
- return loginRequiredError(command);
2477
+ return new CliError(
2478
+ `${command} could not exchange the active Dench session for a gateway key (${code})`,
2479
+ {
2480
+ code,
2481
+ status: response.status,
2482
+ nextActions: [
2483
+ "Run dench sessions --json to confirm the selected session.",
2484
+ 'Run dench signin --kind <kind> --name "AI Agent - Project" to refresh the agent session if it expired.',
2485
+ ],
2486
+ },
2487
+ );
2547
2488
  }
2548
2489
  return new CliError(`${command} could not get a gateway key (${code})`, {
2549
2490
  code,
@@ -2616,10 +2557,9 @@ function agentSessionRequiredError(command: string) {
2616
2557
  }
2617
2558
 
2618
2559
  /**
2619
- * Some legacy `agentWorkspace.*` commands (task create / claim / log /
2620
- * approval request) have separate session vs dev branches. With apiKey
2621
- * mode added, the dev branch would silently receive partial args. Use
2622
- * this to short-circuit those branches with a clear error.
2560
+ * `dench approval request` has separate session vs dev branches. With
2561
+ * apiKey mode added, the dev branch would silently receive partial args.
2562
+ * Use this to short-circuit that branch with a clear error.
2623
2563
  *
2624
2564
  * Asserts away the `apiKey` mode so the caller's downstream type
2625
2565
  * narrowing (e.g. `runtime.workspaceArgs`) keeps working.
@@ -2710,8 +2650,7 @@ function workspaceUrls(host: string, workspaceSlug: string | undefined) {
2710
2650
  function nextContextCommands() {
2711
2651
  return [
2712
2652
  "dench context --json",
2713
- "dench tasks --json",
2714
- 'dench log "Brief progress update"',
2653
+ "dench crm entries list task --json",
2715
2654
  'dench tool search "what you need" --toolkit <app>',
2716
2655
  'dench approval request "Risky action to approve"',
2717
2656
  ];
@@ -2797,15 +2736,6 @@ function formatContext(context: JsonRecord) {
2797
2736
  const workspace = asRecord(context.workspace);
2798
2737
  const connectedApps = asRecord(context.connectedApps);
2799
2738
  const connections = asRecordArray(connectedApps?.connections);
2800
- const tasksRecord = asRecord(context.tasks);
2801
- const tasks =
2802
- asRecordArray(tasksRecord?.assignedToMe).length > 0 ||
2803
- asRecordArray(tasksRecord?.open).length > 0
2804
- ? [
2805
- ...asRecordArray(tasksRecord?.assignedToMe),
2806
- ...asRecordArray(tasksRecord?.open),
2807
- ]
2808
- : asRecordArray(context.tasks);
2809
2739
  const approvals = asRecordArray(context.pendingApprovals);
2810
2740
  const suggestedWork = asRecordArray(context.suggestedWork);
2811
2741
  const memories = asRecordArray(context.memory);
@@ -2828,10 +2758,7 @@ function formatContext(context: JsonRecord) {
2828
2758
  `Agent: ${agentDisplayName(agent)}${
2829
2759
  stringField(agent, "kind") ? ` [${stringField(agent, "kind")}]` : ""
2830
2760
  }`,
2831
- `Counts: ${formatCount(counts?.tasks ?? tasks.length)} visible tasks, ${formatCount(
2832
- counts?.myAssignedTasks ??
2833
- asRecordArray(tasksRecord?.assignedToMe).length,
2834
- )} assigned to me, ${formatCount(
2761
+ `Counts: ${formatCount(
2835
2762
  counts?.myPendingApprovals ?? approvals.length,
2836
2763
  )} pending approvals`,
2837
2764
  ];
@@ -2845,21 +2772,6 @@ function formatContext(context: JsonRecord) {
2845
2772
  );
2846
2773
  }
2847
2774
 
2848
- lines.push("", "Assigned tasks:");
2849
-
2850
- if (tasks.length === 0) {
2851
- lines.push(" none");
2852
- } else {
2853
- lines.push(
2854
- ...tasks
2855
- .slice(0, 5)
2856
- .map(
2857
- (task) =>
2858
- ` - ${stringField(task, "title") ?? stringField(task, "id") ?? "untitled"} [${stringField(task, "status") ?? "unknown"}]`,
2859
- ),
2860
- );
2861
- }
2862
-
2863
2775
  lines.push("", "Pending approvals requested by me:");
2864
2776
  if (approvals.length === 0) {
2865
2777
  lines.push(" none");
@@ -2958,7 +2870,6 @@ function summarizeDevOverview(workspaceSlug: string, data: WorkspaceOverview) {
2958
2870
  workspace: workspaceSlug,
2959
2871
  agents: data.agents.length,
2960
2872
  projects: data.projects.length,
2961
- tasks: data.tasks.length,
2962
2873
  approvals: data.approvals.filter(
2963
2874
  (approval) => approval.status === "pending",
2964
2875
  ).length,
@@ -2966,37 +2877,18 @@ function summarizeDevOverview(workspaceSlug: string, data: WorkspaceOverview) {
2966
2877
  };
2967
2878
  }
2968
2879
 
2969
- function compactTask(task: JsonRecord) {
2970
- return {
2971
- id: task._id ?? task.id,
2972
- title: task.title,
2973
- status: task.status,
2974
- priority: task.priority,
2975
- risk: task.risk,
2976
- assignedAgentId: task.assignedAgentId,
2977
- };
2978
- }
2979
-
2980
2880
  function compactApproval(approval: JsonRecord) {
2981
2881
  return {
2982
2882
  id: approval.id ?? approval._id,
2983
2883
  title: approval.title,
2984
2884
  risk: approval.risk,
2985
2885
  status: approval.status,
2986
- taskId: approval.taskId,
2987
2886
  createdAt: approval.createdAt,
2988
2887
  };
2989
2888
  }
2990
2889
 
2991
- function summarizeAgentStatus(
2992
- status: AgentStatus,
2993
- tasks: JsonRecord[],
2994
- host?: string,
2995
- ) {
2890
+ function summarizeAgentStatus(status: AgentStatus, host?: string) {
2996
2891
  const agentId = status.agent?.id;
2997
- const assignedTasks = agentId
2998
- ? tasks.filter((task) => task.assignedAgentId === agentId)
2999
- : [];
3000
2892
  const pendingApprovals = (status.approvals ?? []).filter(
3001
2893
  (approval) => approval.status === "pending" && approval.agentId === agentId,
3002
2894
  );
@@ -3009,11 +2901,9 @@ function summarizeAgentStatus(
3009
2901
  agent: status.agent,
3010
2902
  counts: {
3011
2903
  ...(status.counts ?? {}),
3012
- myAssignedTasks: assignedTasks.length,
3013
2904
  myPendingApprovals: pendingApprovals.length,
3014
2905
  },
3015
2906
  rules: status.rules ?? [],
3016
- tasks: assignedTasks.map(compactTask),
3017
2907
  pendingApprovals: pendingApprovals.map(compactApproval),
3018
2908
  };
3019
2909
  }
@@ -3021,7 +2911,6 @@ function summarizeAgentStatus(
3021
2911
  function summarizeDevMine(workspaceSlug: string, data: WorkspaceOverview) {
3022
2912
  const agentId = option("--agent") ?? data.agents[0]?._id;
3023
2913
  const agent = data.agents.find((item) => item._id === agentId) ?? null;
3024
- const tasks = data.tasks.filter((task) => task.assignedAgentId === agentId);
3025
2914
  const pendingApprovals = data.approvals.filter(
3026
2915
  (approval) => approval.status === "pending" && approval.agentId === agentId,
3027
2916
  );
@@ -3032,33 +2921,17 @@ function summarizeDevMine(workspaceSlug: string, data: WorkspaceOverview) {
3032
2921
  counts: {
3033
2922
  agents: data.agents.length,
3034
2923
  projects: data.projects.length,
3035
- tasks: data.tasks.length,
3036
2924
  pendingApprovals: data.approvals.filter(
3037
2925
  (approval) => approval.status === "pending",
3038
2926
  ).length,
3039
2927
  rules: data.rules.length,
3040
- myAssignedTasks: tasks.length,
3041
2928
  myPendingApprovals: pendingApprovals.length,
3042
2929
  },
3043
2930
  rules: data.rules,
3044
- tasks: tasks.map(compactTask),
3045
2931
  pendingApprovals: pendingApprovals.map(compactApproval),
3046
2932
  };
3047
2933
  }
3048
2934
 
3049
- async function defaultDevAgentId(
3050
- runtime: Awaited<ReturnType<typeof getRuntime>>,
3051
- ) {
3052
- const data = await devOverview(runtime);
3053
- const agent = data.agents[0];
3054
- if (!agent) {
3055
- throw new Error(
3056
- 'No agent registered yet. Run: dench signin --kind <kind> --name "AI Agent - Project"',
3057
- );
3058
- }
3059
- return agent._id;
3060
- }
3061
-
3062
2935
  async function listArtifacts(runtime: Runtime, limit?: number) {
3063
2936
  if (runtime.mode === "session") {
3064
2937
  try {
@@ -3840,11 +3713,6 @@ async function main() {
3840
3713
  return;
3841
3714
  }
3842
3715
 
3843
- if (command === "tasks" && hasFlag("--help")) {
3844
- tasksHelp();
3845
- return;
3846
- }
3847
-
3848
3716
  if (command === "logout" && hasFlag("--help")) {
3849
3717
  logoutHelp();
3850
3718
  return;
@@ -4343,50 +4211,6 @@ async function main() {
4343
4211
  }
4344
4212
 
4345
4213
  if (command === "suggested-work") {
4346
- if (subcommand === "task") {
4347
- const sessionRuntime = requireSessionRuntime(
4348
- runtime,
4349
- "dench suggested-work task",
4350
- );
4351
- const artifactId = positional(2);
4352
- if (!artifactId) {
4353
- throw new Error("Usage: dench suggested-work task <artifactId>");
4354
- }
4355
- print(
4356
- await sessionRuntime.client
4357
- .mutation(api.functions.agentWorkspace.agentConvertSuggestionToTask, {
4358
- sessionToken: sessionRuntime.sessionToken,
4359
- artifactId: artifactId as never,
4360
- })
4361
- .catch(async (error) => {
4362
- if (!isMissingConvexFunction(error)) throw error;
4363
- const suggestion = (await listSuggestedWork(runtime)).find(
4364
- (item) => stringField(item, "id") === artifactId,
4365
- );
4366
- if (!suggestion) throw error;
4367
- const title = stringField(suggestion, "title") ?? "Suggested work";
4368
- const created = (await sessionRuntime.client.mutation(
4369
- api.functions.agentWorkspace.agentCreateTask,
4370
- {
4371
- sessionToken: sessionRuntime.sessionToken,
4372
- title,
4373
- description: stringField(suggestion, "content"),
4374
- priority: "medium",
4375
- risk: "medium",
4376
- },
4377
- )) as JsonRecord;
4378
- return {
4379
- ok: true,
4380
- fallback: true,
4381
- artifactId,
4382
- title,
4383
- taskId: created.taskId,
4384
- note: "Created a task from suggested work. Artifact status will update after the backend conversion function is deployed.",
4385
- };
4386
- }),
4387
- );
4388
- return;
4389
- }
4390
4214
  print(await listSuggestedWork(runtime));
4391
4215
  return;
4392
4216
  }
@@ -4400,13 +4224,7 @@ async function main() {
4400
4224
  },
4401
4225
  )) as AgentStatus;
4402
4226
  if (scopedStatus) {
4403
- const tasks = (await runtime.client.query(
4404
- api.functions.agentWorkspace.agentListTasks,
4405
- {
4406
- sessionToken: runtime.sessionToken,
4407
- },
4408
- )) as JsonRecord[];
4409
- print(summarizeAgentStatus(status, tasks, runtime.host));
4227
+ print(summarizeAgentStatus(status, runtime.host));
4410
4228
  return;
4411
4229
  }
4412
4230
  print(status);
@@ -4429,26 +4247,6 @@ async function main() {
4429
4247
  return;
4430
4248
  }
4431
4249
 
4432
- if (command === "tasks") {
4433
- if (runtime.mode === "session") {
4434
- print(
4435
- await runtime.client.query(
4436
- api.functions.agentWorkspace.agentListTasks,
4437
- {
4438
- sessionToken: runtime.sessionToken,
4439
- },
4440
- ),
4441
- );
4442
- return;
4443
- }
4444
- if (runtime.mode === "apiKey") {
4445
- throw loginRequiredError("dench tasks (agent-scoped task list)");
4446
- }
4447
- const data = await devOverview(runtime);
4448
- print(data.tasks);
4449
- return;
4450
- }
4451
-
4452
4250
  if (command === "agents") {
4453
4251
  if (runtime.mode === "session") {
4454
4252
  const status = await runtime.client.query(
@@ -4508,176 +4306,6 @@ async function main() {
4508
4306
  return;
4509
4307
  }
4510
4308
 
4511
- if (command === "task" && subcommand === "create") {
4512
- const title = positional(2);
4513
- if (!title) throw new Error("Missing task title");
4514
- const taskArgs = {
4515
- title,
4516
- description: option("--description"),
4517
- priority: (option("--priority") ?? "medium") as "low" | "medium" | "high",
4518
- risk: (option("--risk") ?? "medium") as "low" | "medium" | "high",
4519
- };
4520
- if (runtime.mode === "session") {
4521
- print(
4522
- await runtime.client.mutation(
4523
- api.functions.agentWorkspace.agentCreateTask,
4524
- {
4525
- sessionToken: runtime.sessionToken,
4526
- ...taskArgs,
4527
- },
4528
- ),
4529
- );
4530
- return;
4531
- }
4532
- rejectApiKeyForAgentCommand(runtime, "dench task create");
4533
- const taskId = await runtime.client.mutation(
4534
- api.functions.agentWorkspace.devCreateTask,
4535
- {
4536
- ...runtime.workspaceArgs,
4537
- ...taskArgs,
4538
- },
4539
- );
4540
- print({ ok: true, taskId });
4541
- return;
4542
- }
4543
-
4544
- if (command === "task" && subcommand === "status") {
4545
- const sessionRuntime = requireSessionRuntime(runtime, "dench task status");
4546
- const taskId = positional(2);
4547
- const status = positional(3);
4548
- if (!taskId || !status) {
4549
- throw new Error("Usage: dench task status <taskId> <status>");
4550
- }
4551
- print(
4552
- await sessionRuntime.client.mutation(
4553
- api.functions.agentWorkspace.agentUpdateTaskStatus,
4554
- {
4555
- sessionToken: sessionRuntime.sessionToken,
4556
- taskId: taskId as never,
4557
- status: status as never,
4558
- note: option("--note"),
4559
- },
4560
- ),
4561
- );
4562
- return;
4563
- }
4564
-
4565
- if (
4566
- command === "task" &&
4567
- (subcommand === "comment" ||
4568
- subcommand === "handoff" ||
4569
- subcommand === "block")
4570
- ) {
4571
- const sessionRuntime = requireSessionRuntime(
4572
- runtime,
4573
- `dench task ${subcommand}`,
4574
- );
4575
- const taskId = positional(2);
4576
- const body = positionalsFrom(3).join(" ").trim();
4577
- if (!taskId || !body) {
4578
- throw new Error(`Usage: dench task ${subcommand} <taskId> "message"`);
4579
- }
4580
- print(
4581
- await sessionRuntime.client.mutation(
4582
- api.functions.agentWorkspace.agentAddTaskComment,
4583
- {
4584
- sessionToken: sessionRuntime.sessionToken,
4585
- taskId: taskId as never,
4586
- kind:
4587
- subcommand === "handoff"
4588
- ? "handoff"
4589
- : subcommand === "block"
4590
- ? "blocker"
4591
- : "comment",
4592
- body,
4593
- targetAgentId: option("--agent") as never,
4594
- },
4595
- ),
4596
- );
4597
- return;
4598
- }
4599
-
4600
- if (command === "task" && subcommand === "depends-on") {
4601
- const sessionRuntime = requireSessionRuntime(
4602
- runtime,
4603
- "dench task depends-on",
4604
- );
4605
- const taskId = positional(2);
4606
- const dependsOnTaskId = positional(3);
4607
- if (!taskId || !dependsOnTaskId) {
4608
- throw new Error(
4609
- "Usage: dench task depends-on <taskId> <dependsOnTaskId>",
4610
- );
4611
- }
4612
- print(
4613
- await sessionRuntime.client.mutation(
4614
- api.functions.agentWorkspace.agentAddTaskDependency,
4615
- {
4616
- sessionToken: sessionRuntime.sessionToken,
4617
- taskId: taskId as never,
4618
- dependsOnTaskId: dependsOnTaskId as never,
4619
- note: option("--note"),
4620
- },
4621
- ),
4622
- );
4623
- return;
4624
- }
4625
-
4626
- if (command === "claim") {
4627
- const taskId = positional(1);
4628
- if (!taskId) throw new Error("Missing task id");
4629
- if (runtime.mode === "session") {
4630
- print(
4631
- await runtime.client.mutation(
4632
- api.functions.agentWorkspace.agentClaimTask,
4633
- {
4634
- sessionToken: runtime.sessionToken,
4635
- taskId: taskId as never,
4636
- },
4637
- ),
4638
- );
4639
- return;
4640
- }
4641
- rejectApiKeyForAgentCommand(runtime, "dench claim");
4642
- const agentId = option("--agent") ?? (await defaultDevAgentId(runtime));
4643
- print(
4644
- await runtime.client.mutation(api.functions.agentWorkspace.devClaimTask, {
4645
- ...runtime.workspaceArgs,
4646
- taskId: taskId as never,
4647
- agentId: agentId as never,
4648
- }),
4649
- );
4650
- return;
4651
- }
4652
-
4653
- if (command === "log") {
4654
- const message = positional(1);
4655
- if (!message) throw new Error("Missing log message");
4656
- if (runtime.mode === "session") {
4657
- print(
4658
- await runtime.client.mutation(
4659
- api.functions.agentWorkspace.agentAppendLog,
4660
- {
4661
- sessionToken: runtime.sessionToken,
4662
- message,
4663
- taskId: option("--task") as never,
4664
- },
4665
- ),
4666
- );
4667
- return;
4668
- }
4669
- rejectApiKeyForAgentCommand(runtime, "dench log");
4670
- print(
4671
- await runtime.client.mutation(api.functions.agentWorkspace.devAppendLog, {
4672
- ...runtime.workspaceArgs,
4673
- message,
4674
- agentId: option("--agent") as never,
4675
- taskId: option("--task") as never,
4676
- }),
4677
- );
4678
- return;
4679
- }
4680
-
4681
4309
  if (command === "approval" && subcommand === "request") {
4682
4310
  const title = positional(2);
4683
4311
  if (!title) throw new Error("Missing approval title");
@@ -4690,7 +4318,6 @@ async function main() {
4690
4318
  title,
4691
4319
  reason: option("--reason"),
4692
4320
  risk: (option("--risk") ?? "high") as "low" | "medium" | "high",
4693
- taskId: option("--task") as never,
4694
4321
  },
4695
4322
  ),
4696
4323
  );
@@ -4707,7 +4334,6 @@ async function main() {
4707
4334
  "Agent requests human approval before a risky action.",
4708
4335
  risk: (option("--risk") ?? "high") as "low" | "medium" | "high",
4709
4336
  agentId: option("--agent") as never,
4710
- taskId: option("--task") as never,
4711
4337
  },
4712
4338
  );
4713
4339
  print({ ok: true, approvalId });
package/host.ts CHANGED
@@ -14,7 +14,8 @@ export const DEFAULT_HOST = PRODUCTION_HOST;
14
14
  * deployment + Next.js host backs `https://www.dench.com`.
15
15
  */
16
16
  export const PRODUCTION_API_URL = "https://www.dench.com";
17
- export const PRODUCTION_CONVEX_URL = "https://beaming-alligator-67.convex.cloud";
17
+ export const PRODUCTION_CONVEX_URL =
18
+ "https://beaming-alligator-67.convex.cloud";
18
19
  export const PRODUCTION_GATEWAY_URL = "https://gateway.merseoriginals.com";
19
20
 
20
21
  export type BackendAlias = "production" | "prod" | "staging" | "local";
@@ -52,6 +53,16 @@ export function isLocalHost(input: string) {
52
53
  );
53
54
  }
54
55
 
56
+ export function apiHostForDenchHost(input: string | undefined) {
57
+ if (!input?.trim()) return PRODUCTION_API_URL;
58
+ const normalized = normalizeHost(input);
59
+ // `dench.com` redirects to `www.dench.com` in production. Fetch drops the
60
+ // Authorization header across that redirect, so CLI API calls must start on
61
+ // the canonical API origin.
62
+ if (normalized === PRODUCTION_HOST) return PRODUCTION_API_URL;
63
+ return normalized;
64
+ }
65
+
55
66
  /**
56
67
  * Resolve `local|staging|prod|production|<url>` -> a normalized host
57
68
  * origin. Aliases let `dench backend set <alias>` (and `--backend
@@ -73,7 +84,11 @@ export function resolveBackendAlias(input: string): string {
73
84
  if (normalized === "staging" || normalized === "stage") {
74
85
  return STAGING_HOST;
75
86
  }
76
- if (normalized === "local" || normalized === "localhost" || normalized === "dev") {
87
+ if (
88
+ normalized === "local" ||
89
+ normalized === "localhost" ||
90
+ normalized === "dev"
91
+ ) {
77
92
  return LOCAL_HOST;
78
93
  }
79
94
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dench.com/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Dench agent workspace CLI. v2 unifies auth behind `dench signin`; the legacy `dench login` / `dench onboard` / `dench setup` / `dench what-can-i-do` / `dench register` commands now error with a redirect.",
5
5
  "type": "module",
6
6
  "bin": {