@dench.com/cli 3.6.6-staging.386 → 3.8.0-staging.389
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 +21 -57
- package/app-sdk.d.ts +367 -24
- package/chat-spawn.ts +11 -2
- package/chat.ts +1 -1
- package/context-settings.ts +86 -0
- package/crm.ts +92 -24
- package/cron.ts +19 -0
- package/dench.ts +318 -90
- package/email-inbox.ts +169 -0
- package/email.ts +103 -21
- package/environments.ts +694 -0
- package/lib/api-schemas.ts +160 -14
- package/lib/app-sdk-catalog.generated.ts +39 -19
- package/lib/bundled-references.ts +17 -0
- package/lib/cli-args.ts +30 -3
- package/lib/command-args.ts +252 -0
- package/lib/command-help.ts +93 -0
- package/lib/command-registry.ts +296 -10
- 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/native-email-wait.ts +75 -0
- package/lib/parent-argv.ts +20 -0
- package/lib/public-error.ts +82 -0
- package/lib/references.generated.json +74 -0
- package/package.json +7 -3
- package/reference.ts +47 -0
- package/workspace-secrets.ts +231 -0
- package/agent.ts +0 -37
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { CliArgError, missingFlagValue } from "./cli-args";
|
|
2
|
+
|
|
3
|
+
type PositionalArity = readonly [min: number, max: number | null];
|
|
4
|
+
|
|
5
|
+
type CommandArguments = {
|
|
6
|
+
command: string;
|
|
7
|
+
values: string[];
|
|
8
|
+
switches: string[];
|
|
9
|
+
positionals: PositionalArity;
|
|
10
|
+
flagPositionals: Record<string, PositionalArity>;
|
|
11
|
+
};
|
|
12
|
+
const spec = (
|
|
13
|
+
command: string,
|
|
14
|
+
positionals: PositionalArity,
|
|
15
|
+
values = "",
|
|
16
|
+
switches = "",
|
|
17
|
+
flagPositionals: Record<string, PositionalArity> = {},
|
|
18
|
+
): CommandArguments => ({
|
|
19
|
+
command,
|
|
20
|
+
positionals,
|
|
21
|
+
flagPositionals,
|
|
22
|
+
values: values.split(" ").filter(Boolean),
|
|
23
|
+
switches: switches.split(" ").filter(Boolean),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const browserSpec = (
|
|
27
|
+
verb: string,
|
|
28
|
+
positionals: PositionalArity,
|
|
29
|
+
flagPositionals: Record<string, PositionalArity> = {},
|
|
30
|
+
): CommandArguments =>
|
|
31
|
+
spec(
|
|
32
|
+
`browser ${verb}`,
|
|
33
|
+
positionals,
|
|
34
|
+
"--thread --selector --text --label --value --key --target-id --targetId --pixels --timeout-ms --timeoutMs --x --y --output --url --script",
|
|
35
|
+
"--full-page",
|
|
36
|
+
flagPositionals,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** CLI spellings for dispatchers that do not reject leftovers themselves.
|
|
40
|
+
* Positional ranges follow the actual handlers; null deliberately allows free text
|
|
41
|
+
* or multiple IDs. Flag alternatives cover positional inputs replaced by flags.
|
|
42
|
+
* Shared by pre-dispatch validation and help; API property names are not flags. */
|
|
43
|
+
export const COMMAND_ARGUMENTS = [
|
|
44
|
+
spec(
|
|
45
|
+
"signin",
|
|
46
|
+
[0, 0],
|
|
47
|
+
"--kind --name --email --org-name --org --timeout-ms --otp-code",
|
|
48
|
+
"--new-workspace --no-open",
|
|
49
|
+
),
|
|
50
|
+
spec("logout", [0, 1], "", "--all"),
|
|
51
|
+
spec("use", [1, 1]),
|
|
52
|
+
spec("backend set", [1, 1]),
|
|
53
|
+
spec("backend clear", [0, 0]),
|
|
54
|
+
spec("env list", [0, 0], "--limit --cursor"),
|
|
55
|
+
spec("env show", [1, 1], "--revision"),
|
|
56
|
+
spec("env save", [1, 1], "--path --wait", "--no-wait"),
|
|
57
|
+
spec("env run", [1, 1], "--revision --state-key", "--prepare-local"),
|
|
58
|
+
spec("env delete", [1, 1]),
|
|
59
|
+
spec("secrets set", [1, 1], "--file --kind --expected-version", "--stdin"),
|
|
60
|
+
spec("secrets list", [0, 0]),
|
|
61
|
+
spec("secrets delete", [1, 1], "--expected-version"),
|
|
62
|
+
...["identity", "organisation", "user", "tools", "rules"].map((section) =>
|
|
63
|
+
spec(`context set ${section}`, [0, 0], "--content", "--stdin"),
|
|
64
|
+
),
|
|
65
|
+
...["install", "uninstall", "refresh"].map((verb) =>
|
|
66
|
+
spec(
|
|
67
|
+
`ads ${verb}`,
|
|
68
|
+
[0, 0],
|
|
69
|
+
"--target",
|
|
70
|
+
"--no-open --apply --color --no-color",
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
spec(
|
|
74
|
+
"ads payout setup",
|
|
75
|
+
[0, 0],
|
|
76
|
+
"--target",
|
|
77
|
+
"--no-open --apply --color --no-color",
|
|
78
|
+
),
|
|
79
|
+
spec(
|
|
80
|
+
"ads payout onboard",
|
|
81
|
+
[0, 0],
|
|
82
|
+
"--target",
|
|
83
|
+
"--no-open --apply --color --no-color",
|
|
84
|
+
),
|
|
85
|
+
spec(
|
|
86
|
+
"ads withdraw",
|
|
87
|
+
[0, 0],
|
|
88
|
+
"--amount --idempotency-key --target",
|
|
89
|
+
"--no-open --apply --color --no-color",
|
|
90
|
+
),
|
|
91
|
+
spec(
|
|
92
|
+
"chat new",
|
|
93
|
+
[1, null],
|
|
94
|
+
"--model --file-context --visibility --title --environment",
|
|
95
|
+
"--yolo --follow",
|
|
96
|
+
),
|
|
97
|
+
spec("chat send", [2, null], "--model", "--yolo --follow"),
|
|
98
|
+
spec("chat rename", [2, null], "", "--from-stdin", {
|
|
99
|
+
"--from-stdin": [0, 0],
|
|
100
|
+
}),
|
|
101
|
+
spec("chat delete", [1, null], "--ids", "--yes -y --include-automated", {
|
|
102
|
+
"--ids": [0, null],
|
|
103
|
+
}),
|
|
104
|
+
spec("apps create", [1, 1], "--name --icon --description", "", {
|
|
105
|
+
"--name": [0, 1],
|
|
106
|
+
}),
|
|
107
|
+
spec(
|
|
108
|
+
"apps publish",
|
|
109
|
+
[1, 1],
|
|
110
|
+
"--slug --name --description --icon --visibility --version",
|
|
111
|
+
),
|
|
112
|
+
...["install", "uninstall", "enable", "disable", "pin", "unpin"].map((verb) =>
|
|
113
|
+
spec(`apps ${verb}`, [1, 1]),
|
|
114
|
+
),
|
|
115
|
+
spec("apps webhooks register", [2, 2], "", "--rotate"),
|
|
116
|
+
spec("apps webhooks delete", [2, 2]),
|
|
117
|
+
spec("apps validate", [1, 1]),
|
|
118
|
+
spec(
|
|
119
|
+
"members me",
|
|
120
|
+
[0, 0],
|
|
121
|
+
"--name --tagline --timezone --image-url --avatar",
|
|
122
|
+
"--clear-avatar --auto-timezone",
|
|
123
|
+
),
|
|
124
|
+
spec(
|
|
125
|
+
"cron create",
|
|
126
|
+
[1, 1],
|
|
127
|
+
"--prompt --description --overlap --target --type --at --cron --tz --every --every-ms --connection --event --toolkit --trigger-config --object --where --where-logic --environment",
|
|
128
|
+
"--disabled --delete-after-run --no-secret --signed",
|
|
129
|
+
),
|
|
130
|
+
spec(
|
|
131
|
+
"cron update",
|
|
132
|
+
[1, 1],
|
|
133
|
+
"--name --prompt --description --overlap --enabled --at --cron --tz --every --every-ms --environment",
|
|
134
|
+
"--clear-environment",
|
|
135
|
+
),
|
|
136
|
+
spec("cron run", [1, 1], "", "--wait"),
|
|
137
|
+
spec("cron secret", [1, 1], "", "--off"),
|
|
138
|
+
...["delete", "enable", "disable"].map((verb) =>
|
|
139
|
+
spec(`cron ${verb}`, [1, 1]),
|
|
140
|
+
),
|
|
141
|
+
spec("files rm", [1, 1], "", "--recursive -r"),
|
|
142
|
+
spec("files mv", [2, 2]),
|
|
143
|
+
spec("files write", [1, 1]),
|
|
144
|
+
spec("stage", [1, 2], "", "--clean"),
|
|
145
|
+
...["identity", "organisation", "user", "tools", "mem"].flatMap((group) =>
|
|
146
|
+
["edit", "set"].map((verb) =>
|
|
147
|
+
spec(`${group} ${verb}`, [0, 0], "--content", "--stdin"),
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
spec("mem append", [1, null]),
|
|
151
|
+
spec("model default", [0, 1]),
|
|
152
|
+
spec("model thread", [2, 2]),
|
|
153
|
+
spec("bootstrap complete", [0, 0]),
|
|
154
|
+
spec("bootstrap reopen", [0, 0]),
|
|
155
|
+
spec("bootstrap template", [0, 0], "--content", "--stdin"),
|
|
156
|
+
spec(
|
|
157
|
+
"memory save",
|
|
158
|
+
[2, null],
|
|
159
|
+
"--kind --tags --sensitivity --scope --visibility",
|
|
160
|
+
),
|
|
161
|
+
spec("project create", [1, 1], "--repo --root"),
|
|
162
|
+
spec("approval request", [1, 1], "--reason --risk --agent"),
|
|
163
|
+
spec("approval approve", [1, 1], "--evidence"),
|
|
164
|
+
spec("approval reject", [1, 1], "--evidence"),
|
|
165
|
+
spec("billing topup", [1, 1], "--amount", "--no-open", {
|
|
166
|
+
"--amount": [0, 0],
|
|
167
|
+
}),
|
|
168
|
+
spec("upgrade", [0, 0], "--tier --cycle --billing-cycle", "--no-open"),
|
|
169
|
+
spec("tool run", [1, 1], "--args --account --approval"),
|
|
170
|
+
spec("tool connect", [1, 1], "--callback-url"),
|
|
171
|
+
spec("tool disconnect", [1, 1]),
|
|
172
|
+
spec(
|
|
173
|
+
"browser session create",
|
|
174
|
+
[0, 0],
|
|
175
|
+
"--context-id --contextId --width --height",
|
|
176
|
+
),
|
|
177
|
+
spec("browser session stop", [1, 1]),
|
|
178
|
+
// Value flags replace the joined positional input. The clickAt handler
|
|
179
|
+
// instead lets its first/second positionals override --x/--y individually.
|
|
180
|
+
browserSpec("open", [0, null], { "--url": [0, 0] }),
|
|
181
|
+
browserSpec("state", [0, 0]),
|
|
182
|
+
browserSpec("inspect", [0, 0]),
|
|
183
|
+
browserSpec("navigate", [0, null], { "--url": [0, 0] }),
|
|
184
|
+
browserSpec("click", [0, 0]),
|
|
185
|
+
browserSpec("clickAt", [0, 2]),
|
|
186
|
+
browserSpec("click-at", [0, 2]),
|
|
187
|
+
browserSpec("fill", [0, 0]),
|
|
188
|
+
browserSpec("type", [0, null], { "--value": [0, 0] }),
|
|
189
|
+
browserSpec("key", [0, null], { "--key": [0, 0] }),
|
|
190
|
+
browserSpec("scroll", [0, 1]),
|
|
191
|
+
browserSpec("wait", [0, 0]),
|
|
192
|
+
browserSpec("evaluate", [0, null], { "--script": [0, 0] }),
|
|
193
|
+
browserSpec("tabs", [0, 0]),
|
|
194
|
+
browserSpec("newTab", [0, null], { "--url": [0, 0] }),
|
|
195
|
+
browserSpec("new-tab", [0, null], { "--url": [0, 0] }),
|
|
196
|
+
...["activateTab", "activate-tab", "closeTab", "close-tab"].map((verb) =>
|
|
197
|
+
browserSpec(verb, [0, 1], { "--target-id": [0, 0], "--targetId": [0, 0] }),
|
|
198
|
+
),
|
|
199
|
+
browserSpec("screenshot", [0, 0]),
|
|
200
|
+
browserSpec("stop", [0, 0]),
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
export function assertDeclaredCommandFlags(argv: readonly string[]): void {
|
|
204
|
+
const boundary = argv.indexOf("--");
|
|
205
|
+
const args = argv
|
|
206
|
+
.slice(0, boundary < 0 ? undefined : boundary)
|
|
207
|
+
.filter((arg) => arg !== "--json");
|
|
208
|
+
if (["routine", "routines"].includes(args[0])) args[0] = "cron";
|
|
209
|
+
if (["ad", "adline"].includes(args[0])) args[0] = "ads";
|
|
210
|
+
if (args[0] === "agent" && ["new", "send"].includes(args[1]))
|
|
211
|
+
args[0] = "chat";
|
|
212
|
+
if (args[0] === "crm" && args[1] === "members") args.shift();
|
|
213
|
+
const contract = COMMAND_ARGUMENTS.find(({ command }) =>
|
|
214
|
+
command.split(" ").every((part, index) => args[index] === part),
|
|
215
|
+
);
|
|
216
|
+
if (!contract) return; // Domain parsers keep their own consumed-argument validation.
|
|
217
|
+
const positionals: string[] = [];
|
|
218
|
+
const flags = new Set<string>();
|
|
219
|
+
for (let i = contract.command.split(" ").length; i < args.length; i++) {
|
|
220
|
+
const arg = args[i];
|
|
221
|
+
if (!/^--?[^\d.]/.test(arg)) {
|
|
222
|
+
positionals.push(arg);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
flags.add(arg);
|
|
226
|
+
if (contract.switches.includes(arg)) continue;
|
|
227
|
+
if (contract.values.includes(arg)) {
|
|
228
|
+
const value = args[++i];
|
|
229
|
+
if (missingFlagValue(value, arg))
|
|
230
|
+
throw new CliArgError(`Missing value for ${arg}`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
throw new CliArgError(
|
|
234
|
+
`Unknown flag for ${contract.command}: ${arg}. Run \`dench ${contract.command} --help\` for usage.`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const [min, max] =
|
|
238
|
+
Object.entries(contract.flagPositionals).find(([flag]) =>
|
|
239
|
+
flags.has(flag),
|
|
240
|
+
)?.[1] ?? contract.positionals;
|
|
241
|
+
if (positionals.length < min || (max !== null && positionals.length > max)) {
|
|
242
|
+
const expected =
|
|
243
|
+
max === null
|
|
244
|
+
? `at least ${min}`
|
|
245
|
+
: min === max
|
|
246
|
+
? String(min)
|
|
247
|
+
: `${min}–${max}`;
|
|
248
|
+
throw new CliArgError(
|
|
249
|
+
`${positionals.length < min ? "Missing" : "Unexpected"} positional argument for ${contract.command}: expected ${expected}, received ${positionals.length}. Run \`dench ${contract.command} --help\` for usage.`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { COMMAND_ARGUMENTS } from "./command-args";
|
|
2
|
+
import { apiOperations } from "./command-registry";
|
|
3
|
+
|
|
4
|
+
/** Literal command words only; never interpret a data value as a command. */
|
|
5
|
+
function words(usage: string): string[] {
|
|
6
|
+
const parts = usage
|
|
7
|
+
.replace(/^dench\s+/, "")
|
|
8
|
+
.split(/\s+/)
|
|
9
|
+
.filter(Boolean);
|
|
10
|
+
const end = parts.findIndex((part) => /^[<[(-]/.test(part));
|
|
11
|
+
return end < 0 ? parts : parts.slice(0, end);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function matches(
|
|
15
|
+
prefix: readonly string[],
|
|
16
|
+
candidate: readonly string[],
|
|
17
|
+
): boolean {
|
|
18
|
+
return prefix.every((word, index) =>
|
|
19
|
+
candidate[index]?.split("|").includes(word),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Registry metadata supplies discovery, never invokes command implementations. */
|
|
24
|
+
export function commandHelp(argv: readonly string[], usageText = ""): string {
|
|
25
|
+
const args = [...argv];
|
|
26
|
+
if (args[0] === "help") args.shift();
|
|
27
|
+
const input = words(args.filter((arg) => arg !== "help").join(" "));
|
|
28
|
+
const usageLines = usageText
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => line.trim())
|
|
31
|
+
.filter((line) => line.startsWith("dench "));
|
|
32
|
+
const candidates = [
|
|
33
|
+
...usageLines.map((usage) => ({ usage, description: "" })),
|
|
34
|
+
...COMMAND_ARGUMENTS.map((entry) => ({
|
|
35
|
+
usage:
|
|
36
|
+
`dench ${entry.command} ${[...entry.values.map((flag) => `[${flag} <value>]`), ...entry.switches.map((flag) => `[${flag}]`)].join(" ")}`.trim(),
|
|
37
|
+
description: "",
|
|
38
|
+
})),
|
|
39
|
+
...apiOperations
|
|
40
|
+
.filter((op) => {
|
|
41
|
+
if (!op.deprecated) return true;
|
|
42
|
+
// Keep exact legacy leaf help available without adding those commands
|
|
43
|
+
// back to the broad index or a parent family's discovery.
|
|
44
|
+
const command = words(op.cli);
|
|
45
|
+
return (
|
|
46
|
+
command.length > 1 &&
|
|
47
|
+
input.length >= command.length &&
|
|
48
|
+
matches(input.slice(0, command.length), command)
|
|
49
|
+
);
|
|
50
|
+
})
|
|
51
|
+
.map((op) => ({
|
|
52
|
+
usage: op.cli,
|
|
53
|
+
description: op.deprecated
|
|
54
|
+
? `Compatibility command. ${op.summary}`
|
|
55
|
+
: op.summary,
|
|
56
|
+
})),
|
|
57
|
+
];
|
|
58
|
+
let prefix = input;
|
|
59
|
+
while (
|
|
60
|
+
prefix.length &&
|
|
61
|
+
!candidates.some((item) => matches(prefix, words(item.usage)))
|
|
62
|
+
) {
|
|
63
|
+
prefix = prefix.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
const selected = candidates.filter((item) =>
|
|
66
|
+
matches(prefix, words(item.usage)),
|
|
67
|
+
);
|
|
68
|
+
// Domain usage has real flag spellings; a backend schema is not a CLI parser.
|
|
69
|
+
const exactUsage = selected.filter((item) => !item.description);
|
|
70
|
+
const entries = exactUsage.length ? exactUsage : selected;
|
|
71
|
+
const seen = new Set<string>();
|
|
72
|
+
const lines = entries.filter((item) => {
|
|
73
|
+
if (seen.has(item.usage)) return false;
|
|
74
|
+
seen.add(item.usage);
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
const heading = `Usage: dench ${prefix.join(" ")}${prefix.length ? "" : "<command>"}`;
|
|
78
|
+
return [
|
|
79
|
+
heading,
|
|
80
|
+
...lines
|
|
81
|
+
.slice(0, 12)
|
|
82
|
+
.map(
|
|
83
|
+
(item) =>
|
|
84
|
+
` ${item.usage}${item.description ? `\n ${item.description}` : ""}`,
|
|
85
|
+
),
|
|
86
|
+
...(lines.length > 12
|
|
87
|
+
? [
|
|
88
|
+
` ${lines.length - 12} more commands; add a subcommand before --help.`,
|
|
89
|
+
]
|
|
90
|
+
: []),
|
|
91
|
+
"Options: --help, -h (local help); --json (structured command results).",
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|