@dench.com/cli 3.6.6-staging.386 → 3.7.0-staging.387
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/agent-config.ts +17 -3
- package/app-sdk.d.ts +248 -10
- package/crm.ts +92 -24
- package/dench.ts +128 -19
- package/email-inbox.ts +169 -0
- package/email.ts +103 -21
- package/lib/api-schemas.ts +156 -14
- package/lib/app-sdk-catalog.generated.ts +9 -3
- package/lib/bundled-references.ts +17 -0
- package/lib/cli-args.ts +30 -3
- package/lib/command-args.ts +237 -0
- package/lib/command-help.ts +78 -0
- package/lib/command-registry.ts +75 -3
- package/lib/native-email-wait.ts +75 -0
- package/lib/public-error.ts +82 -0
- package/lib/references.generated.json +66 -0
- package/package.json +3 -1
- package/reference.ts +47 -0
package/crm.ts
CHANGED
|
@@ -239,6 +239,7 @@ function normalizeRpcError(error: unknown): Error {
|
|
|
239
239
|
userFacingConvexErrorMessage(error) ??
|
|
240
240
|
(error instanceof Error ? error.message : String(error)),
|
|
241
241
|
),
|
|
242
|
+
{ cause: error },
|
|
242
243
|
);
|
|
243
244
|
}
|
|
244
245
|
|
|
@@ -246,7 +247,8 @@ export function shift(args: string[], expected: string): string {
|
|
|
246
247
|
try {
|
|
247
248
|
return shiftRaw(args, expected);
|
|
248
249
|
} catch (error) {
|
|
249
|
-
if (error instanceof CliArgError)
|
|
250
|
+
if (error instanceof CliArgError)
|
|
251
|
+
throw new CrmCliError(error.message, { cause: error });
|
|
250
252
|
throw error;
|
|
251
253
|
}
|
|
252
254
|
}
|
|
@@ -290,7 +292,8 @@ function resolveLimitFlag(
|
|
|
290
292
|
try {
|
|
291
293
|
parsed = parseLimitRaw(raw, opts);
|
|
292
294
|
} catch (error) {
|
|
293
|
-
if (error instanceof CliArgError)
|
|
295
|
+
if (error instanceof CliArgError)
|
|
296
|
+
throw new CrmCliError(error.message, { cause: error });
|
|
294
297
|
throw error;
|
|
295
298
|
}
|
|
296
299
|
if (parsed.clamped) {
|
|
@@ -365,7 +368,8 @@ function parseJson(value: string | undefined): unknown {
|
|
|
365
368
|
try {
|
|
366
369
|
return parseJsonRaw(value);
|
|
367
370
|
} catch (error) {
|
|
368
|
-
if (error instanceof CliArgError)
|
|
371
|
+
if (error instanceof CliArgError)
|
|
372
|
+
throw new CrmCliError(error.message, { cause: error });
|
|
369
373
|
throw error;
|
|
370
374
|
}
|
|
371
375
|
}
|
|
@@ -377,7 +381,8 @@ export function assertNoUnknownFlags(
|
|
|
377
381
|
try {
|
|
378
382
|
assertNoUnknownFlagsRaw(args, command);
|
|
379
383
|
} catch (error) {
|
|
380
|
-
if (error instanceof CliArgError)
|
|
384
|
+
if (error instanceof CliArgError)
|
|
385
|
+
throw new CrmCliError(error.message, { cause: error });
|
|
381
386
|
throw error;
|
|
382
387
|
}
|
|
383
388
|
}
|
|
@@ -780,11 +785,23 @@ export async function callQuery(
|
|
|
780
785
|
}
|
|
781
786
|
}
|
|
782
787
|
|
|
788
|
+
/** Every parsed token must be accounted for before an external effect. */
|
|
789
|
+
function assertWriteArguments(ctx: CrmCliContext): void {
|
|
790
|
+
assertNoUnknownFlags(ctx.args, "crm");
|
|
791
|
+
if (ctx.args.length) {
|
|
792
|
+
const error = new CliArgError(
|
|
793
|
+
`Unexpected argument: ${ctx.args[0]}. Run the command with --help for usage.`,
|
|
794
|
+
);
|
|
795
|
+
throw new CrmCliError(error.message, { cause: error });
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
783
799
|
export async function callMutation(
|
|
784
800
|
ctx: CrmCliContext,
|
|
785
801
|
fn: Parameters<ConvexHttpClient["mutation"]>[0],
|
|
786
802
|
args: Record<string, unknown> = {},
|
|
787
803
|
): Promise<unknown> {
|
|
804
|
+
assertWriteArguments(ctx);
|
|
788
805
|
try {
|
|
789
806
|
return await ctx.convex.mutation(fn, {
|
|
790
807
|
...args,
|
|
@@ -800,6 +817,7 @@ export async function callAction(
|
|
|
800
817
|
fn: Parameters<ConvexHttpClient["action"]>[0],
|
|
801
818
|
args: Record<string, unknown> = {},
|
|
802
819
|
): Promise<unknown> {
|
|
820
|
+
assertWriteArguments(ctx);
|
|
803
821
|
try {
|
|
804
822
|
return await ctx.convex.action(fn, {
|
|
805
823
|
...args,
|
|
@@ -914,6 +932,9 @@ const api = {
|
|
|
914
932
|
},
|
|
915
933
|
objects: {
|
|
916
934
|
list: makeFunctionReference<"query">("functions/crm/objects:list"),
|
|
935
|
+
listCompact: makeFunctionReference<"query">(
|
|
936
|
+
"functions/crm/objects:listCompact",
|
|
937
|
+
),
|
|
917
938
|
get: makeFunctionReference<"query">("functions/crm/objects:get"),
|
|
918
939
|
materializeView: makeFunctionReference<"action">(
|
|
919
940
|
"functions/crm/objects:completeView",
|
|
@@ -963,6 +984,9 @@ const api = {
|
|
|
963
984
|
},
|
|
964
985
|
fields: {
|
|
965
986
|
list: makeFunctionReference<"query">("functions/crm/fields:list"),
|
|
987
|
+
listCompact: makeFunctionReference<"query">(
|
|
988
|
+
"functions/crm/fields:listCompact",
|
|
989
|
+
),
|
|
966
990
|
create: makeFunctionReference<"mutation">("functions/crm/fields:create"),
|
|
967
991
|
update: makeFunctionReference<"mutation">("functions/crm/fields:update"),
|
|
968
992
|
remove: makeFunctionReference<"mutation">("functions/crm/fields:remove"),
|
|
@@ -1013,6 +1037,9 @@ const api = {
|
|
|
1013
1037
|
),
|
|
1014
1038
|
aggregate: makeFunctionReference<"query">("functions/crm/query:aggregate"),
|
|
1015
1039
|
search: makeFunctionReference<"query">("functions/crm/query:search"),
|
|
1040
|
+
searchPage: makeFunctionReference<"query">(
|
|
1041
|
+
"functions/crm/query:searchPage",
|
|
1042
|
+
),
|
|
1016
1043
|
},
|
|
1017
1044
|
statuses: {
|
|
1018
1045
|
list: makeFunctionReference<"query">("functions/crm/statuses:list"),
|
|
@@ -1459,8 +1486,16 @@ async function runObjectsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
1459
1486
|
const verb = ctx.args.shift();
|
|
1460
1487
|
switch (verb) {
|
|
1461
1488
|
case "list": {
|
|
1489
|
+
const compact = hasFlag(ctx.args, "--compact");
|
|
1462
1490
|
assertNoUnknownFlags(ctx.args, "crm objects list");
|
|
1463
|
-
out(
|
|
1491
|
+
out(
|
|
1492
|
+
ctx,
|
|
1493
|
+
await callQuery(
|
|
1494
|
+
ctx,
|
|
1495
|
+
compact ? api.objects.listCompact : api.objects.list,
|
|
1496
|
+
{},
|
|
1497
|
+
),
|
|
1498
|
+
);
|
|
1464
1499
|
return;
|
|
1465
1500
|
}
|
|
1466
1501
|
case "get": {
|
|
@@ -2159,8 +2194,16 @@ async function runFieldsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2159
2194
|
switch (verb) {
|
|
2160
2195
|
case "list": {
|
|
2161
2196
|
const objectName = shift(ctx.args, "object name");
|
|
2197
|
+
const compact = hasFlag(ctx.args, "--compact");
|
|
2162
2198
|
assertNoUnknownFlags(ctx.args, "crm fields list");
|
|
2163
|
-
out(
|
|
2199
|
+
out(
|
|
2200
|
+
ctx,
|
|
2201
|
+
await callQuery(
|
|
2202
|
+
ctx,
|
|
2203
|
+
compact ? api.fields.listCompact : api.fields.list,
|
|
2204
|
+
{ objectName },
|
|
2205
|
+
),
|
|
2206
|
+
);
|
|
2164
2207
|
return;
|
|
2165
2208
|
}
|
|
2166
2209
|
case "create": {
|
|
@@ -2294,7 +2337,7 @@ async function runFieldsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2294
2337
|
// Assert before consuming the rest as positional field names so a
|
|
2295
2338
|
// typo'd flag fails loudly rather than getting silently filtered.
|
|
2296
2339
|
assertNoUnknownFlags(ctx.args, "crm fields reorder");
|
|
2297
|
-
const orderedFieldNames =
|
|
2340
|
+
const orderedFieldNames = ctx.args.splice(0);
|
|
2298
2341
|
out(
|
|
2299
2342
|
ctx,
|
|
2300
2343
|
await callMutation(ctx, api.fields.reorder, {
|
|
@@ -2311,7 +2354,7 @@ async function runFieldsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2311
2354
|
// there are no stray flags so a typo'd `--limit` fails loud
|
|
2312
2355
|
// instead of being silently consumed as an enum value.
|
|
2313
2356
|
assertNoUnknownFlags(ctx.args, "crm fields enum-reorder");
|
|
2314
|
-
const orderedValues =
|
|
2357
|
+
const orderedValues = ctx.args.splice(0);
|
|
2315
2358
|
if (orderedValues.length === 0) {
|
|
2316
2359
|
throw new CrmCliError(
|
|
2317
2360
|
"dench crm fields enum-reorder requires at least one value. " +
|
|
@@ -2417,6 +2460,15 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2417
2460
|
const objectName = shift(ctx.args, "object name");
|
|
2418
2461
|
const withMeta = hasFlag(ctx.args, "--with-meta");
|
|
2419
2462
|
const cursorSpecified = ctx.args.includes("--cursor");
|
|
2463
|
+
const cursorValue = ctx.args[ctx.args.indexOf("--cursor") + 1];
|
|
2464
|
+
if (
|
|
2465
|
+
cursorSpecified &&
|
|
2466
|
+
(!cursorValue?.trim() || cursorValue.startsWith("--"))
|
|
2467
|
+
)
|
|
2468
|
+
throw new CrmCliError(
|
|
2469
|
+
"--cursor requires a non-empty continuation cursor.",
|
|
2470
|
+
{ cause: new CliArgError("Invalid --cursor") },
|
|
2471
|
+
);
|
|
2420
2472
|
const cursor = getFlag(ctx.args, "--cursor");
|
|
2421
2473
|
if (cursorSpecified && (!cursor?.trim() || cursor.startsWith("--")))
|
|
2422
2474
|
throw new CrmCliError(
|
|
@@ -2483,8 +2535,7 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2483
2535
|
{ entryCount?: number; visibility?: string } | null,
|
|
2484
2536
|
];
|
|
2485
2537
|
const entries = Array.isArray(result?.page) ? result.page : [];
|
|
2486
|
-
const isDone =
|
|
2487
|
-
typeof result?.isDone === "boolean" ? result.isDone : null;
|
|
2538
|
+
const isDone = typeof result?.isDone === "boolean" ? result.isDone : null;
|
|
2488
2539
|
const truncated = result?.truncated === true;
|
|
2489
2540
|
const continueCursor =
|
|
2490
2541
|
isDone === false ? result?.continueCursor || null : null;
|
|
@@ -3234,25 +3285,31 @@ async function runAggregateCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
3234
3285
|
|
|
3235
3286
|
async function runSearchCommand(ctx: CrmCliContext): Promise<void> {
|
|
3236
3287
|
const objectName = getFlag(ctx.args, "--object");
|
|
3237
|
-
|
|
3238
|
-
|
|
3288
|
+
const selectRaw = getFlag(ctx.args, "--select");
|
|
3289
|
+
const select = selectRaw?.split(",").map((name) => name.trim());
|
|
3290
|
+
if (select && (select.length > 30 || select.some((name) => !name))) {
|
|
3291
|
+
throw new CrmCliError(
|
|
3292
|
+
"--select requires between 1 and 30 nonempty field names.",
|
|
3293
|
+
);
|
|
3294
|
+
}
|
|
3295
|
+
const page = hasFlag(ctx.args, "--page");
|
|
3296
|
+
const cursor = getFlag(ctx.args, "--cursor");
|
|
3297
|
+
const projected = select !== undefined || page || cursor !== undefined;
|
|
3239
3298
|
const limit = resolveLimitFlag(ctx.args, {
|
|
3240
|
-
defaultValue: 50,
|
|
3299
|
+
defaultValue: projected ? 20 : 50,
|
|
3241
3300
|
cap: 100,
|
|
3242
3301
|
command: "crm search",
|
|
3243
3302
|
});
|
|
3244
|
-
// Drain known flags before joining the rest into the search text so a
|
|
3245
|
-
// typo like `--objct` surfaces instead of being concatenated into the
|
|
3246
|
-
// query string.
|
|
3247
3303
|
assertNoUnknownFlags(ctx.args, "crm search");
|
|
3248
3304
|
const text = ctx.args.join(" ").trim();
|
|
3249
3305
|
if (!text) throw new CrmCliError("Search text required");
|
|
3250
3306
|
out(
|
|
3251
3307
|
ctx,
|
|
3252
|
-
await callQuery(ctx, api.query.search, {
|
|
3308
|
+
await callQuery(ctx, projected ? api.query.searchPage : api.query.search, {
|
|
3253
3309
|
text,
|
|
3254
3310
|
objectName,
|
|
3255
3311
|
limit: limit.effective,
|
|
3312
|
+
...(projected ? { select, cursor } : {}),
|
|
3256
3313
|
}),
|
|
3257
3314
|
);
|
|
3258
3315
|
}
|
|
@@ -3382,7 +3439,8 @@ async function runActionsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
3382
3439
|
const objectName = shift(ctx.args, "object name");
|
|
3383
3440
|
const fieldId = shift(ctx.args, "field id");
|
|
3384
3441
|
const actionId = shift(ctx.args, "action id");
|
|
3385
|
-
|
|
3442
|
+
assertNoUnknownFlags(ctx.args, "crm actions run");
|
|
3443
|
+
const entryIds = ctx.args.splice(0);
|
|
3386
3444
|
if (entryIds.length === 0) {
|
|
3387
3445
|
throw new CrmCliError("Provide at least one entry id");
|
|
3388
3446
|
}
|
|
@@ -3922,7 +3980,7 @@ export function remapKeys(
|
|
|
3922
3980
|
// ── SQL escape hatch ─────────────────────────────────────────────────────
|
|
3923
3981
|
|
|
3924
3982
|
async function runSqlCommand(ctx: CrmCliContext): Promise<void> {
|
|
3925
|
-
const sql = ctx.args.join(" ").trim();
|
|
3983
|
+
const sql = ctx.args.splice(0).join(" ").trim();
|
|
3926
3984
|
if (!sql) {
|
|
3927
3985
|
throw new CrmCliError("dench crm sql '<SELECT ... FROM ...>'");
|
|
3928
3986
|
}
|
|
@@ -4411,6 +4469,7 @@ async function enrichCellThroughGateway(
|
|
|
4411
4469
|
ctx: CrmCliContext,
|
|
4412
4470
|
args: CellEnrichmentArgs,
|
|
4413
4471
|
): Promise<Record<string, unknown>> {
|
|
4472
|
+
assertWriteArguments(ctx);
|
|
4414
4473
|
const target = await resolveEnrichmentTarget(ctx, args);
|
|
4415
4474
|
const entry = asEntry(
|
|
4416
4475
|
await callQuery(ctx, api.entries.get, {
|
|
@@ -4434,6 +4493,7 @@ async function enrichObjectThroughGateway(
|
|
|
4434
4493
|
ctx: CrmCliContext,
|
|
4435
4494
|
args: ObjectEnrichmentArgs,
|
|
4436
4495
|
): Promise<Record<string, unknown>> {
|
|
4496
|
+
assertWriteArguments(ctx);
|
|
4437
4497
|
const target = await resolveEnrichmentTarget(ctx, args);
|
|
4438
4498
|
const entries = asEntries(
|
|
4439
4499
|
await callQuery(ctx, api.entries.list, {
|
|
@@ -4615,6 +4675,7 @@ async function runEnrichCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
4615
4675
|
const lookups = input.map((value, index) =>
|
|
4616
4676
|
requireNonEmptyJsonRecord(value, `lookup ${index + 1}`),
|
|
4617
4677
|
);
|
|
4678
|
+
assertWriteArguments(ctx);
|
|
4618
4679
|
out(ctx, await enrichProfiles({ kind, lookups }, ctx.enrichmentGateway));
|
|
4619
4680
|
return;
|
|
4620
4681
|
}
|
|
@@ -4720,10 +4781,14 @@ async function runReportsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
4720
4781
|
}
|
|
4721
4782
|
|
|
4722
4783
|
function crmHelp(): void {
|
|
4723
|
-
console.log(
|
|
4784
|
+
console.log(crmHelpText());
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
export function crmHelpText(): string {
|
|
4788
|
+
return `Usage: dench crm <subcommand>
|
|
4724
4789
|
|
|
4725
4790
|
Objects (CRM tables):
|
|
4726
|
-
dench crm objects list [--json]
|
|
4791
|
+
dench crm objects list [--json] [--compact]
|
|
4727
4792
|
dench crm objects get <name>
|
|
4728
4793
|
dench crm objects create <name> [--description ...] [--default-view kanban] [--icon ...]
|
|
4729
4794
|
dench crm objects update <name> [--default-view ...] [--icon ...] [--display-name "Clients"] [--display-name-singular "Client"] [--visibility workspace|owner_only|owner_plus_members]
|
|
@@ -4810,7 +4875,7 @@ Sidebar (progressive disclosure — Library + home Pinned strip; layout is WORKS
|
|
|
4810
4875
|
shared shortcut, not a Library move; open to any member.
|
|
4811
4876
|
|
|
4812
4877
|
Fields (columns):
|
|
4813
|
-
dench crm fields list <object> [--json]
|
|
4878
|
+
dench crm fields list <object> [--compact] [--json]
|
|
4814
4879
|
dench crm fields create <object> <name> --type text|email|phone|url|number|boolean|date|enum|relation|user|tags|action|file|richtext \\
|
|
4815
4880
|
[--required] [--description ...] [--enum-values JSON] [--enum-colors JSON] \\
|
|
4816
4881
|
[--enum-multiple] [--related-object <name>] [--relationship many_to_one|many_to_many] \\
|
|
@@ -5031,7 +5096,10 @@ Aggregates:
|
|
|
5031
5096
|
dench crm aggregate <object> --field Status [--sum Amount]
|
|
5032
5097
|
|
|
5033
5098
|
Full-text search:
|
|
5034
|
-
dench crm search [--object <name>] [--limit N] <text>
|
|
5099
|
+
dench crm search [--object <name>] [--select "Name,Email"] [--limit N] [--page] [--cursor <cursor>] <text>
|
|
5100
|
+
--select returns a compact page with stable IDs, selected cells, isDone
|
|
5101
|
+
and continueCursor. Follow cursors until isDone for complete results.
|
|
5102
|
+
Without --select/--page/--cursor the legacy full-row array is unchanged.
|
|
5035
5103
|
|
|
5036
5104
|
Statuses (kanban columns):
|
|
5037
5105
|
dench crm statuses list <object>
|
|
@@ -5215,5 +5283,5 @@ unambiguous, but prefer canonical names from 'crm objects list'.
|
|
|
5215
5283
|
|
|
5216
5284
|
Global flags:
|
|
5217
5285
|
--json Output raw JSON instead of pretty-printed text.
|
|
5218
|
-
|
|
5286
|
+
`;
|
|
5219
5287
|
}
|
package/dench.ts
CHANGED
|
@@ -28,10 +28,12 @@ import {
|
|
|
28
28
|
resolveHostFromArgs,
|
|
29
29
|
STAGING_HOST,
|
|
30
30
|
} from "./host";
|
|
31
|
+
import { CliArgError, isHelpRequest } from "./lib/cli-args";
|
|
31
32
|
import { cliVersion } from "./lib/cli-version";
|
|
33
|
+
import { assertDeclaredCommandFlags } from "./lib/command-args";
|
|
32
34
|
import { denchConfigDirectory } from "./lib/config-path.mjs";
|
|
33
35
|
import { resolveOrganizationBySelector } from "./lib/organizationSelector";
|
|
34
|
-
import { backendErrorText } from "./lib/public-error";
|
|
36
|
+
import { backendErrorText, publicErrorDetails } from "./lib/public-error";
|
|
35
37
|
import { formatApprovalOpenMessage, openUrl } from "./openUrl";
|
|
36
38
|
import {
|
|
37
39
|
explicitSessionKeyInput,
|
|
@@ -215,7 +217,10 @@ const api = {
|
|
|
215
217
|
};
|
|
216
218
|
const CONFIG_DIR = denchConfigDirectory();
|
|
217
219
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
218
|
-
const args = process.argv.slice(2)
|
|
220
|
+
const args = process.argv.slice(2).flatMap((arg) => {
|
|
221
|
+
const split = arg.startsWith("--") ? arg.indexOf("=") : -1;
|
|
222
|
+
return split > 2 ? [arg.slice(0, split), arg.slice(split + 1)] : [arg];
|
|
223
|
+
});
|
|
219
224
|
const json = args.includes("--json");
|
|
220
225
|
const filteredArgs = args.filter((arg) => arg !== "--json");
|
|
221
226
|
const scopedStatus = args.includes("--mine") || args.includes("--self");
|
|
@@ -321,13 +326,18 @@ function throwIfCliError(value: unknown) {
|
|
|
321
326
|
}
|
|
322
327
|
const record = value as JsonRecord;
|
|
323
328
|
if (record.ok === false && typeof record.error === "string") {
|
|
324
|
-
throw new
|
|
329
|
+
throw new CliError(record.error, publicErrorDetails(record));
|
|
325
330
|
}
|
|
326
331
|
}
|
|
327
332
|
|
|
328
333
|
function normalizeCliError(error: unknown): CliError {
|
|
329
|
-
if (error instanceof CliError)
|
|
334
|
+
if (error instanceof CliError) {
|
|
335
|
+
error.payload = { ...error.payload, ...publicErrorDetails(error.payload) };
|
|
336
|
+
return error;
|
|
337
|
+
}
|
|
330
338
|
const message = backendErrorText(error);
|
|
339
|
+
if (error instanceof CliArgError)
|
|
340
|
+
return new CliError(message, { code: error.code, retryable: false });
|
|
331
341
|
if (message.includes("Invalid dev agent key")) {
|
|
332
342
|
return new CliError("Invalid dev agent key", {
|
|
333
343
|
code: "invalid_dev_agent_key",
|
|
@@ -369,7 +379,10 @@ function normalizeCliError(error: unknown): CliError {
|
|
|
369
379
|
],
|
|
370
380
|
});
|
|
371
381
|
}
|
|
372
|
-
return new CliError(message
|
|
382
|
+
return new CliError(message, {
|
|
383
|
+
code: "cli_error",
|
|
384
|
+
...publicErrorDetails(error),
|
|
385
|
+
});
|
|
373
386
|
}
|
|
374
387
|
|
|
375
388
|
function _parseJsonObjectOption(name: string) {
|
|
@@ -673,6 +686,8 @@ Subagents:
|
|
|
673
686
|
Help: dench agent help
|
|
674
687
|
|
|
675
688
|
dench --version
|
|
689
|
+
dench reference <name> [--json] Offline CLI-version-matched skill/reference
|
|
690
|
+
dench reference --list [--json] Bundled reference index and revision
|
|
676
691
|
|
|
677
692
|
Self-updating agent harness:
|
|
678
693
|
dench identity show | edit IDENTITY.md (org-wide)
|
|
@@ -2701,12 +2716,21 @@ function stripRuntimeFlags(args: string[]) {
|
|
|
2701
2716
|
"--staging",
|
|
2702
2717
|
"--use-dev-env",
|
|
2703
2718
|
]);
|
|
2704
|
-
const valueFlags = new Set([
|
|
2719
|
+
const valueFlags = new Set([
|
|
2720
|
+
"--convex-url",
|
|
2721
|
+
"--host",
|
|
2722
|
+
"--backend",
|
|
2723
|
+
"--session-key",
|
|
2724
|
+
"--session",
|
|
2725
|
+
]);
|
|
2705
2726
|
const stripped: string[] = [];
|
|
2706
2727
|
for (let i = 0; i < args.length; i++) {
|
|
2707
2728
|
const arg = args[i];
|
|
2708
2729
|
if (booleanFlags.has(arg)) continue;
|
|
2709
2730
|
if (valueFlags.has(arg)) {
|
|
2731
|
+
if (!args[i + 1] || args[i + 1].startsWith("--")) {
|
|
2732
|
+
throw new CliArgError(`Missing value for ${arg}`);
|
|
2733
|
+
}
|
|
2710
2734
|
i++;
|
|
2711
2735
|
continue;
|
|
2712
2736
|
}
|
|
@@ -3392,7 +3416,7 @@ async function resolveFsDaemonEnv(): Promise<NodeJS.ProcessEnv> {
|
|
|
3392
3416
|
}
|
|
3393
3417
|
|
|
3394
3418
|
async function runFsCommand() {
|
|
3395
|
-
const subArgs = args.slice(args.indexOf("fs") + 1);
|
|
3419
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("fs") + 1));
|
|
3396
3420
|
const configuredBinary = process.env.DENCH_FS_DAEMON_BIN?.trim();
|
|
3397
3421
|
const binary = configuredBinary || "dench-fs-daemon";
|
|
3398
3422
|
const daemonEnv = await resolveFsDaemonEnv();
|
|
@@ -4303,10 +4327,95 @@ function isCommandNotFound(error: unknown) {
|
|
|
4303
4327
|
}
|
|
4304
4328
|
|
|
4305
4329
|
async function main() {
|
|
4330
|
+
if (isHelpRequest(args)) {
|
|
4331
|
+
const helpArgs = stripRuntimeFlags(args).filter((arg) => arg !== "--json");
|
|
4332
|
+
if (helpArgs[0] === "help") helpArgs.shift();
|
|
4333
|
+
if (!helpArgs.length || ["--help", "-h"].includes(helpArgs[0])) {
|
|
4334
|
+
help();
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
const path = helpArgs.filter(
|
|
4338
|
+
(arg) => !["help", "--help", "-h"].includes(arg),
|
|
4339
|
+
);
|
|
4340
|
+
// Root help retains domain-specific setup, account and pagination guidance.
|
|
4341
|
+
// These are explicit help-only paths; never pass mutation arguments to a handler.
|
|
4342
|
+
if (path.length === 1) {
|
|
4343
|
+
const roots: Record<string, () => void | Promise<void>> = {
|
|
4344
|
+
reference: async () =>
|
|
4345
|
+
console.log((await import("./reference")).REFERENCE_HELP),
|
|
4346
|
+
tool: toolHelp,
|
|
4347
|
+
integrations: integrationsHelp,
|
|
4348
|
+
backend: backendHelp,
|
|
4349
|
+
signin: signinHelp,
|
|
4350
|
+
login: signinHelp,
|
|
4351
|
+
onboard: signinHelp,
|
|
4352
|
+
setup: signinHelp,
|
|
4353
|
+
"what-can-i-do": signinHelp,
|
|
4354
|
+
register: signinHelp,
|
|
4355
|
+
memory: memoryHelp,
|
|
4356
|
+
billing: billingHelp,
|
|
4357
|
+
logout: logoutHelp,
|
|
4358
|
+
context: contextHelp,
|
|
4359
|
+
artifacts: artifactsHelp,
|
|
4360
|
+
"suggested-work": artifactsHelp,
|
|
4361
|
+
lists: listsCliHelp,
|
|
4362
|
+
leads: leadsHelp,
|
|
4363
|
+
companies: companiesHelp,
|
|
4364
|
+
jobs: jobsHelp,
|
|
4365
|
+
status: statusHelp,
|
|
4366
|
+
web: async () => (await import("./web")).webHelp(),
|
|
4367
|
+
crm: async () =>
|
|
4368
|
+
(await import("./crm")).runCrmCommand({
|
|
4369
|
+
convex: {} as ConvexHttpClient,
|
|
4370
|
+
args: ["help"],
|
|
4371
|
+
}),
|
|
4372
|
+
apps: async () =>
|
|
4373
|
+
(await import("./apps")).runAppsCommand({
|
|
4374
|
+
convex: {} as ConvexHttpClient,
|
|
4375
|
+
args: ["help"],
|
|
4376
|
+
}),
|
|
4377
|
+
};
|
|
4378
|
+
if (roots[path[0]]) {
|
|
4379
|
+
await roots[path[0]]();
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
if (helpArgs[0] === "reference") {
|
|
4384
|
+
console.log((await import("./reference")).REFERENCE_HELP);
|
|
4385
|
+
return;
|
|
4386
|
+
}
|
|
4387
|
+
// Preserve the small, established CRM read help including its cursor contract.
|
|
4388
|
+
if (helpArgs[0] === "crm" && ["query", "aggregate"].includes(helpArgs[1])) {
|
|
4389
|
+
const { runCrmCommand } = await import("./crm");
|
|
4390
|
+
await runCrmCommand({
|
|
4391
|
+
convex: {} as ConvexHttpClient,
|
|
4392
|
+
args: [...helpArgs.slice(1), "--help"],
|
|
4393
|
+
});
|
|
4394
|
+
return;
|
|
4395
|
+
}
|
|
4396
|
+
const { commandHelp } = await import("./lib/command-help");
|
|
4397
|
+
const usage =
|
|
4398
|
+
helpArgs[0] === "email"
|
|
4399
|
+
? (await import("./email")).emailHelpText()
|
|
4400
|
+
: helpArgs[0] === "crm"
|
|
4401
|
+
? (await import("./crm")).crmHelpText()
|
|
4402
|
+
: "";
|
|
4403
|
+
console.log(commandHelp(helpArgs, usage));
|
|
4404
|
+
return;
|
|
4405
|
+
}
|
|
4306
4406
|
const command = positional(0);
|
|
4307
4407
|
const subcommand = positional(1);
|
|
4308
4408
|
|
|
4309
|
-
|
|
4409
|
+
assertDeclaredCommandFlags(stripRuntimeFlags(args));
|
|
4410
|
+
|
|
4411
|
+
if (command === "reference") {
|
|
4412
|
+
await (await import("./reference")).runReferenceCommand(
|
|
4413
|
+
stripRuntimeFlags(args.slice(args.indexOf("reference") + 1)),
|
|
4414
|
+
);
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
4417
|
+
|
|
4418
|
+
if (command === "version" || stripRuntimeFlags(args)[0] === "--version") {
|
|
4310
4419
|
console.log(await cliVersion());
|
|
4311
4420
|
return;
|
|
4312
4421
|
}
|
|
@@ -4741,7 +4850,7 @@ async function main() {
|
|
|
4741
4850
|
}
|
|
4742
4851
|
|
|
4743
4852
|
if (command === "web") {
|
|
4744
|
-
const subArgs = args.slice(args.indexOf("web") + 1);
|
|
4853
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("web") + 1));
|
|
4745
4854
|
const filteredSubArgs = subArgs.filter((a) => a !== "--json");
|
|
4746
4855
|
const { runWebCommand, webHelp } = await import("./web");
|
|
4747
4856
|
const sub = filteredSubArgs[0];
|
|
@@ -4761,7 +4870,7 @@ async function main() {
|
|
|
4761
4870
|
}
|
|
4762
4871
|
|
|
4763
4872
|
if (command === "search") {
|
|
4764
|
-
const subArgs = args.slice(args.indexOf("search") + 1);
|
|
4873
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("search") + 1));
|
|
4765
4874
|
const filteredSubArgs = subArgs.filter((a) => a !== "--json");
|
|
4766
4875
|
const { runSearchCommand } = await import("./search");
|
|
4767
4876
|
const sub = filteredSubArgs[0];
|
|
@@ -4784,7 +4893,7 @@ async function main() {
|
|
|
4784
4893
|
}
|
|
4785
4894
|
|
|
4786
4895
|
if (command === "image") {
|
|
4787
|
-
const subArgs = args.slice(args.indexOf("image") + 1);
|
|
4896
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("image") + 1));
|
|
4788
4897
|
const filteredSubArgs = subArgs.filter((a) => a !== "--json");
|
|
4789
4898
|
const { runImageCommand } = await import("./image");
|
|
4790
4899
|
const sub = filteredSubArgs[0];
|
|
@@ -4804,7 +4913,7 @@ async function main() {
|
|
|
4804
4913
|
}
|
|
4805
4914
|
|
|
4806
4915
|
if (command === "email") {
|
|
4807
|
-
const subArgs = args.slice(args.indexOf("email") + 1);
|
|
4916
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("email") + 1));
|
|
4808
4917
|
const { runEmailCommand } = await import("./email");
|
|
4809
4918
|
const runtime = await getRuntime();
|
|
4810
4919
|
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench email");
|
|
@@ -4813,7 +4922,7 @@ async function main() {
|
|
|
4813
4922
|
| undefined;
|
|
4814
4923
|
try {
|
|
4815
4924
|
if (
|
|
4816
|
-
|
|
4925
|
+
!["draft", "inbox", "send"].includes(subArgs[0]) &&
|
|
4817
4926
|
!subArgs.some(
|
|
4818
4927
|
(arg) => arg === "--mailbox" || arg.startsWith("--mailbox="),
|
|
4819
4928
|
)
|
|
@@ -4922,7 +5031,7 @@ async function main() {
|
|
|
4922
5031
|
}
|
|
4923
5032
|
|
|
4924
5033
|
if (command === "browser") {
|
|
4925
|
-
const subArgs = args.slice(args.indexOf("browser") + 1);
|
|
5034
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("browser") + 1));
|
|
4926
5035
|
const { runBrowserCommand } = await import("./browser");
|
|
4927
5036
|
const runtime = await getRuntime();
|
|
4928
5037
|
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench browser");
|
|
@@ -4947,7 +5056,7 @@ async function main() {
|
|
|
4947
5056
|
// `dench ads` is canonical; `dench ad` and `dench adline` are aliases for
|
|
4948
5057
|
// the same command (one code path in ./ads).
|
|
4949
5058
|
if (command === "ads" || command === "ad" || command === "adline") {
|
|
4950
|
-
const subArgs = args.slice(args.indexOf(command) + 1);
|
|
5059
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
|
|
4951
5060
|
const { runAdsCommand } = await import("./ads");
|
|
4952
5061
|
// Earning is opt-in: resolve the agent session token best-effort so a
|
|
4953
5062
|
// signed-in developer gets credited on flush, but never require login —
|
|
@@ -4983,7 +5092,7 @@ async function main() {
|
|
|
4983
5092
|
}
|
|
4984
5093
|
|
|
4985
5094
|
if (command === "chat") {
|
|
4986
|
-
const subArgs = args.slice(args.indexOf("chat") + 1);
|
|
5095
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("chat") + 1));
|
|
4987
5096
|
const sub = subArgs[0];
|
|
4988
5097
|
if (!sub || sub === "help" || sub === "--help") {
|
|
4989
5098
|
const { runChatCommand } = await import("./chat");
|
|
@@ -5033,7 +5142,7 @@ async function main() {
|
|
|
5033
5142
|
}
|
|
5034
5143
|
|
|
5035
5144
|
if (command === "agent") {
|
|
5036
|
-
const subArgs = args.slice(args.indexOf("agent") + 1);
|
|
5145
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("agent") + 1));
|
|
5037
5146
|
const sub = subArgs[0];
|
|
5038
5147
|
if (!sub || sub === "help" || sub === "--help") {
|
|
5039
5148
|
const { runAgentCommand } = await import("./agent");
|
|
@@ -5110,7 +5219,7 @@ async function main() {
|
|
|
5110
5219
|
// `dench cron`. Same dispatch, same auth, same handler — only the
|
|
5111
5220
|
// top-level command name differs to match the workspace UI rename.
|
|
5112
5221
|
if (command === "cron" || command === "routine" || command === "routines") {
|
|
5113
|
-
const subArgs = args.slice(args.indexOf(command) + 1);
|
|
5222
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
|
|
5114
5223
|
const sub = subArgs[0];
|
|
5115
5224
|
// Help is a no-auth path so users can read the surface without a
|
|
5116
5225
|
// session — mirrors `dench crm help`.
|
|
@@ -5159,7 +5268,7 @@ async function main() {
|
|
|
5159
5268
|
"daily",
|
|
5160
5269
|
]);
|
|
5161
5270
|
if (AGENT_CONFIG_COMMANDS.has(command)) {
|
|
5162
|
-
const subArgs = args.slice(args.indexOf(command) + 1);
|
|
5271
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
|
|
5163
5272
|
if (
|
|
5164
5273
|
subArgs[0] === "help" ||
|
|
5165
5274
|
subArgs.includes("--help") ||
|