libretto 0.1.5 → 0.2.0
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 +213 -17
- package/bin/libretto.mjs +18 -0
- package/dist/cli/cli.js +201 -0
- package/dist/cli/commands/ai.js +21 -0
- package/dist/cli/commands/browser.js +56 -0
- package/dist/cli/commands/execution.js +407 -0
- package/dist/cli/commands/logs.js +65 -0
- package/dist/cli/commands/snapshot.js +99 -0
- package/dist/cli/core/ai-config.js +149 -0
- package/dist/cli/core/browser.js +687 -0
- package/dist/cli/core/context.js +113 -0
- package/dist/cli/core/pause-signals.js +29 -0
- package/dist/cli/core/session.js +183 -0
- package/dist/cli/core/snapshot-analyzer.js +492 -0
- package/dist/cli/core/telemetry.js +350 -0
- package/dist/cli/index.js +13 -0
- package/dist/cli/workers/run-integration-runtime.js +204 -0
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -0
- package/dist/cli/workers/run-integration-worker.js +83 -0
- package/dist/index.cjs +127 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +110 -0
- package/dist/runtime/download/download.cjs +70 -0
- package/dist/runtime/download/download.d.cts +35 -0
- package/dist/runtime/download/download.d.ts +35 -0
- package/dist/runtime/download/download.js +45 -0
- package/dist/runtime/download/index.cjs +30 -0
- package/dist/runtime/download/index.d.cts +3 -0
- package/dist/runtime/download/index.d.ts +3 -0
- package/dist/runtime/download/index.js +8 -0
- package/dist/runtime/extract/extract.cjs +87 -0
- package/dist/runtime/extract/extract.d.cts +23 -0
- package/dist/runtime/extract/extract.d.ts +23 -0
- package/dist/runtime/extract/extract.js +63 -0
- package/dist/runtime/extract/index.cjs +28 -0
- package/dist/runtime/extract/index.d.cts +5 -0
- package/dist/runtime/extract/index.d.ts +5 -0
- package/dist/runtime/extract/index.js +4 -0
- package/dist/runtime/network/index.cjs +28 -0
- package/dist/runtime/network/index.d.cts +4 -0
- package/dist/runtime/network/index.d.ts +4 -0
- package/dist/runtime/network/index.js +6 -0
- package/dist/runtime/network/network.cjs +91 -0
- package/dist/runtime/network/network.d.cts +28 -0
- package/dist/runtime/network/network.d.ts +28 -0
- package/dist/runtime/network/network.js +67 -0
- package/dist/runtime/recovery/agent.cjs +218 -0
- package/dist/runtime/recovery/agent.d.cts +13 -0
- package/dist/runtime/recovery/agent.d.ts +13 -0
- package/dist/runtime/recovery/agent.js +194 -0
- package/dist/runtime/recovery/errors.cjs +122 -0
- package/dist/runtime/recovery/errors.d.cts +31 -0
- package/dist/runtime/recovery/errors.d.ts +31 -0
- package/dist/runtime/recovery/errors.js +98 -0
- package/dist/runtime/recovery/index.cjs +34 -0
- package/dist/runtime/recovery/index.d.cts +7 -0
- package/dist/runtime/recovery/index.d.ts +7 -0
- package/dist/runtime/recovery/index.js +10 -0
- package/dist/runtime/recovery/recovery.cjs +53 -0
- package/dist/runtime/recovery/recovery.d.cts +12 -0
- package/dist/runtime/recovery/recovery.d.ts +12 -0
- package/dist/runtime/recovery/recovery.js +29 -0
- package/dist/runtime/step/index.cjs +31 -0
- package/dist/runtime/step/index.d.cts +7 -0
- package/dist/runtime/step/index.d.ts +7 -0
- package/dist/runtime/step/index.js +6 -0
- package/dist/runtime/step/runner.cjs +208 -0
- package/dist/runtime/step/runner.d.cts +16 -0
- package/dist/runtime/step/runner.d.ts +16 -0
- package/dist/runtime/step/runner.js +187 -0
- package/dist/runtime/step/step.cjs +67 -0
- package/dist/runtime/step/step.d.cts +23 -0
- package/dist/runtime/step/step.d.ts +23 -0
- package/dist/runtime/step/step.js +43 -0
- package/dist/runtime/step/types.cjs +16 -0
- package/dist/runtime/step/types.d.cts +72 -0
- package/dist/runtime/step/types.d.ts +72 -0
- package/dist/runtime/step/types.js +0 -0
- package/dist/shared/config/config.cjs +44 -0
- package/dist/shared/config/config.d.cts +10 -0
- package/dist/shared/config/config.d.ts +10 -0
- package/dist/shared/config/config.js +18 -0
- package/dist/shared/config/index.cjs +32 -0
- package/dist/shared/config/index.d.cts +1 -0
- package/dist/shared/config/index.d.ts +1 -0
- package/dist/shared/config/index.js +10 -0
- package/dist/shared/debug/index.cjs +32 -0
- package/dist/shared/debug/index.d.cts +2 -0
- package/dist/shared/debug/index.d.ts +2 -0
- package/dist/shared/debug/index.js +10 -0
- package/dist/shared/debug/pause.cjs +56 -0
- package/dist/shared/debug/pause.d.cts +23 -0
- package/dist/shared/debug/pause.d.ts +23 -0
- package/dist/shared/debug/pause.js +30 -0
- package/dist/shared/instrumentation/errors.cjs +81 -0
- package/dist/shared/instrumentation/errors.d.cts +12 -0
- package/dist/shared/instrumentation/errors.d.ts +12 -0
- package/dist/shared/instrumentation/errors.js +57 -0
- package/dist/shared/instrumentation/index.cjs +35 -0
- package/dist/shared/instrumentation/index.d.cts +6 -0
- package/dist/shared/instrumentation/index.d.ts +6 -0
- package/dist/shared/instrumentation/index.js +12 -0
- package/dist/shared/instrumentation/instrument.cjs +206 -0
- package/dist/shared/instrumentation/instrument.d.cts +32 -0
- package/dist/shared/instrumentation/instrument.d.ts +32 -0
- package/dist/shared/instrumentation/instrument.js +190 -0
- package/dist/shared/llm/client.cjs +139 -0
- package/dist/shared/llm/client.d.cts +6 -0
- package/dist/shared/llm/client.d.ts +6 -0
- package/dist/shared/llm/client.js +115 -0
- package/dist/shared/llm/index.cjs +28 -0
- package/dist/shared/llm/index.d.cts +3 -0
- package/dist/shared/llm/index.d.ts +3 -0
- package/dist/shared/llm/index.js +4 -0
- package/dist/shared/llm/types.cjs +16 -0
- package/dist/shared/llm/types.d.cts +34 -0
- package/dist/shared/llm/types.d.ts +34 -0
- package/dist/shared/llm/types.js +0 -0
- package/dist/shared/logger/index.cjs +35 -0
- package/dist/shared/logger/index.d.cts +2 -0
- package/dist/shared/logger/index.d.ts +2 -0
- package/dist/shared/logger/index.js +12 -0
- package/dist/shared/logger/logger.cjs +200 -0
- package/dist/shared/logger/logger.d.cts +70 -0
- package/dist/shared/logger/logger.d.ts +70 -0
- package/dist/shared/logger/logger.js +176 -0
- package/dist/shared/logger/sinks.cjs +160 -0
- package/dist/shared/logger/sinks.d.cts +9 -0
- package/dist/shared/logger/sinks.d.ts +9 -0
- package/dist/shared/logger/sinks.js +124 -0
- package/dist/shared/paths/paths.cjs +104 -0
- package/dist/shared/paths/paths.d.cts +10 -0
- package/dist/shared/paths/paths.d.ts +10 -0
- package/dist/shared/paths/paths.js +73 -0
- package/dist/shared/run/api.cjs +35 -0
- package/dist/shared/run/api.d.cts +3 -0
- package/dist/shared/run/api.d.ts +3 -0
- package/dist/shared/run/api.js +12 -0
- package/dist/shared/run/browser.cjs +98 -0
- package/dist/shared/run/browser.d.cts +22 -0
- package/dist/shared/run/browser.d.ts +22 -0
- package/dist/shared/run/browser.js +74 -0
- package/dist/shared/state/index.cjs +38 -0
- package/dist/shared/state/index.d.cts +2 -0
- package/dist/shared/state/index.d.ts +2 -0
- package/dist/shared/state/index.js +16 -0
- package/dist/shared/state/session-state.cjs +85 -0
- package/dist/shared/state/session-state.d.cts +34 -0
- package/dist/shared/state/session-state.d.ts +34 -0
- package/dist/shared/state/session-state.js +56 -0
- package/dist/shared/visualization/ghost-cursor.cjs +174 -0
- package/dist/shared/visualization/ghost-cursor.d.cts +37 -0
- package/dist/shared/visualization/ghost-cursor.d.ts +37 -0
- package/dist/shared/visualization/ghost-cursor.js +145 -0
- package/dist/shared/visualization/highlight.cjs +134 -0
- package/dist/shared/visualization/highlight.d.cts +22 -0
- package/dist/shared/visualization/highlight.d.ts +22 -0
- package/dist/shared/visualization/highlight.js +108 -0
- package/dist/shared/visualization/index.cjs +45 -0
- package/dist/shared/visualization/index.d.cts +3 -0
- package/dist/shared/visualization/index.d.ts +3 -0
- package/dist/shared/visualization/index.js +24 -0
- package/dist/shared/workflow/workflow.cjs +47 -0
- package/dist/shared/workflow/workflow.d.cts +33 -0
- package/dist/shared/workflow/workflow.d.ts +33 -0
- package/dist/shared/workflow/workflow.js +21 -0
- package/package.json +123 -26
- package/.npmignore +0 -2
- package/bin/libretto +0 -31
- package/lib/connect.js +0 -34
- package/lib/export.js +0 -224
- package/lib/import.js +0 -166
- package/lib/index.js +0 -8
- package/lib/log.js +0 -9
- package/lib/validate.js +0 -20
- package/makefile +0 -8
- package/src/connect.coffee +0 -25
- package/src/export.coffee +0 -222
- package/src/import.coffee +0 -166
- package/src/index.coffee +0 -3
- package/src/log.coffee +0 -3
- package/src/validate.coffee +0 -10
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Logger, createFileLogSink } from "../../shared/logger/index.js";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { cwd } from "node:process";
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { validateSessionName } from "./session.js";
|
|
7
|
+
function getRepoRoot() {
|
|
8
|
+
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
9
|
+
encoding: "utf-8"
|
|
10
|
+
});
|
|
11
|
+
if (result.status === 0 && result.stdout) {
|
|
12
|
+
return result.stdout.trim();
|
|
13
|
+
}
|
|
14
|
+
return cwd();
|
|
15
|
+
}
|
|
16
|
+
const REPO_ROOT = getRepoRoot();
|
|
17
|
+
const LIBRETTO_CONFIG_DIR = join(REPO_ROOT, ".libretto");
|
|
18
|
+
const LIBRETTO_CONFIG_PATH = join(LIBRETTO_CONFIG_DIR, "config.json");
|
|
19
|
+
const PROFILES_DIR = join(LIBRETTO_CONFIG_DIR, "profiles");
|
|
20
|
+
const LIBRETTO_SESSIONS_DIR = join(LIBRETTO_CONFIG_DIR, "sessions");
|
|
21
|
+
const LIBRETTO_GITIGNORE_PATH = join(LIBRETTO_CONFIG_DIR, ".gitignore");
|
|
22
|
+
const LIBRETTO_GITIGNORE_CONTENT = [
|
|
23
|
+
"# Local libretto runtime state",
|
|
24
|
+
"sessions/",
|
|
25
|
+
"profiles/",
|
|
26
|
+
""
|
|
27
|
+
].join("\n");
|
|
28
|
+
function getSessionDir(session) {
|
|
29
|
+
return join(LIBRETTO_SESSIONS_DIR, session);
|
|
30
|
+
}
|
|
31
|
+
function getSessionStatePath(session) {
|
|
32
|
+
return join(getSessionDir(session), "state.json");
|
|
33
|
+
}
|
|
34
|
+
function getSessionLogsPath(session) {
|
|
35
|
+
return join(getSessionDir(session), "logs.jsonl");
|
|
36
|
+
}
|
|
37
|
+
function getSessionNetworkLogPath(session) {
|
|
38
|
+
return join(getSessionDir(session), "network.jsonl");
|
|
39
|
+
}
|
|
40
|
+
function getSessionActionsLogPath(session) {
|
|
41
|
+
return join(getSessionDir(session), "actions.jsonl");
|
|
42
|
+
}
|
|
43
|
+
function getSessionSnapshotsDir(session) {
|
|
44
|
+
return join(getSessionDir(session), "snapshots");
|
|
45
|
+
}
|
|
46
|
+
function getSessionSnapshotRunDir(session, snapshotRunId) {
|
|
47
|
+
return join(getSessionSnapshotsDir(session), snapshotRunId);
|
|
48
|
+
}
|
|
49
|
+
function ensureLibrettoSetup() {
|
|
50
|
+
mkdirSync(LIBRETTO_CONFIG_DIR, { recursive: true });
|
|
51
|
+
mkdirSync(LIBRETTO_SESSIONS_DIR, { recursive: true });
|
|
52
|
+
mkdirSync(PROFILES_DIR, { recursive: true });
|
|
53
|
+
if (!existsSync(LIBRETTO_GITIGNORE_PATH)) {
|
|
54
|
+
writeFileSync(LIBRETTO_GITIGNORE_PATH, LIBRETTO_GITIGNORE_CONTENT, "utf-8");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function createLoggerForSession(session) {
|
|
58
|
+
validateSessionName(session);
|
|
59
|
+
const sessionDir = getSessionDir(session);
|
|
60
|
+
mkdirSync(sessionDir, { recursive: true });
|
|
61
|
+
const logFilePath = getSessionLogsPath(session);
|
|
62
|
+
return new Logger(["libretto-cli"], [createFileLogSink({ filePath: logFilePath })]);
|
|
63
|
+
}
|
|
64
|
+
async function closeLogger(logger) {
|
|
65
|
+
if (!logger) return;
|
|
66
|
+
await logger.close();
|
|
67
|
+
}
|
|
68
|
+
async function withSessionLogger(session, run) {
|
|
69
|
+
const logger = createLoggerForSession(session);
|
|
70
|
+
try {
|
|
71
|
+
return await run(logger);
|
|
72
|
+
} finally {
|
|
73
|
+
await closeLogger(logger);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
let llmClientFactory = null;
|
|
77
|
+
function setLLMClientFactory(factory) {
|
|
78
|
+
llmClientFactory = factory;
|
|
79
|
+
}
|
|
80
|
+
function getLLMClientFactory() {
|
|
81
|
+
return llmClientFactory;
|
|
82
|
+
}
|
|
83
|
+
function maybeConfigureLLMClientFactoryFromEnv() {
|
|
84
|
+
if (llmClientFactory) return;
|
|
85
|
+
const hasAnyCreds = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
86
|
+
if (!hasAnyCreds) return;
|
|
87
|
+
setLLMClientFactory(async (_logger, model) => {
|
|
88
|
+
const { createLLMClient } = await import("../../shared/llm/index.js");
|
|
89
|
+
return createLLMClient(model);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
LIBRETTO_CONFIG_DIR,
|
|
94
|
+
LIBRETTO_CONFIG_PATH,
|
|
95
|
+
LIBRETTO_GITIGNORE_PATH,
|
|
96
|
+
LIBRETTO_SESSIONS_DIR,
|
|
97
|
+
PROFILES_DIR,
|
|
98
|
+
REPO_ROOT,
|
|
99
|
+
closeLogger,
|
|
100
|
+
createLoggerForSession,
|
|
101
|
+
ensureLibrettoSetup,
|
|
102
|
+
getLLMClientFactory,
|
|
103
|
+
getSessionActionsLogPath,
|
|
104
|
+
getSessionDir,
|
|
105
|
+
getSessionLogsPath,
|
|
106
|
+
getSessionNetworkLogPath,
|
|
107
|
+
getSessionSnapshotRunDir,
|
|
108
|
+
getSessionSnapshotsDir,
|
|
109
|
+
getSessionStatePath,
|
|
110
|
+
maybeConfigureLLMClientFactoryFromEnv,
|
|
111
|
+
setLLMClientFactory,
|
|
112
|
+
withSessionLogger
|
|
113
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getSessionDir } from "./context.js";
|
|
5
|
+
function getPauseSignalPaths(session) {
|
|
6
|
+
const sessionDir = getSessionDir(session);
|
|
7
|
+
return {
|
|
8
|
+
pausedSignalPath: join(sessionDir, `${session}.paused`),
|
|
9
|
+
resumeSignalPath: join(sessionDir, `${session}.resume`),
|
|
10
|
+
completedSignalPath: join(sessionDir, `${session}.completed`),
|
|
11
|
+
failedSignalPath: join(sessionDir, `${session}.failed`),
|
|
12
|
+
outputSignalPath: join(sessionDir, `${session}.output`)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function removeSignalIfExists(path) {
|
|
16
|
+
if (!existsSync(path)) return;
|
|
17
|
+
try {
|
|
18
|
+
await unlink(path);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
const code = error.code;
|
|
21
|
+
if (code !== "ENOENT") {
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
getPauseSignalPaths,
|
|
28
|
+
removeSignalIfExists
|
|
29
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
unlinkSync,
|
|
7
|
+
writeFileSync
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import {
|
|
10
|
+
getSessionDir,
|
|
11
|
+
getSessionLogsPath,
|
|
12
|
+
getSessionStatePath,
|
|
13
|
+
LIBRETTO_SESSIONS_DIR
|
|
14
|
+
} from "./context.js";
|
|
15
|
+
import {
|
|
16
|
+
SESSION_STATE_VERSION,
|
|
17
|
+
SessionStatusSchema,
|
|
18
|
+
parseSessionStateContent,
|
|
19
|
+
serializeSessionState
|
|
20
|
+
} from "../../shared/state/index.js";
|
|
21
|
+
const SESSION_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
22
|
+
const SESSION_DEFAULT = "default";
|
|
23
|
+
const SESSION_DEV_SERVER = "dev-server";
|
|
24
|
+
const SESSION_BROWSER_AGENT = "browser-agent";
|
|
25
|
+
function logFileForSession(session) {
|
|
26
|
+
validateSessionName(session);
|
|
27
|
+
const dir = getSessionDir(session);
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
return getSessionLogsPath(session);
|
|
30
|
+
}
|
|
31
|
+
function validateSessionName(session) {
|
|
32
|
+
if (!SESSION_NAME_PATTERN.test(session) || session.includes("..") || session.includes("/") || session.includes("\\")) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"Invalid session name. Use only letters, numbers, dots, underscores, and dashes."
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getStateFilePath(session) {
|
|
39
|
+
validateSessionName(session);
|
|
40
|
+
const sessionDir = getSessionDir(session);
|
|
41
|
+
mkdirSync(sessionDir, { recursive: true });
|
|
42
|
+
return getSessionStatePath(session);
|
|
43
|
+
}
|
|
44
|
+
function readSessionState(session, logger) {
|
|
45
|
+
const stateFile = getStateFilePath(session);
|
|
46
|
+
if (!existsSync(stateFile)) {
|
|
47
|
+
logger?.info("session-state-not-found", { session, stateFile });
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const content = readFileSync(stateFile, "utf-8");
|
|
52
|
+
const state = parseSessionStateContent(content, stateFile);
|
|
53
|
+
logger?.info("session-state-read", {
|
|
54
|
+
session,
|
|
55
|
+
port: state.port,
|
|
56
|
+
pid: state.pid
|
|
57
|
+
});
|
|
58
|
+
return state;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
logger?.warn("session-state-parse-error", {
|
|
61
|
+
error: err instanceof Error ? err.message : String(err),
|
|
62
|
+
session,
|
|
63
|
+
stateFile
|
|
64
|
+
});
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function listActiveSessions() {
|
|
69
|
+
if (!existsSync(LIBRETTO_SESSIONS_DIR)) return [];
|
|
70
|
+
return readdirSync(LIBRETTO_SESSIONS_DIR).filter(
|
|
71
|
+
(session) => existsSync(getSessionStatePath(session))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function throwSessionNotFoundError(session) {
|
|
75
|
+
const active = listActiveSessions();
|
|
76
|
+
const lines = [`No session "${session}" found.`];
|
|
77
|
+
if (active.length > 0) {
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push("Active sessions:");
|
|
80
|
+
for (const name of active) {
|
|
81
|
+
lines.push(` ${name}`);
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
lines.push("");
|
|
85
|
+
lines.push("No active sessions.");
|
|
86
|
+
}
|
|
87
|
+
lines.push("");
|
|
88
|
+
lines.push("Start one with:");
|
|
89
|
+
lines.push(` libretto-cli open <url> --session ${session}`);
|
|
90
|
+
throw new Error(lines.join("\n"));
|
|
91
|
+
}
|
|
92
|
+
function assertSessionStateExistsOrThrow(session) {
|
|
93
|
+
const stateFile = getStateFilePath(session);
|
|
94
|
+
if (!existsSync(stateFile)) {
|
|
95
|
+
throwSessionNotFoundError(session);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function readSessionStateOrThrow(session) {
|
|
99
|
+
const stateFile = getStateFilePath(session);
|
|
100
|
+
if (!existsSync(stateFile)) {
|
|
101
|
+
throwSessionNotFoundError(session);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
return parseSessionStateContent(readFileSync(stateFile, "utf-8"), stateFile);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Could not read session state for "${session}": ${err instanceof Error ? err.message : String(err)}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function writeSessionState(state, logger) {
|
|
112
|
+
const stateFile = getStateFilePath(state.session);
|
|
113
|
+
const fileState = serializeSessionState(state);
|
|
114
|
+
writeFileSync(stateFile, JSON.stringify(fileState, null, 2), "utf-8");
|
|
115
|
+
logger?.info("session-state-write", {
|
|
116
|
+
session: state.session,
|
|
117
|
+
stateFile,
|
|
118
|
+
port: state.port,
|
|
119
|
+
pid: state.pid
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function clearSessionState(session, logger) {
|
|
123
|
+
const stateFile = getStateFilePath(session);
|
|
124
|
+
if (!existsSync(stateFile)) {
|
|
125
|
+
logger?.info("session-state-clear-missing", { session, stateFile });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
unlinkSync(stateFile);
|
|
129
|
+
logger?.info("session-state-cleared", { session, stateFile });
|
|
130
|
+
}
|
|
131
|
+
function isSessionStatus(value) {
|
|
132
|
+
return SessionStatusSchema.safeParse(value).success;
|
|
133
|
+
}
|
|
134
|
+
function isPidRunning(pid) {
|
|
135
|
+
try {
|
|
136
|
+
process.kill(pid, 0);
|
|
137
|
+
return true;
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function setSessionStatus(session, status, logger) {
|
|
143
|
+
const state = readSessionState(session, logger);
|
|
144
|
+
if (!state) return;
|
|
145
|
+
if (state.status === status) return;
|
|
146
|
+
writeSessionState({
|
|
147
|
+
...state,
|
|
148
|
+
status
|
|
149
|
+
}, logger);
|
|
150
|
+
}
|
|
151
|
+
function assertSessionAvailableForStart(session, logger) {
|
|
152
|
+
const existingState = readSessionState(session, logger);
|
|
153
|
+
if (!existingState) return;
|
|
154
|
+
if (isSessionStatus(existingState.status)) {
|
|
155
|
+
if (existingState.status === "completed" || existingState.status === "failed" || existingState.status === "exited") {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!isPidRunning(existingState.pid)) {
|
|
160
|
+
setSessionStatus(session, "exited", logger);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const endpoint = `http://127.0.0.1:${existingState.port}`;
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Session "${session}" is already open and connected to ${endpoint} (pid ${existingState.pid}). Create a new session or close the current one with: libretto-cli close --session ${session}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
SESSION_BROWSER_AGENT,
|
|
170
|
+
SESSION_DEFAULT,
|
|
171
|
+
SESSION_DEV_SERVER,
|
|
172
|
+
SESSION_STATE_VERSION,
|
|
173
|
+
assertSessionAvailableForStart,
|
|
174
|
+
assertSessionStateExistsOrThrow,
|
|
175
|
+
clearSessionState,
|
|
176
|
+
getStateFilePath,
|
|
177
|
+
logFileForSession,
|
|
178
|
+
readSessionState,
|
|
179
|
+
readSessionStateOrThrow,
|
|
180
|
+
setSessionStatus,
|
|
181
|
+
validateSessionName,
|
|
182
|
+
writeSessionState
|
|
183
|
+
};
|