@dench.com/cli 3.6.0 → 3.6.2-staging.369
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/app-sdk.d.ts +7 -0
- package/crm-context.ts +66 -0
- package/crm.ts +5 -0
- package/dench.ts +3 -2
- package/lib/app-sdk-catalog.generated.ts +1 -0
- package/lib/command-registry.ts +19 -0
- package/lib/config-path.mjs +8 -0
- package/lib/load-dev-env.mjs +7 -5
- package/package.json +2 -1
package/app-sdk.d.ts
CHANGED
|
@@ -1040,6 +1040,8 @@ export interface ApiRequests {
|
|
|
1040
1040
|
"crm.activity.list": Record<string, unknown>;
|
|
1041
1041
|
/** The record's generated Brief: health, who owes the next move, open threads, commitments, risks, next step. Read this before the ledger. — `dench crm brief` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/brief */
|
|
1042
1042
|
"crm.brief.get": Record<string, unknown>;
|
|
1043
|
+
/** Read-only source diagnostics: accessible evidence, association reasons, last Brief source manifest, generation queue, and your mailbox delivery health. Bounded, cursor-paged snapshot; does not certify complete history. — `dench crm context` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/context */
|
|
1044
|
+
"crm.context.get": Record<string, unknown>;
|
|
1043
1045
|
/** Rebuild a record's Brief from the current timeline. — `dench crm brief --regenerate` — POST /api/v1/crm/objects/{objectName}/entries/{entryId}/brief/regenerate */
|
|
1044
1046
|
"crm.brief.regenerate": Record<string, unknown>;
|
|
1045
1047
|
/** Bounded per-source counts of a CRM entry's activity ledger. — `dench crm activity` — GET /api/v1/crm/objects/{objectName}/entries/{entryId}/activity/count */
|
|
@@ -4175,6 +4177,7 @@ export interface ApiResponses {
|
|
|
4175
4177
|
"crm.entries.history": unknown;
|
|
4176
4178
|
"crm.activity.list": unknown;
|
|
4177
4179
|
"crm.brief.get": unknown;
|
|
4180
|
+
"crm.context.get": unknown;
|
|
4178
4181
|
"crm.brief.regenerate": unknown;
|
|
4179
4182
|
"crm.activity.count": unknown;
|
|
4180
4183
|
"crm.notes.list": unknown;
|
|
@@ -7507,6 +7510,10 @@ export type DenchApiTree = {
|
|
|
7507
7510
|
/** Search for companies through the enrichment gateway (also `dench companies find`). (`dench crm companies search`) */
|
|
7508
7511
|
search(args: Req<"crm.companies.search">): Promise<Res<"crm.companies.search">>;
|
|
7509
7512
|
};
|
|
7513
|
+
context: {
|
|
7514
|
+
/** Read-only source diagnostics: accessible evidence, association reasons, last Brief source manifest, generation queue, and your mailbox delivery health. Bounded, cursor-paged snapshot; does not certify complete history. (`dench crm context`) */
|
|
7515
|
+
get(args: Req<"crm.context.get">): Promise<Res<"crm.context.get">>;
|
|
7516
|
+
};
|
|
7510
7517
|
detections: {
|
|
7511
7518
|
/** List account-detection verdicts — why a domain (or personal address) did or did not become an account: verdict, counterparty type, probabilities, evidence threads, the rows created. `kind` picks the creator: `detections` (email + sweep), `linkedin` (the LinkedIn resolver's rows) or `all` (default). Newest first. (`dench crm detections list`) */
|
|
7512
7519
|
list(args?: Req<"crm.detections.list">): Promise<Res<"crm.detections.list">>;
|
package/crm-context.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { makeFunctionReference } from "convex/server";
|
|
2
|
+
import {
|
|
3
|
+
assertNoUnknownFlags,
|
|
4
|
+
type CrmCliContext,
|
|
5
|
+
callQuery,
|
|
6
|
+
out,
|
|
7
|
+
shift,
|
|
8
|
+
} from "./crm";
|
|
9
|
+
import { getFlag } from "./lib/cli-args";
|
|
10
|
+
|
|
11
|
+
const getContext = makeFunctionReference<"query">(
|
|
12
|
+
"functions/crm/contextDiagnostics:getForEntry",
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// Keep the shipped CLI independent of application-only Convex source files.
|
|
16
|
+
type ContextPage = {
|
|
17
|
+
record: { label: string };
|
|
18
|
+
generation: { state: string };
|
|
19
|
+
scopeLimited: boolean;
|
|
20
|
+
sourceLimited: boolean;
|
|
21
|
+
sources: Array<{
|
|
22
|
+
occurredAt: number;
|
|
23
|
+
kind: string;
|
|
24
|
+
title: string;
|
|
25
|
+
association: string;
|
|
26
|
+
usedByLastBrief: boolean;
|
|
27
|
+
sourceTable: string;
|
|
28
|
+
sourceId: string;
|
|
29
|
+
}>;
|
|
30
|
+
mailboxes: Array<{ email: string; label: string }>;
|
|
31
|
+
nextCursor: string | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Read-only: inspecting a sync never starts a generation or connector call. */
|
|
35
|
+
export async function runContextCommand(ctx: CrmCliContext): Promise<void> {
|
|
36
|
+
const objectName = shift(ctx.args, "object name");
|
|
37
|
+
const entryId = shift(ctx.args, "entry id");
|
|
38
|
+
const cursor = getFlag(ctx.args, "--cursor");
|
|
39
|
+
assertNoUnknownFlags(ctx.args, "crm context");
|
|
40
|
+
const result = (await callQuery(ctx, getContext, {
|
|
41
|
+
objectName,
|
|
42
|
+
entryId,
|
|
43
|
+
...(cursor ? { cursor } : {}),
|
|
44
|
+
})) as ContextPage;
|
|
45
|
+
if (ctx.jsonOutput) {
|
|
46
|
+
out(ctx, result);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const lines = [
|
|
50
|
+
`${result.record.label} · Brief ${result.generation.state}`,
|
|
51
|
+
"Bounded snapshot of sources you can access; complete history is not certified.",
|
|
52
|
+
...(result.scopeLimited ? ["Related-record scope reached its limit."] : []),
|
|
53
|
+
...(result.sourceLimited
|
|
54
|
+
? ["Source inspection reached its read limit."]
|
|
55
|
+
: []),
|
|
56
|
+
...result.sources.map(
|
|
57
|
+
(row) =>
|
|
58
|
+
`${new Date(row.occurredAt).toISOString()} ${row.kind} ${row.title} [${row.association}; ${row.usedByLastBrief ? "used by last Brief" : "not in last Brief"}] ${row.sourceTable}:${row.sourceId}`,
|
|
59
|
+
),
|
|
60
|
+
...result.mailboxes.map((row) => `${row.email}: ${row.label}`),
|
|
61
|
+
...(result.nextCursor
|
|
62
|
+
? [`More sources: repeat with --cursor '${result.nextCursor}'`]
|
|
63
|
+
: []),
|
|
64
|
+
];
|
|
65
|
+
out(ctx, lines.join("\n"));
|
|
66
|
+
}
|
package/crm.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
runTasksCommand,
|
|
41
41
|
runWatchCommand,
|
|
42
42
|
} from "./crm-comments";
|
|
43
|
+
import { runContextCommand } from "./crm-context";
|
|
43
44
|
import { runDetectionsCommand } from "./crm-detections";
|
|
44
45
|
import { runPrivacyCommand } from "./crm-privacy";
|
|
45
46
|
import {
|
|
@@ -1087,6 +1088,8 @@ export async function runCrmCommand(opts: {
|
|
|
1087
1088
|
return await runActivityCommand(ctx);
|
|
1088
1089
|
case "brief":
|
|
1089
1090
|
return await runBriefCommand(ctx);
|
|
1091
|
+
case "context":
|
|
1092
|
+
return await runContextCommand(ctx);
|
|
1090
1093
|
case "notes":
|
|
1091
1094
|
return await runNotesCommand(ctx);
|
|
1092
1095
|
case "watch":
|
|
@@ -1164,6 +1167,7 @@ const CRM_SUBCOMMANDS = [
|
|
|
1164
1167
|
"comments",
|
|
1165
1168
|
"activity",
|
|
1166
1169
|
"brief",
|
|
1170
|
+
"context",
|
|
1167
1171
|
"notes",
|
|
1168
1172
|
"watch",
|
|
1169
1173
|
"tasks",
|
|
@@ -4775,6 +4779,7 @@ Privacy (sharing is the default; this is YOUR opt-out for one record):
|
|
|
4775
4779
|
|
|
4776
4780
|
Brief (what the record means right now — the FIRST thing to read):
|
|
4777
4781
|
dench crm brief <object> <entryId> [--regenerate] [--json]
|
|
4782
|
+
dench crm context <object> <entryId> [--cursor <cursor>] [--json]
|
|
4778
4783
|
Generated from the timeline; read-only. Health, who owes the next
|
|
4779
4784
|
move, open threads, commitments, risks, next step.
|
|
4780
4785
|
Empty on a record nobody has opened yet — the call itself starts one;
|
package/dench.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
stat,
|
|
11
11
|
writeFile,
|
|
12
12
|
} from "node:fs/promises";
|
|
13
|
-
import {
|
|
13
|
+
import { hostname, userInfo } from "node:os";
|
|
14
14
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { ConvexHttpClient } from "convex/browser";
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
STAGING_HOST,
|
|
30
30
|
} from "./host";
|
|
31
31
|
import { cliVersion } from "./lib/cli-version";
|
|
32
|
+
import { denchConfigDirectory } from "./lib/config-path.mjs";
|
|
32
33
|
import { resolveOrganizationBySelector } from "./lib/organizationSelector";
|
|
33
34
|
import { backendErrorText } from "./lib/public-error";
|
|
34
35
|
import { normalizeUnipileToolkitSlug } from "./lib/unipile-toolkits";
|
|
@@ -210,7 +211,7 @@ const api = {
|
|
|
210
211
|
},
|
|
211
212
|
},
|
|
212
213
|
};
|
|
213
|
-
const CONFIG_DIR =
|
|
214
|
+
const CONFIG_DIR = denchConfigDirectory();
|
|
214
215
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
215
216
|
const args = process.argv.slice(2);
|
|
216
217
|
const json = args.includes("--json");
|
|
@@ -186,6 +186,7 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
|
|
|
186
186
|
{"id":"crm.entries.history","group":"crm","summary":"List a CRM entry's audit history.","cli":"dench crm history","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/history","permission":"crm:read","pathParams":["objectName","entryId"]},
|
|
187
187
|
{"id":"crm.activity.list","group":"crm","summary":"List a CRM entry's activity ledger (comments, notes, field changes, emails, meetings, Slack, LinkedIn) in one timeline, newest first by default.","cli":"dench crm activity","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/activity","permission":"crm:read","pathParams":["objectName","entryId"]},
|
|
188
188
|
{"id":"crm.brief.get","group":"crm","summary":"The record's generated Brief: health, who owes the next move, open threads, commitments, risks, next step. Read this before the ledger.","cli":"dench crm brief","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/brief","permission":"crm:read","pathParams":["objectName","entryId"]},
|
|
189
|
+
{"id":"crm.context.get","group":"crm","summary":"Read-only source diagnostics: accessible evidence, association reasons, last Brief source manifest, generation queue, and your mailbox delivery health. Bounded, cursor-paged snapshot; does not certify complete history.","cli":"dench crm context","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/context","permission":"crm:read","pathParams":["objectName","entryId"]},
|
|
189
190
|
{"id":"crm.brief.regenerate","group":"crm","summary":"Rebuild a record's Brief from the current timeline.","cli":"dench crm brief --regenerate","method":"POST","path":"/crm/objects/{objectName}/entries/{entryId}/brief/regenerate","permission":"crm:write","pathParams":["objectName","entryId"]},
|
|
190
191
|
{"id":"crm.activity.count","group":"crm","summary":"Bounded per-source counts of a CRM entry's activity ledger.","cli":"dench crm activity","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/activity/count","permission":"crm:read","pathParams":["objectName","entryId"]},
|
|
191
192
|
{"id":"crm.notes.list","group":"crm","summary":"List a CRM entry's notes (titled, document-style comments), newest first.","cli":"dench crm notes list","method":"GET","path":"/crm/objects/{objectName}/entries/{entryId}/notes","permission":"crm:read","pathParams":["objectName","entryId"]},
|
package/lib/command-registry.ts
CHANGED
|
@@ -1625,6 +1625,25 @@ const rawApiOperations = [
|
|
|
1625
1625
|
}),
|
|
1626
1626
|
},
|
|
1627
1627
|
}),
|
|
1628
|
+
op({
|
|
1629
|
+
id: "crm.context.get",
|
|
1630
|
+
group: "crm",
|
|
1631
|
+
summary:
|
|
1632
|
+
"Read-only source diagnostics: accessible evidence, association reasons, last Brief source manifest, generation queue, and your mailbox delivery health. Bounded, cursor-paged snapshot; does not certify complete history.",
|
|
1633
|
+
cli: "dench crm context",
|
|
1634
|
+
method: "GET",
|
|
1635
|
+
path: "/crm/objects/{objectName}/entries/{entryId}/context",
|
|
1636
|
+
requestSchema: noBody,
|
|
1637
|
+
responseSchema: anyResponse,
|
|
1638
|
+
backend: {
|
|
1639
|
+
...convex("query", "functions/crm/contextDiagnostics:getForEntry"),
|
|
1640
|
+
transformArgs: (input) => ({
|
|
1641
|
+
objectName: input.objectName,
|
|
1642
|
+
entryId: input.entryId,
|
|
1643
|
+
...(typeof input.cursor === "string" ? { cursor: input.cursor } : {}),
|
|
1644
|
+
}),
|
|
1645
|
+
},
|
|
1646
|
+
}),
|
|
1628
1647
|
op({
|
|
1629
1648
|
id: "crm.brief.regenerate",
|
|
1630
1649
|
group: "crm",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
/** Explicit CLI state isolation for managed agents; never changes the OS home. */
|
|
5
|
+
export function denchConfigDirectory(env = process.env) {
|
|
6
|
+
const configured = env.DENCH_CONFIG_DIR?.trim();
|
|
7
|
+
return configured ? resolve(configured) : join(homedir(), ".dench");
|
|
8
|
+
}
|
package/lib/load-dev-env.mjs
CHANGED
|
@@ -35,13 +35,12 @@
|
|
|
35
35
|
// disabled.
|
|
36
36
|
|
|
37
37
|
import { existsSync, readFileSync } from "node:fs";
|
|
38
|
-
import { homedir } from "node:os";
|
|
39
38
|
import { dirname, join } from "node:path";
|
|
40
39
|
import { fileURLToPath } from "node:url";
|
|
40
|
+
import { denchConfigDirectory } from "./config-path.mjs";
|
|
41
41
|
|
|
42
42
|
const WORKSPACE_PACKAGE_NAME = "ironclaw-cloud";
|
|
43
43
|
const ENV_FILES = [".env", ".env.local"];
|
|
44
|
-
const CONFIG_PATH = join(homedir(), ".dench", "config.json");
|
|
45
44
|
|
|
46
45
|
const TRUE_ENV_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
47
46
|
|
|
@@ -55,8 +54,9 @@ function argvOptIn(argv) {
|
|
|
55
54
|
return argv.includes("--dev") || argv.includes("--use-dev-env");
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
function configOptIn() {
|
|
57
|
+
function configOptIn(env) {
|
|
59
58
|
try {
|
|
59
|
+
const CONFIG_PATH = join(denchConfigDirectory(env), "config.json");
|
|
60
60
|
if (!existsSync(CONFIG_PATH)) return false;
|
|
61
61
|
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
62
62
|
const config = JSON.parse(raw);
|
|
@@ -70,7 +70,9 @@ function configOptIn() {
|
|
|
70
70
|
|
|
71
71
|
function isLocalUrl(value) {
|
|
72
72
|
try {
|
|
73
|
-
const url = new URL(
|
|
73
|
+
const url = new URL(
|
|
74
|
+
/^https?:\/\//.test(value) ? value : `https://${value}`,
|
|
75
|
+
);
|
|
74
76
|
const host = url.hostname;
|
|
75
77
|
return (
|
|
76
78
|
host === "localhost" ||
|
|
@@ -90,7 +92,7 @@ export function shouldLoadDevEnv({
|
|
|
90
92
|
} = {}) {
|
|
91
93
|
if (envFlagEnabled(env.DENCH_USE_DEV_ENV)) return "env";
|
|
92
94
|
if (argvOptIn(argv)) return "argv";
|
|
93
|
-
if (configOptIn()) return "config";
|
|
95
|
+
if (configOptIn(env)) return "config";
|
|
94
96
|
return false;
|
|
95
97
|
}
|
|
96
98
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dench.com/cli",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.2-staging.369",
|
|
4
4
|
"description": "Dench agent workspace CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"chat-spawn.ts",
|
|
32
32
|
"crm.ts",
|
|
33
33
|
"crm-comments.ts",
|
|
34
|
+
"crm-context.ts",
|
|
34
35
|
"crm-analytics.ts",
|
|
35
36
|
"crm-detections.ts",
|
|
36
37
|
"crm-privacy.ts",
|