@dench.com/cli 3.6.5-staging.384 → 3.6.6-staging.386
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 +107 -21
- package/dench.ts +7 -3
- package/package.json +1 -1
package/crm.ts
CHANGED
|
@@ -1063,6 +1063,15 @@ const api = {
|
|
|
1063
1063
|
},
|
|
1064
1064
|
};
|
|
1065
1065
|
|
|
1066
|
+
/** These read commands have local, command-specific help (no runtime needed). */
|
|
1067
|
+
export function isCrmReadHelpRequest(args: readonly string[]): boolean {
|
|
1068
|
+
const command = args.find((arg) => arg !== "--json");
|
|
1069
|
+
return (
|
|
1070
|
+
(command === "query" || command === "aggregate") &&
|
|
1071
|
+
(args.includes("--help") || args.includes("-h"))
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1066
1075
|
export async function runCrmCommand(opts: {
|
|
1067
1076
|
convex: ConvexHttpClient;
|
|
1068
1077
|
args: string[];
|
|
@@ -1088,6 +1097,21 @@ export async function runCrmCommand(opts: {
|
|
|
1088
1097
|
crmHelp();
|
|
1089
1098
|
return;
|
|
1090
1099
|
}
|
|
1100
|
+
if (isCrmReadHelpRequest(opts.args)) {
|
|
1101
|
+
if (subcommand === "query") {
|
|
1102
|
+
console.log(`Usage: dench crm query <object> [--where '<JSON>'] [--select Name,Email] [--sort -Score] [--limit N] [--json]
|
|
1103
|
+
--page [--cursor '<continueCursor>'] Return {page,isDone,continueCursor} in index order; omit --sort.
|
|
1104
|
+
--assigned-to me|unassigned|<userId|email> (or --mine) [--assignee-field <Field>]
|
|
1105
|
+
Default limit: 100. Maximum: 1000 for a one-shot array, 100 for a cursor page.
|
|
1106
|
+
Continue cursor pages until isDone, including empty filtered pages.
|
|
1107
|
+
Example: dench crm query company --where '{"Status":"New"}' --select Name --limit 25 --json`);
|
|
1108
|
+
} else {
|
|
1109
|
+
console.log(`Usage: dench crm aggregate <object> --field <Field> [--sum <NumberField>] [--json]
|
|
1110
|
+
Count rows per value of --field. --sum also totals a number field within each bucket.
|
|
1111
|
+
Example: dench crm aggregate deal --field Stage --sum Amount --json`);
|
|
1112
|
+
}
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1091
1115
|
switch (subcommand) {
|
|
1092
1116
|
case "objects":
|
|
1093
1117
|
return await runObjectsCommand(ctx);
|
|
@@ -2391,37 +2415,92 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2391
2415
|
switch (verb) {
|
|
2392
2416
|
case "list": {
|
|
2393
2417
|
const objectName = shift(ctx.args, "object name");
|
|
2418
|
+
const withMeta = hasFlag(ctx.args, "--with-meta");
|
|
2419
|
+
const cursorSpecified = ctx.args.includes("--cursor");
|
|
2420
|
+
const cursor = getFlag(ctx.args, "--cursor");
|
|
2421
|
+
if (cursorSpecified && (!cursor?.trim() || cursor.startsWith("--")))
|
|
2422
|
+
throw new CrmCliError(
|
|
2423
|
+
"--cursor requires a non-empty continuation cursor.",
|
|
2424
|
+
);
|
|
2425
|
+
const assigneeScopeArgs = resolveAssigneeScopeFlags(ctx.args);
|
|
2394
2426
|
const limit = resolveLimitFlag(ctx.args, {
|
|
2395
2427
|
defaultValue: 100,
|
|
2396
|
-
cap:
|
|
2428
|
+
cap:
|
|
2429
|
+
withMeta && assigneeScopeArgs.assignedTo !== undefined ? 100 : 1000,
|
|
2397
2430
|
command: "crm entries list",
|
|
2431
|
+
...(withMeta
|
|
2432
|
+
? {
|
|
2433
|
+
fullDatasetHint:
|
|
2434
|
+
"Continue with --with-meta --cursor '<continueCursor>' when a cursor is returned.",
|
|
2435
|
+
}
|
|
2436
|
+
: {}),
|
|
2398
2437
|
});
|
|
2399
|
-
|
|
2400
|
-
|
|
2438
|
+
if (cursor !== undefined && !withMeta) {
|
|
2439
|
+
throw new CrmCliError(
|
|
2440
|
+
"--cursor requires --with-meta on crm entries list.",
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2401
2443
|
assertNoUnknownFlags(ctx.args, "crm entries list");
|
|
2402
|
-
const entries = await callQuery(ctx, api.entries.list, {
|
|
2403
|
-
objectName,
|
|
2404
|
-
limit: limit.effective,
|
|
2405
|
-
...assigneeScopeArgs,
|
|
2406
|
-
});
|
|
2407
2444
|
if (!withMeta) {
|
|
2408
2445
|
// Bare-array output: the clamp warning already went to stderr
|
|
2409
2446
|
// (stdout must stay a valid JSON array in --json mode).
|
|
2410
|
-
out(
|
|
2447
|
+
out(
|
|
2448
|
+
ctx,
|
|
2449
|
+
await callQuery(ctx, api.entries.list, {
|
|
2450
|
+
objectName,
|
|
2451
|
+
limit: limit.effective,
|
|
2452
|
+
...assigneeScopeArgs,
|
|
2453
|
+
}),
|
|
2454
|
+
);
|
|
2411
2455
|
return;
|
|
2412
2456
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2457
|
+
// Catalog counts include hidden and unassigned records. Only the
|
|
2458
|
+
// readable page can tell us whether this caller has a continuation.
|
|
2459
|
+
const [result, object] = (await Promise.all([
|
|
2460
|
+
assigneeScopeArgs.assignedTo !== undefined
|
|
2461
|
+
? callQuery(ctx, api.query.scanEntries, {
|
|
2462
|
+
objectName,
|
|
2463
|
+
dsl: { limit: limit.effective },
|
|
2464
|
+
paginationOpts: {
|
|
2465
|
+
cursor: cursor ?? null,
|
|
2466
|
+
numItems: limit.effective,
|
|
2467
|
+
},
|
|
2468
|
+
...assigneeScopeArgs,
|
|
2469
|
+
})
|
|
2470
|
+
: callQuery(ctx, api.entries.listPage, {
|
|
2471
|
+
objectName,
|
|
2472
|
+
limit: limit.effective,
|
|
2473
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
2474
|
+
}),
|
|
2475
|
+
callQuery(ctx, api.objects.get, { name: objectName }),
|
|
2476
|
+
])) as [
|
|
2477
|
+
{
|
|
2478
|
+
page?: unknown[];
|
|
2479
|
+
isDone?: boolean;
|
|
2480
|
+
continueCursor?: string | null;
|
|
2481
|
+
truncated?: boolean;
|
|
2482
|
+
} | null,
|
|
2483
|
+
{ entryCount?: number; visibility?: string } | null,
|
|
2484
|
+
];
|
|
2485
|
+
const entries = Array.isArray(result?.page) ? result.page : [];
|
|
2486
|
+
const isDone =
|
|
2487
|
+
typeof result?.isDone === "boolean" ? result.isDone : null;
|
|
2488
|
+
const truncated = result?.truncated === true;
|
|
2489
|
+
const continueCursor =
|
|
2490
|
+
isDone === false ? result?.continueCursor || null : null;
|
|
2491
|
+
const hasMore = continueCursor
|
|
2492
|
+
? true
|
|
2493
|
+
: isDone === true && !truncated
|
|
2494
|
+
? false
|
|
2495
|
+
: null;
|
|
2421
2496
|
out(ctx, {
|
|
2422
2497
|
objectName,
|
|
2423
|
-
totalCount
|
|
2424
|
-
|
|
2498
|
+
totalCount:
|
|
2499
|
+
typeof object?.entryCount === "number" ? object.entryCount : null,
|
|
2500
|
+
totalCountScope: "object",
|
|
2501
|
+
visibility: object?.visibility ?? null,
|
|
2502
|
+
returnedCount: entries.length,
|
|
2503
|
+
returnedCountScope: "readable",
|
|
2425
2504
|
limit: limit.effective,
|
|
2426
2505
|
...(limit.clamped
|
|
2427
2506
|
? {
|
|
@@ -2431,7 +2510,10 @@ async function runEntriesCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
2431
2510
|
},
|
|
2432
2511
|
}
|
|
2433
2512
|
: {}),
|
|
2434
|
-
hasMore
|
|
2513
|
+
hasMore,
|
|
2514
|
+
isDone,
|
|
2515
|
+
truncated,
|
|
2516
|
+
continueCursor,
|
|
2435
2517
|
entries,
|
|
2436
2518
|
});
|
|
2437
2519
|
return;
|
|
@@ -4785,8 +4867,12 @@ Fields (columns):
|
|
|
4785
4867
|
Update the color (e.g. "#3b82f6") for an existing enum option.
|
|
4786
4868
|
|
|
4787
4869
|
Entries (rows):
|
|
4788
|
-
dench crm entries list <object> [--limit N] [--with-meta] [--json]
|
|
4870
|
+
dench crm entries list <object> [--limit N] [--with-meta [--cursor '<continueCursor>']] [--json]
|
|
4789
4871
|
[--assigned-to me|unassigned|<userId|email> | --mine] [--assignee-field <Field>]
|
|
4872
|
+
--with-meta includes readable pagination; totalCount is the entire object's catalog count,
|
|
4873
|
+
not a readable or assigned total. Continue only when continueCursor is returned, preserving filters.
|
|
4874
|
+
Scoped reads may be truncated with no cursor (hasMore: null); repeating the same call will not advance.
|
|
4875
|
+
Assigned metadata pages cap at 100 rows; other list reads cap at 1000.
|
|
4790
4876
|
dench crm entries get <object> <entryId> [--with-context] (--with-context appends the newest activity + notes)
|
|
4791
4877
|
dench crm entries sync-history <object> <entryId> [--status]
|
|
4792
4878
|
Queue a full, paged pull of the record's email history from every
|
package/dench.ts
CHANGED
|
@@ -4459,18 +4459,22 @@ async function main() {
|
|
|
4459
4459
|
if (command === "crm") {
|
|
4460
4460
|
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("crm") + 1));
|
|
4461
4461
|
const sub = subArgs[0];
|
|
4462
|
+
const { runCrmCommand, isCrmReadHelpRequest } = await import("./crm");
|
|
4462
4463
|
// Short-circuit help so users don't need a session to read the
|
|
4463
4464
|
// surface. The crm dispatcher's own help resolves --json + the
|
|
4464
4465
|
// subcommand list inline.
|
|
4465
|
-
if (
|
|
4466
|
-
|
|
4466
|
+
if (
|
|
4467
|
+
!sub ||
|
|
4468
|
+
sub === "help" ||
|
|
4469
|
+
sub === "--help" ||
|
|
4470
|
+
isCrmReadHelpRequest(subArgs)
|
|
4471
|
+
) {
|
|
4467
4472
|
// runCrmCommand only needs `convex` for actual queries; help
|
|
4468
4473
|
// doesn't touch it. Pass a minimal stub.
|
|
4469
4474
|
// biome-ignore lint/suspicious/noExplicitAny: harmless help-only stub
|
|
4470
4475
|
await runCrmCommand({ convex: {} as any, args: subArgs });
|
|
4471
4476
|
return;
|
|
4472
4477
|
}
|
|
4473
|
-
const { runCrmCommand } = await import("./crm");
|
|
4474
4478
|
const runtime = await getRuntime();
|
|
4475
4479
|
// CRM is org-scoped — accept either an agent session token
|
|
4476
4480
|
// (`dch_agent_*` from `dench signin`) or a unified Dench API key
|