libretto 0.5.0 → 0.5.1
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 +106 -36
- package/dist/cli/cli.js +22 -97
- package/dist/cli/commands/browser.js +86 -59
- package/dist/cli/commands/execution.js +199 -86
- package/dist/cli/commands/init.js +30 -8
- package/dist/cli/commands/logs.js +4 -5
- package/dist/cli/commands/shared.js +30 -29
- package/dist/cli/commands/snapshot.js +26 -39
- package/dist/cli/core/ai-config.js +9 -2
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +132 -29
- package/dist/cli/core/context.js +4 -1
- package/dist/cli/core/session-telemetry.js +5 -2
- package/dist/cli/core/session.js +21 -8
- package/dist/cli/core/snapshot-analyzer.js +14 -31
- package/dist/cli/core/snapshot-api-config.js +2 -6
- package/dist/cli/core/telemetry.js +10 -2
- package/dist/cli/framework/simple-cli.js +45 -25
- package/dist/cli/router.js +14 -21
- package/dist/cli/workers/run-integration-runtime.js +24 -5
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -10
- package/dist/runtime/download/download.js +5 -1
- package/dist/runtime/extract/extract.js +11 -2
- package/dist/runtime/network/network.js +8 -1
- package/dist/runtime/recovery/agent.js +6 -2
- package/dist/runtime/recovery/errors.js +3 -1
- package/dist/runtime/recovery/recovery.js +3 -1
- package/dist/shared/condense-dom/condense-dom.js +6 -13
- package/dist/shared/config/config.d.ts +1 -9
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.d.ts +2 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/pause.js +9 -3
- package/dist/shared/instrumentation/instrument.js +101 -5
- package/dist/shared/llm/ai-sdk-adapter.js +3 -1
- package/dist/shared/llm/client.js +3 -1
- package/dist/shared/logger/index.js +4 -1
- package/dist/shared/run/api.js +3 -1
- package/dist/shared/run/browser.js +7 -2
- package/dist/shared/state/session-state.d.ts +2 -1
- package/dist/shared/state/session-state.js +5 -2
- package/dist/shared/visualization/ghost-cursor.js +19 -10
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +4 -5
- package/dist/shared/workflow/workflow.js +3 -5
- package/package.json +6 -2
- package/scripts/check-skills-sync.mjs +25 -0
- package/scripts/compare-eval-summary.mjs +47 -0
- package/scripts/postinstall.mjs +15 -15
- package/scripts/prepare-release.sh +97 -0
- package/scripts/skills-libretto.mjs +103 -0
- package/scripts/summarize-evals.mjs +135 -0
- package/scripts/sync-skills.mjs +12 -0
- package/skills/libretto/SKILL.md +113 -49
- package/skills/libretto/references/code-generation-rules.md +208 -0
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- package/skills/libretto/references/site-security-review.md +143 -0
- package/src/cli/cli.ts +23 -110
- package/src/cli/commands/browser.ts +94 -70
- package/src/cli/commands/execution.ts +233 -102
- package/src/cli/commands/init.ts +32 -9
- package/src/cli/commands/logs.ts +7 -7
- package/src/cli/commands/shared.ts +36 -37
- package/src/cli/commands/snapshot.ts +44 -59
- package/src/cli/core/ai-config.ts +12 -3
- package/src/cli/core/api-snapshot-analyzer.ts +17 -6
- package/src/cli/core/browser.ts +178 -41
- package/src/cli/core/context.ts +7 -2
- package/src/cli/core/session-telemetry.ts +19 -8
- package/src/cli/core/session.ts +21 -7
- package/src/cli/core/snapshot-analyzer.ts +26 -46
- package/src/cli/core/snapshot-api-config.ts +170 -175
- package/src/cli/core/telemetry.ts +16 -3
- package/src/cli/framework/simple-cli.ts +144 -77
- package/src/cli/router.ts +13 -21
- package/src/cli/workers/run-integration-runtime.ts +36 -9
- package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
- package/src/cli/workers/run-integration-worker.ts +1 -4
- package/src/index.ts +73 -66
- package/src/runtime/download/download.ts +62 -58
- package/src/runtime/download/index.ts +5 -5
- package/src/runtime/extract/extract.ts +71 -61
- package/src/runtime/network/index.ts +3 -3
- package/src/runtime/network/network.ts +99 -93
- package/src/runtime/recovery/agent.ts +217 -212
- package/src/runtime/recovery/errors.ts +107 -104
- package/src/runtime/recovery/index.ts +3 -3
- package/src/runtime/recovery/recovery.ts +38 -35
- package/src/shared/condense-dom/condense-dom.ts +15 -18
- package/src/shared/config/config.ts +0 -19
- package/src/shared/config/index.ts +0 -5
- package/src/shared/debug/pause.ts +57 -51
- package/src/shared/instrumentation/errors.ts +64 -62
- package/src/shared/instrumentation/index.ts +5 -5
- package/src/shared/instrumentation/instrument.ts +339 -209
- package/src/shared/llm/ai-sdk-adapter.ts +58 -55
- package/src/shared/llm/client.ts +181 -174
- package/src/shared/llm/types.ts +39 -39
- package/src/shared/logger/index.ts +11 -4
- package/src/shared/logger/logger.ts +312 -306
- package/src/shared/logger/sinks.ts +118 -114
- package/src/shared/paths/paths.ts +50 -49
- package/src/shared/paths/repo-root.ts +17 -17
- package/src/shared/run/api.ts +5 -1
- package/src/shared/run/browser.ts +12 -3
- package/src/shared/state/index.ts +9 -9
- package/src/shared/state/session-state.ts +46 -43
- package/src/shared/visualization/ghost-cursor.ts +161 -148
- package/src/shared/visualization/highlight.ts +89 -86
- package/src/shared/visualization/index.ts +13 -13
- package/src/shared/workflow/workflow.ts +19 -25
- package/skills/libretto/references/reverse-engineering-network-requests.md +0 -39
- package/skills/libretto/references/user-action-log.md +0 -31
|
@@ -1,191 +1,188 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
type AiConfig,
|
|
5
|
-
readAiConfig,
|
|
6
|
-
} from "./ai-config.js";
|
|
3
|
+
import { type AiConfig, readAiConfig } from "./ai-config.js";
|
|
7
4
|
import { LIBRETTO_CONFIG_PATH, REPO_ROOT } from "./context.js";
|
|
8
5
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
hasProviderCredentials,
|
|
7
|
+
parseModel,
|
|
8
|
+
type Provider,
|
|
12
9
|
} from "../../shared/llm/client.js";
|
|
13
10
|
|
|
14
11
|
const DEFAULT_SNAPSHOT_MODELS = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
openai: "openai/gpt-5.4",
|
|
13
|
+
anthropic: "anthropic/claude-sonnet-4-6",
|
|
14
|
+
google: "google/gemini-3-flash-preview",
|
|
15
|
+
vertex: "vertex/gemini-2.5-pro",
|
|
19
16
|
} as const satisfies Record<Provider, string>;
|
|
20
17
|
|
|
21
18
|
export type SnapshotApiModelSelection = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
model: string;
|
|
20
|
+
provider: Provider;
|
|
21
|
+
source:
|
|
22
|
+
| "config"
|
|
23
|
+
| "env:auto-openai"
|
|
24
|
+
| "env:auto-anthropic"
|
|
25
|
+
| "env:auto-google"
|
|
26
|
+
| "env:auto-vertex";
|
|
30
27
|
};
|
|
31
28
|
|
|
32
29
|
export class SnapshotApiUnavailableError extends Error {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
constructor(message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "SnapshotApiUnavailableError";
|
|
33
|
+
}
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
function providerSetupSentence(provider: Provider): string {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
switch (provider) {
|
|
38
|
+
case "openai":
|
|
39
|
+
return "Add OPENAI_API_KEY to .env or as a shell environment variable.";
|
|
40
|
+
case "anthropic":
|
|
41
|
+
return "Add ANTHROPIC_API_KEY to .env or as a shell environment variable.";
|
|
42
|
+
case "google":
|
|
43
|
+
return "Add GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY to .env or as a shell environment variable.";
|
|
44
|
+
case "vertex":
|
|
45
|
+
return "Add GOOGLE_CLOUD_PROJECT or GCLOUD_PROJECT to .env or as a shell environment variable, and make sure application default credentials are configured.";
|
|
46
|
+
}
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
function defaultModelCommandLine(): string {
|
|
53
|
-
|
|
50
|
+
return "npx libretto ai configure openai | anthropic | gemini | vertex";
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
function providerMissingCredentialSummary(provider: Provider): string {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
switch (provider) {
|
|
55
|
+
case "openai":
|
|
56
|
+
return "OPENAI_API_KEY is missing";
|
|
57
|
+
case "anthropic":
|
|
58
|
+
return "ANTHROPIC_API_KEY is missing";
|
|
59
|
+
case "google":
|
|
60
|
+
return "GEMINI_API_KEY and GOOGLE_GENERATIVE_AI_API_KEY are missing";
|
|
61
|
+
case "vertex":
|
|
62
|
+
return "GOOGLE_CLOUD_PROJECT and GCLOUD_PROJECT are missing";
|
|
63
|
+
}
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
function noSnapshotApiConfiguredMessage(): string {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
return [
|
|
68
|
+
"Failed to analyze snapshot because no snapshot analyzer is configured.",
|
|
69
|
+
`Add OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY, or GOOGLE_CLOUD_PROJECT to .env or as a shell environment variable, or choose a default model with \`${defaultModelCommandLine()}\`.`,
|
|
70
|
+
"For more info, run `npx libretto init`.",
|
|
71
|
+
].join(" ");
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
function missingProviderSnapshotMessage(
|
|
78
|
-
|
|
75
|
+
selection: SnapshotApiModelSelection,
|
|
79
76
|
): string {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
const configuredSource =
|
|
78
|
+
selection.source === "config"
|
|
79
|
+
? ` in ${LIBRETTO_CONFIG_PATH}`
|
|
80
|
+
: " from process env or .env";
|
|
81
|
+
return [
|
|
82
|
+
`Failed to analyze snapshot because ${selection.provider} is configured${configuredSource}, but ${providerMissingCredentialSummary(selection.provider)}.`,
|
|
83
|
+
providerSetupSentence(selection.provider),
|
|
84
|
+
"For more info, run `npx libretto init`.",
|
|
85
|
+
].join(" ");
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
function readWorktreeEnvPath(): string | null {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
89
|
+
const gitPath = join(REPO_ROOT, ".git");
|
|
90
|
+
if (!existsSync(gitPath)) return null;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const gitPointer = readFileSync(gitPath, "utf-8").trim();
|
|
94
|
+
const match = gitPointer.match(/^gitdir:\s*(.+)$/i);
|
|
95
|
+
if (!match?.[1]) return null;
|
|
96
|
+
const worktreeGitDir = resolve(REPO_ROOT, match[1].trim());
|
|
97
|
+
const commonGitDir = resolve(worktreeGitDir, "..", "..");
|
|
98
|
+
return join(dirname(commonGitDir), ".env");
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
export function loadSnapshotEnv(): void {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
105
|
+
if (process.env.LIBRETTO_DISABLE_DOTENV?.trim() === "1") return;
|
|
106
|
+
|
|
107
|
+
const envPathCandidates = [
|
|
108
|
+
join(REPO_ROOT, ".env"),
|
|
109
|
+
readWorktreeEnvPath(),
|
|
110
|
+
].filter((value): value is string => Boolean(value));
|
|
111
|
+
|
|
112
|
+
const envPath = envPathCandidates.find((candidate) => existsSync(candidate));
|
|
113
|
+
if (!envPath) return;
|
|
114
|
+
|
|
115
|
+
for (const line of readFileSync(envPath, "utf-8").split("\n")) {
|
|
116
|
+
const parsed = parseDotEnvAssignment(line);
|
|
117
|
+
if (!parsed) continue;
|
|
118
|
+
if (!(parsed.key in process.env)) {
|
|
119
|
+
process.env[parsed.key] = parsed.value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
export function parseDotEnvAssignment(
|
|
128
|
-
|
|
125
|
+
line: string,
|
|
129
126
|
): { key: string; value: string } | null {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
127
|
+
const trimmed = line.trim();
|
|
128
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
129
|
+
|
|
130
|
+
const withoutExport = trimmed.startsWith("export ")
|
|
131
|
+
? trimmed.slice("export ".length).trimStart()
|
|
132
|
+
: trimmed;
|
|
133
|
+
const eqIdx = withoutExport.indexOf("=");
|
|
134
|
+
if (eqIdx < 1) return null;
|
|
135
|
+
|
|
136
|
+
const key = withoutExport.slice(0, eqIdx).trim();
|
|
137
|
+
if (!key) return null;
|
|
138
|
+
|
|
139
|
+
const rawValue = withoutExport.slice(eqIdx + 1).trimStart();
|
|
140
|
+
if (!rawValue) {
|
|
141
|
+
return { key, value: "" };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (rawValue.startsWith('"')) {
|
|
145
|
+
const closeIdx = rawValue.indexOf('"', 1);
|
|
146
|
+
if (closeIdx > 0) {
|
|
147
|
+
return { key, value: rawValue.slice(1, closeIdx) };
|
|
148
|
+
}
|
|
149
|
+
return { key, value: rawValue.slice(1) };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (rawValue.startsWith("'")) {
|
|
153
|
+
const closeIdx = rawValue.indexOf("'", 1);
|
|
154
|
+
if (closeIdx > 0) {
|
|
155
|
+
return { key, value: rawValue.slice(1, closeIdx) };
|
|
156
|
+
}
|
|
157
|
+
return { key, value: rawValue.slice(1) };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const inlineCommentIndex = rawValue.search(/\s#/);
|
|
161
|
+
const value =
|
|
162
|
+
inlineCommentIndex >= 0
|
|
163
|
+
? rawValue.slice(0, inlineCommentIndex).trimEnd()
|
|
164
|
+
: rawValue.trim();
|
|
165
|
+
return { key, value };
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
function inferAutoSnapshotModel(): SnapshotApiModelSelection | null {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
169
|
+
const providersInPriorityOrder: Provider[] = [
|
|
170
|
+
"openai",
|
|
171
|
+
"anthropic",
|
|
172
|
+
"google",
|
|
173
|
+
"vertex",
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
for (const provider of providersInPriorityOrder) {
|
|
177
|
+
if (!hasProviderCredentials(provider)) continue;
|
|
178
|
+
return {
|
|
179
|
+
model: DEFAULT_SNAPSHOT_MODELS[provider],
|
|
180
|
+
provider,
|
|
181
|
+
source: `env:auto-${provider}` as SnapshotApiModelSelection["source"],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return null;
|
|
189
186
|
}
|
|
190
187
|
|
|
191
188
|
/**
|
|
@@ -196,41 +193,39 @@ function inferAutoSnapshotModel(): SnapshotApiModelSelection | null {
|
|
|
196
193
|
* 2. Auto-detect from available API credentials in env
|
|
197
194
|
*/
|
|
198
195
|
export function resolveSnapshotApiModel(
|
|
199
|
-
|
|
196
|
+
config: AiConfig | null = readAiConfig(),
|
|
200
197
|
): SnapshotApiModelSelection | null {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
198
|
+
loadSnapshotEnv();
|
|
199
|
+
|
|
200
|
+
if (config?.model) {
|
|
201
|
+
const { provider } = parseModel(config.model);
|
|
202
|
+
return {
|
|
203
|
+
model: config.model,
|
|
204
|
+
provider,
|
|
205
|
+
source: "config",
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return inferAutoSnapshotModel();
|
|
213
210
|
}
|
|
214
211
|
|
|
215
212
|
export function resolveSnapshotApiModelOrThrow(
|
|
216
|
-
|
|
213
|
+
config: AiConfig | null = readAiConfig(),
|
|
217
214
|
): SnapshotApiModelSelection {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return selection;
|
|
215
|
+
const selection = resolveSnapshotApiModel(config);
|
|
216
|
+
if (!selection) {
|
|
217
|
+
throw new SnapshotApiUnavailableError(noSnapshotApiConfiguredMessage());
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!hasProviderCredentials(selection.provider)) {
|
|
221
|
+
throw new SnapshotApiUnavailableError(
|
|
222
|
+
missingProviderSnapshotMessage(selection),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return selection;
|
|
232
227
|
}
|
|
233
228
|
|
|
234
229
|
export function isSnapshotApiUnavailableError(error: unknown): boolean {
|
|
235
|
-
|
|
230
|
+
return error instanceof SnapshotApiUnavailableError;
|
|
236
231
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
appendFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
writeFileSync,
|
|
6
|
+
} from "node:fs";
|
|
2
7
|
import type { Page } from "playwright";
|
|
3
8
|
import {
|
|
4
9
|
getSessionActionsLogPath,
|
|
@@ -21,7 +26,12 @@ export type NetworkLogEntry = {
|
|
|
21
26
|
|
|
22
27
|
export function readNetworkLog(
|
|
23
28
|
session: string,
|
|
24
|
-
opts: {
|
|
29
|
+
opts: {
|
|
30
|
+
last?: number;
|
|
31
|
+
filter?: string;
|
|
32
|
+
method?: string;
|
|
33
|
+
pageId?: string;
|
|
34
|
+
} = {},
|
|
25
35
|
): NetworkLogEntry[] {
|
|
26
36
|
assertSessionStateExistsOrThrow(session);
|
|
27
37
|
const logPath = getSessionNetworkLogPath(session);
|
|
@@ -99,7 +109,10 @@ export function parentLogAction(
|
|
|
99
109
|
): void {
|
|
100
110
|
try {
|
|
101
111
|
const record = { ts: new Date().toISOString(), ...entry };
|
|
102
|
-
appendFileSync(
|
|
112
|
+
appendFileSync(
|
|
113
|
+
getSessionActionsLogPath(session),
|
|
114
|
+
JSON.stringify(record) + "\n",
|
|
115
|
+
);
|
|
103
116
|
} catch {}
|
|
104
117
|
}
|
|
105
118
|
|