@dench.com/cli 2.1.1 → 2.1.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 +507 -0
- package/crm.ts +209 -0
- package/dench.ts +124 -41
- package/fs-daemon.ts +201 -2
- package/lib/api-schemas.ts +1100 -0
- package/lib/command-registry.ts +1664 -0
- package/lib/organizationSelector.ts +58 -0
- package/linkedin.ts +210 -0
- package/package.json +6 -2
package/crm.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* The full surface mirrors the plan's "dench-cli CRM surface" section:
|
|
18
18
|
* crm objects list / get / create / update / delete / rename
|
|
19
|
+
* crm views list / get / entries
|
|
19
20
|
* crm fields list / create / update / delete / reorder
|
|
20
21
|
* crm entries list / get / create / create-many / update / update-many / delete / bulk-delete
|
|
21
22
|
* crm cells get / set / set-many / append
|
|
@@ -330,6 +331,29 @@ async function callMutation(
|
|
|
330
331
|
}
|
|
331
332
|
}
|
|
332
333
|
|
|
334
|
+
async function suppressForwardedConvexLogs<T>(
|
|
335
|
+
fn: () => Promise<T>,
|
|
336
|
+
): Promise<T> {
|
|
337
|
+
const originalLog = console.log;
|
|
338
|
+
const originalInfo = console.info;
|
|
339
|
+
const shouldSuppress = (args: unknown[]) =>
|
|
340
|
+
typeof args[0] === "string" && args[0].startsWith("[CONVEX ");
|
|
341
|
+
console.log = (...args: Parameters<typeof console.log>) => {
|
|
342
|
+
if (shouldSuppress(args)) return;
|
|
343
|
+
originalLog(...args);
|
|
344
|
+
};
|
|
345
|
+
console.info = (...args: Parameters<typeof console.info>) => {
|
|
346
|
+
if (shouldSuppress(args)) return;
|
|
347
|
+
originalInfo(...args);
|
|
348
|
+
};
|
|
349
|
+
try {
|
|
350
|
+
return await fn();
|
|
351
|
+
} finally {
|
|
352
|
+
console.log = originalLog;
|
|
353
|
+
console.info = originalInfo;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
333
357
|
function out(ctx: CrmCliContext, value: unknown): void {
|
|
334
358
|
if (ctx.jsonOutput) {
|
|
335
359
|
console.log(JSON.stringify(value, null, 2));
|
|
@@ -410,6 +434,9 @@ const api = {
|
|
|
410
434
|
objects: {
|
|
411
435
|
list: makeFunctionReference<"query">("functions/crm/objects:list"),
|
|
412
436
|
get: makeFunctionReference<"query">("functions/crm/objects:get"),
|
|
437
|
+
materializeView: makeFunctionReference<"query">(
|
|
438
|
+
"functions/crm/objects:materializeView",
|
|
439
|
+
),
|
|
413
440
|
create: makeFunctionReference<"mutation">("functions/crm/objects:create"),
|
|
414
441
|
update: makeFunctionReference<"mutation">("functions/crm/objects:update"),
|
|
415
442
|
rename: makeFunctionReference<"mutation">("functions/crm/objects:rename"),
|
|
@@ -507,6 +534,8 @@ export async function runCrmCommand(opts: {
|
|
|
507
534
|
switch (subcommand) {
|
|
508
535
|
case "objects":
|
|
509
536
|
return await runObjectsCommand(ctx);
|
|
537
|
+
case "views":
|
|
538
|
+
return await runViewsCommand(ctx);
|
|
510
539
|
case "fields":
|
|
511
540
|
return await runFieldsCommand(ctx);
|
|
512
541
|
case "entries":
|
|
@@ -636,6 +665,181 @@ async function runObjectsCommand(ctx: CrmCliContext): Promise<void> {
|
|
|
636
665
|
}
|
|
637
666
|
}
|
|
638
667
|
|
|
668
|
+
type SavedViewForCli = Record<string, unknown> & {
|
|
669
|
+
name?: unknown;
|
|
670
|
+
pinnedEntryIds?: unknown;
|
|
671
|
+
filters?: unknown;
|
|
672
|
+
sort?: unknown;
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
type CrmObjectWithViews = {
|
|
676
|
+
name?: string;
|
|
677
|
+
savedViews?: SavedViewForCli[];
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
function savedViewsFromObject(
|
|
681
|
+
objectName: string,
|
|
682
|
+
object: unknown,
|
|
683
|
+
): SavedViewForCli[] {
|
|
684
|
+
if (!object || typeof object !== "object") {
|
|
685
|
+
throw new CrmCliError(`CRM object "${objectName}" not found`);
|
|
686
|
+
}
|
|
687
|
+
const savedViews = (object as CrmObjectWithViews).savedViews;
|
|
688
|
+
return Array.isArray(savedViews) ? savedViews : [];
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function readViewName(view: SavedViewForCli): string | null {
|
|
692
|
+
return typeof view.name === "string" && view.name.trim()
|
|
693
|
+
? view.name.trim()
|
|
694
|
+
: null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function resolveSavedView(
|
|
698
|
+
objectName: string,
|
|
699
|
+
views: SavedViewForCli[],
|
|
700
|
+
requestedName: string,
|
|
701
|
+
): SavedViewForCli {
|
|
702
|
+
const trimmed = requestedName.trim();
|
|
703
|
+
const exact = views.find((view) => readViewName(view) === trimmed);
|
|
704
|
+
if (exact) return exact;
|
|
705
|
+
|
|
706
|
+
const lower = trimmed.toLowerCase();
|
|
707
|
+
const insensitiveMatches = views.filter(
|
|
708
|
+
(view) => readViewName(view)?.toLowerCase() === lower,
|
|
709
|
+
);
|
|
710
|
+
if (insensitiveMatches.length === 1) return insensitiveMatches[0];
|
|
711
|
+
if (insensitiveMatches.length > 1) {
|
|
712
|
+
throw new CrmCliError(
|
|
713
|
+
`Multiple views named like "${requestedName}" exist on ${objectName}; use the exact case-sensitive name.`,
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const available = views.map(readViewName).filter(Boolean).join(", ");
|
|
718
|
+
throw new CrmCliError(
|
|
719
|
+
`View "${requestedName}" not found on ${objectName}${
|
|
720
|
+
available ? `. Available views: ${available}` : ". No saved views exist."
|
|
721
|
+
}`,
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function summarizeSavedView(view: SavedViewForCli): Record<string, unknown> {
|
|
726
|
+
const pinned = Array.isArray(view.pinnedEntryIds)
|
|
727
|
+
? view.pinnedEntryIds
|
|
728
|
+
: undefined;
|
|
729
|
+
return {
|
|
730
|
+
...view,
|
|
731
|
+
pinnedCount: pinned?.length ?? 0,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
async function runViewsCommand(ctx: CrmCliContext): Promise<void> {
|
|
736
|
+
const verb = ctx.args.shift();
|
|
737
|
+
switch (verb) {
|
|
738
|
+
case "list": {
|
|
739
|
+
const objectName = shift(ctx.args, "object name");
|
|
740
|
+
assertNoUnknownFlags(ctx.args, "crm views list");
|
|
741
|
+
const object = await callQuery(ctx, api.objects.get, {
|
|
742
|
+
name: objectName,
|
|
743
|
+
});
|
|
744
|
+
const views = savedViewsFromObject(objectName, object).map(
|
|
745
|
+
summarizeSavedView,
|
|
746
|
+
);
|
|
747
|
+
out(ctx, views);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
case "get": {
|
|
751
|
+
const objectName = shift(ctx.args, "object name");
|
|
752
|
+
const viewName = shift(ctx.args, "view name");
|
|
753
|
+
assertNoUnknownFlags(ctx.args, "crm views get");
|
|
754
|
+
const object = await callQuery(ctx, api.objects.get, {
|
|
755
|
+
name: objectName,
|
|
756
|
+
});
|
|
757
|
+
const view = resolveSavedView(
|
|
758
|
+
objectName,
|
|
759
|
+
savedViewsFromObject(objectName, object),
|
|
760
|
+
viewName,
|
|
761
|
+
);
|
|
762
|
+
out(ctx, summarizeSavedView(view));
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
case "entries": {
|
|
766
|
+
const objectName = shift(ctx.args, "object name");
|
|
767
|
+
const viewName = shift(ctx.args, "view name");
|
|
768
|
+
const page = Number.parseInt(getFlag(ctx.args, "--page") ?? "1", 10);
|
|
769
|
+
const pageSize = Number.parseInt(
|
|
770
|
+
getFlagWithAliases(ctx.args, "--page-size", ["--limit"]) ?? "100",
|
|
771
|
+
10,
|
|
772
|
+
);
|
|
773
|
+
const withMeta = hasFlag(ctx.args, "--with-meta");
|
|
774
|
+
assertNoUnknownFlags(ctx.args, "crm views entries");
|
|
775
|
+
if (!Number.isFinite(page) || page < 1) {
|
|
776
|
+
throw new CrmCliError(`--page must be a positive integer, got ${page}`);
|
|
777
|
+
}
|
|
778
|
+
if (!Number.isFinite(pageSize) || pageSize < 1) {
|
|
779
|
+
throw new CrmCliError(
|
|
780
|
+
`--page-size/--limit must be a positive integer, got ${pageSize}`,
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const object = await callQuery(ctx, api.objects.get, {
|
|
785
|
+
name: objectName,
|
|
786
|
+
});
|
|
787
|
+
const view = resolveSavedView(
|
|
788
|
+
objectName,
|
|
789
|
+
savedViewsFromObject(objectName, object),
|
|
790
|
+
viewName,
|
|
791
|
+
);
|
|
792
|
+
const pinnedEntryIds = Array.isArray(view.pinnedEntryIds)
|
|
793
|
+
? view.pinnedEntryIds.filter(
|
|
794
|
+
(id): id is string => typeof id === "string" && id.length > 0,
|
|
795
|
+
)
|
|
796
|
+
: undefined;
|
|
797
|
+
const materialized = (await suppressForwardedConvexLogs(() =>
|
|
798
|
+
callQuery(ctx, api.objects.materializeView, {
|
|
799
|
+
objectName,
|
|
800
|
+
page,
|
|
801
|
+
pageSize,
|
|
802
|
+
filters: view.filters,
|
|
803
|
+
sort: Array.isArray(view.sort) ? view.sort : undefined,
|
|
804
|
+
pinnedEntryIds,
|
|
805
|
+
includeReverseRelations: false,
|
|
806
|
+
}),
|
|
807
|
+
)) as {
|
|
808
|
+
entries?: unknown[];
|
|
809
|
+
totalCount?: number;
|
|
810
|
+
page?: number;
|
|
811
|
+
pageSize?: number;
|
|
812
|
+
truncated?: boolean;
|
|
813
|
+
} | null;
|
|
814
|
+
|
|
815
|
+
if (!materialized) {
|
|
816
|
+
throw new CrmCliError(`CRM object "${objectName}" not found`);
|
|
817
|
+
}
|
|
818
|
+
const entries = Array.isArray(materialized.entries)
|
|
819
|
+
? materialized.entries
|
|
820
|
+
: [];
|
|
821
|
+
if (!withMeta) {
|
|
822
|
+
out(ctx, entries);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
out(ctx, {
|
|
826
|
+
objectName,
|
|
827
|
+
viewName: readViewName(view) ?? viewName,
|
|
828
|
+
view: summarizeSavedView(view),
|
|
829
|
+
totalCount: materialized.totalCount ?? entries.length,
|
|
830
|
+
returnedCount: entries.length,
|
|
831
|
+
page: materialized.page ?? page,
|
|
832
|
+
pageSize: materialized.pageSize ?? pageSize,
|
|
833
|
+
truncated: materialized.truncated ?? false,
|
|
834
|
+
entries,
|
|
835
|
+
});
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
default:
|
|
839
|
+
throw new CrmCliError(`Unknown crm views verb: ${verb ?? "<none>"}`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
639
843
|
async function runFieldsCommand(ctx: CrmCliContext): Promise<void> {
|
|
640
844
|
const verb = ctx.args.shift();
|
|
641
845
|
switch (verb) {
|
|
@@ -2418,6 +2622,11 @@ Objects (CRM tables):
|
|
|
2418
2622
|
dench crm objects rename <from> <to>
|
|
2419
2623
|
dench crm objects delete <name>
|
|
2420
2624
|
|
|
2625
|
+
Views (saved CRM subsets):
|
|
2626
|
+
dench crm views list <object> [--json]
|
|
2627
|
+
dench crm views get <object> <view-name> [--json]
|
|
2628
|
+
dench crm views entries <object> <view-name> [--limit N] [--page N] [--with-meta] [--json]
|
|
2629
|
+
|
|
2421
2630
|
Fields (columns):
|
|
2422
2631
|
dench crm fields list <object> [--json]
|
|
2423
2632
|
dench crm fields create <object> <name> --type text|email|phone|url|number|boolean|date|enum|relation|user|tags|action|file|richtext \\
|
package/dench.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
resolveHostFromArgs,
|
|
28
28
|
STAGING_HOST,
|
|
29
29
|
} from "./host";
|
|
30
|
+
import { resolveOrganizationBySelector } from "./lib/organizationSelector";
|
|
30
31
|
import { formatApprovalOpenMessage, openUrl } from "./openUrl";
|
|
31
32
|
import {
|
|
32
33
|
explicitSessionKeyInput,
|
|
@@ -94,6 +95,10 @@ type ConfigFile = {
|
|
|
94
95
|
sessions?: Record<string, StoredSession>;
|
|
95
96
|
};
|
|
96
97
|
|
|
98
|
+
function createCliConvexClient(convexUrl: string): ConvexHttpClient {
|
|
99
|
+
return new ConvexHttpClient(convexUrl, { logger: false });
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
type WorkspaceOverview = {
|
|
98
103
|
agents: Array<{ _id: string } & JsonRecord>;
|
|
99
104
|
projects: JsonRecord[];
|
|
@@ -223,6 +228,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
223
228
|
"--compact",
|
|
224
229
|
"--dev",
|
|
225
230
|
"--follow",
|
|
231
|
+
"--full-page",
|
|
226
232
|
"--help",
|
|
227
233
|
"--mine",
|
|
228
234
|
"--self",
|
|
@@ -470,6 +476,20 @@ Live web search (Exa via the Dench Gateway, auths with the active \`dench signin
|
|
|
470
476
|
dench search answer "<query>" [--json]
|
|
471
477
|
Help: dench search help
|
|
472
478
|
|
|
479
|
+
Cloud browser (Browserbase, runs entirely in Dench Cloud):
|
|
480
|
+
dench browser open [url] [--json]
|
|
481
|
+
dench browser inspect [--json]
|
|
482
|
+
dench browser navigate <url>
|
|
483
|
+
dench browser click --selector <css> | --text "Button text"
|
|
484
|
+
dench browser fill --label "Email" --value "me@example.com"
|
|
485
|
+
dench browser screenshot [--output page.jpg] [--full-page]
|
|
486
|
+
dench browser stop
|
|
487
|
+
Help: dench browser help
|
|
488
|
+
|
|
489
|
+
LinkedIn outreach (Browserbase, verified before CRM update):
|
|
490
|
+
dench linkedin connect <profileUrl> [--entry people:<entryId>] [--json]
|
|
491
|
+
Help: dench linkedin help
|
|
492
|
+
|
|
473
493
|
Image generation / editing (via the Dench Gateway):
|
|
474
494
|
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]
|
|
475
495
|
dench image edit "<prompt>" --input <path> [--input <path> ...] [--mask <path>] [--size ...] [--quality ...] [--format ...] [--save-path ...] [--output ...] [--json]
|
|
@@ -499,6 +519,7 @@ Spawn a new chat-turn workflow (same as the web UI):
|
|
|
499
519
|
File sync:
|
|
500
520
|
dench fs status [--json] [--workspace /workspace] [--no-hash] [--drift-limit N]
|
|
501
521
|
dench fs sync --initial --org-id <orgId> [--workspace /workspace]
|
|
522
|
+
dench fs pull <path> [<path>...] [--wait] [--timeout-ms N] [--json]
|
|
502
523
|
dench fs stream-open <path>
|
|
503
524
|
dench fs stream-append <token> <chunk>
|
|
504
525
|
dench fs stream-close <token>
|
|
@@ -1585,7 +1606,7 @@ async function signin() {
|
|
|
1585
1606
|
const explicitKey = explicitSessionKeyInput({ args: filteredArgs });
|
|
1586
1607
|
const host = resolveHostFromArgs(filteredArgs, undefined);
|
|
1587
1608
|
const convexUrl = await discoverConvexUrl(host);
|
|
1588
|
-
const client =
|
|
1609
|
+
const client = createCliConvexClient(convexUrl);
|
|
1589
1610
|
|
|
1590
1611
|
const sessionToken = `dch_agent_${randomUrlSafe(32)}`;
|
|
1591
1612
|
const sessionTokenHash = await sha256Hex(sessionToken);
|
|
@@ -1970,11 +1991,9 @@ async function resolveOrgChoiceForOtp(input: {
|
|
|
1970
1991
|
orgSelector: string | null;
|
|
1971
1992
|
}): Promise<{ organizationId?: string; newOrganizationName?: string }> {
|
|
1972
1993
|
if (input.orgSelector) {
|
|
1973
|
-
const match =
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
org.slug === input.orgSelector ||
|
|
1977
|
-
org.name === input.orgSelector,
|
|
1994
|
+
const match = resolveOrganizationBySelector(
|
|
1995
|
+
input.organizations,
|
|
1996
|
+
input.orgSelector,
|
|
1978
1997
|
);
|
|
1979
1998
|
if (!match) {
|
|
1980
1999
|
throw new CliError(
|
|
@@ -2325,7 +2344,7 @@ async function getRuntime() {
|
|
|
2325
2344
|
return {
|
|
2326
2345
|
mode: "session" as const,
|
|
2327
2346
|
host: resolveApiHost(host),
|
|
2328
|
-
client:
|
|
2347
|
+
client: createCliConvexClient(stored.session.convexUrl),
|
|
2329
2348
|
sessionToken: stored.session.sessionToken,
|
|
2330
2349
|
organization: stored.session.organization,
|
|
2331
2350
|
};
|
|
@@ -2340,48 +2359,55 @@ async function getRuntime() {
|
|
|
2340
2359
|
});
|
|
2341
2360
|
}
|
|
2342
2361
|
|
|
2343
|
-
// Sandbox session-mode path: workflow minting provisions a real
|
|
2344
|
-
// dch_agent_* token at sandbox-create time. Prefer it over apiKey so
|
|
2345
|
-
// agentWorkspace.* commands (Composio tools) work inside sandboxes.
|
|
2346
|
-
const sandboxAgentToken = process.env.DENCH_AGENT_SESSION_TOKEN?.trim();
|
|
2347
2362
|
const sandboxConvexUrl = resolveApiKeyConvexUrl();
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2363
|
+
const orgId = process.env.DENCH_ORG_ID?.trim();
|
|
2364
|
+
const orgSlug = process.env.DENCH_ORG_SLUG?.trim();
|
|
2365
|
+
const sandboxOrganization =
|
|
2366
|
+
orgId && orgSlug
|
|
2367
|
+
? { id: orgId, name: orgSlug, slug: orgSlug }
|
|
2368
|
+
: undefined;
|
|
2369
|
+
|
|
2370
|
+
// Sandbox / automation path. PREFER the unified Dench API key over the
|
|
2371
|
+
// `dch_agent_*` agent-session token.
|
|
2372
|
+
//
|
|
2373
|
+
// Both are baked into sandbox envVars (src/lib/secrets/sandbox-tokens.ts)
|
|
2374
|
+
// and the server's `requireCrmAccess` / `requireCliWorkspaceAccess`
|
|
2375
|
+
// accept EITHER for the full CRM + files + integration surface. But the
|
|
2376
|
+
// agent-session token EXPIRES (SANDBOX_AGENT_SESSION_TTL_MS, 7d) while
|
|
2377
|
+
// the API key is org-secret-keyed with no TTL. The old order tried the
|
|
2378
|
+
// agent token first and — critically — `getRuntime` keys the session
|
|
2379
|
+
// branch purely on the token being PRESENT, never on validity, so an
|
|
2380
|
+
// expired token in a long-lived / reused sandbox would be used blindly
|
|
2381
|
+
// and the server would reject it, even though a perfectly valid API key
|
|
2382
|
+
// sat right next to it. The server itself documents the API key as the
|
|
2383
|
+
// "preferred path for dench-cli and any automation outside the browser"
|
|
2384
|
+
// and the agent token as legacy back-compat that "new sandboxes do not
|
|
2385
|
+
// need". So prefer the key; it removes the expired-token failure mode
|
|
2386
|
+
// entirely for unattended cron/heartbeat runs.
|
|
2387
|
+
const apiKey = process.env.DENCH_API_KEY?.trim();
|
|
2388
|
+
if (apiKey && sandboxConvexUrl) {
|
|
2351
2389
|
const apiHost = resolveApiHost(host);
|
|
2352
2390
|
return {
|
|
2353
|
-
mode: "
|
|
2391
|
+
mode: "apiKey" as const,
|
|
2354
2392
|
host: apiHost,
|
|
2355
|
-
client:
|
|
2356
|
-
sessionToken:
|
|
2357
|
-
organization:
|
|
2358
|
-
orgId && orgSlug
|
|
2359
|
-
? { id: orgId, name: orgSlug, slug: orgSlug }
|
|
2360
|
-
: undefined,
|
|
2393
|
+
client: createCliConvexClient(sandboxConvexUrl),
|
|
2394
|
+
sessionToken: apiKey,
|
|
2395
|
+
organization: sandboxOrganization,
|
|
2361
2396
|
};
|
|
2362
2397
|
}
|
|
2363
2398
|
|
|
2364
|
-
//
|
|
2365
|
-
//
|
|
2366
|
-
//
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
// explicit `dench signin` setups keep their existing behavior.
|
|
2370
|
-
const apiKey = process.env.DENCH_API_KEY?.trim();
|
|
2371
|
-
const convexUrl = resolveApiKeyConvexUrl();
|
|
2372
|
-
if (apiKey && convexUrl) {
|
|
2373
|
-
const orgId = process.env.DENCH_ORG_ID?.trim();
|
|
2374
|
-
const orgSlug = process.env.DENCH_ORG_SLUG?.trim();
|
|
2399
|
+
// Fallback: no API key present (older sandbox images / custom setups) —
|
|
2400
|
+
// use the agent-session token. Still subject to expiry, but it's the
|
|
2401
|
+
// only credential available in this branch.
|
|
2402
|
+
const sandboxAgentToken = process.env.DENCH_AGENT_SESSION_TOKEN?.trim();
|
|
2403
|
+
if (sandboxAgentToken && sandboxConvexUrl) {
|
|
2375
2404
|
const apiHost = resolveApiHost(host);
|
|
2376
2405
|
return {
|
|
2377
|
-
mode: "
|
|
2406
|
+
mode: "session" as const,
|
|
2378
2407
|
host: apiHost,
|
|
2379
|
-
client:
|
|
2380
|
-
sessionToken:
|
|
2381
|
-
organization:
|
|
2382
|
-
orgId && orgSlug
|
|
2383
|
-
? { id: orgId, name: orgSlug, slug: orgSlug }
|
|
2384
|
-
: undefined,
|
|
2408
|
+
client: createCliConvexClient(sandboxConvexUrl),
|
|
2409
|
+
sessionToken: sandboxAgentToken,
|
|
2410
|
+
organization: sandboxOrganization,
|
|
2385
2411
|
};
|
|
2386
2412
|
}
|
|
2387
2413
|
|
|
@@ -2394,7 +2420,7 @@ async function getRuntime() {
|
|
|
2394
2420
|
|
|
2395
2421
|
return {
|
|
2396
2422
|
mode: "dev" as const,
|
|
2397
|
-
client:
|
|
2423
|
+
client: createCliConvexClient(readEnv("NEXT_PUBLIC_CONVEX_URL")),
|
|
2398
2424
|
workspaceArgs: {
|
|
2399
2425
|
devKey: readEnv("DENCH_DEV_AGENT_KEY"),
|
|
2400
2426
|
workspaceSlug: readEnv("DENCH_WORKSPACE"),
|
|
@@ -2933,7 +2959,7 @@ async function listArtifacts(runtime: Runtime, limit?: number) {
|
|
|
2933
2959
|
}
|
|
2934
2960
|
|
|
2935
2961
|
async function listSuggestedWork(runtime: Runtime) {
|
|
2936
|
-
if (runtime.mode === "session") {
|
|
2962
|
+
if (runtime.mode === "session" || runtime.mode === "apiKey") {
|
|
2937
2963
|
const context = await buildContext(runtime);
|
|
2938
2964
|
return asRecordArray(asRecord(context)?.suggestedWork);
|
|
2939
2965
|
}
|
|
@@ -3616,6 +3642,30 @@ async function main() {
|
|
|
3616
3642
|
return;
|
|
3617
3643
|
}
|
|
3618
3644
|
|
|
3645
|
+
if (
|
|
3646
|
+
command === "browser" &&
|
|
3647
|
+
(!subcommand || subcommand === "help" || hasFlag("--help"))
|
|
3648
|
+
) {
|
|
3649
|
+
const { runBrowserCommand } = await import("./browser");
|
|
3650
|
+
await runBrowserCommand({
|
|
3651
|
+
args: subcommand ? [subcommand] : ["help"],
|
|
3652
|
+
runtime: { host: "", bearerToken: "" },
|
|
3653
|
+
});
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
if (
|
|
3658
|
+
command === "linkedin" &&
|
|
3659
|
+
(!subcommand || subcommand === "help" || hasFlag("--help"))
|
|
3660
|
+
) {
|
|
3661
|
+
const { runLinkedInCommand } = await import("./linkedin");
|
|
3662
|
+
await runLinkedInCommand({
|
|
3663
|
+
args: subcommand ? [subcommand] : ["help"],
|
|
3664
|
+
runtime: { host: "", bearerToken: "" },
|
|
3665
|
+
});
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3619
3669
|
if (
|
|
3620
3670
|
command === "billing" &&
|
|
3621
3671
|
(!subcommand || subcommand === "help" || hasFlag("--help"))
|
|
@@ -3880,6 +3930,39 @@ async function main() {
|
|
|
3880
3930
|
return;
|
|
3881
3931
|
}
|
|
3882
3932
|
|
|
3933
|
+
if (command === "browser") {
|
|
3934
|
+
const subArgs = args.slice(args.indexOf("browser") + 1);
|
|
3935
|
+
const { runBrowserCommand } = await import("./browser");
|
|
3936
|
+
const runtime = await getRuntime();
|
|
3937
|
+
const authedRuntime = requireAuthenticatedRuntime(runtime, "dench browser");
|
|
3938
|
+
await runBrowserCommand({
|
|
3939
|
+
args: subArgs,
|
|
3940
|
+
runtime: {
|
|
3941
|
+
host: authedRuntime.host,
|
|
3942
|
+
bearerToken: authedRuntime.sessionToken,
|
|
3943
|
+
},
|
|
3944
|
+
});
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
if (command === "linkedin") {
|
|
3949
|
+
const subArgs = args.slice(args.indexOf("linkedin") + 1);
|
|
3950
|
+
const { runLinkedInCommand } = await import("./linkedin");
|
|
3951
|
+
const runtime = await getRuntime();
|
|
3952
|
+
const authedRuntime = requireAuthenticatedRuntime(
|
|
3953
|
+
runtime,
|
|
3954
|
+
"dench linkedin",
|
|
3955
|
+
);
|
|
3956
|
+
await runLinkedInCommand({
|
|
3957
|
+
args: subArgs,
|
|
3958
|
+
runtime: {
|
|
3959
|
+
host: authedRuntime.host,
|
|
3960
|
+
bearerToken: authedRuntime.sessionToken,
|
|
3961
|
+
},
|
|
3962
|
+
});
|
|
3963
|
+
return;
|
|
3964
|
+
}
|
|
3965
|
+
|
|
3883
3966
|
if (command === "chat") {
|
|
3884
3967
|
const subArgs = args.slice(args.indexOf("chat") + 1);
|
|
3885
3968
|
const sub = subArgs[0];
|