@dench.com/cli 2.0.2 → 2.0.4
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 +55 -13
- package/chat-spawn.ts +12 -2
- package/dench.ts +54 -132
- package/host.ts +17 -2
- package/package.json +1 -1
package/agent-config.ts
CHANGED
|
@@ -34,11 +34,51 @@ import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
|
34
34
|
import { tmpdir } from "node:os";
|
|
35
35
|
import { join } from "node:path";
|
|
36
36
|
import type { ConvexHttpClient } from "convex/browser";
|
|
37
|
-
import {
|
|
37
|
+
import { makeFunctionReference } from "convex/server";
|
|
38
38
|
import { CliArgError, getFlag, hasFlag } from "./lib/cli-args";
|
|
39
39
|
|
|
40
40
|
class AgentConfigCliError extends Error {}
|
|
41
41
|
|
|
42
|
+
const api = {
|
|
43
|
+
functions: {
|
|
44
|
+
agentConfig: {
|
|
45
|
+
getAgentConfig: makeFunctionReference<"query">(
|
|
46
|
+
"functions/agentConfig:getAgentConfig",
|
|
47
|
+
),
|
|
48
|
+
updateIdentity: makeFunctionReference<"mutation">(
|
|
49
|
+
"functions/agentConfig:updateIdentity",
|
|
50
|
+
),
|
|
51
|
+
updateOrganisation: makeFunctionReference<"mutation">(
|
|
52
|
+
"functions/agentConfig:updateOrganisation",
|
|
53
|
+
),
|
|
54
|
+
updateMemory: makeFunctionReference<"mutation">(
|
|
55
|
+
"functions/agentConfig:updateMemory",
|
|
56
|
+
),
|
|
57
|
+
updateToolsNotes: makeFunctionReference<"mutation">(
|
|
58
|
+
"functions/agentConfig:updateToolsNotes",
|
|
59
|
+
),
|
|
60
|
+
updateUserProfile: makeFunctionReference<"mutation">(
|
|
61
|
+
"functions/agentConfig:updateUserProfile",
|
|
62
|
+
),
|
|
63
|
+
updateHeartbeat: makeFunctionReference<"mutation">(
|
|
64
|
+
"functions/agentConfig:updateHeartbeat",
|
|
65
|
+
),
|
|
66
|
+
updateBootstrapTemplate: makeFunctionReference<"mutation">(
|
|
67
|
+
"functions/agentConfig:updateBootstrapTemplate",
|
|
68
|
+
),
|
|
69
|
+
completeBootstrap: makeFunctionReference<"mutation">(
|
|
70
|
+
"functions/agentConfig:completeBootstrap",
|
|
71
|
+
),
|
|
72
|
+
setDefaultChatModelV2: makeFunctionReference<"mutation">(
|
|
73
|
+
"functions/agentConfig:setDefaultChatModelV2",
|
|
74
|
+
),
|
|
75
|
+
setThreadModelOverride: makeFunctionReference<"mutation">(
|
|
76
|
+
"functions/agentConfig:setThreadModelOverride",
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
42
82
|
type CliCtx = {
|
|
43
83
|
convex: ConvexHttpClient;
|
|
44
84
|
args: string[];
|
|
@@ -86,7 +126,9 @@ async function readContentFromStdin(): Promise<string> {
|
|
|
86
126
|
return new Promise((resolve, reject) => {
|
|
87
127
|
const chunks: Buffer[] = [];
|
|
88
128
|
process.stdin.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
89
|
-
process.stdin.on("end", () =>
|
|
129
|
+
process.stdin.on("end", () =>
|
|
130
|
+
resolve(Buffer.concat(chunks).toString("utf8")),
|
|
131
|
+
);
|
|
90
132
|
process.stdin.on("error", reject);
|
|
91
133
|
});
|
|
92
134
|
}
|
|
@@ -150,7 +192,10 @@ async function loadAgentConfig(ctx: CliCtx): Promise<{
|
|
|
150
192
|
userProfile: { content: string; updatedAt: number } | null;
|
|
151
193
|
organizationId: string;
|
|
152
194
|
}> {
|
|
153
|
-
const result = (await runQuery(
|
|
195
|
+
const result = (await runQuery(
|
|
196
|
+
ctx,
|
|
197
|
+
api.functions.agentConfig.getAgentConfig,
|
|
198
|
+
)) as {
|
|
154
199
|
agentConfig: {
|
|
155
200
|
identity: { content: string; updatedAt: number } | null;
|
|
156
201
|
organisation: { content: string; updatedAt: number } | null;
|
|
@@ -212,14 +257,14 @@ async function handleSimpleMagicFile(
|
|
|
212
257
|
} else {
|
|
213
258
|
const config = await loadAgentConfig(ctx);
|
|
214
259
|
const initial = opts.perMember
|
|
215
|
-
? config.userProfile?.content ?? ""
|
|
260
|
+
? (config.userProfile?.content ?? "")
|
|
216
261
|
: opts.field === "memoryAggregate"
|
|
217
|
-
? config.agentConfig.memoryAggregate?.content ?? ""
|
|
262
|
+
? (config.agentConfig.memoryAggregate?.content ?? "")
|
|
218
263
|
: opts.field === "toolsNotes"
|
|
219
|
-
? config.agentConfig.toolsNotes?.content ?? ""
|
|
264
|
+
? (config.agentConfig.toolsNotes?.content ?? "")
|
|
220
265
|
: opts.field === "identity"
|
|
221
|
-
? config.agentConfig.identity?.content ?? ""
|
|
222
|
-
: config.agentConfig.organisation?.content ?? "";
|
|
266
|
+
? (config.agentConfig.identity?.content ?? "")
|
|
267
|
+
: (config.agentConfig.organisation?.content ?? "");
|
|
223
268
|
content = openEditor(initial, opts.label);
|
|
224
269
|
}
|
|
225
270
|
if (!content.trim().length) {
|
|
@@ -519,8 +564,7 @@ export async function runMemoryAggregateCommand(ctx: CliCtx): Promise<void> {
|
|
|
519
564
|
}
|
|
520
565
|
if (sub === "append") {
|
|
521
566
|
const text = ctx.args.slice(1).join(" ").trim();
|
|
522
|
-
if (!text)
|
|
523
|
-
throw new AgentConfigCliError("Usage: dench mem append <text>");
|
|
567
|
+
if (!text) throw new AgentConfigCliError("Usage: dench mem append <text>");
|
|
524
568
|
await runMutation(ctx, api.functions.agentConfig.updateMemory, {
|
|
525
569
|
content: text,
|
|
526
570
|
append: true,
|
|
@@ -593,9 +637,7 @@ export async function runDailyCommand(ctx: CliCtx): Promise<void> {
|
|
|
593
637
|
if (sub === "show") {
|
|
594
638
|
const date = ctx.args[1];
|
|
595
639
|
if (!date) {
|
|
596
|
-
throw new AgentConfigCliError(
|
|
597
|
-
"Usage: dench daily show <YYYY-MM-DD>",
|
|
598
|
-
);
|
|
640
|
+
throw new AgentConfigCliError("Usage: dench daily show <YYYY-MM-DD>");
|
|
599
641
|
}
|
|
600
642
|
out(
|
|
601
643
|
ctx,
|
package/chat-spawn.ts
CHANGED
|
@@ -16,8 +16,6 @@
|
|
|
16
16
|
* (`/api/chat/[runId]/stream`) and pretty-prints text deltas + tool
|
|
17
17
|
* calls until the workflow finishes.
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
import { getCurrentRuntimeTimeZone } from "@/lib/timezone";
|
|
21
19
|
import { hasFlag } from "./lib/cli-args";
|
|
22
20
|
|
|
23
21
|
class ChatSpawnError extends Error {}
|
|
@@ -33,6 +31,18 @@ type ChatSpawnContext = {
|
|
|
33
31
|
jsonOutput: boolean;
|
|
34
32
|
};
|
|
35
33
|
|
|
34
|
+
function getCurrentRuntimeTimeZone(): string | null {
|
|
35
|
+
try {
|
|
36
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
37
|
+
if (!timeZone || timeZone.length > 100) return null;
|
|
38
|
+
// Validate that Intl accepts the resolved value before forwarding it.
|
|
39
|
+
Intl.DateTimeFormat("en-US", { timeZone }).format(new Date());
|
|
40
|
+
return timeZone;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
function consumeFlagValue(args: string[], name: string): string | undefined {
|
|
37
47
|
const idx = args.indexOf(name);
|
|
38
48
|
if (idx === -1) return undefined;
|
package/dench.ts
CHANGED
|
@@ -16,11 +16,11 @@ import { ConvexHttpClient } from "convex/browser";
|
|
|
16
16
|
import { makeFunctionReference } from "convex/server";
|
|
17
17
|
import { agentKindLabel, normalizeAgentKind } from "./agentKind";
|
|
18
18
|
import {
|
|
19
|
+
apiHostForDenchHost,
|
|
19
20
|
DEFAULT_HOST,
|
|
20
21
|
isLocalHost,
|
|
21
22
|
LOCAL_HOST,
|
|
22
23
|
normalizeHost,
|
|
23
|
-
PRODUCTION_API_URL,
|
|
24
24
|
PRODUCTION_CONVEX_URL,
|
|
25
25
|
PRODUCTION_HOST,
|
|
26
26
|
resolveBackendAlias,
|
|
@@ -440,13 +440,13 @@ Workspace members (for CRM \`user\`-type fields):
|
|
|
440
440
|
cells assign to real members instead of being stored as unlinked
|
|
441
441
|
display-name strings. See \`dench members help\`.
|
|
442
442
|
|
|
443
|
-
Live web search (Exa via the Dench
|
|
443
|
+
Live web search (Exa via the Dench Gateway, auths with the active \`dench signin\` agent session or DENCH_API_KEY):
|
|
444
444
|
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]
|
|
445
445
|
dench search contents <url> [<url>...] [--max-chars 4000] [--summary "..."] [--json]
|
|
446
446
|
dench search answer "<query>" [--json]
|
|
447
447
|
Help: dench search help
|
|
448
448
|
|
|
449
|
-
Image generation / editing (
|
|
449
|
+
Image generation / editing (via the Dench Gateway):
|
|
450
450
|
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]
|
|
451
451
|
dench image edit "<prompt>" --input <path> [--input <path> ...] [--mask <path>] [--size ...] [--quality ...] [--format ...] [--save-path ...] [--output ...] [--json]
|
|
452
452
|
Help: dench image help
|
|
@@ -472,7 +472,7 @@ Spawn a new chat-turn workflow (same as the web UI):
|
|
|
472
472
|
|
|
473
473
|
Help: dench chat help
|
|
474
474
|
|
|
475
|
-
File sync
|
|
475
|
+
File sync:
|
|
476
476
|
dench fs status [--json] [--workspace /workspace] [--no-hash] [--drift-limit N]
|
|
477
477
|
dench fs sync --initial --org-id <orgId> [--workspace /workspace]
|
|
478
478
|
dench fs stream-open <path>
|
|
@@ -488,7 +488,7 @@ returns ENOSYS on the FUSE-backed volume mount):
|
|
|
488
488
|
Persist scratch -> workspace (cp from /tmp into /workspace + Convex sync):
|
|
489
489
|
dench stage <local-path> [<workspace-dest>] [--clean] [--json]
|
|
490
490
|
|
|
491
|
-
Subagents
|
|
491
|
+
Subagents:
|
|
492
492
|
dench agent spawn '<goal>' [--parent-run-id <id>] [--sandbox own|share_parent] [--time-budget-ms N]
|
|
493
493
|
dench agent await --hook <token> | --children id1,id2,id3
|
|
494
494
|
dench agent message <toRunId> '<text>' [--from-run-id <id>]
|
|
@@ -501,7 +501,7 @@ Subagents (Daytona-native rewrite):
|
|
|
501
501
|
|
|
502
502
|
dench --version
|
|
503
503
|
|
|
504
|
-
Self-updating agent harness
|
|
504
|
+
Self-updating agent harness:
|
|
505
505
|
dench identity show | edit IDENTITY.md (org-wide)
|
|
506
506
|
dench organisation show | edit ORGANISATION.md
|
|
507
507
|
dench user show | edit USER.md (per-member)
|
|
@@ -1993,16 +1993,6 @@ async function resolveOrgChoiceForOtp(input: {
|
|
|
1993
1993
|
return { newOrganizationName: newName.trim() };
|
|
1994
1994
|
}
|
|
1995
1995
|
|
|
1996
|
-
function normalizeApiBase(input: string) {
|
|
1997
|
-
const trimmed = input.trim();
|
|
1998
|
-
const withProtocol = /^https?:\/\//.test(trimmed)
|
|
1999
|
-
? trimmed
|
|
2000
|
-
: /^(localhost|127\.0\.0\.1|\[::1\])(?::|$)/.test(trimmed)
|
|
2001
|
-
? `http://${trimmed}`
|
|
2002
|
-
: `https://${trimmed}`;
|
|
2003
|
-
return new URL(withProtocol).origin;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
1996
|
async function parseResponseBody(response: Response) {
|
|
2007
1997
|
const text = await response.text();
|
|
2008
1998
|
if (!text) return {};
|
|
@@ -2048,25 +2038,31 @@ function parseBillingTopupAmountUsd() {
|
|
|
2048
2038
|
}
|
|
2049
2039
|
|
|
2050
2040
|
async function billingStatus(runtime: Runtime) {
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2041
|
+
const authedRuntime = requireAuthenticatedRuntime(
|
|
2042
|
+
runtime,
|
|
2043
|
+
"dench billing status",
|
|
2044
|
+
);
|
|
2045
|
+
const status = await authedRuntime.client.query(
|
|
2053
2046
|
api.functions.agentWorkspace.getAgentBillingStatus,
|
|
2054
|
-
{ sessionToken:
|
|
2047
|
+
{ sessionToken: authedRuntime.sessionToken },
|
|
2055
2048
|
);
|
|
2056
2049
|
print(json ? status : formatBillingStatus(status));
|
|
2057
2050
|
}
|
|
2058
2051
|
|
|
2059
2052
|
async function billingTopup(runtime: Runtime) {
|
|
2060
|
-
const
|
|
2053
|
+
const authedRuntime = requireAuthenticatedRuntime(
|
|
2054
|
+
runtime,
|
|
2055
|
+
"dench billing topup",
|
|
2056
|
+
);
|
|
2061
2057
|
const amountUsd = parseBillingTopupAmountUsd();
|
|
2062
2058
|
const response = await fetch(
|
|
2063
|
-
`${
|
|
2059
|
+
`${authedRuntime.host.replace(/\/+$/, "")}/api/stripe/create-ai-topup`,
|
|
2064
2060
|
{
|
|
2065
2061
|
method: "POST",
|
|
2066
2062
|
headers: {
|
|
2067
2063
|
accept: "application/json",
|
|
2068
2064
|
"content-type": "application/json",
|
|
2069
|
-
authorization: `Bearer ${
|
|
2065
|
+
authorization: `Bearer ${authedRuntime.sessionToken}`,
|
|
2070
2066
|
},
|
|
2071
2067
|
body: JSON.stringify({
|
|
2072
2068
|
amountUsd,
|
|
@@ -2133,7 +2129,7 @@ async function billingTopup(runtime: Runtime) {
|
|
|
2133
2129
|
* needed in that case). We surface either outcome cleanly.
|
|
2134
2130
|
*/
|
|
2135
2131
|
async function denchUpgrade(runtime: Runtime) {
|
|
2136
|
-
const
|
|
2132
|
+
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench upgrade");
|
|
2137
2133
|
const tierFlag = option("--tier")?.trim().toLowerCase() || "pro";
|
|
2138
2134
|
const cycleFlag =
|
|
2139
2135
|
option("--cycle")?.trim().toLowerCase() ||
|
|
@@ -2156,13 +2152,13 @@ async function denchUpgrade(runtime: Runtime) {
|
|
|
2156
2152
|
const internalTier = tierFlag === "pro" ? "desktop" : "cloud";
|
|
2157
2153
|
|
|
2158
2154
|
const response = await fetch(
|
|
2159
|
-
`${
|
|
2155
|
+
`${authedRuntime.host.replace(/\/+$/, "")}/api/stripe/create-checkout`,
|
|
2160
2156
|
{
|
|
2161
2157
|
method: "POST",
|
|
2162
2158
|
headers: {
|
|
2163
2159
|
accept: "application/json",
|
|
2164
2160
|
"content-type": "application/json",
|
|
2165
|
-
authorization: `Bearer ${
|
|
2161
|
+
authorization: `Bearer ${authedRuntime.sessionToken}`,
|
|
2166
2162
|
},
|
|
2167
2163
|
body: JSON.stringify({
|
|
2168
2164
|
tier: internalTier,
|
|
@@ -2256,16 +2252,14 @@ function resolveApiKeyConvexUrl(): string | undefined {
|
|
|
2256
2252
|
/**
|
|
2257
2253
|
* Resolve the Next.js API host the CLI should hit for HTTP calls (e.g.
|
|
2258
2254
|
* Stripe billing topup, the gateway proxy). Same defense-in-depth
|
|
2259
|
-
* pattern as `resolveApiKeyConvexUrl`: env wins, then the
|
|
2260
|
-
* Dench host the runtime is targeting
|
|
2261
|
-
* `PRODUCTION_API_URL` as the last resort.
|
|
2255
|
+
* pattern as `resolveApiKeyConvexUrl`: env wins, then the canonical API
|
|
2256
|
+
* origin for the Dench host the runtime is targeting.
|
|
2262
2257
|
*/
|
|
2263
2258
|
function resolveApiHost(fallbackHost: string): string {
|
|
2264
2259
|
return (
|
|
2265
2260
|
process.env.DENCH_API_URL?.trim() ||
|
|
2266
2261
|
process.env.DENCH_INTERNAL_BASE?.trim() ||
|
|
2267
|
-
fallbackHost
|
|
2268
|
-
PRODUCTION_API_URL
|
|
2262
|
+
apiHostForDenchHost(fallbackHost)
|
|
2269
2263
|
);
|
|
2270
2264
|
}
|
|
2271
2265
|
|
|
@@ -2305,7 +2299,7 @@ async function getRuntime() {
|
|
|
2305
2299
|
if (stored.status === "found") {
|
|
2306
2300
|
return {
|
|
2307
2301
|
mode: "session" as const,
|
|
2308
|
-
host,
|
|
2302
|
+
host: resolveApiHost(host),
|
|
2309
2303
|
client: new ConvexHttpClient(stored.session.convexUrl),
|
|
2310
2304
|
sessionToken: stored.session.sessionToken,
|
|
2311
2305
|
organization: stored.session.organization,
|
|
@@ -2406,25 +2400,6 @@ function stripRuntimeFlags(args: string[]) {
|
|
|
2406
2400
|
return stripped;
|
|
2407
2401
|
}
|
|
2408
2402
|
|
|
2409
|
-
/**
|
|
2410
|
-
* `session` mode is required for `agentWorkspace.*` calls because they
|
|
2411
|
-
* gate on `validateAgentSession` server-side, which only accepts the
|
|
2412
|
-
* `dch_agent_*` token format. Sandboxes that only have a unified
|
|
2413
|
-
* `DENCH_API_KEY` (apiKey mode) cannot impersonate a specific agent
|
|
2414
|
-
* and will fail at the server with `Invalid agent session` — surface
|
|
2415
|
-
* that here as a clear error instead.
|
|
2416
|
-
*/
|
|
2417
|
-
function requireSessionRuntime(
|
|
2418
|
-
runtime: Runtime,
|
|
2419
|
-
command: string,
|
|
2420
|
-
): Extract<Runtime, { mode: "session" }> {
|
|
2421
|
-
if (runtime.mode === "session") return runtime;
|
|
2422
|
-
if (runtime.mode === "apiKey") {
|
|
2423
|
-
throw agentSessionRequiredError(command);
|
|
2424
|
-
}
|
|
2425
|
-
throw loginRequiredError(command);
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
2403
|
/**
|
|
2429
2404
|
* Org-scoped commands (`dench crm`, `dench files`, etc.) accept either
|
|
2430
2405
|
* a `dch_agent_*` agent session token OR a unified Dench API key
|
|
@@ -2486,7 +2461,17 @@ function gatewayAuthError(
|
|
|
2486
2461
|
});
|
|
2487
2462
|
}
|
|
2488
2463
|
if (response.status === 401) {
|
|
2489
|
-
return
|
|
2464
|
+
return new CliError(
|
|
2465
|
+
`${command} could not exchange the active Dench session for a gateway key (${code})`,
|
|
2466
|
+
{
|
|
2467
|
+
code,
|
|
2468
|
+
status: response.status,
|
|
2469
|
+
nextActions: [
|
|
2470
|
+
"Run dench sessions --json to confirm the selected session.",
|
|
2471
|
+
'Run dench signin --kind <kind> --name "AI Agent - Project" to refresh the agent session if it expired.',
|
|
2472
|
+
],
|
|
2473
|
+
},
|
|
2474
|
+
);
|
|
2490
2475
|
}
|
|
2491
2476
|
return new CliError(`${command} could not get a gateway key (${code})`, {
|
|
2492
2477
|
code,
|
|
@@ -2545,36 +2530,6 @@ function encodeDevCrmSessionToken(args: {
|
|
|
2545
2530
|
return `dch_dev_${encodeURIComponent(args.workspaceSlug)}:${encodeURIComponent(args.devKey)}`;
|
|
2546
2531
|
}
|
|
2547
2532
|
|
|
2548
|
-
function agentSessionRequiredError(command: string) {
|
|
2549
|
-
return new CliError(
|
|
2550
|
-
`${command} requires an agent session — DENCH_API_KEY is org-scoped and cannot impersonate a specific agent`,
|
|
2551
|
-
{
|
|
2552
|
-
code: "agent_session_required",
|
|
2553
|
-
nextActions: [
|
|
2554
|
-
'Run dench signin --kind <kind> --name "AI Agent - Project" to mint an agent session for this command.',
|
|
2555
|
-
"Or run the equivalent action via the workspace UI / dench crm if it is org-level (e.g. CRM data, files).",
|
|
2556
|
-
],
|
|
2557
|
-
},
|
|
2558
|
-
);
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
/**
|
|
2562
|
-
* `dench approval request` has separate session vs dev branches. With
|
|
2563
|
-
* apiKey mode added, the dev branch would silently receive partial args.
|
|
2564
|
-
* Use this to short-circuit that branch with a clear error.
|
|
2565
|
-
*
|
|
2566
|
-
* Asserts away the `apiKey` mode so the caller's downstream type
|
|
2567
|
-
* narrowing (e.g. `runtime.workspaceArgs`) keeps working.
|
|
2568
|
-
*/
|
|
2569
|
-
function rejectApiKeyForAgentCommand(
|
|
2570
|
-
runtime: Runtime,
|
|
2571
|
-
command: string,
|
|
2572
|
-
): asserts runtime is Exclude<Runtime, { mode: "apiKey" }> {
|
|
2573
|
-
if (runtime.mode === "apiKey") {
|
|
2574
|
-
throw agentSessionRequiredError(command);
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
|
|
2578
2533
|
function asRecord(value: unknown): JsonRecord | undefined {
|
|
2579
2534
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2580
2535
|
return undefined;
|
|
@@ -2681,7 +2636,7 @@ async function connectedAppsContext(runtime: Runtime) {
|
|
|
2681
2636
|
async function buildContext(runtime: Runtime) {
|
|
2682
2637
|
let mine: JsonRecord;
|
|
2683
2638
|
let urls: JsonRecord;
|
|
2684
|
-
if (runtime.mode === "session") {
|
|
2639
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
2685
2640
|
mine = (await runtime.client.query(
|
|
2686
2641
|
api.functions.agentWorkspace.whatCanIDoHere,
|
|
2687
2642
|
{ sessionToken: runtime.sessionToken },
|
|
@@ -2694,20 +2649,7 @@ async function buildContext(runtime: Runtime) {
|
|
|
2694
2649
|
);
|
|
2695
2650
|
urls = {};
|
|
2696
2651
|
} else {
|
|
2697
|
-
|
|
2698
|
-
// `whatCanIDoHere` (it gates on `validateAgentSession`). Return a
|
|
2699
|
-
// minimal context derived from sandbox envVars so dench-cli help
|
|
2700
|
-
// surfaces still work without a full login.
|
|
2701
|
-
mine = {
|
|
2702
|
-
authMode: "apiKey",
|
|
2703
|
-
organization: runtime.organization ?? null,
|
|
2704
|
-
runId: process.env.DENCH_RUN_ID?.trim() ?? null,
|
|
2705
|
-
parentRunId: process.env.DENCH_PARENT_RUN_ID?.trim() ?? null,
|
|
2706
|
-
rootRunId: process.env.DENCH_ROOT_RUN_ID?.trim() ?? null,
|
|
2707
|
-
};
|
|
2708
|
-
urls = runtime.host
|
|
2709
|
-
? workspaceUrls(runtime.host, runtime.organization?.slug)
|
|
2710
|
-
: {};
|
|
2652
|
+
throw loginRequiredError("dench context");
|
|
2711
2653
|
}
|
|
2712
2654
|
|
|
2713
2655
|
return {
|
|
@@ -2935,7 +2877,7 @@ function summarizeDevMine(workspaceSlug: string, data: WorkspaceOverview) {
|
|
|
2935
2877
|
}
|
|
2936
2878
|
|
|
2937
2879
|
async function listArtifacts(runtime: Runtime, limit?: number) {
|
|
2938
|
-
if (runtime.mode === "session") {
|
|
2880
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
2939
2881
|
try {
|
|
2940
2882
|
return (await runtime.client.query(
|
|
2941
2883
|
api.functions.agentWorkspace.agentListArtifacts,
|
|
@@ -2953,12 +2895,6 @@ async function listArtifacts(runtime: Runtime, limit?: number) {
|
|
|
2953
2895
|
);
|
|
2954
2896
|
}
|
|
2955
2897
|
}
|
|
2956
|
-
if (runtime.mode === "apiKey") {
|
|
2957
|
-
// No agent identity → no artifact list. Return empty so callers
|
|
2958
|
-
// (e.g. `dench artifacts`, `dench suggested-work`) degrade gracefully
|
|
2959
|
-
// instead of throwing for sandbox callers.
|
|
2960
|
-
return [] as JsonRecord[];
|
|
2961
|
-
}
|
|
2962
2898
|
const data = await devOverview(runtime);
|
|
2963
2899
|
return asRecordArray(data.recentArtifacts);
|
|
2964
2900
|
}
|
|
@@ -4167,15 +4103,15 @@ async function main() {
|
|
|
4167
4103
|
}
|
|
4168
4104
|
|
|
4169
4105
|
if (command === "memory") {
|
|
4170
|
-
const
|
|
4106
|
+
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench memory");
|
|
4171
4107
|
if (subcommand === "search") {
|
|
4172
4108
|
const query = positionalsFrom(2).join(" ").trim();
|
|
4173
4109
|
if (!query) throw new Error('Usage: dench memory search "query"');
|
|
4174
4110
|
print(
|
|
4175
|
-
await
|
|
4111
|
+
await authedRuntime.client.query(
|
|
4176
4112
|
api.functions.agentWorkspace.agentSearchMemory,
|
|
4177
4113
|
{
|
|
4178
|
-
sessionToken:
|
|
4114
|
+
sessionToken: authedRuntime.sessionToken,
|
|
4179
4115
|
query,
|
|
4180
4116
|
limit: parseNumberOption("--limit"),
|
|
4181
4117
|
},
|
|
@@ -4190,10 +4126,10 @@ async function main() {
|
|
|
4190
4126
|
throw new Error('Usage: dench memory save <key> "text"');
|
|
4191
4127
|
}
|
|
4192
4128
|
print(
|
|
4193
|
-
await
|
|
4129
|
+
await authedRuntime.client.mutation(
|
|
4194
4130
|
api.functions.agentWorkspace.agentSaveMemory,
|
|
4195
4131
|
{
|
|
4196
|
-
sessionToken:
|
|
4132
|
+
sessionToken: authedRuntime.sessionToken,
|
|
4197
4133
|
key,
|
|
4198
4134
|
kind: (option("--kind") ?? "fact") as never,
|
|
4199
4135
|
text,
|
|
@@ -4218,9 +4154,9 @@ async function main() {
|
|
|
4218
4154
|
}
|
|
4219
4155
|
|
|
4220
4156
|
if (command === "status") {
|
|
4221
|
-
if (runtime.mode === "session") {
|
|
4157
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
4222
4158
|
const status = (await runtime.client.query(
|
|
4223
|
-
api.functions.agentWorkspace.
|
|
4159
|
+
api.functions.agentWorkspace.whatCanIDoHere,
|
|
4224
4160
|
{
|
|
4225
4161
|
sessionToken: runtime.sessionToken,
|
|
4226
4162
|
},
|
|
@@ -4232,13 +4168,6 @@ async function main() {
|
|
|
4232
4168
|
print(status);
|
|
4233
4169
|
return;
|
|
4234
4170
|
}
|
|
4235
|
-
if (runtime.mode === "apiKey") {
|
|
4236
|
-
// No agent session → no agent-status query. Fall back to a thin
|
|
4237
|
-
// env-derived snapshot so sandbox callers still get something
|
|
4238
|
-
// useful from `dench status`.
|
|
4239
|
-
print(await buildContext(runtime));
|
|
4240
|
-
return;
|
|
4241
|
-
}
|
|
4242
4171
|
const data = await devOverview(runtime);
|
|
4243
4172
|
const output = scopedStatus
|
|
4244
4173
|
? summarizeDevMine(runtime.workspaceArgs.workspaceSlug, data)
|
|
@@ -4250,9 +4179,9 @@ async function main() {
|
|
|
4250
4179
|
}
|
|
4251
4180
|
|
|
4252
4181
|
if (command === "agents") {
|
|
4253
|
-
if (runtime.mode === "session") {
|
|
4182
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
4254
4183
|
const status = await runtime.client.query(
|
|
4255
|
-
api.functions.agentWorkspace.
|
|
4184
|
+
api.functions.agentWorkspace.whatCanIDoHere,
|
|
4256
4185
|
{
|
|
4257
4186
|
sessionToken: runtime.sessionToken,
|
|
4258
4187
|
},
|
|
@@ -4260,18 +4189,15 @@ async function main() {
|
|
|
4260
4189
|
print(status.agents);
|
|
4261
4190
|
return;
|
|
4262
4191
|
}
|
|
4263
|
-
if (runtime.mode === "apiKey") {
|
|
4264
|
-
throw loginRequiredError("dench agents");
|
|
4265
|
-
}
|
|
4266
4192
|
const data = await devOverview(runtime);
|
|
4267
4193
|
print(data.agents);
|
|
4268
4194
|
return;
|
|
4269
4195
|
}
|
|
4270
4196
|
|
|
4271
4197
|
if (command === "approvals") {
|
|
4272
|
-
if (runtime.mode === "session") {
|
|
4198
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
4273
4199
|
const status = await runtime.client.query(
|
|
4274
|
-
api.functions.agentWorkspace.
|
|
4200
|
+
api.functions.agentWorkspace.whatCanIDoHere,
|
|
4275
4201
|
{
|
|
4276
4202
|
sessionToken: runtime.sessionToken,
|
|
4277
4203
|
},
|
|
@@ -4279,9 +4205,6 @@ async function main() {
|
|
|
4279
4205
|
print(status.approvals);
|
|
4280
4206
|
return;
|
|
4281
4207
|
}
|
|
4282
|
-
if (runtime.mode === "apiKey") {
|
|
4283
|
-
throw loginRequiredError("dench approvals");
|
|
4284
|
-
}
|
|
4285
4208
|
const data = await devOverview(runtime);
|
|
4286
4209
|
print(data.approvals);
|
|
4287
4210
|
return;
|
|
@@ -4311,7 +4234,7 @@ async function main() {
|
|
|
4311
4234
|
if (command === "approval" && subcommand === "request") {
|
|
4312
4235
|
const title = positional(2);
|
|
4313
4236
|
if (!title) throw new Error("Missing approval title");
|
|
4314
|
-
if (runtime.mode === "session") {
|
|
4237
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
4315
4238
|
print(
|
|
4316
4239
|
await runtime.client.mutation(
|
|
4317
4240
|
api.functions.agentWorkspace.agentRequestApproval,
|
|
@@ -4325,7 +4248,6 @@ async function main() {
|
|
|
4325
4248
|
);
|
|
4326
4249
|
return;
|
|
4327
4250
|
}
|
|
4328
|
-
rejectApiKeyForAgentCommand(runtime, "dench approval request");
|
|
4329
4251
|
const approvalId = await runtime.client.mutation(
|
|
4330
4252
|
api.functions.agentWorkspace.devRequestApproval,
|
|
4331
4253
|
{
|
|
@@ -4348,15 +4270,15 @@ async function main() {
|
|
|
4348
4270
|
) {
|
|
4349
4271
|
const approvalId = positional(2);
|
|
4350
4272
|
if (!approvalId) throw new Error("Missing approval id");
|
|
4351
|
-
const
|
|
4273
|
+
const authedRuntime = requireAuthenticatedRuntime(
|
|
4352
4274
|
runtime,
|
|
4353
4275
|
"dench approval approve/reject",
|
|
4354
4276
|
);
|
|
4355
4277
|
const decision = subcommand === "approve" ? "approved" : "rejected";
|
|
4356
|
-
const result = await
|
|
4278
|
+
const result = await authedRuntime.client.mutation(
|
|
4357
4279
|
api.functions.agentWorkspace.agentDecideApproval,
|
|
4358
4280
|
{
|
|
4359
|
-
sessionToken:
|
|
4281
|
+
sessionToken: authedRuntime.sessionToken,
|
|
4360
4282
|
approvalId: approvalId as never,
|
|
4361
4283
|
decision,
|
|
4362
4284
|
evidence: option("--evidence"),
|
package/host.ts
CHANGED
|
@@ -14,7 +14,8 @@ export const DEFAULT_HOST = PRODUCTION_HOST;
|
|
|
14
14
|
* deployment + Next.js host backs `https://www.dench.com`.
|
|
15
15
|
*/
|
|
16
16
|
export const PRODUCTION_API_URL = "https://www.dench.com";
|
|
17
|
-
export const PRODUCTION_CONVEX_URL =
|
|
17
|
+
export const PRODUCTION_CONVEX_URL =
|
|
18
|
+
"https://beaming-alligator-67.convex.cloud";
|
|
18
19
|
export const PRODUCTION_GATEWAY_URL = "https://gateway.merseoriginals.com";
|
|
19
20
|
|
|
20
21
|
export type BackendAlias = "production" | "prod" | "staging" | "local";
|
|
@@ -52,6 +53,16 @@ export function isLocalHost(input: string) {
|
|
|
52
53
|
);
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
export function apiHostForDenchHost(input: string | undefined) {
|
|
57
|
+
if (!input?.trim()) return PRODUCTION_API_URL;
|
|
58
|
+
const normalized = normalizeHost(input);
|
|
59
|
+
// `dench.com` redirects to `www.dench.com` in production. Fetch drops the
|
|
60
|
+
// Authorization header across that redirect, so CLI API calls must start on
|
|
61
|
+
// the canonical API origin.
|
|
62
|
+
if (normalized === PRODUCTION_HOST) return PRODUCTION_API_URL;
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
|
|
55
66
|
/**
|
|
56
67
|
* Resolve `local|staging|prod|production|<url>` -> a normalized host
|
|
57
68
|
* origin. Aliases let `dench backend set <alias>` (and `--backend
|
|
@@ -73,7 +84,11 @@ export function resolveBackendAlias(input: string): string {
|
|
|
73
84
|
if (normalized === "staging" || normalized === "stage") {
|
|
74
85
|
return STAGING_HOST;
|
|
75
86
|
}
|
|
76
|
-
if (
|
|
87
|
+
if (
|
|
88
|
+
normalized === "local" ||
|
|
89
|
+
normalized === "localhost" ||
|
|
90
|
+
normalized === "dev"
|
|
91
|
+
) {
|
|
77
92
|
return LOCAL_HOST;
|
|
78
93
|
}
|
|
79
94
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dench.com/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Dench agent workspace CLI. v2 unifies auth behind `dench signin`; the legacy `dench login` / `dench onboard` / `dench setup` / `dench what-can-i-do` / `dench register` commands now error with a redirect.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|