@dench.com/cli 3.7.0-staging.387 → 3.8.1-staging.390
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 +43 -44
- package/agent-config.ts +5 -55
- package/app-sdk.d.ts +253 -43
- package/campaign.ts +335 -54
- package/chat-spawn.ts +11 -2
- package/chat.ts +3 -5
- package/context-settings.ts +86 -0
- package/cron.ts +19 -0
- package/dench.ts +202 -83
- package/environments.ts +694 -0
- package/lib/api-schemas.ts +190 -20
- package/lib/app-sdk-catalog.generated.ts +34 -20
- package/lib/campaign-steps.ts +474 -12
- package/lib/campaign-timeline.ts +69 -4
- package/lib/command-args.ts +19 -4
- package/lib/command-help.ts +17 -2
- package/lib/command-registry.ts +225 -11
- package/lib/environment-archive.ts +255 -0
- package/lib/environment-cli-args.ts +96 -0
- package/lib/environment-contract.ts +237 -0
- package/lib/environment-execution.ts +769 -0
- package/lib/environment-source.ts +165 -0
- package/lib/load-dev-env.mjs +3 -1
- package/lib/load-dev-env.test.mjs +26 -9
- package/lib/parent-argv.ts +20 -0
- package/lib/references.generated.json +26 -18
- package/package.json +5 -3
- package/workspace-secrets.ts +231 -0
- package/agent.ts +0 -37
package/chat-spawn.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* (`/api/chat/[runId]/stream`) and pretty-prints text deltas + tool
|
|
17
17
|
* calls until the workflow finishes.
|
|
18
18
|
*/
|
|
19
|
-
import { hasFlag } from "./lib/cli-args";
|
|
19
|
+
import { assertNoUnknownFlags, hasFlag } from "./lib/cli-args";
|
|
20
20
|
|
|
21
21
|
class ChatSpawnError extends Error {}
|
|
22
22
|
|
|
@@ -47,6 +47,10 @@ function consumeFlagValue(args: string[], name: string): string | undefined {
|
|
|
47
47
|
const idx = args.indexOf(name);
|
|
48
48
|
if (idx === -1) return undefined;
|
|
49
49
|
const value = args[idx + 1];
|
|
50
|
+
if (!value || value.startsWith("--"))
|
|
51
|
+
throw new ChatSpawnError(`${name} requires a value.`);
|
|
52
|
+
if (args.indexOf(name, idx + 1) !== -1)
|
|
53
|
+
throw new ChatSpawnError(`Duplicate flag: ${name}`);
|
|
50
54
|
args.splice(idx, 2);
|
|
51
55
|
return value;
|
|
52
56
|
}
|
|
@@ -372,17 +376,21 @@ function joinPositionalsExceptFlags(args: string[]): string {
|
|
|
372
376
|
}
|
|
373
377
|
|
|
374
378
|
async function runChatNew(ctx: ChatSpawnContext): Promise<void> {
|
|
379
|
+
const environment = consumeFlagValue(ctx.args, "--environment");
|
|
380
|
+
if (environment !== undefined && !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
381
|
+
throw new ChatSpawnError("Invalid environment name");
|
|
375
382
|
const model = consumeFlagValue(ctx.args, "--model");
|
|
376
383
|
const fileContextPath = consumeFlagValue(ctx.args, "--file-context");
|
|
377
384
|
const visibility = consumeFlagValue(ctx.args, "--visibility");
|
|
378
385
|
const title = consumeFlagValue(ctx.args, "--title");
|
|
379
386
|
const yolo = consumeFlag(ctx.args, "--yolo");
|
|
380
387
|
const follow = consumeFlag(ctx.args, "--follow");
|
|
388
|
+
assertNoUnknownFlags(ctx.args, "chat new");
|
|
381
389
|
|
|
382
390
|
const prompt = joinPositionalsExceptFlags(ctx.args);
|
|
383
391
|
if (!prompt) {
|
|
384
392
|
throw new ChatSpawnError(
|
|
385
|
-
'Usage: dench chat new "<prompt>" [--model M] [--file-context PATH] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]',
|
|
393
|
+
'Usage: dench chat new "<prompt>" [--environment NAME] [--model M] [--file-context PATH] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]',
|
|
386
394
|
);
|
|
387
395
|
}
|
|
388
396
|
|
|
@@ -391,6 +399,7 @@ async function runChatNew(ctx: ChatSpawnContext): Promise<void> {
|
|
|
391
399
|
ctx.runtime.bearerToken,
|
|
392
400
|
{
|
|
393
401
|
prompt,
|
|
402
|
+
...(environment ? { environment } : {}),
|
|
394
403
|
model,
|
|
395
404
|
fileContextPath,
|
|
396
405
|
visibility,
|
package/chat.ts
CHANGED
|
@@ -145,7 +145,7 @@ PERMANENTLY delete one or more past chat threads (and all their messages):
|
|
|
145
145
|
server REFUSES to delete them unless \`--include-automated\` is set.
|
|
146
146
|
|
|
147
147
|
Spawn a new chat-turn workflow (same agent loop as the web UI):
|
|
148
|
-
dench chat new "<prompt>" [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
148
|
+
dench chat new "<prompt>" [--environment NAME] [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
149
149
|
dench chat send <thread-id> "<message>" [--model <m>] [--yolo] [--follow] [--json]
|
|
150
150
|
dench chat follow <thread-id> [--json]
|
|
151
151
|
dench chat tree [<root-thread-id>] [--limit N] [--json]
|
|
@@ -155,10 +155,8 @@ Spawn a new chat-turn workflow (same agent loop as the web UI):
|
|
|
155
155
|
thread hierarchy from a root thread.
|
|
156
156
|
|
|
157
157
|
Approval policy:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
--yolo overrides for this single chat turn — every approval gate is
|
|
161
|
-
off, equivalent to flipping the org to YOLO mode for this run.
|
|
158
|
+
Workspace agent runs use Full access by default.
|
|
159
|
+
--yolo remains accepted for compatibility.
|
|
162
160
|
|
|
163
161
|
Auth:
|
|
164
162
|
Inside a sandbox, this just works — DENCH_API_KEY + DENCH_RUN_ID are
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { makeFunctionReference } from "convex/server";
|
|
3
|
+
import { CliArgError } from "./lib/cli-args";
|
|
4
|
+
import { parseEnvironmentOptions } from "./lib/environment-cli-args";
|
|
5
|
+
|
|
6
|
+
export const CONTEXT_SECTIONS = [
|
|
7
|
+
"identity",
|
|
8
|
+
"organisation",
|
|
9
|
+
"user",
|
|
10
|
+
"tools",
|
|
11
|
+
"rules",
|
|
12
|
+
] as const;
|
|
13
|
+
export function validateContextSet(input: readonly string[]) {
|
|
14
|
+
const parsed = parseEnvironmentOptions(
|
|
15
|
+
input,
|
|
16
|
+
["--content"],
|
|
17
|
+
["--stdin", "--json"],
|
|
18
|
+
);
|
|
19
|
+
const [verb, section] = parsed.positional;
|
|
20
|
+
if (
|
|
21
|
+
verb !== "set" ||
|
|
22
|
+
parsed.positional.length !== 2 ||
|
|
23
|
+
!CONTEXT_SECTIONS.includes(section as never) ||
|
|
24
|
+
parsed.child.length
|
|
25
|
+
)
|
|
26
|
+
throw new CliArgError(
|
|
27
|
+
`Usage: dench context set <${CONTEXT_SECTIONS.join("|")}> --stdin | --content <text> [--json]`,
|
|
28
|
+
);
|
|
29
|
+
if (
|
|
30
|
+
Number(parsed.flags["--stdin"] === true) +
|
|
31
|
+
Number(typeof parsed.flags["--content"] === "string") !==
|
|
32
|
+
1
|
|
33
|
+
)
|
|
34
|
+
throw new CliArgError(
|
|
35
|
+
"Choose exactly one of --stdin or --content. Store credentials with dench secrets set, never in context.",
|
|
36
|
+
);
|
|
37
|
+
return {
|
|
38
|
+
section: section as (typeof CONTEXT_SECTIONS)[number],
|
|
39
|
+
content: parsed.flags["--content"] as string | undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function runContextSet(opts: {
|
|
44
|
+
convex: ConvexHttpClient;
|
|
45
|
+
args: string[];
|
|
46
|
+
sessionToken?: string;
|
|
47
|
+
readStdin?: () => Promise<string>;
|
|
48
|
+
}) {
|
|
49
|
+
const parsed = validateContextSet(opts.args);
|
|
50
|
+
const content =
|
|
51
|
+
parsed.content ??
|
|
52
|
+
(await (
|
|
53
|
+
opts.readStdin ??
|
|
54
|
+
(async () => {
|
|
55
|
+
const chunks: Buffer[] = [];
|
|
56
|
+
let size = 0;
|
|
57
|
+
for await (const chunk of process.stdin) {
|
|
58
|
+
size += Buffer.byteLength(chunk);
|
|
59
|
+
if (size > 32000)
|
|
60
|
+
throw new CliArgError(
|
|
61
|
+
"Context exceeds 32 KB. Keep standing instructions short; use workspace files for references.",
|
|
62
|
+
);
|
|
63
|
+
chunks.push(Buffer.from(chunk));
|
|
64
|
+
}
|
|
65
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
66
|
+
})
|
|
67
|
+
)());
|
|
68
|
+
if (!content.trim() || Buffer.byteLength(content) > 32000)
|
|
69
|
+
throw new CliArgError("Context must contain 1–32000 UTF-8 bytes.");
|
|
70
|
+
const fn = {
|
|
71
|
+
identity: "updateIdentity",
|
|
72
|
+
organisation: "updateOrganisation",
|
|
73
|
+
user: "updateUserProfile",
|
|
74
|
+
tools: "updateToolsNotes",
|
|
75
|
+
rules: "writeMagicFileFromUI",
|
|
76
|
+
}[parsed.section];
|
|
77
|
+
await opts.convex.mutation(
|
|
78
|
+
makeFunctionReference<"mutation">(`functions/agentConfig:${fn}`),
|
|
79
|
+
{
|
|
80
|
+
...(opts.sessionToken ? { sessionToken: opts.sessionToken } : {}),
|
|
81
|
+
content,
|
|
82
|
+
...(parsed.section === "rules" ? { path: "/RULES.md" } : {}),
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
console.log(JSON.stringify({ ok: true, section: parsed.section }));
|
|
86
|
+
}
|
package/cron.ts
CHANGED
|
@@ -392,6 +392,9 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
392
392
|
"--prompt is required (becomes the user message of each cron-fired chat thread)",
|
|
393
393
|
);
|
|
394
394
|
}
|
|
395
|
+
const environment = getFlag(ctx.args, "--environment");
|
|
396
|
+
if (environment && !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
397
|
+
throw new CronCliError("Invalid environment name");
|
|
395
398
|
const description = getFlag(ctx.args, "--description");
|
|
396
399
|
const overlapPolicy = getFlag(ctx.args, "--overlap") as
|
|
397
400
|
| "skip"
|
|
@@ -431,6 +434,7 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
431
434
|
triggerType,
|
|
432
435
|
name,
|
|
433
436
|
prompt,
|
|
437
|
+
environment,
|
|
434
438
|
description,
|
|
435
439
|
overlapPolicy,
|
|
436
440
|
disabled,
|
|
@@ -462,6 +466,7 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
462
466
|
const created = (await callMutation(ctx, createCronTriggerRef, {
|
|
463
467
|
name,
|
|
464
468
|
prompt,
|
|
469
|
+
environment,
|
|
465
470
|
triggerType,
|
|
466
471
|
schedule,
|
|
467
472
|
description,
|
|
@@ -551,6 +556,7 @@ async function runSecret(ctx: CronCliContext): Promise<void> {
|
|
|
551
556
|
}
|
|
552
557
|
|
|
553
558
|
export type AppOrCrmCreateBase = {
|
|
559
|
+
environment?: string;
|
|
554
560
|
triggerType: "composio" | "crm";
|
|
555
561
|
name: string;
|
|
556
562
|
prompt: string;
|
|
@@ -584,6 +590,7 @@ export function buildAppOrCrmCreateBody(
|
|
|
584
590
|
const body: Record<string, unknown> = {
|
|
585
591
|
name: base.name,
|
|
586
592
|
prompt: base.prompt,
|
|
593
|
+
environment: base.environment,
|
|
587
594
|
triggerType: base.triggerType,
|
|
588
595
|
description: base.description,
|
|
589
596
|
overlapPolicy: base.overlapPolicy,
|
|
@@ -731,6 +738,15 @@ async function runEvents(ctx: CronCliContext): Promise<void> {
|
|
|
731
738
|
|
|
732
739
|
async function runUpdate(ctx: CronCliContext): Promise<void> {
|
|
733
740
|
const jobId = shift(ctx.args, "job id");
|
|
741
|
+
const environment = getFlag(ctx.args, "--environment");
|
|
742
|
+
const clearEnvironment = hasFlag(ctx.args, "--clear-environment");
|
|
743
|
+
if (
|
|
744
|
+
environment &&
|
|
745
|
+
(clearEnvironment || !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
746
|
+
)
|
|
747
|
+
throw new CronCliError(
|
|
748
|
+
"Choose a valid --environment name or --clear-environment",
|
|
749
|
+
);
|
|
734
750
|
const name = getFlag(ctx.args, "--name");
|
|
735
751
|
const prompt = getFlag(ctx.args, "--prompt");
|
|
736
752
|
const description = getFlag(ctx.args, "--description");
|
|
@@ -753,6 +769,7 @@ async function runUpdate(ctx: CronCliContext): Promise<void> {
|
|
|
753
769
|
const schedule = consumeScheduleFlags(ctx.args);
|
|
754
770
|
const result = await callMutation(ctx, updateCronTriggerRef, {
|
|
755
771
|
jobId,
|
|
772
|
+
environment: clearEnvironment ? null : environment,
|
|
756
773
|
name,
|
|
757
774
|
prompt,
|
|
758
775
|
description,
|
|
@@ -866,6 +883,8 @@ Browse:
|
|
|
866
883
|
dench cron events <toolkit> [--json] # app-event types for a connected app
|
|
867
884
|
|
|
868
885
|
Create / update:
|
|
886
|
+
Add --environment <name> to bind a ready reusable setup.
|
|
887
|
+
New runs use its latest ready revision; --clear-environment removes the binding on update.
|
|
869
888
|
dench cron create "Daily standup digest" \\
|
|
870
889
|
--prompt "Pull yesterday's commits + Slack #standup msgs and post a summary…" \\
|
|
871
890
|
--type schedule # default. also: webhook | app | crm (see below)
|
package/dench.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { cliVersion } from "./lib/cli-version";
|
|
|
33
33
|
import { assertDeclaredCommandFlags } from "./lib/command-args";
|
|
34
34
|
import { denchConfigDirectory } from "./lib/config-path.mjs";
|
|
35
35
|
import { resolveOrganizationBySelector } from "./lib/organizationSelector";
|
|
36
|
+
import { normalizeParentArgv } from "./lib/parent-argv";
|
|
36
37
|
import { backendErrorText, publicErrorDetails } from "./lib/public-error";
|
|
37
38
|
import { formatApprovalOpenMessage, openUrl } from "./openUrl";
|
|
38
39
|
import {
|
|
@@ -217,13 +218,13 @@ const api = {
|
|
|
217
218
|
};
|
|
218
219
|
const CONFIG_DIR = denchConfigDirectory();
|
|
219
220
|
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
221
|
+
// A child process owns every token after `--`, including --host and --json.
|
|
222
|
+
// Normalize only Dench's parent arguments; pass child argv through byte for byte.
|
|
223
|
+
const { args, runtimeArgs } = normalizeParentArgv(process.argv.slice(2));
|
|
224
|
+
const json = runtimeArgs.includes("--json");
|
|
225
|
+
const filteredArgs = runtimeArgs.filter((arg) => arg !== "--json");
|
|
226
|
+
const scopedStatus =
|
|
227
|
+
runtimeArgs.includes("--mine") || runtimeArgs.includes("--self");
|
|
227
228
|
|
|
228
229
|
function readEnv(name: string) {
|
|
229
230
|
const value = process.env[name]?.trim();
|
|
@@ -645,14 +646,11 @@ Past chat threads (own + shared in this workspace):
|
|
|
645
646
|
dench chat delete --ids id1,id2 [--yes] [--json] (PERMANENTLY deletes threads)
|
|
646
647
|
|
|
647
648
|
Spawn a new chat-turn workflow (same as the web UI):
|
|
648
|
-
dench chat new "<prompt>" [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
649
|
+
dench chat new "<prompt>" [--environment NAME] [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
649
650
|
dench chat send <thread-id> "<message>" [--model <m>] [--yolo] [--follow] [--json]
|
|
650
651
|
dench chat follow <thread-id> [--json] (tail a live thread by id)
|
|
651
652
|
dench chat tree [<root-thread-id>] [--limit N] [--json]
|
|
652
653
|
(parent-child thread tree)
|
|
653
|
-
dench agent new ... (alias for dench chat new)
|
|
654
|
-
dench agent send ... (alias for dench chat send)
|
|
655
|
-
dench agent follow ... (alias for dench chat follow)
|
|
656
654
|
|
|
657
655
|
Help: dench chat help
|
|
658
656
|
|
|
@@ -674,32 +672,23 @@ returns ENOSYS on the FUSE-backed volume mount):
|
|
|
674
672
|
Persist scratch -> workspace (cp from /tmp into /workspace + cloud sync):
|
|
675
673
|
dench stage <local-path> [<workspace-dest>] [--clean] [--json]
|
|
676
674
|
|
|
677
|
-
|
|
678
|
-
dench
|
|
679
|
-
dench
|
|
680
|
-
dench
|
|
681
|
-
dench
|
|
682
|
-
dench agent pause <runId>
|
|
683
|
-
dench agent resume <runId> ['optional prompt']
|
|
684
|
-
dench agent continue <prevRunId> '<prompt>'
|
|
685
|
-
dench agent tree [<rootRunId>]
|
|
686
|
-
Help: dench agent help
|
|
675
|
+
Reusable workspace setup:
|
|
676
|
+
dench env list | show <name> | save <name> --path <directory> | run <name> -- <command> | delete <name>
|
|
677
|
+
dench secrets set <NAME> --stdin [--kind env|file]
|
|
678
|
+
dench secrets list | delete <NAME> metadata only; values are never printed
|
|
679
|
+
Help: dench env --help, dench secrets --help
|
|
687
680
|
|
|
688
681
|
dench --version
|
|
689
682
|
dench reference <name> [--json] Offline CLI-version-matched skill/reference
|
|
690
683
|
dench reference --list [--json] Bundled reference index and revision
|
|
691
684
|
|
|
692
|
-
|
|
693
|
-
dench
|
|
694
|
-
dench
|
|
695
|
-
dench
|
|
696
|
-
dench
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
dench model default <id|clear>
|
|
700
|
-
dench model thread <threadId> <id|clear>
|
|
701
|
-
dench daily today | show <YYYY-MM-DD>
|
|
702
|
-
Help: dench identity help
|
|
685
|
+
Standing context and memory:
|
|
686
|
+
dench context --sections identity,organisation,user,memory,rules,tools [--json]
|
|
687
|
+
dench context set <identity|organisation|user|tools|rules> --stdin
|
|
688
|
+
dench memory search "<query>" | save <key> "<fact>"
|
|
689
|
+
dench model default <id|clear> | thread <threadId> <id|clear>
|
|
690
|
+
Context describes preferences and instructions. Environments store setup;
|
|
691
|
+
secrets store credentials. A memory note does not install or save either.
|
|
703
692
|
|
|
704
693
|
External tools:
|
|
705
694
|
dench integrations is an alias for dench tool status -- it lists connected apps.
|
|
@@ -761,13 +750,14 @@ function contextHelp() {
|
|
|
761
750
|
Usage:
|
|
762
751
|
dench context [--instructions] [--json]
|
|
763
752
|
dench context --sections organisation,user,memory [--json]
|
|
764
|
-
dench
|
|
753
|
+
dench context set <identity|organisation|user|tools|rules> --stdin | --content "text" [--json]
|
|
765
754
|
|
|
766
755
|
Shows the current workspace, current agent, assigned tasks, pending approvals
|
|
767
756
|
requested by this agent, connected apps when available, and useful next commands.
|
|
768
757
|
|
|
769
758
|
Use --instructions (or dench instructions) to print the standing workspace
|
|
770
|
-
instructions
|
|
759
|
+
instructions for the current workspace. Context and memory do not persist
|
|
760
|
+
credentials or installed software: use dench secrets and dench env.
|
|
771
761
|
`);
|
|
772
762
|
}
|
|
773
763
|
|
|
@@ -2709,7 +2699,7 @@ type Runtime = Awaited<ReturnType<typeof getRuntime>>;
|
|
|
2709
2699
|
* `dench members list --json` print the human table and `dench crm … --json`
|
|
2710
2700
|
* fall back to text on empty results (`(no rows)`), breaking `| jq` pipelines.
|
|
2711
2701
|
*/
|
|
2712
|
-
function stripRuntimeFlags(args: string[]) {
|
|
2702
|
+
function stripRuntimeFlags(args: string[], validateValues = true) {
|
|
2713
2703
|
const booleanFlags = new Set([
|
|
2714
2704
|
"--dev",
|
|
2715
2705
|
"--prod",
|
|
@@ -2726,10 +2716,15 @@ function stripRuntimeFlags(args: string[]) {
|
|
|
2726
2716
|
const stripped: string[] = [];
|
|
2727
2717
|
for (let i = 0; i < args.length; i++) {
|
|
2728
2718
|
const arg = args[i];
|
|
2719
|
+
if (arg === "--") {
|
|
2720
|
+
stripped.push(...args.slice(i));
|
|
2721
|
+
break;
|
|
2722
|
+
}
|
|
2729
2723
|
if (booleanFlags.has(arg)) continue;
|
|
2730
2724
|
if (valueFlags.has(arg)) {
|
|
2731
|
-
if (!args[i + 1] || args[i + 1].startsWith("
|
|
2732
|
-
throw new CliArgError(`Missing value for ${arg}`);
|
|
2725
|
+
if (!args[i + 1] || args[i + 1].startsWith("-")) {
|
|
2726
|
+
if (validateValues) throw new CliArgError(`Missing value for ${arg}`);
|
|
2727
|
+
continue;
|
|
2733
2728
|
}
|
|
2734
2729
|
i++;
|
|
2735
2730
|
continue;
|
|
@@ -4327,22 +4322,36 @@ function isCommandNotFound(error: unknown) {
|
|
|
4327
4322
|
}
|
|
4328
4323
|
|
|
4329
4324
|
async function main() {
|
|
4330
|
-
if (isHelpRequest(
|
|
4331
|
-
const helpArgs = stripRuntimeFlags(
|
|
4325
|
+
if (isHelpRequest(runtimeArgs)) {
|
|
4326
|
+
const helpArgs = stripRuntimeFlags(runtimeArgs, false).filter(
|
|
4327
|
+
(arg) => !["--json", "--help", "-h"].includes(arg),
|
|
4328
|
+
);
|
|
4332
4329
|
if (helpArgs[0] === "help") helpArgs.shift();
|
|
4333
|
-
if (!helpArgs.length
|
|
4330
|
+
if (!helpArgs.length) {
|
|
4334
4331
|
help();
|
|
4335
4332
|
return;
|
|
4336
4333
|
}
|
|
4337
4334
|
const path = helpArgs.filter(
|
|
4338
4335
|
(arg) => !["help", "--help", "-h"].includes(arg),
|
|
4339
4336
|
);
|
|
4337
|
+
if (path[0] === "daily") {
|
|
4338
|
+
console.log(
|
|
4339
|
+
`dench ${path.join(" ")} was retired. Search history with dench chat search or read existing daily files with dench files download. Harness A does not automatically write daily logs.`,
|
|
4340
|
+
);
|
|
4341
|
+
return;
|
|
4342
|
+
}
|
|
4340
4343
|
// Root help retains domain-specific setup, account and pagination guidance.
|
|
4341
4344
|
// These are explicit help-only paths; never pass mutation arguments to a handler.
|
|
4342
4345
|
if (path.length === 1) {
|
|
4343
4346
|
const roots: Record<string, () => void | Promise<void>> = {
|
|
4344
4347
|
reference: async () =>
|
|
4345
4348
|
console.log((await import("./reference")).REFERENCE_HELP),
|
|
4349
|
+
env: async () =>
|
|
4350
|
+
console.log((await import("./environments")).environmentHelpText()),
|
|
4351
|
+
secrets: async () =>
|
|
4352
|
+
console.log(
|
|
4353
|
+
(await import("./workspace-secrets")).workspaceSecretsHelpText(),
|
|
4354
|
+
),
|
|
4346
4355
|
tool: toolHelp,
|
|
4347
4356
|
integrations: integrationsHelp,
|
|
4348
4357
|
backend: backendHelp,
|
|
@@ -4399,14 +4408,136 @@ async function main() {
|
|
|
4399
4408
|
? (await import("./email")).emailHelpText()
|
|
4400
4409
|
: helpArgs[0] === "crm"
|
|
4401
4410
|
? (await import("./crm")).crmHelpText()
|
|
4402
|
-
: ""
|
|
4411
|
+
: helpArgs[0] === "env"
|
|
4412
|
+
? (await import("./environments")).environmentHelpText()
|
|
4413
|
+
: helpArgs[0] === "secrets"
|
|
4414
|
+
? (await import("./workspace-secrets")).workspaceSecretsHelpText()
|
|
4415
|
+
: "";
|
|
4403
4416
|
console.log(commandHelp(helpArgs, usage));
|
|
4404
4417
|
return;
|
|
4405
4418
|
}
|
|
4406
4419
|
const command = positional(0);
|
|
4407
4420
|
const subcommand = positional(1);
|
|
4408
4421
|
|
|
4409
|
-
assertDeclaredCommandFlags(stripRuntimeFlags(
|
|
4422
|
+
assertDeclaredCommandFlags(stripRuntimeFlags(runtimeArgs));
|
|
4423
|
+
|
|
4424
|
+
// New setup commands validate before authentication and never consume child flags.
|
|
4425
|
+
if (command === "env" || command === "secrets") {
|
|
4426
|
+
const rawSubArgs = args.slice(args.indexOf(command) + 1);
|
|
4427
|
+
const helpRequested =
|
|
4428
|
+
runtimeArgs.includes("--help") ||
|
|
4429
|
+
runtimeArgs.includes("-h") ||
|
|
4430
|
+
subcommand === "help";
|
|
4431
|
+
const subArgs = helpRequested ? rawSubArgs : stripRuntimeFlags(rawSubArgs);
|
|
4432
|
+
const mod =
|
|
4433
|
+
command === "env"
|
|
4434
|
+
? await import("./environments")
|
|
4435
|
+
: await import("./workspace-secrets");
|
|
4436
|
+
if ("validateEnvironmentArgs" in mod) {
|
|
4437
|
+
const parsed = mod.validateEnvironmentArgs(subArgs);
|
|
4438
|
+
if (parsed.verb === "help") {
|
|
4439
|
+
console.log(mod.environmentHelpText());
|
|
4440
|
+
return;
|
|
4441
|
+
}
|
|
4442
|
+
const runtime = requireAuthenticatedRuntime(
|
|
4443
|
+
await getRuntime(),
|
|
4444
|
+
"dench env",
|
|
4445
|
+
);
|
|
4446
|
+
await mod.runEnvironmentCommand({
|
|
4447
|
+
convex: runtime.client,
|
|
4448
|
+
args: subArgs,
|
|
4449
|
+
sessionToken: runtime.sessionToken,
|
|
4450
|
+
apiUrl: runtime.host,
|
|
4451
|
+
});
|
|
4452
|
+
} else {
|
|
4453
|
+
const parsed = mod.validateWorkspaceSecretsArgs(subArgs);
|
|
4454
|
+
if (parsed.verb === "help") {
|
|
4455
|
+
console.log(mod.workspaceSecretsHelpText());
|
|
4456
|
+
return;
|
|
4457
|
+
}
|
|
4458
|
+
const runtime = requireAuthenticatedRuntime(
|
|
4459
|
+
await getRuntime(),
|
|
4460
|
+
"dench secrets",
|
|
4461
|
+
);
|
|
4462
|
+
await mod.runWorkspaceSecretsCommand({
|
|
4463
|
+
convex: runtime.client,
|
|
4464
|
+
args: subArgs,
|
|
4465
|
+
sessionToken: runtime.sessionToken,
|
|
4466
|
+
});
|
|
4467
|
+
}
|
|
4468
|
+
return;
|
|
4469
|
+
}
|
|
4470
|
+
if (
|
|
4471
|
+
command === "context" &&
|
|
4472
|
+
(runtimeArgs.includes("--help") ||
|
|
4473
|
+
runtimeArgs.includes("-h") ||
|
|
4474
|
+
subcommand === "help")
|
|
4475
|
+
) {
|
|
4476
|
+
contextHelp();
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
if (command === "daily") {
|
|
4480
|
+
const guidance =
|
|
4481
|
+
"Daily-log commands were retired. Search actual history with dench chat search or read existing daily files with dench files download. Nothing writes daily logs automatically in Harness A.";
|
|
4482
|
+
if (
|
|
4483
|
+
runtimeArgs.includes("--help") ||
|
|
4484
|
+
runtimeArgs.includes("-h") ||
|
|
4485
|
+
subcommand === "help"
|
|
4486
|
+
) {
|
|
4487
|
+
console.log(guidance);
|
|
4488
|
+
return;
|
|
4489
|
+
}
|
|
4490
|
+
throw new CliError(guidance, {
|
|
4491
|
+
code: "deprecated_command",
|
|
4492
|
+
retryable: false,
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
if (command === "context" && subcommand === "set") {
|
|
4496
|
+
const { validateContextSet, runContextSet } = await import(
|
|
4497
|
+
"./context-settings"
|
|
4498
|
+
);
|
|
4499
|
+
const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
|
|
4500
|
+
validateContextSet(subArgs);
|
|
4501
|
+
const runtime = requireAuthenticatedRuntime(
|
|
4502
|
+
await getRuntime(),
|
|
4503
|
+
"dench context set",
|
|
4504
|
+
);
|
|
4505
|
+
await runContextSet({
|
|
4506
|
+
convex: runtime.client,
|
|
4507
|
+
args: subArgs,
|
|
4508
|
+
sessionToken: runtime.sessionToken,
|
|
4509
|
+
});
|
|
4510
|
+
return;
|
|
4511
|
+
}
|
|
4512
|
+
if (
|
|
4513
|
+
command === "memory" &&
|
|
4514
|
+
(runtimeArgs.includes("--help") || runtimeArgs.includes("-h"))
|
|
4515
|
+
) {
|
|
4516
|
+
memoryHelp();
|
|
4517
|
+
return;
|
|
4518
|
+
}
|
|
4519
|
+
if (command === "memory" && subcommand === "save") {
|
|
4520
|
+
const kind = option("--kind") ?? "fact";
|
|
4521
|
+
if (
|
|
4522
|
+
![
|
|
4523
|
+
"fact",
|
|
4524
|
+
"decision",
|
|
4525
|
+
"preference",
|
|
4526
|
+
"goal",
|
|
4527
|
+
"tool_note",
|
|
4528
|
+
"other",
|
|
4529
|
+
"convention",
|
|
4530
|
+
"project_context",
|
|
4531
|
+
"agent_lesson",
|
|
4532
|
+
"docs_knowledge",
|
|
4533
|
+
"open_goal",
|
|
4534
|
+
].includes(kind)
|
|
4535
|
+
)
|
|
4536
|
+
throw new CliError(
|
|
4537
|
+
"Unsupported memory kind. Use memory for facts/preferences; use dench secrets set NAME --stdin for credentials and dench env save for reusable setup.",
|
|
4538
|
+
{ code: "validation_failed", retryable: false },
|
|
4539
|
+
);
|
|
4540
|
+
}
|
|
4410
4541
|
|
|
4411
4542
|
if (command === "reference") {
|
|
4412
4543
|
await (await import("./reference")).runReferenceCommand(
|
|
@@ -4415,7 +4546,10 @@ async function main() {
|
|
|
4415
4546
|
return;
|
|
4416
4547
|
}
|
|
4417
4548
|
|
|
4418
|
-
if (
|
|
4549
|
+
if (
|
|
4550
|
+
command === "version" ||
|
|
4551
|
+
stripRuntimeFlags(runtimeArgs)[0] === "--version"
|
|
4552
|
+
) {
|
|
4419
4553
|
console.log(await cliVersion());
|
|
4420
4554
|
return;
|
|
4421
4555
|
}
|
|
@@ -5144,41 +5278,31 @@ async function main() {
|
|
|
5144
5278
|
if (command === "agent") {
|
|
5145
5279
|
const subArgs = stripRuntimeFlags(args.slice(args.indexOf("agent") + 1));
|
|
5146
5280
|
const sub = subArgs[0];
|
|
5147
|
-
if (
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
// `agent` so users who associate chat threads with the agent
|
|
5156
|
-
// surface still find them; the actual workflow is identical to
|
|
5157
|
-
// `dench chat new` / `dench chat send` / `dench chat follow`.
|
|
5158
|
-
if (sub === "new" || sub === "send" || sub === "follow") {
|
|
5159
|
-
const { runChatSpawnCommand } = await import("./chat-spawn");
|
|
5160
|
-
const runtime = await getRuntime();
|
|
5161
|
-
const authedRuntime = requireAuthenticatedRuntime(
|
|
5162
|
-
runtime,
|
|
5163
|
-
`dench agent ${sub}`,
|
|
5281
|
+
if (
|
|
5282
|
+
!sub ||
|
|
5283
|
+
sub === "help" ||
|
|
5284
|
+
subArgs.includes("--help") ||
|
|
5285
|
+
subArgs.includes("-h")
|
|
5286
|
+
) {
|
|
5287
|
+
console.log(
|
|
5288
|
+
"Use dench chat new, send, follow, or tree. agent new/send/follow remain compatibility aliases. In Harness A, use spawn_task/read_task for child tasks.",
|
|
5164
5289
|
);
|
|
5165
|
-
await runChatSpawnCommand({
|
|
5166
|
-
runtime: {
|
|
5167
|
-
host: authedRuntime.host,
|
|
5168
|
-
bearerToken: authedRuntime.sessionToken,
|
|
5169
|
-
},
|
|
5170
|
-
args: subArgs.slice(1),
|
|
5171
|
-
subcommand: sub,
|
|
5172
|
-
});
|
|
5173
5290
|
return;
|
|
5174
5291
|
}
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5292
|
+
if (sub !== "new" && sub !== "send" && sub !== "follow")
|
|
5293
|
+
throw new CliError(
|
|
5294
|
+
"Unsupported agent command. Use dench chat new/send/follow/tree or the harness subagent tools.",
|
|
5295
|
+
{ code: "unsupported_capability", retryable: false },
|
|
5296
|
+
);
|
|
5297
|
+
const { runChatSpawnCommand } = await import("./chat-spawn");
|
|
5298
|
+
const runtime = requireAuthenticatedRuntime(
|
|
5299
|
+
await getRuntime(),
|
|
5300
|
+
`dench chat ${sub}`,
|
|
5301
|
+
);
|
|
5302
|
+
await runChatSpawnCommand({
|
|
5303
|
+
runtime: { host: runtime.host, bearerToken: runtime.sessionToken },
|
|
5304
|
+
args: subArgs.slice(1),
|
|
5305
|
+
subcommand: sub,
|
|
5182
5306
|
});
|
|
5183
5307
|
return;
|
|
5184
5308
|
}
|
|
@@ -5253,7 +5377,7 @@ async function main() {
|
|
|
5253
5377
|
// ── OpenClaw-parity self-update CLI surface ──────────────────────────
|
|
5254
5378
|
//
|
|
5255
5379
|
// Dispatch all `dench identity / organisation / user / tools / mem /
|
|
5256
|
-
// bootstrap / model
|
|
5380
|
+
// bootstrap / model` traffic through the shared
|
|
5257
5381
|
// agent-config CLI module. Each command requires the same auth shape
|
|
5258
5382
|
// as `dench cron` (Bearer DENCH_API_KEY or `dench signin` session
|
|
5259
5383
|
// token) so server-side `requireCronAccess` accepts both.
|
|
@@ -5265,7 +5389,6 @@ async function main() {
|
|
|
5265
5389
|
"mem",
|
|
5266
5390
|
"bootstrap",
|
|
5267
5391
|
"model",
|
|
5268
|
-
"daily",
|
|
5269
5392
|
]);
|
|
5270
5393
|
if (AGENT_CONFIG_COMMANDS.has(command)) {
|
|
5271
5394
|
const subArgs = stripRuntimeFlags(args.slice(args.indexOf(command) + 1));
|
|
@@ -5318,10 +5441,6 @@ async function main() {
|
|
|
5318
5441
|
await mod.runModelCommand(ctxBase);
|
|
5319
5442
|
return;
|
|
5320
5443
|
}
|
|
5321
|
-
if (command === "daily") {
|
|
5322
|
-
await mod.runDailyCommand(ctxBase);
|
|
5323
|
-
return;
|
|
5324
|
-
}
|
|
5325
5444
|
}
|
|
5326
5445
|
|
|
5327
5446
|
if (command === "signin") {
|
|
@@ -5369,7 +5488,7 @@ async function main() {
|
|
|
5369
5488
|
return;
|
|
5370
5489
|
}
|
|
5371
5490
|
|
|
5372
|
-
if (command === "context" &&
|
|
5491
|
+
if (command === "context" && runtimeArgs.includes("--sections")) {
|
|
5373
5492
|
const raw = args[args.indexOf("--sections") + 1];
|
|
5374
5493
|
const sections = raw
|
|
5375
5494
|
?.split(",")
|
|
@@ -5407,7 +5526,7 @@ async function main() {
|
|
|
5407
5526
|
return;
|
|
5408
5527
|
}
|
|
5409
5528
|
|
|
5410
|
-
if (command === "context" &&
|
|
5529
|
+
if (command === "context" && runtimeArgs.includes("--instructions")) {
|
|
5411
5530
|
const instructions = await buildWorkspaceInstructionsContext(runtime);
|
|
5412
5531
|
print(
|
|
5413
5532
|
json ? instructions : (stringField(instructions, "instructions") ?? ""),
|