@dench.com/cli 2.3.0 → 2.3.2
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/browser.ts +280 -5
- package/dench.ts +58 -3
- package/lib/api-schemas.ts +34 -0
- package/lib/command-registry.ts +64 -0
- package/package.json +1 -1
package/browser.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `dench browser` — CLI control for
|
|
2
|
+
* `dench browser` — CLI control for Dench Browser.
|
|
3
3
|
*
|
|
4
|
-
* The CLI does not launch Chrome locally and never receives
|
|
5
|
-
* credentials. It sends
|
|
6
|
-
* /api/browser/cli, where Dench Cloud drives Browserbase server-side.
|
|
4
|
+
* The CLI does not launch Chrome locally and never receives provider
|
|
5
|
+
* credentials. It sends bearer-authenticated JSON commands to Dench Cloud.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { createHash } from "node:crypto";
|
|
@@ -15,6 +14,9 @@ import { hasFlag } from "./lib/cli-args";
|
|
|
15
14
|
type RuntimeBundle = {
|
|
16
15
|
host: string;
|
|
17
16
|
bearerToken: string;
|
|
17
|
+
gatewayBaseUrl?: string;
|
|
18
|
+
gatewayBearerToken?: string;
|
|
19
|
+
gatewayHeaders?: Record<string, string>;
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
type BrowserCliContext = {
|
|
@@ -46,6 +48,15 @@ type BrowserResponse = Record<string, unknown> & {
|
|
|
46
48
|
threadUrl?: string;
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
type DenchBrowserSessionResponse = {
|
|
52
|
+
provider?: string;
|
|
53
|
+
sessionId?: string;
|
|
54
|
+
connectUrl?: string;
|
|
55
|
+
liveViewUrl?: string;
|
|
56
|
+
status?: string;
|
|
57
|
+
contextId?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
49
60
|
class BrowserCliError extends Error {}
|
|
50
61
|
|
|
51
62
|
const ACTIONS = new Set([
|
|
@@ -72,7 +83,7 @@ const ACTIONS = new Set([
|
|
|
72
83
|
function browserHelp(): void {
|
|
73
84
|
console.log(`Usage: dench browser <action> [options]
|
|
74
85
|
|
|
75
|
-
Control
|
|
86
|
+
Control Dench Browser without opening dench.com locally.
|
|
76
87
|
Commands authenticate with the active dench signin session or DENCH_API_KEY.
|
|
77
88
|
|
|
78
89
|
Common:
|
|
@@ -95,6 +106,11 @@ Common:
|
|
|
95
106
|
dench browser close-tab <targetId>
|
|
96
107
|
dench browser stop
|
|
97
108
|
|
|
109
|
+
Playwright sessions:
|
|
110
|
+
dench browser session create --json
|
|
111
|
+
dench browser session status <sessionId> --json
|
|
112
|
+
dench browser session stop <sessionId> --json
|
|
113
|
+
|
|
98
114
|
State:
|
|
99
115
|
The CLI remembers the latest browser thread per Dench host/session.
|
|
100
116
|
Use --thread <threadId> to control a specific thread, or
|
|
@@ -213,10 +229,54 @@ function buildCliBrowserUrl(host: string): string {
|
|
|
213
229
|
return `${host.replace(/\/+$/, "")}/api/browser/cli`;
|
|
214
230
|
}
|
|
215
231
|
|
|
232
|
+
function normalizeGatewayBaseUrl(value: string | undefined): string {
|
|
233
|
+
const explicit = value?.trim();
|
|
234
|
+
if (explicit) return explicit.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
235
|
+
if ("DENCH_GATEWAY_URL" in process.env || "GATEWAY_URL" in process.env) {
|
|
236
|
+
const env =
|
|
237
|
+
process.env.DENCH_GATEWAY_URL?.trim() || process.env.GATEWAY_URL?.trim();
|
|
238
|
+
if (env) return env.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
239
|
+
throw new BrowserCliError(
|
|
240
|
+
"DENCH_GATEWAY_URL is set but empty — configure a gateway URL or unset the variable",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (/localhost|127\.0\.0\.1/.test(process.env.DENCH_HOST ?? "")) {
|
|
244
|
+
return "http://localhost:8787";
|
|
245
|
+
}
|
|
246
|
+
return "https://gateway.merseoriginals.com";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildGatewayBrowserUrl(
|
|
250
|
+
runtime: RuntimeBundle,
|
|
251
|
+
path = "/v1/browser/sessions",
|
|
252
|
+
): string {
|
|
253
|
+
return `${normalizeGatewayBaseUrl(runtime.gatewayBaseUrl)}${path}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function buildRegisterSessionUrl(runtime: RuntimeBundle): string {
|
|
257
|
+
const convex = (
|
|
258
|
+
process.env.CONVEX_URL ||
|
|
259
|
+
process.env.NEXT_PUBLIC_CONVEX_URL ||
|
|
260
|
+
""
|
|
261
|
+
).trim();
|
|
262
|
+
if (convex) {
|
|
263
|
+
const siteBase = convex
|
|
264
|
+
.replace(/\/+$/, "")
|
|
265
|
+
.replace(/\.convex\.cloud$/i, ".convex.site");
|
|
266
|
+
return `${siteBase}/api/browser/register-session`;
|
|
267
|
+
}
|
|
268
|
+
const apiBase =
|
|
269
|
+
process.env.DENCH_API_URL?.trim() ||
|
|
270
|
+
process.env.DENCH_HOST?.trim() ||
|
|
271
|
+
runtime.host;
|
|
272
|
+
return `${apiBase.replace(/\/+$/, "")}/api/browser/register-session`;
|
|
273
|
+
}
|
|
274
|
+
|
|
216
275
|
async function postJson(
|
|
217
276
|
url: string,
|
|
218
277
|
bearer: string,
|
|
219
278
|
body: unknown,
|
|
279
|
+
extraHeaders: Record<string, string> = {},
|
|
220
280
|
): Promise<{ ok: boolean; payload: unknown; status: number }> {
|
|
221
281
|
const response = await fetch(url, {
|
|
222
282
|
method: "POST",
|
|
@@ -224,6 +284,7 @@ async function postJson(
|
|
|
224
284
|
accept: "application/json",
|
|
225
285
|
authorization: `Bearer ${bearer}`,
|
|
226
286
|
"content-type": "application/json",
|
|
287
|
+
...extraHeaders,
|
|
227
288
|
},
|
|
228
289
|
body: JSON.stringify(body),
|
|
229
290
|
});
|
|
@@ -237,14 +298,223 @@ async function postJson(
|
|
|
237
298
|
return { ok: response.ok, payload, status: response.status };
|
|
238
299
|
}
|
|
239
300
|
|
|
301
|
+
async function getJson(
|
|
302
|
+
url: string,
|
|
303
|
+
bearer: string,
|
|
304
|
+
extraHeaders: Record<string, string> = {},
|
|
305
|
+
): Promise<{ ok: boolean; payload: unknown; status: number }> {
|
|
306
|
+
const response = await fetch(url, {
|
|
307
|
+
method: "GET",
|
|
308
|
+
headers: {
|
|
309
|
+
accept: "application/json",
|
|
310
|
+
authorization: `Bearer ${bearer}`,
|
|
311
|
+
...extraHeaders,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
const text = await response.text();
|
|
315
|
+
let payload: unknown;
|
|
316
|
+
try {
|
|
317
|
+
payload = text ? JSON.parse(text) : null;
|
|
318
|
+
} catch {
|
|
319
|
+
payload = { raw: text };
|
|
320
|
+
}
|
|
321
|
+
return { ok: response.ok, payload, status: response.status };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function deleteJson(
|
|
325
|
+
url: string,
|
|
326
|
+
bearer: string,
|
|
327
|
+
extraHeaders: Record<string, string> = {},
|
|
328
|
+
): Promise<{ ok: boolean; payload: unknown; status: number }> {
|
|
329
|
+
const response = await fetch(url, {
|
|
330
|
+
method: "DELETE",
|
|
331
|
+
headers: {
|
|
332
|
+
accept: "application/json",
|
|
333
|
+
authorization: `Bearer ${bearer}`,
|
|
334
|
+
...extraHeaders,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
const text = await response.text();
|
|
338
|
+
let payload: unknown;
|
|
339
|
+
try {
|
|
340
|
+
payload = text ? JSON.parse(text) : null;
|
|
341
|
+
} catch {
|
|
342
|
+
payload = { raw: text };
|
|
343
|
+
}
|
|
344
|
+
return { ok: response.ok, payload, status: response.status };
|
|
345
|
+
}
|
|
346
|
+
|
|
240
347
|
function payloadError(payload: unknown): string {
|
|
241
348
|
if (payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
242
349
|
const error = (payload as { error?: unknown }).error;
|
|
243
350
|
if (typeof error === "string" && error.trim()) return error;
|
|
351
|
+
if (error && typeof error === "object" && !Array.isArray(error)) {
|
|
352
|
+
const message = (error as { message?: unknown }).message;
|
|
353
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
354
|
+
}
|
|
244
355
|
}
|
|
245
356
|
return "Cloud browser request failed";
|
|
246
357
|
}
|
|
247
358
|
|
|
359
|
+
function gatewayBearerToken(runtime: RuntimeBundle): string {
|
|
360
|
+
return runtime.gatewayBearerToken?.trim() || runtime.bearerToken;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function gatewayBrowserHeaders(runtime: RuntimeBundle): Record<string, string> {
|
|
364
|
+
const runId = process.env.DENCH_RUN_ID?.trim();
|
|
365
|
+
return {
|
|
366
|
+
...(runtime.gatewayHeaders ?? {}),
|
|
367
|
+
...(runId ? { "x-dench-run-id": runId } : {}),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function registerScriptSession(args: {
|
|
372
|
+
runtime: RuntimeBundle;
|
|
373
|
+
session: DenchBrowserSessionResponse;
|
|
374
|
+
status: string;
|
|
375
|
+
}): Promise<void> {
|
|
376
|
+
const runId = process.env.DENCH_RUN_ID?.trim();
|
|
377
|
+
const sessionId = args.session.sessionId?.trim();
|
|
378
|
+
if (!runId || !sessionId) return;
|
|
379
|
+
try {
|
|
380
|
+
await postJson(
|
|
381
|
+
buildRegisterSessionUrl(args.runtime),
|
|
382
|
+
args.runtime.bearerToken,
|
|
383
|
+
{
|
|
384
|
+
runId,
|
|
385
|
+
sessionId,
|
|
386
|
+
liveViewUrl: args.session.liveViewUrl,
|
|
387
|
+
status: args.status,
|
|
388
|
+
},
|
|
389
|
+
);
|
|
390
|
+
} catch {
|
|
391
|
+
// Best-effort: browser automation must not fail because UI registration
|
|
392
|
+
// could not link the session to the run.
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function parseSessionCreateBody(args: string[]): Record<string, unknown> {
|
|
397
|
+
// The per-user browser profile is resolved server-side by the gateway
|
|
398
|
+
// from the authenticated Dench API key plus DENCH_RUN_ID when present
|
|
399
|
+
// (Convex `browserIdentities`).
|
|
400
|
+
// `--context-id` stays only as an explicit override for manual testing;
|
|
401
|
+
// there is no env-var fallback.
|
|
402
|
+
const contextId =
|
|
403
|
+
consumeFlagValue(args, "--context-id") ||
|
|
404
|
+
consumeFlagValue(args, "--contextId") ||
|
|
405
|
+
"";
|
|
406
|
+
const width = parseNumberFlag(args, "--width");
|
|
407
|
+
const height = parseNumberFlag(args, "--height");
|
|
408
|
+
return {
|
|
409
|
+
...(contextId ? { contextId } : {}),
|
|
410
|
+
...(width || height
|
|
411
|
+
? {
|
|
412
|
+
viewport: {
|
|
413
|
+
...(width ? { width } : {}),
|
|
414
|
+
...(height ? { height } : {}),
|
|
415
|
+
},
|
|
416
|
+
}
|
|
417
|
+
: {}),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async function runSessionCommand(ctx: BrowserCliContext): Promise<void> {
|
|
422
|
+
ctx.args.shift(); // session
|
|
423
|
+
const subcommand = ctx.args.shift() ?? "create";
|
|
424
|
+
const bearerToken = gatewayBearerToken(ctx.runtime);
|
|
425
|
+
const gatewayHeaders = gatewayBrowserHeaders(ctx.runtime);
|
|
426
|
+
|
|
427
|
+
if (subcommand === "create") {
|
|
428
|
+
const body = parseSessionCreateBody(ctx.args);
|
|
429
|
+
const result = await postJson(
|
|
430
|
+
buildGatewayBrowserUrl(ctx.runtime),
|
|
431
|
+
bearerToken,
|
|
432
|
+
body,
|
|
433
|
+
gatewayHeaders,
|
|
434
|
+
);
|
|
435
|
+
if (!result.ok) {
|
|
436
|
+
throw new BrowserCliError(
|
|
437
|
+
`${payloadError(result.payload)} (${result.status})`,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
const session =
|
|
441
|
+
result.payload && typeof result.payload === "object"
|
|
442
|
+
? (result.payload as DenchBrowserSessionResponse)
|
|
443
|
+
: {};
|
|
444
|
+
await registerScriptSession({
|
|
445
|
+
runtime: ctx.runtime,
|
|
446
|
+
session,
|
|
447
|
+
status: session.status ?? "live",
|
|
448
|
+
});
|
|
449
|
+
output(
|
|
450
|
+
ctx,
|
|
451
|
+
ctx.jsonOutput ? session : `Browser session ${session.sessionId}`,
|
|
452
|
+
);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (subcommand === "status") {
|
|
457
|
+
const sessionId = ctx.args[0]?.trim();
|
|
458
|
+
if (!sessionId) {
|
|
459
|
+
throw new BrowserCliError(
|
|
460
|
+
"dench browser session status requires a session id",
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
const result = await getJson(
|
|
464
|
+
buildGatewayBrowserUrl(
|
|
465
|
+
ctx.runtime,
|
|
466
|
+
`/v1/browser/sessions/${encodeURIComponent(sessionId)}`,
|
|
467
|
+
),
|
|
468
|
+
bearerToken,
|
|
469
|
+
gatewayHeaders,
|
|
470
|
+
);
|
|
471
|
+
if (!result.ok) {
|
|
472
|
+
throw new BrowserCliError(
|
|
473
|
+
`${payloadError(result.payload)} (${result.status})`,
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
output(
|
|
477
|
+
ctx,
|
|
478
|
+
ctx.jsonOutput ? result.payload : JSON.stringify(result.payload, null, 2),
|
|
479
|
+
);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (subcommand === "stop") {
|
|
484
|
+
const sessionId = ctx.args[0]?.trim();
|
|
485
|
+
if (!sessionId) {
|
|
486
|
+
throw new BrowserCliError(
|
|
487
|
+
"dench browser session stop requires a session id",
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
const result = await deleteJson(
|
|
491
|
+
buildGatewayBrowserUrl(
|
|
492
|
+
ctx.runtime,
|
|
493
|
+
`/v1/browser/sessions/${encodeURIComponent(sessionId)}`,
|
|
494
|
+
),
|
|
495
|
+
bearerToken,
|
|
496
|
+
gatewayHeaders,
|
|
497
|
+
);
|
|
498
|
+
if (!result.ok) {
|
|
499
|
+
throw new BrowserCliError(
|
|
500
|
+
`${payloadError(result.payload)} (${result.status})`,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
await registerScriptSession({
|
|
504
|
+
runtime: ctx.runtime,
|
|
505
|
+
session: { sessionId },
|
|
506
|
+
status: "ended",
|
|
507
|
+
});
|
|
508
|
+
output(
|
|
509
|
+
ctx,
|
|
510
|
+
ctx.jsonOutput ? result.payload : `Stopped browser session ${sessionId}`,
|
|
511
|
+
);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
throw new BrowserCliError(`Unknown browser session command: ${subcommand}`);
|
|
516
|
+
}
|
|
517
|
+
|
|
248
518
|
async function maybeWriteScreenshot(
|
|
249
519
|
response: BrowserResponse,
|
|
250
520
|
outputPath: string | undefined,
|
|
@@ -459,6 +729,11 @@ export async function runBrowserCommand(opts: {
|
|
|
459
729
|
return;
|
|
460
730
|
}
|
|
461
731
|
|
|
732
|
+
if (first === "session") {
|
|
733
|
+
await runSessionCommand(ctx);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
|
|
462
737
|
const { body, outputPath } = parseActionAndBody(ctx);
|
|
463
738
|
if (!body.threadId) {
|
|
464
739
|
body.threadId = await readRememberedThreadId(opts.runtime);
|
package/dench.ts
CHANGED
|
@@ -163,6 +163,14 @@ const api = {
|
|
|
163
163
|
"functions/agentWorkspace:whatCanIDoHere",
|
|
164
164
|
),
|
|
165
165
|
},
|
|
166
|
+
agentConfig: {
|
|
167
|
+
workspaceInstructions: makeFunctionReference<"query">(
|
|
168
|
+
"functions/agentConfig:workspaceInstructions",
|
|
169
|
+
),
|
|
170
|
+
devWorkspaceInstructions: makeFunctionReference<"query">(
|
|
171
|
+
"functions/agentConfig:devWorkspaceInstructions",
|
|
172
|
+
),
|
|
173
|
+
},
|
|
166
174
|
files: {
|
|
167
175
|
listTree: makeFunctionReference<"query">("functions/files:listTree"),
|
|
168
176
|
deleteFile: makeFunctionReference<"mutation">(
|
|
@@ -450,7 +458,8 @@ Usage:
|
|
|
450
458
|
dench use <session-key-or-workspace-slug> [--host <host>] [--json]
|
|
451
459
|
dench logout [session-key-or-workspace-slug] [--session <key-or-scope>] [--host <host>] [--all] [--json]
|
|
452
460
|
dench backend [show|set|clear] [<production|staging|local|<url>>] [--json]
|
|
453
|
-
dench context [--json]
|
|
461
|
+
dench context [--instructions] [--json]
|
|
462
|
+
dench instructions [--json]
|
|
454
463
|
dench status [--mine|--self] [--json]
|
|
455
464
|
dench memory search "query" [--limit 8] [--json]
|
|
456
465
|
dench memory save <key> "text" [--kind fact|decision|preference|goal|tool_note|other] [--tags a,b] [--sensitivity normal|broad|sensitive] [--scope user|org] [--json]
|
|
@@ -501,7 +510,7 @@ Live web search (Exa via the Dench Gateway, auths with the active \`dench signin
|
|
|
501
510
|
dench search answer "<query>" [--json]
|
|
502
511
|
Help: dench search help
|
|
503
512
|
|
|
504
|
-
Cloud browser (
|
|
513
|
+
Cloud browser (runs entirely in Dench Cloud):
|
|
505
514
|
dench browser open [url] [--json]
|
|
506
515
|
dench browser inspect [--json]
|
|
507
516
|
dench browser navigate <url>
|
|
@@ -682,10 +691,14 @@ function contextHelp() {
|
|
|
682
691
|
console.log(`Dench context
|
|
683
692
|
|
|
684
693
|
Usage:
|
|
685
|
-
dench context [--json]
|
|
694
|
+
dench context [--instructions] [--json]
|
|
695
|
+
dench instructions [--json]
|
|
686
696
|
|
|
687
697
|
Shows the current workspace, current agent, assigned tasks, pending approvals
|
|
688
698
|
requested by this agent, connected apps when available, and useful next commands.
|
|
699
|
+
|
|
700
|
+
Use --instructions (or dench instructions) to print the standing workspace
|
|
701
|
+
instructions external agents should treat as system-level guidance.
|
|
689
702
|
`);
|
|
690
703
|
}
|
|
691
704
|
|
|
@@ -2900,6 +2913,24 @@ function formatContext(context: JsonRecord) {
|
|
|
2900
2913
|
return lines.join("\n");
|
|
2901
2914
|
}
|
|
2902
2915
|
|
|
2916
|
+
async function buildWorkspaceInstructionsContext(
|
|
2917
|
+
runtime: Awaited<ReturnType<typeof getRuntime>>,
|
|
2918
|
+
) {
|
|
2919
|
+
if (runtime.mode === "dev") {
|
|
2920
|
+
return (await runtime.client.query(
|
|
2921
|
+
api.functions.agentConfig.devWorkspaceInstructions,
|
|
2922
|
+
runtime.workspaceArgs,
|
|
2923
|
+
)) as JsonRecord;
|
|
2924
|
+
}
|
|
2925
|
+
if (runtime.mode !== "session" && runtime.mode !== "apiKey") {
|
|
2926
|
+
throw loginRequiredError("dench context --instructions");
|
|
2927
|
+
}
|
|
2928
|
+
return (await runtime.client.query(
|
|
2929
|
+
api.functions.agentConfig.workspaceInstructions,
|
|
2930
|
+
{ sessionToken: runtime.sessionToken },
|
|
2931
|
+
)) as JsonRecord;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2903
2934
|
async function devOverview(runtime: Awaited<ReturnType<typeof getRuntime>>) {
|
|
2904
2935
|
if (runtime.mode !== "dev") {
|
|
2905
2936
|
throw new Error("Not in dev mode");
|
|
@@ -4053,11 +4084,19 @@ async function main() {
|
|
|
4053
4084
|
const { runBrowserCommand } = await import("./browser");
|
|
4054
4085
|
const runtime = await getRuntime();
|
|
4055
4086
|
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench browser");
|
|
4087
|
+
const filteredSubArgs = subArgs.filter((a) => a !== "--json");
|
|
4088
|
+
const gatewayAuth =
|
|
4089
|
+
filteredSubArgs[0] === "session"
|
|
4090
|
+
? await resolveGatewayCommandAuth(runtime, "dench browser session")
|
|
4091
|
+
: undefined;
|
|
4056
4092
|
await runBrowserCommand({
|
|
4057
4093
|
args: subArgs,
|
|
4058
4094
|
runtime: {
|
|
4059
4095
|
host: authedRuntime.host,
|
|
4060
4096
|
bearerToken: authedRuntime.sessionToken,
|
|
4097
|
+
gatewayBearerToken: gatewayAuth?.bearerToken,
|
|
4098
|
+
gatewayBaseUrl: gatewayAuth?.gatewayBaseUrl,
|
|
4099
|
+
gatewayHeaders: gatewayAuth?.gatewayHeaders,
|
|
4061
4100
|
},
|
|
4062
4101
|
});
|
|
4063
4102
|
return;
|
|
@@ -4347,6 +4386,22 @@ async function main() {
|
|
|
4347
4386
|
return;
|
|
4348
4387
|
}
|
|
4349
4388
|
|
|
4389
|
+
if (command === "context" && args.includes("--instructions")) {
|
|
4390
|
+
const instructions = await buildWorkspaceInstructionsContext(runtime);
|
|
4391
|
+
print(
|
|
4392
|
+
json ? instructions : (stringField(instructions, "instructions") ?? ""),
|
|
4393
|
+
);
|
|
4394
|
+
return;
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
if (command === "instructions") {
|
|
4398
|
+
const instructions = await buildWorkspaceInstructionsContext(runtime);
|
|
4399
|
+
print(
|
|
4400
|
+
json ? instructions : (stringField(instructions, "instructions") ?? ""),
|
|
4401
|
+
);
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
4404
|
+
|
|
4350
4405
|
if (command === "context") {
|
|
4351
4406
|
const context = await buildContext(runtime);
|
|
4352
4407
|
print(json ? context : formatContext(context));
|
package/lib/api-schemas.ts
CHANGED
|
@@ -340,6 +340,18 @@ const workspaceStatusPayload = z
|
|
|
340
340
|
"Workspace orientation payload shared by /context, /status, /agents, and /approvals.",
|
|
341
341
|
);
|
|
342
342
|
|
|
343
|
+
const workspaceInstructionsResponse = z.object({
|
|
344
|
+
instructions: z
|
|
345
|
+
.string()
|
|
346
|
+
.describe(
|
|
347
|
+
"Markdown standing instructions for external agents connected to this workspace.",
|
|
348
|
+
),
|
|
349
|
+
workspace: z.object({
|
|
350
|
+
name: z.string(),
|
|
351
|
+
slug: z.string().optional(),
|
|
352
|
+
}),
|
|
353
|
+
});
|
|
354
|
+
|
|
343
355
|
const memoryDoc = z
|
|
344
356
|
.object({
|
|
345
357
|
_id: z.string(),
|
|
@@ -1183,6 +1195,7 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
1183
1195
|
|
|
1184
1196
|
// Workspace
|
|
1185
1197
|
"workspace.context": noInput,
|
|
1198
|
+
"workspace.instructions": noInput,
|
|
1186
1199
|
"workspace.status": noInput,
|
|
1187
1200
|
"workspace.agents": noInput,
|
|
1188
1201
|
"workspace.approvals.list": noInput,
|
|
@@ -1909,6 +1922,7 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
1909
1922
|
|
|
1910
1923
|
// Workspace orientation
|
|
1911
1924
|
"workspace.context": workspaceStatusPayload,
|
|
1925
|
+
"workspace.instructions": workspaceInstructionsResponse,
|
|
1912
1926
|
"workspace.status": workspaceStatusPayload,
|
|
1913
1927
|
"workspace.agents": workspaceStatusPayload,
|
|
1914
1928
|
"workspace.approvals.list": workspaceStatusPayload,
|
|
@@ -2532,6 +2546,13 @@ export const convexExtras: Record<string, ConvexExtras> = {
|
|
|
2532
2546
|
// ---------------------------------------------------------------------------
|
|
2533
2547
|
|
|
2534
2548
|
export const requestExamples: Record<string, unknown> = {
|
|
2549
|
+
"browser.action": {
|
|
2550
|
+
action: "open",
|
|
2551
|
+
url: "https://example.com",
|
|
2552
|
+
},
|
|
2553
|
+
"browser.session.create": {
|
|
2554
|
+
viewport: { width: 1280, height: 800 },
|
|
2555
|
+
},
|
|
2535
2556
|
"crm.objects.create": {
|
|
2536
2557
|
name: "lead",
|
|
2537
2558
|
description: "Sales leads",
|
|
@@ -2703,6 +2724,14 @@ const workspaceStatusExample = {
|
|
|
2703
2724
|
};
|
|
2704
2725
|
|
|
2705
2726
|
export const responseExamples: Record<string, unknown> = {
|
|
2727
|
+
"browser.session.create": {
|
|
2728
|
+
provider: "dench_browser",
|
|
2729
|
+
sessionId: "bb_sess_123",
|
|
2730
|
+
connectUrl:
|
|
2731
|
+
"wss://gateway.merseoriginals.com/v1/browser/sessions/bb_sess_123/cdp?relay_token=...",
|
|
2732
|
+
liveViewUrl: "https://www.dench.com/dench?...",
|
|
2733
|
+
status: "live",
|
|
2734
|
+
},
|
|
2706
2735
|
"commands.list": {
|
|
2707
2736
|
operations: [
|
|
2708
2737
|
{
|
|
@@ -2742,6 +2771,11 @@ export const responseExamples: Record<string, unknown> = {
|
|
|
2742
2771
|
},
|
|
2743
2772
|
},
|
|
2744
2773
|
"workspace.context": workspaceStatusExample,
|
|
2774
|
+
"workspace.instructions": {
|
|
2775
|
+
workspace: { name: "Acme", slug: "acme" },
|
|
2776
|
+
instructions:
|
|
2777
|
+
"# Dench Workspace Instructions\n\nWorkspace: Acme (acme)\n\n## Identity\nYou are Dench...",
|
|
2778
|
+
},
|
|
2745
2779
|
"workspace.status": workspaceStatusExample,
|
|
2746
2780
|
"workspace.agents": workspaceStatusExample,
|
|
2747
2781
|
"workspace.approvals.list": workspaceStatusExample,
|
package/lib/command-registry.ts
CHANGED
|
@@ -34,6 +34,7 @@ export type CustomOperation = {
|
|
|
34
34
|
| "upgrade"
|
|
35
35
|
| "filesMove"
|
|
36
36
|
| "filesStage"
|
|
37
|
+
| "browserAction"
|
|
37
38
|
| "signinOtpStart"
|
|
38
39
|
| "signinOtpVerify"
|
|
39
40
|
| "signinOtpFinalize";
|
|
@@ -244,6 +245,18 @@ const rawApiOperations = [
|
|
|
244
245
|
responseSchema: anyResponse,
|
|
245
246
|
backend: convex("query", "functions/agentWorkspace:whatCanIDoHere"),
|
|
246
247
|
}),
|
|
248
|
+
op({
|
|
249
|
+
id: "workspace.instructions",
|
|
250
|
+
group: "workspace",
|
|
251
|
+
summary:
|
|
252
|
+
"Return the current workspace's standing instructions for external agents.",
|
|
253
|
+
cli: "dench context --instructions",
|
|
254
|
+
method: "GET",
|
|
255
|
+
path: "/context/instructions",
|
|
256
|
+
requestSchema: noBody,
|
|
257
|
+
responseSchema: anyResponse,
|
|
258
|
+
backend: convex("query", "functions/agentConfig:workspaceInstructions"),
|
|
259
|
+
}),
|
|
247
260
|
op({
|
|
248
261
|
id: "workspace.status",
|
|
249
262
|
group: "workspace",
|
|
@@ -1963,6 +1976,57 @@ const rawApiOperations = [
|
|
|
1963
1976
|
backend: gateway("DELETE", "/composio/connections/{connectionId}"),
|
|
1964
1977
|
}),
|
|
1965
1978
|
|
|
1979
|
+
// Browser automation (Dench Browser). The provider stays server-side; the
|
|
1980
|
+
// per-(org,user) browser profile is resolved by the gateway, so callers never
|
|
1981
|
+
// see or send provider credentials or context ids.
|
|
1982
|
+
op({
|
|
1983
|
+
id: "browser.action",
|
|
1984
|
+
group: "browser",
|
|
1985
|
+
summary:
|
|
1986
|
+
"Drive Dench Browser with a single interactive action (open, inspect, click, fill, type, screenshot, stop). Pass the step in `action` plus its arguments.",
|
|
1987
|
+
cli: "dench browser",
|
|
1988
|
+
method: "POST",
|
|
1989
|
+
path: "/browser/cli",
|
|
1990
|
+
requestSchema: anyObject,
|
|
1991
|
+
responseSchema: anyResponse,
|
|
1992
|
+
backend: custom("browserAction", "bearer"),
|
|
1993
|
+
}),
|
|
1994
|
+
op({
|
|
1995
|
+
id: "browser.session.create",
|
|
1996
|
+
group: "browser",
|
|
1997
|
+
summary:
|
|
1998
|
+
"Create a Dench Browser session and return a CDP connect URL for Playwright (chromium.connectOverCDP) plus a live-view URL. Use for durable scripts run from the sandbox.",
|
|
1999
|
+
cli: "dench browser session create",
|
|
2000
|
+
method: "POST",
|
|
2001
|
+
path: "/browser/sessions",
|
|
2002
|
+
requestSchema: anyObject,
|
|
2003
|
+
responseSchema: anyResponse,
|
|
2004
|
+
backend: gateway("POST", "/browser/sessions"),
|
|
2005
|
+
}),
|
|
2006
|
+
op({
|
|
2007
|
+
id: "browser.session.status",
|
|
2008
|
+
group: "browser",
|
|
2009
|
+
summary:
|
|
2010
|
+
"Get the status, connect URL, and live-view URL for a Dench Browser session.",
|
|
2011
|
+
cli: "dench browser session status",
|
|
2012
|
+
method: "GET",
|
|
2013
|
+
path: "/browser/sessions/{sessionId}",
|
|
2014
|
+
requestSchema: anyObject,
|
|
2015
|
+
responseSchema: anyResponse,
|
|
2016
|
+
backend: gateway("GET", "/browser/sessions/{sessionId}"),
|
|
2017
|
+
}),
|
|
2018
|
+
op({
|
|
2019
|
+
id: "browser.session.stop",
|
|
2020
|
+
group: "browser",
|
|
2021
|
+
summary: "Stop a Dench Browser session and release the remote browser.",
|
|
2022
|
+
cli: "dench browser session stop",
|
|
2023
|
+
method: "DELETE",
|
|
2024
|
+
path: "/browser/sessions/{sessionId}",
|
|
2025
|
+
requestSchema: anyObject,
|
|
2026
|
+
responseSchema: anyResponse,
|
|
2027
|
+
backend: gateway("DELETE", "/browser/sessions/{sessionId}"),
|
|
2028
|
+
}),
|
|
2029
|
+
|
|
1966
2030
|
// Agent harness config.
|
|
1967
2031
|
op({
|
|
1968
2032
|
id: "agentConfig.identity.show",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dench.com/cli",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
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": {
|