@dench.com/cli 3.5.8-staging.350 → 3.6.0-staging.356
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/README.md +65 -55
- package/app-sdk.d.ts +1286 -31
- package/apps.ts +1 -1
- package/campaign.ts +1949 -212
- package/crm.ts +21 -12
- package/cron.ts +6 -5
- package/dench.ts +76 -106
- package/fs-daemon.ts +6 -6
- package/image.ts +4 -4
- package/leads.ts +60 -22
- package/lib/api-schemas.ts +1094 -30
- package/lib/app-sdk-catalog.generated.ts +82 -6
- package/lib/app-sdk-surface.ts +240 -0
- package/lib/campaign-agent.ts +1564 -0
- package/lib/campaign-csv.ts +883 -0
- package/lib/campaign-filters.ts +141 -0
- package/lib/campaign-lines.ts +217 -0
- package/lib/campaign-steps.ts +721 -0
- package/lib/campaign-timeline.ts +189 -0
- package/lib/cli-version.ts +14 -0
- package/lib/command-registry.ts +491 -8
- package/lib/connection-source.ts +10 -0
- package/lib/enrichment-gateway.ts +11 -2
- package/lib/public-error.ts +41 -0
- package/lib/search-pricing.ts +9 -0
- package/package.json +1 -1
- package/search.ts +5 -34
- package/tools.ts +51 -42
- package/web.ts +1 -1
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
|
-
|
|
237
|
-
(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 =
|
|
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, --
|
|
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 --
|
|
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 =
|
|
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 =
|
|
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] [--
|
|
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 --
|
|
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 --
|
|
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] [--
|
|
4954
|
-
dench crm enrich object <object> --field <field> [--input-field <field>] [--category people|company] [--
|
|
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
|
|
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|
|
|
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
|
|
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 |
|
|
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
|
|
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
|
|
362
|
-
"Use --dev only for a local/dev
|
|
363
|
-
"For normal terminal usage, run dench signin or set DENCH_API_KEY
|
|
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
|
|
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
|
|
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 <
|
|
525
|
-
CRM
|
|
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 (
|
|
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
|
|
628
|
-
dench campaign
|
|
629
|
-
dench campaign
|
|
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
|
|
634
|
-
|
|
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 +
|
|
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
|
|
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
|
|
745
|
-
DENCH_DEV_AGENT_KEY=... DENCH_WORKSPACE=...
|
|
746
|
-
Inside the dench.com repo, --dev also auto-loads .env / .env.local
|
|
747
|
-
|
|
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
|
-
|
|
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
|
|
895
|
+
Lists every app connected to this organization:
|
|
929
896
|
|
|
930
|
-
-
|
|
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: "
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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
|
|
946
|
-
false,
|
|
947
|
-
is disconnected.
|
|
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
|
|
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
|
-
|
|
3066
|
-
...(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")
|
|
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
|
|
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?.
|
|
3169
|
+
if (connectedApps?.nativeAvailable === false) {
|
|
3201
3170
|
lines.push(
|
|
3202
3171
|
` (could not check Gmail/Outlook/Calendar/LinkedIn: ${
|
|
3203
|
-
stringField(connectedApps, "
|
|
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
|
-
`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1695
|
+
lines.push(`in cloud: ${report.counts.inConvex}`);
|
|
1696
1696
|
lines.push(`only on disk: ${report.counts.onlyOnDisk}`);
|
|
1697
|
-
lines.push(`only in
|
|
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
|
|
1708
|
+
lines.push(` - ${item.path} (only in cloud)`);
|
|
1709
1709
|
} else {
|
|
1710
1710
|
lines.push(
|
|
1711
|
-
` ! ${item.path} disk=${item.diskHashShort}…
|
|
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(
|
|
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
|
|
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
|
|
392
|
-
|
|
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.
|