libretto 0.6.24 → 0.6.26
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 +9 -1
- package/README.template.md +9 -1
- package/dist/cli/commands/browser.js +17 -10
- package/dist/cli/commands/cloud-credentials.js +70 -0
- package/dist/cli/commands/deploy.js +24 -2
- package/dist/cli/commands/execution.js +9 -30
- package/dist/cli/commands/import-chrome-profiles.js +46 -0
- package/dist/cli/commands/profiles.js +71 -0
- package/dist/cli/commands/shared.js +1 -3
- package/dist/cli/core/browser.js +89 -75
- package/dist/cli/core/daemon/daemon.js +47 -35
- package/dist/cli/core/daemon/ipc.js +3 -0
- package/dist/cli/core/deploy-artifact.js +85 -22
- package/dist/cli/core/profiles.js +47 -0
- package/dist/cli/core/prompt.js +9 -0
- package/dist/cli/core/providers/libretto-cloud.js +6 -2
- package/dist/cli/core/session-logs.js +325 -0
- package/dist/cli/core/telemetry.js +110 -311
- package/dist/cli/core/workflow-runner/runner.js +65 -0
- package/dist/cli/router.js +9 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -0
- package/dist/shared/workflow/auth-profile-name.d.ts +3 -0
- package/dist/shared/workflow/auth-profile-name.js +29 -0
- package/dist/shared/workflow/auth-profile-state.d.ts +20 -0
- package/dist/shared/workflow/auth-profile-state.js +105 -0
- package/dist/shared/workflow/authenticate.d.ts +17 -0
- package/dist/shared/workflow/authenticate.js +37 -0
- package/dist/shared/workflow/credentials.d.ts +5 -0
- package/dist/shared/workflow/credentials.js +68 -0
- package/dist/shared/workflow/workflow.d.ts +16 -1
- package/dist/shared/workflow/workflow.js +56 -4
- package/package.json +1 -1
- package/skills/libretto/SKILL.md +3 -4
- package/skills/libretto/references/auth-profiles.md +61 -11
- package/skills/libretto/references/code-generation-rules.md +31 -1
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/commands/browser.ts +19 -11
- package/src/cli/commands/cloud-credentials.ts +82 -0
- package/src/cli/commands/deploy.ts +41 -2
- package/src/cli/commands/execution.ts +10 -31
- package/src/cli/commands/import-chrome-profiles.ts +46 -0
- package/src/cli/commands/profiles.ts +90 -0
- package/src/cli/commands/shared.ts +4 -8
- package/src/cli/core/browser.ts +102 -91
- package/src/cli/core/daemon/config.ts +4 -1
- package/src/cli/core/daemon/daemon.ts +52 -44
- package/src/cli/core/daemon/ipc.ts +15 -0
- package/src/cli/core/deploy-artifact.ts +131 -32
- package/src/cli/core/profiles.ts +53 -0
- package/src/cli/core/prompt.ts +15 -0
- package/src/cli/core/providers/libretto-cloud.ts +6 -2
- package/src/cli/core/providers/types.ts +4 -1
- package/src/cli/core/session-logs.ts +445 -0
- package/src/cli/core/telemetry.ts +142 -413
- package/src/cli/core/workflow-runner/runner.ts +86 -1
- package/src/cli/router.ts +8 -0
- package/src/index.ts +10 -0
- package/src/shared/workflow/auth-profile-name.ts +27 -0
- package/src/shared/workflow/auth-profile-state.ts +144 -0
- package/src/shared/workflow/authenticate.ts +63 -0
- package/src/shared/workflow/credentials.ts +91 -0
- package/src/shared/workflow/workflow.ts +89 -4
package/src/cli/core/browser.ts
CHANGED
|
@@ -5,16 +5,31 @@ import {
|
|
|
5
5
|
type CDPSession,
|
|
6
6
|
type Page,
|
|
7
7
|
} from "playwright";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import {
|
|
9
|
+
existsSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
11
14
|
import { createServer } from "node:net";
|
|
15
|
+
import { join } from "node:path";
|
|
12
16
|
import { isWindowsNamedPipePath } from "../../shared/ipc/socket-transport.js";
|
|
13
17
|
import type { LoggerApi } from "../../shared/logger/index.js";
|
|
14
18
|
import type { SessionAccessMode } from "../../shared/state/index.js";
|
|
15
19
|
import type { Experiments } from "./experiments.js";
|
|
16
|
-
import { getSessionProviderClosePath
|
|
20
|
+
import { getSessionProviderClosePath } from "./context.js";
|
|
17
21
|
import { readLibrettoConfig } from "./config.js";
|
|
22
|
+
import {
|
|
23
|
+
captureAuthProfileStorageState,
|
|
24
|
+
parseAuthProfileSites,
|
|
25
|
+
} from "../../shared/workflow/auth-profile-state.js";
|
|
26
|
+
import {
|
|
27
|
+
formatMissingLocalAuthProfileMessage,
|
|
28
|
+
getProfilePath,
|
|
29
|
+
hasProfile,
|
|
30
|
+
normalizeProfileName,
|
|
31
|
+
writeProfile,
|
|
32
|
+
} from "./profiles.js";
|
|
18
33
|
import {
|
|
19
34
|
getCloudProviderApi,
|
|
20
35
|
getProviderStartupTimeoutMs,
|
|
@@ -111,14 +126,6 @@ export function normalizeDomain(url: URL): string {
|
|
|
111
126
|
return url.hostname.replace(/^www\./, "");
|
|
112
127
|
}
|
|
113
128
|
|
|
114
|
-
export function getProfilePath(domain: string): string {
|
|
115
|
-
return join(PROFILES_DIR, `${domain}.json`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function hasProfile(domain: string): boolean {
|
|
119
|
-
return existsSync(getProfilePath(domain));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
129
|
async function tryConnectToCDP(
|
|
123
130
|
endpoint: string,
|
|
124
131
|
logger: LoggerApi,
|
|
@@ -403,7 +410,7 @@ export async function runOpen(
|
|
|
403
410
|
options: {
|
|
404
411
|
viewport?: { width: number; height: number };
|
|
405
412
|
accessMode?: SessionAccessMode;
|
|
406
|
-
|
|
413
|
+
authProfileName?: string;
|
|
407
414
|
experiments: Experiments;
|
|
408
415
|
},
|
|
409
416
|
): Promise<void> {
|
|
@@ -427,40 +434,43 @@ export async function runOpen(
|
|
|
427
434
|
|
|
428
435
|
const browserMode = headed ? "headed" : "headless";
|
|
429
436
|
|
|
430
|
-
// When --auth-profile is provided, use that
|
|
431
|
-
//
|
|
432
|
-
const
|
|
433
|
-
?
|
|
437
|
+
// When --auth-profile is provided, use that named profile instead of
|
|
438
|
+
// deriving a legacy domain profile from the URL.
|
|
439
|
+
const authProfileName = options?.authProfileName
|
|
440
|
+
? normalizeProfileName(options.authProfileName)
|
|
434
441
|
: undefined;
|
|
435
|
-
if (
|
|
436
|
-
const authProfilePath = getProfilePath(
|
|
437
|
-
if (!
|
|
442
|
+
if (authProfileName) {
|
|
443
|
+
const authProfilePath = getProfilePath(authProfileName);
|
|
444
|
+
if (!hasProfile(authProfileName)) {
|
|
438
445
|
throw new Error(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
446
|
+
formatMissingLocalAuthProfileMessage({
|
|
447
|
+
profileName: authProfileName,
|
|
448
|
+
profilePath: authProfilePath,
|
|
449
|
+
session,
|
|
450
|
+
}),
|
|
442
451
|
);
|
|
443
452
|
}
|
|
444
453
|
}
|
|
445
454
|
|
|
446
455
|
const supportsSavedProfile =
|
|
447
456
|
parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:";
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
const
|
|
457
|
+
const profileName =
|
|
458
|
+
authProfileName ?? (supportsSavedProfile ? normalizeDomain(parsedUrl) : undefined);
|
|
459
|
+
const profilePath = profileName ? getProfilePath(profileName) : undefined;
|
|
460
|
+
const useProfile = profileName ? hasProfile(profileName) : false;
|
|
451
461
|
|
|
452
462
|
logger.info("open-launching", {
|
|
453
463
|
url,
|
|
454
464
|
mode: browserMode,
|
|
455
465
|
session,
|
|
456
466
|
port,
|
|
457
|
-
|
|
467
|
+
profileName,
|
|
458
468
|
useProfile,
|
|
459
469
|
profilePath: useProfile ? profilePath : undefined,
|
|
460
470
|
});
|
|
461
471
|
|
|
462
472
|
if (useProfile) {
|
|
463
|
-
console.log(`Loading saved profile
|
|
473
|
+
console.log(`Loading saved profile ${profileName}`);
|
|
464
474
|
}
|
|
465
475
|
console.log(`Launching ${browserMode} browser (session: ${session})...`);
|
|
466
476
|
|
|
@@ -607,83 +617,84 @@ export async function runOpenWithProvider(
|
|
|
607
617
|
}
|
|
608
618
|
|
|
609
619
|
export async function runSave(
|
|
610
|
-
|
|
620
|
+
profileName: string,
|
|
611
621
|
session: string,
|
|
612
622
|
logger: LoggerApi,
|
|
623
|
+
options: { sites: string } = { sites: "" },
|
|
613
624
|
): Promise<void> {
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
const domain = normalizeDomain(normalizeUrl(urlOrDomain));
|
|
621
|
-
const profilePath = getProfilePath(domain);
|
|
625
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
626
|
+
const sites = parseAuthProfileSites(options.sites);
|
|
627
|
+
if (sites.length === 0) {
|
|
628
|
+
throw new Error("Pass at least one site with --sites <site[,site]>.");
|
|
629
|
+
}
|
|
622
630
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
631
|
+
logger.info("save-start", { profileName: normalizedProfileName, session, sites });
|
|
632
|
+
const state = readSessionStateOrThrow(session);
|
|
633
|
+
if (!state.daemonSocketPath) {
|
|
634
|
+
throw new Error(
|
|
635
|
+
`Session "${session}" has no daemon socket. Close and reopen the session, then run libretto save again.`,
|
|
626
636
|
);
|
|
637
|
+
}
|
|
638
|
+
const client = await DaemonClient.connect(state.daemonSocketPath);
|
|
627
639
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
delete cookie.partitionKey;
|
|
632
|
-
}
|
|
633
|
-
return cookie;
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
await cdpSession.detach();
|
|
637
|
-
|
|
638
|
-
const origins: Array<{
|
|
639
|
-
origin: string;
|
|
640
|
-
localStorage: Array<{ name: string; value: string }>;
|
|
641
|
-
}> = [];
|
|
642
|
-
|
|
643
|
-
for (const ctx of browser.contexts()) {
|
|
644
|
-
for (const pg of ctx.pages()) {
|
|
645
|
-
try {
|
|
646
|
-
const origin = new URL(pg.url()).origin;
|
|
647
|
-
const localStorage = await pg.evaluate(() => {
|
|
648
|
-
const items: Array<{ name: string; value: string }> = [];
|
|
649
|
-
for (let i = 0; i < window.localStorage.length; i++) {
|
|
650
|
-
const key = window.localStorage.key(i);
|
|
651
|
-
if (key) {
|
|
652
|
-
items.push({
|
|
653
|
-
name: key,
|
|
654
|
-
value: window.localStorage.getItem(key) || "",
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
return items;
|
|
659
|
-
});
|
|
660
|
-
if (localStorage.length > 0) {
|
|
661
|
-
origins.push({ origin, localStorage });
|
|
662
|
-
}
|
|
663
|
-
} catch {
|
|
664
|
-
// Skip pages that can't be accessed.
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
const state = { cookies, origins };
|
|
670
|
-
await mkdir(dirname(profilePath), { recursive: true });
|
|
671
|
-
await writeFile(profilePath, JSON.stringify(state, null, 2));
|
|
640
|
+
try {
|
|
641
|
+
const storageState = await client.captureAuthProfileStorageState({ sites });
|
|
642
|
+
const profilePath = await writeProfile(normalizedProfileName, storageState);
|
|
672
643
|
|
|
673
644
|
logger.info("save-success", {
|
|
674
|
-
|
|
645
|
+
profileName: normalizedProfileName,
|
|
646
|
+
sites,
|
|
675
647
|
profilePath,
|
|
676
|
-
cookieCount: cookies
|
|
677
|
-
originCount: origins
|
|
648
|
+
cookieCount: storageState.cookies?.length ?? 0,
|
|
649
|
+
originCount: storageState.origins?.length ?? 0,
|
|
678
650
|
});
|
|
679
|
-
console.log(`Profile saved
|
|
651
|
+
console.log(`Profile saved: ${normalizedProfileName}`);
|
|
680
652
|
console.log(` Location: ${profilePath}`);
|
|
681
|
-
console.log(`
|
|
653
|
+
console.log(` Sites: ${sites.join(", ")}`);
|
|
654
|
+
console.log(
|
|
655
|
+
` Cookies: ${storageState.cookies?.length ?? 0}, Origins: ${storageState.origins?.length ?? 0}`,
|
|
656
|
+
);
|
|
682
657
|
} catch (err) {
|
|
683
|
-
logger.error("save-error", { error: err,
|
|
658
|
+
logger.error("save-error", { error: err, profileName, session, sites });
|
|
684
659
|
throw err;
|
|
685
660
|
} finally {
|
|
686
|
-
|
|
661
|
+
client.destroy();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export async function runFetchChromeProfile(
|
|
666
|
+
profileName: string,
|
|
667
|
+
cdpUrl: string,
|
|
668
|
+
logger: LoggerApi,
|
|
669
|
+
options: { sites: string },
|
|
670
|
+
): Promise<void> {
|
|
671
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
672
|
+
const sites = parseAuthProfileSites(options.sites);
|
|
673
|
+
if (sites.length === 0) {
|
|
674
|
+
throw new Error("Pass at least one site with --sites <site[,site]>.");
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
logger.info("fetch-chrome-profile-start", {
|
|
678
|
+
profileName: normalizedProfileName,
|
|
679
|
+
cdpUrl,
|
|
680
|
+
sites,
|
|
681
|
+
});
|
|
682
|
+
const browser = await chromium.connectOverCDP(cdpUrl);
|
|
683
|
+
try {
|
|
684
|
+
const context = browser.contexts()[0];
|
|
685
|
+
if (!context) {
|
|
686
|
+
throw new Error("Connected Chrome instance has no browser context.");
|
|
687
|
+
}
|
|
688
|
+
const state = await captureAuthProfileStorageState(context, sites);
|
|
689
|
+
const profilePath = await writeProfile(normalizedProfileName, state);
|
|
690
|
+
console.log(`Profile fetched: ${normalizedProfileName}`);
|
|
691
|
+
console.log(` Location: ${profilePath}`);
|
|
692
|
+
console.log(` Sites: ${sites.join(", ")}`);
|
|
693
|
+
console.log(
|
|
694
|
+
` Cookies: ${state.cookies?.length ?? 0}, Origins: ${state.origins?.length ?? 0}`,
|
|
695
|
+
);
|
|
696
|
+
} finally {
|
|
697
|
+
disconnectBrowser(browser, logger);
|
|
687
698
|
}
|
|
688
699
|
}
|
|
689
700
|
|
|
@@ -40,6 +40,8 @@ export type DaemonBrowserProviderConfig = {
|
|
|
40
40
|
kind: "provider";
|
|
41
41
|
providerName: string;
|
|
42
42
|
initialUrl?: string;
|
|
43
|
+
authProfileName?: string;
|
|
44
|
+
authProfilePersist?: boolean;
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export type DaemonWorkflowConfig = {
|
|
@@ -48,7 +50,8 @@ export type DaemonWorkflowConfig = {
|
|
|
48
50
|
visualize?: boolean;
|
|
49
51
|
stayOpenOnSuccess?: boolean;
|
|
50
52
|
tsconfigPath?: string;
|
|
51
|
-
|
|
53
|
+
authProfileName?: string;
|
|
54
|
+
authProfilePersist?: boolean;
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
export type DaemonConfig = {
|
|
@@ -55,13 +55,13 @@ import {
|
|
|
55
55
|
type DaemonExecResult,
|
|
56
56
|
type DaemonToCliApi,
|
|
57
57
|
} from "./ipc.js";
|
|
58
|
-
import { wrapPageForActionLogging } from "../
|
|
58
|
+
import { wrapPageForActionLogging } from "../session-logs.js";
|
|
59
59
|
import {
|
|
60
|
+
formatMissingLocalAuthProfileMessage,
|
|
60
61
|
getProfilePath,
|
|
61
62
|
hasProfile,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
} from "../browser.js";
|
|
63
|
+
normalizeProfileName,
|
|
64
|
+
} from "../profiles.js";
|
|
65
65
|
import { handlePages } from "./pages.js";
|
|
66
66
|
import { handleExec, handleReadonlyExec } from "./exec.js";
|
|
67
67
|
import { DaemonExecRepl } from "./exec-repl.js";
|
|
@@ -90,6 +90,7 @@ import {
|
|
|
90
90
|
} from "../workflow-runtime.js";
|
|
91
91
|
import { WorkflowController } from "../workflow-runner/runner.js";
|
|
92
92
|
import { validateWorkflowInput } from "../../../shared/workflow/workflow.js";
|
|
93
|
+
import { captureAuthProfileStorageState } from "../../../shared/workflow/auth-profile-state.js";
|
|
93
94
|
|
|
94
95
|
function isOperationalPage(page: Page): boolean {
|
|
95
96
|
const url = page.url();
|
|
@@ -119,34 +120,17 @@ class UserFacingStartupError extends Error {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
function getMissingLocalAuthProfileError(args: {
|
|
123
|
-
normalizedDomain: string;
|
|
124
|
-
profilePath: string;
|
|
125
|
-
session: string;
|
|
126
|
-
}): string {
|
|
127
|
-
return [
|
|
128
|
-
`Local auth profile not found for domain "${args.normalizedDomain}".`,
|
|
129
|
-
`Expected profile file: ${args.profilePath}`,
|
|
130
|
-
"To create it:",
|
|
131
|
-
` 1. libretto open https://${args.normalizedDomain} --headed --session ${args.session}`,
|
|
132
|
-
" 2. Log in manually in the browser window.",
|
|
133
|
-
` 3. libretto save ${args.normalizedDomain} --session ${args.session}`,
|
|
134
|
-
].join("\n");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
123
|
function resolveAuthProfileStorageStatePath(args: {
|
|
138
|
-
|
|
124
|
+
authProfileName?: string;
|
|
139
125
|
session: string;
|
|
140
126
|
}): string | undefined {
|
|
141
|
-
if (!args.
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
const profilePath = getProfilePath(normalizedDomain);
|
|
146
|
-
if (!hasProfile(normalizedDomain)) {
|
|
127
|
+
if (!args.authProfileName) return undefined;
|
|
128
|
+
const profileName = normalizeProfileName(args.authProfileName);
|
|
129
|
+
const profilePath = getProfilePath(profileName);
|
|
130
|
+
if (!hasProfile(profileName)) {
|
|
147
131
|
throw new UserFacingStartupError(
|
|
148
|
-
|
|
149
|
-
|
|
132
|
+
formatMissingLocalAuthProfileMessage({
|
|
133
|
+
profileName,
|
|
150
134
|
profilePath,
|
|
151
135
|
session: args.session,
|
|
152
136
|
}),
|
|
@@ -379,6 +363,12 @@ class BrowserDaemon {
|
|
|
379
363
|
workflow?: DaemonWorkflowConfig;
|
|
380
364
|
}): Promise<BrowserDaemon> {
|
|
381
365
|
const { session, browser: config } = args;
|
|
366
|
+
const storageStatePath =
|
|
367
|
+
config.storageStatePath ??
|
|
368
|
+
resolveAuthProfileStorageStatePath({
|
|
369
|
+
authProfileName: args.workflow?.authProfileName,
|
|
370
|
+
session,
|
|
371
|
+
});
|
|
382
372
|
const windowPositionArg = config.windowPosition
|
|
383
373
|
? `--window-position=${config.windowPosition.x},${config.windowPosition.y}`
|
|
384
374
|
: undefined;
|
|
@@ -396,13 +386,6 @@ class BrowserDaemon {
|
|
|
396
386
|
],
|
|
397
387
|
});
|
|
398
388
|
|
|
399
|
-
const storageStatePath =
|
|
400
|
-
config.storageStatePath ??
|
|
401
|
-
resolveAuthProfileStorageStatePath({
|
|
402
|
-
authProfileDomain: args.workflow?.authProfileDomain,
|
|
403
|
-
session,
|
|
404
|
-
});
|
|
405
|
-
|
|
406
389
|
const context = await browser.newContext({
|
|
407
390
|
...(storageStatePath ? { storageState: storageStatePath } : {}),
|
|
408
391
|
viewport: {
|
|
@@ -491,7 +474,10 @@ class BrowserDaemon {
|
|
|
491
474
|
getProviderSession: () => providerSession,
|
|
492
475
|
});
|
|
493
476
|
try {
|
|
494
|
-
providerSession = await provider.createSession(
|
|
477
|
+
providerSession = await provider.createSession({
|
|
478
|
+
authProfileName: config.authProfileName,
|
|
479
|
+
authProfilePersist: config.authProfilePersist,
|
|
480
|
+
});
|
|
495
481
|
const browser = await chromium.connectOverCDP(
|
|
496
482
|
providerSession.cdpEndpoint,
|
|
497
483
|
);
|
|
@@ -665,6 +651,10 @@ class BrowserDaemon {
|
|
|
665
651
|
this.withRequestTimeout(() => handlePages(this.pageById, this.page)),
|
|
666
652
|
exec: (args) => this.runExec(args),
|
|
667
653
|
readonlyExec: (args) => this.runReadonlyExec(args),
|
|
654
|
+
captureAuthProfileStorageState: (args) =>
|
|
655
|
+
this.withRequestTimeout(() =>
|
|
656
|
+
captureAuthProfileStorageState(this.context, args.sites),
|
|
657
|
+
),
|
|
668
658
|
snapshot: (args) => this.runSnapshot(args),
|
|
669
659
|
getWorkflowStatus: () => this.getWorkflowStatus(),
|
|
670
660
|
resumeWorkflow: () => this.resumeWorkflow(),
|
|
@@ -810,6 +800,7 @@ class BrowserDaemon {
|
|
|
810
800
|
page: this.page,
|
|
811
801
|
context: this.context,
|
|
812
802
|
logger: this.logger,
|
|
803
|
+
refreshLocalAuthProfiles: !this.externallyManaged,
|
|
813
804
|
onLog: (event) => {
|
|
814
805
|
void this.broadcast("workflowOutput", event);
|
|
815
806
|
},
|
|
@@ -942,12 +933,29 @@ async function main(): Promise<void> {
|
|
|
942
933
|
config.browser.kind === "launch" ? config.browser.headed : false;
|
|
943
934
|
|
|
944
935
|
let loadedWorkflow: ExportedLibrettoWorkflow | undefined;
|
|
936
|
+
let workflowConfig = config.workflow;
|
|
937
|
+
let browserConfig = config.browser;
|
|
945
938
|
if (config.workflow) {
|
|
946
939
|
try {
|
|
947
940
|
loadedWorkflow = await loadDefaultWorkflow(
|
|
948
941
|
getAbsoluteIntegrationPath(config.workflow.integrationPath),
|
|
949
942
|
);
|
|
950
943
|
validateWorkflowInput(loadedWorkflow, config.workflow.params ?? {});
|
|
944
|
+
const authProfileName = loadedWorkflow.authProfileName;
|
|
945
|
+
const authProfilePersist =
|
|
946
|
+
loadedWorkflow.authProfileRefresh === true;
|
|
947
|
+
workflowConfig = {
|
|
948
|
+
...config.workflow,
|
|
949
|
+
authProfileName,
|
|
950
|
+
authProfilePersist,
|
|
951
|
+
};
|
|
952
|
+
if (config.browser.kind === "provider") {
|
|
953
|
+
browserConfig = {
|
|
954
|
+
...config.browser,
|
|
955
|
+
authProfileName,
|
|
956
|
+
authProfilePersist,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
951
959
|
} catch (error) {
|
|
952
960
|
throw new UserFacingStartupError(
|
|
953
961
|
error instanceof Error ? error.message : String(error),
|
|
@@ -956,30 +964,30 @@ async function main(): Promise<void> {
|
|
|
956
964
|
}
|
|
957
965
|
|
|
958
966
|
const daemon =
|
|
959
|
-
|
|
967
|
+
browserConfig.kind === "provider"
|
|
960
968
|
? await BrowserDaemon.connectToProvider({
|
|
961
969
|
session: config.session,
|
|
962
970
|
experiments: config.experiments,
|
|
963
|
-
browser:
|
|
971
|
+
browser: browserConfig,
|
|
964
972
|
})
|
|
965
|
-
:
|
|
973
|
+
: browserConfig.kind === "connect"
|
|
966
974
|
? await BrowserDaemon.connectToEndpoint({
|
|
967
975
|
session: config.session,
|
|
968
976
|
experiments: config.experiments,
|
|
969
|
-
browser:
|
|
977
|
+
browser: browserConfig,
|
|
970
978
|
})
|
|
971
979
|
: await BrowserDaemon.launchBrowser({
|
|
972
980
|
session: config.session,
|
|
973
981
|
experiments: config.experiments,
|
|
974
|
-
browser:
|
|
975
|
-
workflow:
|
|
982
|
+
browser: browserConfig,
|
|
983
|
+
workflow: workflowConfig,
|
|
976
984
|
});
|
|
977
985
|
|
|
978
|
-
if (
|
|
986
|
+
if (workflowConfig) {
|
|
979
987
|
void waitForSessionState(config.session)
|
|
980
988
|
.then(() =>
|
|
981
989
|
daemon.startWorkflow({
|
|
982
|
-
workflow:
|
|
990
|
+
workflow: workflowConfig,
|
|
983
991
|
headed,
|
|
984
992
|
loadedWorkflow,
|
|
985
993
|
}),
|
|
@@ -10,6 +10,7 @@ import { connectToIpcSocket } from "../../../shared/ipc/socket-transport.js";
|
|
|
10
10
|
import type { LoggerApi } from "../../../shared/logger/index.js";
|
|
11
11
|
import type { SnapshotDiff } from "../../../shared/snapshot/diff-snapshots.js";
|
|
12
12
|
import type { Snapshot } from "../../../shared/snapshot/types.js";
|
|
13
|
+
import type { AuthProfileStorageState } from "../../../shared/workflow/auth-profile-state.js";
|
|
13
14
|
import { REPO_ROOT } from "../context.js";
|
|
14
15
|
import type { WorkflowStatus } from "../workflow-runner/runner.js";
|
|
15
16
|
import type { DaemonConfig } from "./config.js";
|
|
@@ -43,6 +44,10 @@ export type DaemonSnapshotResult = {
|
|
|
43
44
|
snapshot: Snapshot;
|
|
44
45
|
};
|
|
45
46
|
|
|
47
|
+
export type DaemonCaptureAuthProfileStorageStateArgs = {
|
|
48
|
+
sites: string[];
|
|
49
|
+
};
|
|
50
|
+
|
|
46
51
|
export type DaemonCloseResult = { replayUrl?: string };
|
|
47
52
|
|
|
48
53
|
export type DaemonCommandResult<T> =
|
|
@@ -56,6 +61,9 @@ export type CliToDaemonApi = {
|
|
|
56
61
|
pages(): DaemonPageSummary[];
|
|
57
62
|
exec(args: DaemonExecArgs): DaemonExecResult;
|
|
58
63
|
readonlyExec(args: DaemonReadonlyExecArgs): DaemonExecResult;
|
|
64
|
+
captureAuthProfileStorageState(
|
|
65
|
+
args: DaemonCaptureAuthProfileStorageStateArgs,
|
|
66
|
+
): AuthProfileStorageState;
|
|
59
67
|
snapshot(args: DaemonSnapshotArgs): DaemonSnapshotResult;
|
|
60
68
|
getWorkflowStatus(): WorkflowStatus;
|
|
61
69
|
resumeWorkflow(): void;
|
|
@@ -207,6 +215,7 @@ export type DaemonResultMap = {
|
|
|
207
215
|
pages: DaemonPageSummary[];
|
|
208
216
|
exec: DaemonExecSuccess;
|
|
209
217
|
"readonly-exec": DaemonExecSuccess;
|
|
218
|
+
captureAuthProfileStorageState: AuthProfileStorageState;
|
|
210
219
|
snapshot: DaemonSnapshotResult;
|
|
211
220
|
};
|
|
212
221
|
|
|
@@ -445,6 +454,12 @@ export class DaemonClient {
|
|
|
445
454
|
return this.ipc.call.readonlyExec(args);
|
|
446
455
|
}
|
|
447
456
|
|
|
457
|
+
async captureAuthProfileStorageState(
|
|
458
|
+
args: DaemonCaptureAuthProfileStorageStateArgs,
|
|
459
|
+
): Promise<AuthProfileStorageState> {
|
|
460
|
+
return this.ipc.call.captureAuthProfileStorageState(args);
|
|
461
|
+
}
|
|
462
|
+
|
|
448
463
|
async snapshot(
|
|
449
464
|
args: DaemonSnapshotArgs = {},
|
|
450
465
|
): Promise<DaemonResultMap["snapshot"]> {
|