@dench.com/cli 3.5.8-staging.350 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/crm.ts CHANGED
@@ -74,6 +74,7 @@ import {
74
74
  enrichProfiles,
75
75
  } from "./lib/enrichment-gateway";
76
76
  import { closestMatch, levenshteinDistance } from "./lib/levenshtein";
77
+ import { publicErrorMessage } from "./lib/public-error";
77
78
 
78
79
  type JsonRecord = Record<string, unknown>;
79
80
 
@@ -233,8 +234,10 @@ function userFacingConvexErrorMessage(error: unknown): string | null {
233
234
  function normalizeRpcError(error: unknown): Error {
234
235
  if (error instanceof CrmCliError) return error;
235
236
  return new CrmCliError(
236
- userFacingConvexErrorMessage(error) ??
237
- (error instanceof Error ? error.message : String(error)),
237
+ publicErrorMessage(
238
+ userFacingConvexErrorMessage(error) ??
239
+ (error instanceof Error ? error.message : String(error)),
240
+ ),
238
241
  );
239
242
  }
240
243
 
@@ -840,7 +843,9 @@ function getFieldEnrichmentFlag(args: string[]):
840
843
  | undefined {
841
844
  const category = parseCategoryFlag(getFlag(args, "--enrichment-category"));
842
845
  const key = getFlag(args, "--enrichment-key");
843
- const apolloPath = getFlag(args, "--apollo-path");
846
+ const apolloPath = getFlagWithAliases(args, "--response-path", [
847
+ "--apollo-path",
848
+ ]);
844
849
  const inputFieldName = getFlag(args, "--input-field");
845
850
  const sawAny =
846
851
  category !== undefined ||
@@ -850,7 +855,7 @@ function getFieldEnrichmentFlag(args: string[]):
850
855
  if (!sawAny) return undefined;
851
856
  if (!category || !apolloPath || !inputFieldName) {
852
857
  throw new CrmCliError(
853
- "Enrichment fields require --enrichment-category people|company, --apollo-path <path>, and --input-field <field>.",
858
+ "Enrichment fields require --enrichment-category people|company, --response-path <path>, and --input-field <field>.",
854
859
  );
855
860
  }
856
861
  return {
@@ -3991,7 +3996,7 @@ function resolveEnrichmentColumn(args: {
3991
3996
  };
3992
3997
  }
3993
3998
  throw new CrmCliError(
3994
- `No enrichment catalog column found for "${args.fieldName}". Pass --apollo-path for a custom gateway response path.`,
3999
+ `No enrichment catalog column found for "${args.fieldName}". Pass --response-path for a custom response path.`,
3995
4000
  );
3996
4001
  }
3997
4002
 
@@ -4493,7 +4498,9 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
4493
4498
  const fieldName = shift(ctx.args, "field name");
4494
4499
  const inputFieldName = getFlag(ctx.args, "--input-field");
4495
4500
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
4496
- const apolloPath = getFlag(ctx.args, "--apollo-path");
4501
+ const apolloPath = getFlagWithAliases(ctx.args, "--response-path", [
4502
+ "--apollo-path",
4503
+ ]);
4497
4504
  const overwrite = hasFlag(ctx.args, "--overwrite");
4498
4505
  assertNoUnknownFlags(ctx.args, "crm enrich cell");
4499
4506
  out(
@@ -4516,13 +4523,15 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
4516
4523
  const missingOnly = !hasFlag(ctx.args, "--all");
4517
4524
  const inputFieldName = getFlag(ctx.args, "--input-field");
4518
4525
  const category = parseCategoryFlag(getFlag(ctx.args, "--category"));
4519
- const apolloPath = getFlag(ctx.args, "--apollo-path");
4526
+ const apolloPath = getFlagWithAliases(ctx.args, "--response-path", [
4527
+ "--apollo-path",
4528
+ ]);
4520
4529
  const overwrite = hasFlag(ctx.args, "--overwrite");
4521
4530
  const limit = parsePositiveInt("--limit", getFlag(ctx.args, "--limit"));
4522
4531
  assertNoUnknownFlags(ctx.args, "crm enrich object");
4523
4532
  if (!fieldName) {
4524
4533
  throw new CrmCliError(
4525
- "dench crm enrich object <name> --field <field> [--input-field <field>] [--category people|company] [--apollo-path <path>] [--limit N] [--all] [--overwrite]",
4534
+ "dench crm enrich object <name> --field <field> [--input-field <field>] [--category people|company] [--response-path <path>] [--limit N] [--all] [--overwrite]",
4526
4535
  );
4527
4536
  }
4528
4537
  out(
@@ -4679,9 +4688,9 @@ Fields (columns):
4679
4688
  dench crm fields create <object> <name> --type text|email|phone|url|number|boolean|date|enum|relation|user|tags|action|file|richtext \\
4680
4689
  [--required] [--description ...] [--enum-values JSON] [--enum-colors JSON] \\
4681
4690
  [--enum-multiple] [--related-object <name>] [--relationship many_to_one|many_to_many] \\
4682
- [--indexed | --no-indexed] [--enrichment-category people|company --apollo-path <path> --input-field <field>]
4691
+ [--indexed | --no-indexed] [--enrichment-category people|company --response-path <path> --input-field <field>]
4683
4692
  dench crm fields update <object> <field> [--description ...] [--required] [--enum-values JSON] [--indexed | --no-indexed] \\
4684
- [--enrichment-category people|company --apollo-path <path> --input-field <field> | --clear-enrichment]
4693
+ [--enrichment-category people|company --response-path <path> --input-field <field> | --clear-enrichment]
4685
4694
  dench crm fields create <object> "Email opened" --calculation email_opened [--campaign <id>]
4686
4695
  dench crm fields update <object> <field> --calculation <metric> [--campaign <id>]
4687
4696
  Metrics: email_opened, email_clicked, replied, linkedin_connected.
@@ -4950,8 +4959,8 @@ People + companies shortcuts:
4950
4959
 
4951
4960
  Cell / row / object enrichment:
4952
4961
  dench crm enrich profiles --kind people|companies --file lookups.json [--json]
4953
- dench crm enrich cell <object> <entryId> <field> [--input-field <field>] [--category people|company] [--apollo-path <path>] [--overwrite]
4954
- dench crm enrich object <object> --field <field> [--input-field <field>] [--category people|company] [--apollo-path <path>] [--limit N] [--all] [--overwrite]
4962
+ dench crm enrich cell <object> <entryId> <field> [--input-field <field>] [--category people|company] [--response-path <path>] [--overwrite]
4963
+ dench crm enrich object <object> --field <field> [--input-field <field>] [--category people|company] [--response-path <path>] [--limit N] [--all] [--overwrite]
4955
4964
 
4956
4965
  Reports (chart-ready JSON):
4957
4966
  dench crm reports generate '<name>' --object <object> --type pie|bar|line|table --group-by <field> [--metric count|sum --metric-field <field>]
package/cron.ts CHANGED
@@ -409,14 +409,15 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
409
409
  if (target && target !== "chat_turn") {
410
410
  throw new CronCliError(`--target must be chat_turn (got "${target}")`);
411
411
  }
412
- const triggerType = (getFlag(ctx.args, "--type") ?? "schedule") as
412
+ const requestedType = getFlag(ctx.args, "--type") ?? "schedule";
413
+ const triggerType = (requestedType === "app" ? "composio" : requestedType) as
413
414
  | "schedule"
414
415
  | "webhook"
415
416
  | "composio"
416
417
  | "crm";
417
418
  if (!["schedule", "webhook", "composio", "crm"].includes(triggerType)) {
418
419
  throw new CronCliError(
419
- `--type must be schedule|webhook|composio|crm (got "${triggerType}").`,
420
+ `--type must be schedule|webhook|app|crm (got "${requestedType}").`,
420
421
  );
421
422
  }
422
423
 
@@ -601,7 +602,7 @@ export function buildAppOrCrmCreateBody(
601
602
  " Discover events: dench cron events <toolkit> (use the event slug)\n" +
602
603
  "Example:\n" +
603
604
  ' dench cron create "New GitHub star" --prompt "Thank the new stargazer" \\\n' +
604
- " --type composio --connection ca_123 --event GITHUB_STAR_ADDED_EVENT --toolkit github",
605
+ " --type app --connection ca_123 --event GITHUB_STAR_ADDED_EVENT --toolkit github",
605
606
  );
606
607
  }
607
608
  const toolkit = getFlag(args, "--toolkit");
@@ -867,7 +868,7 @@ Browse:
867
868
  Create / update:
868
869
  dench cron create "Daily standup digest" \\
869
870
  --prompt "Pull yesterday's commits + Slack #standup msgs and post a summary…" \\
870
- --type schedule # default. also: webhook | composio | crm (see below)
871
+ --type schedule # default. also: webhook | app | crm (see below)
871
872
  --every 1d # or --cron "0 9 * * 1-5" or --at 2026-05-04T09:00:00Z
872
873
  [--description "..."] # human-readable note shown in dashboard / list
873
874
  [--overlap skip|queue|kill_old] # default: skip
@@ -885,7 +886,7 @@ Create / update:
885
886
  # 1. dench apps --json → connectedAccountId + toolkit
886
887
  # 2. dench cron events <toolkit> → event slug
887
888
  dench cron create "New GitHub star" --prompt "Thank the new stargazer" \\
888
- --type composio --connection <connectedAccountId> --event <triggerSlug> \\
889
+ --type app --connection <connectedAccountId> --event <triggerSlug> \\
889
890
  [--toolkit <slug>] [--trigger-config '<json>']
890
891
 
891
892
  # CRM-event automation (fires on a CRM object create/update/delete).
package/dench.ts CHANGED
@@ -28,7 +28,9 @@ import {
28
28
  resolveHostFromArgs,
29
29
  STAGING_HOST,
30
30
  } from "./host";
31
+ import { cliVersion } from "./lib/cli-version";
31
32
  import { resolveOrganizationBySelector } from "./lib/organizationSelector";
33
+ import { backendErrorText } from "./lib/public-error";
32
34
  import { normalizeUnipileToolkitSlug } from "./lib/unipile-toolkits";
33
35
  import { formatApprovalOpenMessage, openUrl } from "./openUrl";
34
36
  import {
@@ -47,6 +49,7 @@ import {
47
49
  withSavedSession,
48
50
  } from "./session";
49
51
  import {
52
+ connectionSource,
50
53
  fetchConnectedAppsSummary,
51
54
  fetchUnipileConnections,
52
55
  runToolCommand,
@@ -214,19 +217,6 @@ const json = args.includes("--json");
214
217
  const filteredArgs = args.filter((arg) => arg !== "--json");
215
218
  const scopedStatus = args.includes("--mine") || args.includes("--self");
216
219
 
217
- async function cliVersion() {
218
- try {
219
- const packageJson = JSON.parse(
220
- await readFile(new URL("./package.json", import.meta.url), "utf8"),
221
- ) as { version?: unknown };
222
- return typeof packageJson.version === "string"
223
- ? packageJson.version
224
- : "unknown";
225
- } catch {
226
- return "unknown";
227
- }
228
- }
229
-
230
220
  function readEnv(name: string) {
231
221
  const value = process.env[name]?.trim();
232
222
  if (!value) {
@@ -248,6 +238,12 @@ function hasFlag(name: string) {
248
238
  const BOOLEAN_FLAGS = new Set([
249
239
  "--all",
250
240
  "--allow-duplicates",
241
+ "--allow-active",
242
+ "--raw",
243
+ "--check",
244
+ "--allow-single-braces",
245
+ "--with-ids",
246
+ "--yes",
251
247
  "--ack-linkedin-risk",
252
248
  "--stop-on-reply",
253
249
  "--no-stop-on-reply",
@@ -326,41 +322,16 @@ function throwIfCliError(value: unknown) {
326
322
  }
327
323
  }
328
324
 
329
- /**
330
- * Production Convex deployments mask `error.message` ("[Request ID: …]
331
- * Server Error") but forward `ConvexError.data` verbatim. Prefer the data
332
- * payload so agents see the actionable message instead of the mask.
333
- */
334
- function convexErrorData(error: unknown): string | undefined {
335
- if (!error || typeof error !== "object" || !("data" in error)) {
336
- return undefined;
337
- }
338
- const data = (error as { data?: unknown }).data;
339
- if (typeof data === "string" && data.trim()) return data;
340
- if (data && typeof data === "object") {
341
- const message = (data as { message?: unknown }).message;
342
- if (typeof message === "string" && message.trim()) return message;
343
- try {
344
- return JSON.stringify(data);
345
- } catch {
346
- return undefined;
347
- }
348
- }
349
- return undefined;
350
- }
351
-
352
325
  function normalizeCliError(error: unknown): CliError {
353
326
  if (error instanceof CliError) return error;
354
- const message =
355
- convexErrorData(error) ??
356
- (error instanceof Error ? error.message : String(error));
327
+ const message = backendErrorText(error);
357
328
  if (message.includes("Invalid dev agent key")) {
358
329
  return new CliError("Invalid dev agent key", {
359
330
  code: "invalid_dev_agent_key",
360
331
  nextActions: [
361
- "Check that DENCH_DEV_AGENT_KEY in your shell matches the Convex deployment env.",
362
- "Use --dev only for a local/dev Convex deployment configured with that key.",
363
- "For normal terminal usage, run dench signin or set DENCH_API_KEY + CONVEX_URL.",
332
+ "Check that DENCH_DEV_AGENT_KEY in your shell matches the dev deployment env.",
333
+ "Use --dev only for a local/dev deployment configured with that key.",
334
+ "For normal terminal usage, run dench signin or set DENCH_API_KEY.",
364
335
  ],
365
336
  });
366
337
  }
@@ -369,7 +340,7 @@ function normalizeCliError(error: unknown): CliError {
369
340
  code: "dev_access_disabled",
370
341
  nextActions: [
371
342
  "Remove --dev and run dench signin for terminal access.",
372
- "Or use DENCH_API_KEY + CONVEX_URL for sandbox/automation access.",
343
+ "Or use DENCH_API_KEY for sandbox/automation access.",
373
344
  ],
374
345
  });
375
346
  }
@@ -389,7 +360,7 @@ function normalizeCliError(error: unknown): CliError {
389
360
  return new CliError("Invalid Dench API key", {
390
361
  code: "invalid_api_key",
391
362
  nextActions: [
392
- "Check DENCH_API_KEY and CONVEX_URL point to the same organization/deployment.",
363
+ "Check that DENCH_API_KEY is a current key for your workspace.",
393
364
  "Unset DENCH_API_KEY to use a saved dench signin session instead.",
394
365
  "Or run dench signin to create a new terminal session.",
395
366
  ],
@@ -521,8 +492,8 @@ Usage:
521
492
  dench tool status [toolkit] [--json]
522
493
  dench tool connect <toolkit> [--json]
523
494
  dench tool search "create github issue" [--toolkit github] [--limit 20] [--compact] [--json]
524
- dench tool run <composio_tool_slug> --args '{"key":"value"}' [--account <connected_account_id>] [--approval <approvalId>] [--json]
525
- CRM (Daytona-native rewrite):
495
+ dench tool run <tool_slug> --args '{"key":"value"}' [--account <connected_account_id>] [--approval <approvalId>] [--json]
496
+ CRM:
526
497
  dench crm objects <list|get|create|update|rename|delete> ...
527
498
  dench crm fields <list|create|update|delete|reorder> <object> ...
528
499
  dench crm entries <list|get|create|create-many|update|update-many|delete|bulk-delete> <object> ...
@@ -579,7 +550,7 @@ Workspace members (for CRM \`user\`-type fields, and who's doing what):
579
550
  dench members presence [<userId|email>...] online / away / offline
580
551
  dench members me [--name --tagline --timezone --avatar <file>] your own profile
581
552
 
582
- Live web search (Exa via the Dench Gateway, auths with the active \`dench signin\` agent session or DENCH_API_KEY):
553
+ Live web search (via the Dench Gateway, auths with the active \`dench signin\` agent session or DENCH_API_KEY):
583
554
  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]
584
555
  dench search contents <url> [<url>...] [--max-chars 4000] [--summary "..."] [--json]
585
556
  dench search answer "<query>" [--json]
@@ -624,14 +595,23 @@ Managed email campaigns:
624
595
  Help: dench email help
625
596
 
626
597
  Outreach campaigns (email + LinkedIn steps on one schedule; leads move New → Contacted → Replied):
627
- dench campaign list [--all] [--json]
628
- dench campaign create --name "Founder outreach" [--timezone America/Los_Angeles] [--window "Mon-Fri 9-18"] [--daily-cap 30] [--json]
629
- dench campaign steps set <campaign> --file steps.json [--json]
630
- dench campaign add <campaign> --object lead --view "Q3 targets" [--json]
598
+ dench campaign accounts [--json] (sender ids, warm-up and capacity)
599
+ dench campaign draft --name "Founder outreach" --request-id founders-q4 [--timezone America/Los_Angeles] [--window "Mon-Fri 9-18"] [--json]
600
+ dench campaign search --filters '{"titles":["Founder"]}' [--not-industries "Staffing and Recruiting"] [--limit 25] [--out audience.json] [--json] (1 cent per person returned)
601
+ dench campaign add <campaign> --people-file audience.json | --csv people.csv | --list <artifactId> | --search '{...}' | --object lead --view "Q3 targets" [--json]
602
+ dench campaign steps get|set|edit <campaign> [--file steps.json] [--json] (set: drafts only; edit: text and waits, also after launch)
603
+ dench campaign personalize set <campaign> --file lines.json (AI-written lines, about 3 cents a person)
604
+ dench campaign preview <campaign> [--person <name>] [--step N] (the exact messages one person gets)
605
+ dench campaign launch <campaign> --check [--json] (readiness only; never launches)
631
606
  dench campaign launch <campaign> [--ack-linkedin-risk] [--json]
632
607
  dench campaign get <campaign> [--json] (status, funnel, Now/Next, holds, people)
633
- dench campaign pause|resume|archive <campaign> [--json]
634
- Help: dench campaign help
608
+ dench campaign people|events|replies|pipeline <campaign> [--json]
609
+ dench campaign duplicate|pause|resume|archive <campaign> [--json]
610
+ dench campaign stop|remove|resume-person <campaign> --person <id> [--json]
611
+ dench campaign person show|set <campaign> --person <id> [--json] · dench campaign sync <campaign> · dench campaign where <entryId>
612
+ dench campaign builder profile|save|get|build|create|opener|autofill|terms|estimate [--json]
613
+ dench campaign sources get|signals|set|preview <campaign> [--json]
614
+ Help: dench campaign help (recipe from a brief to launch: references/CAMPAIGNS.md)
635
615
 
636
616
  Image generation / editing (via the Dench Gateway):
637
617
  dench image generate "<prompt>" [--size 1024x1024] [--quality auto|low|medium|high] [--format png|jpeg|webp] [--save-path /workspace/path/file.png] [--output <local file>] [--no-write] [--json]
@@ -674,7 +654,7 @@ returns ENOSYS on the FUSE-backed volume mount):
674
654
  dench files rm <path> [--recursive] [--json]
675
655
  dench files download <path> [<local-dest>] [--url] [--json]
676
656
 
677
- Persist scratch -> workspace (cp from /tmp into /workspace + Convex sync):
657
+ Persist scratch -> workspace (cp from /tmp into /workspace + cloud sync):
678
658
  dench stage <local-path> [<workspace-dest>] [--clean] [--json]
679
659
 
680
660
  Subagents:
@@ -704,7 +684,7 @@ Self-updating agent harness:
704
684
 
705
685
  External tools:
706
686
  dench integrations is an alias for dench tool status -- it lists connected apps.
707
- dench apps is the App Store (create / install / publish / list), not Composio.
687
+ dench apps is the App Store (create / install / publish / list), not connected apps.
708
688
  dench tool with no subcommand prints tool help.
709
689
  Read-only FETCH, GET, LIST, SEARCH, READ, and FIND tools do not need manual approval.
710
690
 
@@ -741,10 +721,10 @@ Backends and hosts:
741
721
  dench --host https://example.com <cmd>
742
722
  DENCH_HOST=https://example.com dench <cmd>
743
723
 
744
- Dev fallback (run against a local Convex deployment with a dev key):
745
- DENCH_DEV_AGENT_KEY=... DENCH_WORKSPACE=... NEXT_PUBLIC_CONVEX_URL=... dench status --dev
746
- Inside the dench.com repo, --dev also auto-loads .env / .env.local
747
- so GATEWAY_URL etc. flow through. Use --use-dev-env (or
724
+ Dev fallback (run against a local dev deployment with a dev key):
725
+ DENCH_DEV_AGENT_KEY=... DENCH_WORKSPACE=... dench status --dev
726
+ Inside the dench.com repo, --dev also auto-loads .env / .env.local,
727
+ which carry the dev backend URL and GATEWAY_URL etc. Use --use-dev-env (or
748
728
  DENCH_USE_DEV_ENV=1) when you want that auto-load without switching
749
729
  the runtime to the dev workspace mode.
750
730
 
@@ -800,22 +780,9 @@ lists agent suggested-work artifacts. Rename/delete saved searches with
800
780
  `);
801
781
  }
802
782
 
803
- function listsCliHelp() {
804
- console.log(`Dench lists
805
-
806
- Usage:
807
- dench lists [--limit 25] [--json]
808
- dench lists show <id> [--json]
809
- dench lists save --title "Shortlist" --people-file <people.json> [--json]
810
- dench lists rename <id> "New name" [--json]
811
- dench lists update <id> [--title "New name"] [--filters-json '{"titles":["CTO"]}'] [--json]
812
- dench lists delete <id> [--json]
813
-
814
- Lists are saved searches from Find (people / companies / job postings).
815
- Create one with \`dench leads|companies|jobs find --save-as "Name"\`.
816
- Delete removes the list, its exported CSV/JSONL files, and any linked watch.
817
- CRM lists (saved views) are managed with \`dench crm views ...\`.
818
- `);
783
+ async function listsCliHelp() {
784
+ const { listsHelp } = await import("./leads");
785
+ listsHelp();
819
786
  }
820
787
 
821
788
  function leadsHelp() {
@@ -925,26 +892,26 @@ function integrationsHelp() {
925
892
  Usage:
926
893
  dench integrations [--json]
927
894
 
928
- Lists every app connected to this organization, from both brokers:
895
+ Lists every app connected to this organization:
929
896
 
930
- - Composio apps (GitHub, Notion, HubSpot, …), each with a
897
+ - Connected apps (GitHub, Notion, HubSpot, …), each with a
931
898
  \`connectedAccountId\` you can pass to \`dench tool run --account\`.
932
- - Gmail, Outlook, Calendar, LinkedIn (\`source: "unipile"\`) and Slack
933
- (\`source: "dench"\`), which Dench connects itself. These have no
934
- connected-account id, but they run through the SAME \`dench tool
935
- search\` / \`dench tool run\` — find the slug and run it. \`dench tool
936
- connect slack\` prints the in-house Slack install URL (org admin);
937
- \`dench tool connect <gmail|outlook|linkedin|calendar>\` prints a
938
- Unipile hosted-auth link using the web app's configuration. Open
939
- that link to authorize the account, then check \`dench tool status\`.
940
- Slack is ALWAYS in-house (never Composio): SLACK_* tools can post
941
- to / read any channel, with Block Kit blocks and buttons.
899
+ - Gmail, Outlook, Calendar, LinkedIn and Slack (\`source: "dench"\`), which
900
+ Dench connects itself. These have no connected-account id, but they run
901
+ through the SAME \`dench tool search\` / \`dench tool run\` — find the
902
+ slug and run it. \`dench tool connect slack\` prints the in-house Slack
903
+ install URL (org admin); \`dench tool connect <gmail|outlook|linkedin|calendar>\`
904
+ prints a Dench sign-in link using the web app's configuration. Open that
905
+ link to authorize the account, then check \`dench tool status\`.
906
+ Slack is ALWAYS in-house: SLACK_* tools can post to / read any channel,
907
+ with Block Kit blocks and buttons.
942
908
  For reading, the CRM is usually better: calendar events sync into
943
909
  \`calendar_event\` and recordings into \`meeting\`.
944
910
 
945
- Other toolkits connect through Composio. If \`unipileAvailable\` is
946
- false, native status could not be checked; do not assume an account
947
- is disconnected. Any Composio lookup failure is reported separately.
911
+ Other toolkits connect as connected apps. If \`nativeAvailable\` is
912
+ false, Dench-connected accounts could not be checked; do not assume an
913
+ account is disconnected. A failure to check connected apps is reported
914
+ separately.
948
915
 
949
916
  Authenticates with the active \`dench signin\` agent session or
950
917
  DENCH_API_KEY (Bearer), whichever is set. Same output as
@@ -2579,7 +2546,7 @@ function loginRequiredError(
2579
2546
  : {}),
2580
2547
  nextActions: [
2581
2548
  ...mismatchActions,
2582
- "Set DENCH_API_KEY (and CONVEX_URL) in your env if running inside a sandbox or CI.",
2549
+ "Set DENCH_API_KEY in your env if running inside a sandbox or CI.",
2583
2550
  'Or run dench signin --kind <kind> --name "AI Agent - Project" to mint an agent session.',
2584
2551
  "If already approved, run dench sessions --json, then dench use <session-key-or-workspace-slug>.",
2585
2552
  ],
@@ -3062,8 +3029,8 @@ async function connectedAppsContext(runtime: Runtime) {
3062
3029
  reason: errorMessage(error),
3063
3030
  count: unipile.connections.length,
3064
3031
  connections: unipile.connections,
3065
- unipileAvailable: unipile.available,
3066
- ...(unipile.error ? { unipileError: unipile.error } : {}),
3032
+ nativeAvailable: unipile.available,
3033
+ ...(unipile.error ? { nativeError: unipile.error } : {}),
3067
3034
  };
3068
3035
  }
3069
3036
  }
@@ -3183,7 +3150,9 @@ function formatContext(context: JsonRecord) {
3183
3150
  ...connections.slice(0, 8).map((connection) => {
3184
3151
  const label = stringField(connection, "accountLabel");
3185
3152
  const via =
3186
- stringField(connection, "source") === "unipile" ? " via Dench" : "";
3153
+ connectionSource(stringField(connection, "source"), "app") === "dench"
3154
+ ? " via Dench"
3155
+ : "";
3187
3156
  return ` - ${stringField(connection, "toolkit") ?? "unknown"} [${
3188
3157
  stringField(connection, "status") ?? "unknown"
3189
3158
  }]${label ? ` ${label}` : ""}${via}`;
@@ -3191,16 +3160,16 @@ function formatContext(context: JsonRecord) {
3191
3160
  );
3192
3161
  if (connectedApps?.available === false) {
3193
3162
  lines.push(
3194
- ` (could not check Composio apps: ${
3163
+ ` (could not check connected apps: ${
3195
3164
  stringField(connectedApps, "error", "reason") ?? "unknown error"
3196
3165
  })`,
3197
3166
  );
3198
3167
  }
3199
3168
  }
3200
- if (connectedApps?.unipileAvailable === false) {
3169
+ if (connectedApps?.nativeAvailable === false) {
3201
3170
  lines.push(
3202
3171
  ` (could not check Gmail/Outlook/Calendar/LinkedIn: ${
3203
- stringField(connectedApps, "unipileError") ?? "unknown error"
3172
+ stringField(connectedApps, "nativeError") ?? "unknown error"
3204
3173
  })`,
3205
3174
  );
3206
3175
  }
@@ -3549,7 +3518,7 @@ async function uploadConvexFileBytes(
3549
3518
  });
3550
3519
  if (!response.ok) {
3551
3520
  throw new Error(
3552
- `convex storage upload failed (${response.status}): ${await response
3521
+ `workspace storage upload failed (${response.status}): ${await response
3553
3522
  .text()
3554
3523
  .catch(() => "")}`,
3555
3524
  );
@@ -3633,7 +3602,7 @@ async function runFilesMv() {
3633
3602
  await listConvexTreeAt(runtime, apiKey, parentOfPath(from))
3634
3603
  ).find((row) => row.path === from);
3635
3604
  if (!sourceSelf && sourceRows.length === 0) {
3636
- throw new CliError(`Source not found in Convex: ${from}`, {
3605
+ throw new CliError(`Source not found in workspace storage: ${from}`, {
3637
3606
  code: "files_mv_not_found",
3638
3607
  nextActions: [
3639
3608
  "Run `dench files ls` on the parent directory to confirm.",
@@ -3704,7 +3673,7 @@ async function runFilesRm() {
3704
3673
  print({ ok: true, path: target, removedFiles: 0, removedDirs: 0 });
3705
3674
  } else {
3706
3675
  process.stdout.write(
3707
- `dench files rm: ${target} not present in Convex (already absent)\n`,
3676
+ `dench files rm: ${target} not present in workspace storage (already absent)\n`,
3708
3677
  );
3709
3678
  }
3710
3679
  return;
@@ -3779,7 +3748,7 @@ async function runFilesCat() {
3779
3748
  } | null;
3780
3749
 
3781
3750
  if (!file) {
3782
- throw new CliError(`Not found in Convex: ${target}`, {
3751
+ throw new CliError(`Not found in workspace storage: ${target}`, {
3783
3752
  code: "files_cat_not_found",
3784
3753
  nextActions: [
3785
3754
  "Run `dench files ls` on the parent directory to confirm.",
@@ -3915,7 +3884,7 @@ async function runFilesDownload() {
3915
3884
  if (!isDir) {
3916
3885
  const bytes = await downloadConvexFileBytes(runtime, apiKey, target);
3917
3886
  if (!bytes) {
3918
- throw new CliError(`Not found in Convex: ${target}`, {
3887
+ throw new CliError(`Not found in workspace storage: ${target}`, {
3919
3888
  code: "files_download_not_found",
3920
3889
  nextActions: [
3921
3890
  "Run `dench files ls` on the parent directory to confirm.",
@@ -4005,10 +3974,10 @@ async function runFilesCommand() {
4005
3974
  " dench files download <path> [<local-dest>] [--url] [--json]",
4006
3975
  "",
4007
3976
  "Paths are POSIX paths under /workspace; you can pass either form.",
4008
- "These mutate Convex directly (and the daemon syncs to the volume).",
3977
+ "These change workspace storage directly (and the daemon syncs to the volume).",
4009
3978
  "cat prints inline text and is the only way to read the magic files",
4010
3979
  "(IDENTITY.md, ORGANISATION.md, …), which have no stored bytes.",
4011
- "download fetches canonical bytes from Convex (dirs recurse);",
3980
+ "download fetches the canonical bytes from workspace storage (dirs recurse);",
4012
3981
  "--url prints a short-lived signed URL instead of saving.",
4013
3982
  ].join("\n"),
4014
3983
  );
@@ -4444,7 +4413,7 @@ async function main() {
4444
4413
  }
4445
4414
 
4446
4415
  if (command === "lists" && (subcommand === "help" || hasFlag("--help"))) {
4447
- listsCliHelp();
4416
+ await listsCliHelp();
4448
4417
  return;
4449
4418
  }
4450
4419
 
@@ -5131,6 +5100,7 @@ async function main() {
5131
5100
  args: subArgs,
5132
5101
  jsonOutput: json,
5133
5102
  sessionToken: authedRuntime.sessionToken,
5103
+ host: authedRuntime.host,
5134
5104
  });
5135
5105
  return;
5136
5106
  }
@@ -5396,7 +5366,7 @@ async function main() {
5396
5366
  jsonOutput: json,
5397
5367
  sessionToken,
5398
5368
  });
5399
- if (!handled) listsCliHelp();
5369
+ if (!handled) await listsCliHelp();
5400
5370
  return;
5401
5371
  }
5402
5372
 
package/fs-daemon.ts CHANGED
@@ -545,7 +545,7 @@ async function processPullRequests(args: {
545
545
  deadline,
546
546
  );
547
547
  if (!row || row.isDir || !row.contentHash) {
548
- throw new Error("File is not present in Convex fileTree.");
548
+ throw new Error("File is not present in the workspace file tree.");
549
549
  }
550
550
  await pullDownRemoteFile(args.client, {
551
551
  expectedHash: row.contentHash,
@@ -600,7 +600,7 @@ async function runDaemon(daemonArgs: Args): Promise<void> {
600
600
  );
601
601
  if (mount.isFuse) {
602
602
  console.log(
603
- "[dench-fs-daemon] FUSE backing detected; enabling polling mode (inotify is not delivered through Mountpoint-S3)",
603
+ "[dench-fs-daemon] FUSE backing detected; enabling polling mode (inotify is not delivered through the mounted workspace)",
604
604
  );
605
605
  }
606
606
  daemonMode = true;
@@ -1692,9 +1692,9 @@ function printStatusHuman(report: StatusReport): void {
1692
1692
  lines.push("file counts");
1693
1693
  lines.push("─".repeat(40));
1694
1694
  lines.push(`on disk: ${report.counts.onDisk}`);
1695
- lines.push(`in convex: ${report.counts.inConvex}`);
1695
+ lines.push(`in cloud: ${report.counts.inConvex}`);
1696
1696
  lines.push(`only on disk: ${report.counts.onlyOnDisk}`);
1697
- lines.push(`only in convex: ${report.counts.onlyInConvex}`);
1697
+ lines.push(`only in cloud: ${report.counts.onlyInConvex}`);
1698
1698
  lines.push(`hash mismatches: ${report.counts.hashMismatches}`);
1699
1699
 
1700
1700
  if (report.drift.length > 0) {
@@ -1705,10 +1705,10 @@ function printStatusHuman(report: StatusReport): void {
1705
1705
  if (item.kind === "only_disk") {
1706
1706
  lines.push(` + ${item.path} ${item.size}B (only on disk)`);
1707
1707
  } else if (item.kind === "only_convex") {
1708
- lines.push(` - ${item.path} (only in convex)`);
1708
+ lines.push(` - ${item.path} (only in cloud)`);
1709
1709
  } else {
1710
1710
  lines.push(
1711
- ` ! ${item.path} disk=${item.diskHashShort}… convex=${item.convexHashShort}…`,
1711
+ ` ! ${item.path} disk=${item.diskHashShort}… cloud=${item.convexHashShort}…`,
1712
1712
  );
1713
1713
  }
1714
1714
  }
package/image.ts CHANGED
@@ -177,7 +177,7 @@ function formatGenerationResult(
177
177
  localPath: string | null,
178
178
  ): string {
179
179
  const lines: string[] = [];
180
- lines.push(`Generated image (${res.model ?? "gpt-image-2"})`);
180
+ lines.push("Generated image");
181
181
  if (res.prompt) {
182
182
  lines.push(` Prompt: ${res.prompt}`);
183
183
  }
@@ -386,10 +386,10 @@ async function runEditSubcommand(ctx: ImageCliContext): Promise<void> {
386
386
  function imageHelp(): void {
387
387
  console.log(`Usage: dench image <subcommand>
388
388
 
389
- Generate or edit images via the Dench Cloud Gateway (gpt-image-2). Auths
389
+ Generate or edit images via the Dench Cloud Gateway. Auths
390
390
  with DENCH_API_KEY or the active \`dench signin\` agent session. Agent
391
- sessions require a paid workspace. Each call writes the bytes to Convex
392
- Storage (workspace tree updates immediately) and, when running inside a sandbox,
391
+ sessions require a paid workspace. Each call writes the bytes to workspace
392
+ storage (workspace tree updates immediately) and, when running inside a sandbox,
393
393
  also writes the decoded bytes to
394
394
  local disk so subsequent bash commands see the file without waiting on
395
395
  the daemon's downward sync.