@steipete/oracle 0.9.0 → 0.10.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/LICENSE +1 -1
- package/README.md +61 -48
- package/dist/bin/oracle-cli.js +455 -402
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +275 -117
- package/dist/src/browser/actions/navigation.js +161 -137
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +390 -295
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +62 -48
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +127 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +32 -28
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +95 -81
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +103 -93
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +51 -47
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +16 -13
- package/dist/src/oracle/run.js +156 -134
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +77 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +66 -62
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/types.js +0 -1
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import fs from
|
|
3
|
-
import { createWriteStream } from
|
|
4
|
-
import net from
|
|
5
|
-
import { DEFAULT_MODEL, formatElapsed } from
|
|
6
|
-
import { safeModelSlug } from
|
|
7
|
-
import { getOracleHomeDir } from
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { createWriteStream } from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import { DEFAULT_MODEL, formatElapsed } from "./oracle.js";
|
|
6
|
+
import { safeModelSlug } from "./oracle/modelResolver.js";
|
|
7
|
+
import { getOracleHomeDir } from "./oracleHome.js";
|
|
8
8
|
export function getSessionsDir() {
|
|
9
|
-
return path.join(getOracleHomeDir(),
|
|
10
|
-
}
|
|
11
|
-
const METADATA_FILENAME =
|
|
12
|
-
const LEGACY_SESSION_FILENAME =
|
|
13
|
-
const LEGACY_REQUEST_FILENAME =
|
|
14
|
-
const MODELS_DIRNAME =
|
|
15
|
-
const MODEL_JSON_EXTENSION =
|
|
16
|
-
const MODEL_LOG_EXTENSION =
|
|
9
|
+
return path.join(getOracleHomeDir(), "sessions");
|
|
10
|
+
}
|
|
11
|
+
const METADATA_FILENAME = "meta.json";
|
|
12
|
+
const LEGACY_SESSION_FILENAME = "session.json";
|
|
13
|
+
const LEGACY_REQUEST_FILENAME = "request.json";
|
|
14
|
+
const MODELS_DIRNAME = "models";
|
|
15
|
+
const MODEL_JSON_EXTENSION = ".json";
|
|
16
|
+
const MODEL_LOG_EXTENSION = ".log";
|
|
17
17
|
const MAX_STATUS_LIMIT = 1000;
|
|
18
18
|
const ZOMBIE_MAX_AGE_MS = 60 * 60 * 1000; // 60 minutes
|
|
19
19
|
const CHROME_RUNTIME_TIMEOUT_MS = 250;
|
|
20
|
-
const DEFAULT_SLUG =
|
|
20
|
+
const DEFAULT_SLUG = "session";
|
|
21
21
|
const MAX_SLUG_WORDS = 5;
|
|
22
22
|
const MIN_CUSTOM_SLUG_WORDS = 3;
|
|
23
23
|
const MAX_SLUG_WORD_LENGTH = 10;
|
|
@@ -28,15 +28,13 @@ export async function ensureSessionStorage() {
|
|
|
28
28
|
await ensureDir(getSessionsDir());
|
|
29
29
|
}
|
|
30
30
|
function slugify(text, maxWords = MAX_SLUG_WORDS) {
|
|
31
|
-
const normalized = text?.toLowerCase() ??
|
|
31
|
+
const normalized = text?.toLowerCase() ?? "";
|
|
32
32
|
const words = normalized.match(/[a-z0-9]+/g) ?? [];
|
|
33
|
-
const trimmed = words
|
|
34
|
-
|
|
35
|
-
.map((word) => word.slice(0, MAX_SLUG_WORD_LENGTH));
|
|
36
|
-
return trimmed.length > 0 ? trimmed.join('-') : DEFAULT_SLUG;
|
|
33
|
+
const trimmed = words.slice(0, maxWords).map((word) => word.slice(0, MAX_SLUG_WORD_LENGTH));
|
|
34
|
+
return trimmed.length > 0 ? trimmed.join("-") : DEFAULT_SLUG;
|
|
37
35
|
}
|
|
38
36
|
function countSlugWords(slug) {
|
|
39
|
-
return slug.split(
|
|
37
|
+
return slug.split("-").filter(Boolean).length;
|
|
40
38
|
}
|
|
41
39
|
function normalizeCustomSlug(candidate) {
|
|
42
40
|
const slug = slugify(candidate, MAX_SLUG_WORDS);
|
|
@@ -65,7 +63,7 @@ function legacySessionPath(id) {
|
|
|
65
63
|
return path.join(sessionDir(id), LEGACY_SESSION_FILENAME);
|
|
66
64
|
}
|
|
67
65
|
function logPath(id) {
|
|
68
|
-
return path.join(sessionDir(id),
|
|
66
|
+
return path.join(sessionDir(id), "output.log");
|
|
69
67
|
}
|
|
70
68
|
function modelsDir(id) {
|
|
71
69
|
return path.join(sessionDir(id), MODELS_DIRNAME);
|
|
@@ -106,7 +104,7 @@ async function listModelRunFiles(sessionId) {
|
|
|
106
104
|
}
|
|
107
105
|
const jsonPath = path.join(dir, entry);
|
|
108
106
|
try {
|
|
109
|
-
const raw = await fs.readFile(jsonPath,
|
|
107
|
+
const raw = await fs.readFile(jsonPath, "utf8");
|
|
110
108
|
const parsed = JSON.parse(raw);
|
|
111
109
|
const normalized = ensureModelLogReference(sessionId, parsed);
|
|
112
110
|
result.push(normalized);
|
|
@@ -126,7 +124,7 @@ function ensureModelLogReference(sessionId, record) {
|
|
|
126
124
|
}
|
|
127
125
|
async function readModelRunFile(sessionId, model) {
|
|
128
126
|
try {
|
|
129
|
-
const raw = await fs.readFile(modelJsonPath(sessionId, model),
|
|
127
|
+
const raw = await fs.readFile(modelJsonPath(sessionId, model), "utf8");
|
|
130
128
|
const parsed = JSON.parse(raw);
|
|
131
129
|
return ensureModelLogReference(sessionId, parsed);
|
|
132
130
|
}
|
|
@@ -138,14 +136,14 @@ export async function updateModelRunMetadata(sessionId, model, updates) {
|
|
|
138
136
|
await ensureDir(modelsDir(sessionId));
|
|
139
137
|
const existing = (await readModelRunFile(sessionId, model)) ?? {
|
|
140
138
|
model,
|
|
141
|
-
status:
|
|
139
|
+
status: "pending",
|
|
142
140
|
};
|
|
143
141
|
const next = ensureModelLogReference(sessionId, {
|
|
144
142
|
...existing,
|
|
145
143
|
...updates,
|
|
146
144
|
model,
|
|
147
145
|
});
|
|
148
|
-
await fs.writeFile(modelJsonPath(sessionId, model), JSON.stringify(next, null, 2),
|
|
146
|
+
await fs.writeFile(modelJsonPath(sessionId, model), JSON.stringify(next, null, 2), "utf8");
|
|
149
147
|
return next;
|
|
150
148
|
}
|
|
151
149
|
export async function readModelRunMetadata(sessionId, model) {
|
|
@@ -157,7 +155,7 @@ export async function initializeSession(options, cwd, notifications, baseSlugOve
|
|
|
157
155
|
const sessionId = await ensureUniqueSessionId(baseSlug);
|
|
158
156
|
const dir = sessionDir(sessionId);
|
|
159
157
|
await ensureDir(dir);
|
|
160
|
-
const mode = options.mode ??
|
|
158
|
+
const mode = options.mode ?? "api";
|
|
161
159
|
const browserConfig = options.browserConfig;
|
|
162
160
|
const modelList = Array.isArray(options.models) && options.models.length > 0
|
|
163
161
|
? options.models
|
|
@@ -167,12 +165,12 @@ export async function initializeSession(options, cwd, notifications, baseSlugOve
|
|
|
167
165
|
const metadata = {
|
|
168
166
|
id: sessionId,
|
|
169
167
|
createdAt: new Date().toISOString(),
|
|
170
|
-
status:
|
|
171
|
-
promptPreview: (options.prompt ||
|
|
168
|
+
status: "pending",
|
|
169
|
+
promptPreview: (options.prompt || "").slice(0, 160),
|
|
172
170
|
model: modelList[0] ?? options.model,
|
|
173
171
|
models: modelList.map((modelName) => ({
|
|
174
172
|
model: modelName,
|
|
175
|
-
status:
|
|
173
|
+
status: "pending",
|
|
176
174
|
})),
|
|
177
175
|
cwd,
|
|
178
176
|
mode,
|
|
@@ -220,19 +218,19 @@ export async function initializeSession(options, cwd, notifications, baseSlugOve
|
|
|
220
218
|
},
|
|
221
219
|
};
|
|
222
220
|
await ensureDir(modelsDir(sessionId));
|
|
223
|
-
await fs.writeFile(metaPath(sessionId), JSON.stringify(metadata, null, 2),
|
|
221
|
+
await fs.writeFile(metaPath(sessionId), JSON.stringify(metadata, null, 2), "utf8");
|
|
224
222
|
await Promise.all((modelList.length > 0 ? modelList : [metadata.model ?? DEFAULT_MODEL]).map(async (modelName) => {
|
|
225
223
|
const jsonPath = modelJsonPath(sessionId, modelName);
|
|
226
224
|
const logFilePath = modelLogPath(sessionId, modelName);
|
|
227
225
|
const modelRecord = {
|
|
228
226
|
model: modelName,
|
|
229
|
-
status:
|
|
227
|
+
status: "pending",
|
|
230
228
|
log: { path: path.relative(sessionDir(sessionId), logFilePath) },
|
|
231
229
|
};
|
|
232
|
-
await fs.writeFile(jsonPath, JSON.stringify(modelRecord, null, 2),
|
|
233
|
-
await fs.writeFile(logFilePath,
|
|
230
|
+
await fs.writeFile(jsonPath, JSON.stringify(modelRecord, null, 2), "utf8");
|
|
231
|
+
await fs.writeFile(logFilePath, "", "utf8");
|
|
234
232
|
}));
|
|
235
|
-
await fs.writeFile(logPath(sessionId),
|
|
233
|
+
await fs.writeFile(logPath(sessionId), "", "utf8");
|
|
236
234
|
return metadata;
|
|
237
235
|
}
|
|
238
236
|
export async function readSessionMetadata(sessionId) {
|
|
@@ -251,12 +249,12 @@ export async function updateSessionMetadata(sessionId, updates) {
|
|
|
251
249
|
(await readLegacySessionMetadata(sessionId)) ??
|
|
252
250
|
{ id: sessionId };
|
|
253
251
|
const next = { ...existing, ...updates };
|
|
254
|
-
await fs.writeFile(metaPath(sessionId), JSON.stringify(next, null, 2),
|
|
252
|
+
await fs.writeFile(metaPath(sessionId), JSON.stringify(next, null, 2), "utf8");
|
|
255
253
|
return next;
|
|
256
254
|
}
|
|
257
255
|
async function readModernSessionMetadata(sessionId) {
|
|
258
256
|
try {
|
|
259
|
-
const raw = await fs.readFile(metaPath(sessionId),
|
|
257
|
+
const raw = await fs.readFile(metaPath(sessionId), "utf8");
|
|
260
258
|
const parsed = JSON.parse(raw);
|
|
261
259
|
if (!isSessionMetadataRecord(parsed)) {
|
|
262
260
|
return null;
|
|
@@ -271,7 +269,7 @@ async function readModernSessionMetadata(sessionId) {
|
|
|
271
269
|
}
|
|
272
270
|
async function readLegacySessionMetadata(sessionId) {
|
|
273
271
|
try {
|
|
274
|
-
const raw = await fs.readFile(legacySessionPath(sessionId),
|
|
272
|
+
const raw = await fs.readFile(legacySessionPath(sessionId), "utf8");
|
|
275
273
|
const parsed = JSON.parse(raw);
|
|
276
274
|
const enriched = await attachModelRuns(parsed, sessionId);
|
|
277
275
|
const runtimeChecked = await markDeadBrowser(enriched, { persist: false });
|
|
@@ -282,7 +280,7 @@ async function readLegacySessionMetadata(sessionId) {
|
|
|
282
280
|
}
|
|
283
281
|
}
|
|
284
282
|
function isSessionMetadataRecord(value) {
|
|
285
|
-
return Boolean(value && typeof value.id ===
|
|
283
|
+
return Boolean(value && typeof value.id === "string" && value.status);
|
|
286
284
|
}
|
|
287
285
|
async function attachModelRuns(meta, sessionId) {
|
|
288
286
|
const runs = await listModelRunFiles(sessionId);
|
|
@@ -296,8 +294,8 @@ export function createSessionLogWriter(sessionId, model) {
|
|
|
296
294
|
if (model) {
|
|
297
295
|
void ensureDir(modelsDir(sessionId));
|
|
298
296
|
}
|
|
299
|
-
const stream = createWriteStream(targetPath, { flags:
|
|
300
|
-
const logLine = (line =
|
|
297
|
+
const stream = createWriteStream(targetPath, { flags: "a" });
|
|
298
|
+
const logLine = (line = "") => {
|
|
301
299
|
stream.write(`${line}\n`);
|
|
302
300
|
};
|
|
303
301
|
const writeChunk = (chunk) => {
|
|
@@ -320,7 +318,7 @@ export async function listSessionsMetadata() {
|
|
|
320
318
|
}
|
|
321
319
|
return metas.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
322
320
|
}
|
|
323
|
-
export function filterSessionsByRange(metas, { hours = 24, includeAll = false, limit = 100 }) {
|
|
321
|
+
export function filterSessionsByRange(metas, { hours = 24, includeAll = false, limit = 100, }) {
|
|
324
322
|
const maxLimit = Math.min(limit, MAX_STATUS_LIMIT);
|
|
325
323
|
let filtered = metas;
|
|
326
324
|
if (!includeAll) {
|
|
@@ -335,29 +333,31 @@ export async function readSessionLog(sessionId) {
|
|
|
335
333
|
const runs = await listModelRunFiles(sessionId);
|
|
336
334
|
if (runs.length === 0) {
|
|
337
335
|
try {
|
|
338
|
-
return await fs.readFile(logPath(sessionId),
|
|
336
|
+
return await fs.readFile(logPath(sessionId), "utf8");
|
|
339
337
|
}
|
|
340
338
|
catch {
|
|
341
|
-
return
|
|
339
|
+
return "";
|
|
342
340
|
}
|
|
343
341
|
}
|
|
344
342
|
const sections = [];
|
|
345
343
|
let hasContent = false;
|
|
346
344
|
const ordered = runs
|
|
347
345
|
.slice()
|
|
348
|
-
.sort((a, b) =>
|
|
346
|
+
.sort((a, b) => a.startedAt && b.startedAt
|
|
347
|
+
? a.startedAt.localeCompare(b.startedAt)
|
|
348
|
+
: a.model.localeCompare(b.model));
|
|
349
349
|
for (const run of ordered) {
|
|
350
350
|
const logFile = run.log?.path
|
|
351
351
|
? path.isAbsolute(run.log.path)
|
|
352
352
|
? run.log.path
|
|
353
353
|
: path.join(sessionDir(sessionId), run.log.path)
|
|
354
354
|
: modelLogPath(sessionId, run.model);
|
|
355
|
-
let body =
|
|
355
|
+
let body = "";
|
|
356
356
|
try {
|
|
357
|
-
body = await fs.readFile(logFile,
|
|
357
|
+
body = await fs.readFile(logFile, "utf8");
|
|
358
358
|
}
|
|
359
359
|
catch {
|
|
360
|
-
body =
|
|
360
|
+
body = "";
|
|
361
361
|
}
|
|
362
362
|
if (body.length > 0) {
|
|
363
363
|
hasContent = true;
|
|
@@ -366,20 +366,20 @@ export async function readSessionLog(sessionId) {
|
|
|
366
366
|
}
|
|
367
367
|
if (!hasContent) {
|
|
368
368
|
try {
|
|
369
|
-
return await fs.readFile(logPath(sessionId),
|
|
369
|
+
return await fs.readFile(logPath(sessionId), "utf8");
|
|
370
370
|
}
|
|
371
371
|
catch {
|
|
372
372
|
// ignore and return structured header-only log
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
return sections.join(
|
|
375
|
+
return sections.join("\n\n");
|
|
376
376
|
}
|
|
377
377
|
export async function readModelLog(sessionId, model) {
|
|
378
378
|
try {
|
|
379
|
-
return await fs.readFile(modelLogPath(sessionId, model),
|
|
379
|
+
return await fs.readFile(modelLogPath(sessionId, model), "utf8");
|
|
380
380
|
}
|
|
381
381
|
catch {
|
|
382
|
-
return
|
|
382
|
+
return "";
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
export async function readSessionRequest(sessionId) {
|
|
@@ -388,7 +388,7 @@ export async function readSessionRequest(sessionId) {
|
|
|
388
388
|
return modern.options;
|
|
389
389
|
}
|
|
390
390
|
try {
|
|
391
|
-
const raw = await fs.readFile(requestPath(sessionId),
|
|
391
|
+
const raw = await fs.readFile(requestPath(sessionId), "utf8");
|
|
392
392
|
const parsed = JSON.parse(raw);
|
|
393
393
|
if (isSessionMetadataRecord(parsed)) {
|
|
394
394
|
return parsed.options ?? null;
|
|
@@ -452,7 +452,7 @@ export async function getSessionPaths(sessionId) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
if (missing.length > 0) {
|
|
455
|
-
throw new Error(`Session "${sessionId}" is missing: ${missing.join(
|
|
455
|
+
throw new Error(`Session "${sessionId}" is missing: ${missing.join(", ")}`);
|
|
456
456
|
}
|
|
457
457
|
return { dir, metadata, log, request };
|
|
458
458
|
}
|
|
@@ -460,7 +460,7 @@ async function markZombie(meta, { persist }) {
|
|
|
460
460
|
if (!(await isZombie(meta))) {
|
|
461
461
|
return meta;
|
|
462
462
|
}
|
|
463
|
-
if (meta.mode ===
|
|
463
|
+
if (meta.mode === "browser") {
|
|
464
464
|
const runtime = meta.browser?.runtime;
|
|
465
465
|
if (runtime) {
|
|
466
466
|
const signals = [];
|
|
@@ -468,7 +468,7 @@ async function markZombie(meta, { persist }) {
|
|
|
468
468
|
signals.push(isProcessAlive(runtime.chromePid));
|
|
469
469
|
}
|
|
470
470
|
if (runtime.chromePort) {
|
|
471
|
-
const host = runtime.chromeHost ??
|
|
471
|
+
const host = runtime.chromeHost ?? "127.0.0.1";
|
|
472
472
|
signals.push(await isPortOpen(host, runtime.chromePort));
|
|
473
473
|
}
|
|
474
474
|
if (signals.some(Boolean)) {
|
|
@@ -479,17 +479,17 @@ async function markZombie(meta, { persist }) {
|
|
|
479
479
|
const maxAgeMs = resolveZombieMaxAgeMs(meta);
|
|
480
480
|
const updated = {
|
|
481
481
|
...meta,
|
|
482
|
-
status:
|
|
482
|
+
status: "error",
|
|
483
483
|
errorMessage: `Session marked as zombie (> ${formatElapsed(maxAgeMs)} stale)`,
|
|
484
484
|
completedAt: new Date().toISOString(),
|
|
485
485
|
};
|
|
486
486
|
if (persist) {
|
|
487
|
-
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2),
|
|
487
|
+
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2), "utf8");
|
|
488
488
|
}
|
|
489
489
|
return updated;
|
|
490
490
|
}
|
|
491
491
|
async function markDeadBrowser(meta, { persist }) {
|
|
492
|
-
if (meta.status !==
|
|
492
|
+
if (meta.status !== "running" || meta.mode !== "browser") {
|
|
493
493
|
return meta;
|
|
494
494
|
}
|
|
495
495
|
const runtime = meta.browser?.runtime;
|
|
@@ -501,7 +501,7 @@ async function markDeadBrowser(meta, { persist }) {
|
|
|
501
501
|
signals.push(isProcessAlive(runtime.chromePid));
|
|
502
502
|
}
|
|
503
503
|
if (runtime.chromePort) {
|
|
504
|
-
const host = runtime.chromeHost ??
|
|
504
|
+
const host = runtime.chromeHost ?? "127.0.0.1";
|
|
505
505
|
signals.push(await isPortOpen(host, runtime.chromePort));
|
|
506
506
|
}
|
|
507
507
|
if (signals.length === 0 || signals.some(Boolean)) {
|
|
@@ -510,24 +510,24 @@ async function markDeadBrowser(meta, { persist }) {
|
|
|
510
510
|
const response = meta.response
|
|
511
511
|
? {
|
|
512
512
|
...meta.response,
|
|
513
|
-
status:
|
|
514
|
-
incompleteReason: meta.response.incompleteReason ??
|
|
513
|
+
status: "error",
|
|
514
|
+
incompleteReason: meta.response.incompleteReason ?? "chrome-disconnected",
|
|
515
515
|
}
|
|
516
|
-
: { status:
|
|
516
|
+
: { status: "error", incompleteReason: "chrome-disconnected" };
|
|
517
517
|
const updated = {
|
|
518
518
|
...meta,
|
|
519
|
-
status:
|
|
520
|
-
errorMessage:
|
|
519
|
+
status: "error",
|
|
520
|
+
errorMessage: "Browser session ended (Chrome is no longer reachable)",
|
|
521
521
|
completedAt: new Date().toISOString(),
|
|
522
522
|
response,
|
|
523
523
|
};
|
|
524
524
|
if (persist) {
|
|
525
|
-
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2),
|
|
525
|
+
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2), "utf8");
|
|
526
526
|
}
|
|
527
527
|
return updated;
|
|
528
528
|
}
|
|
529
529
|
async function isZombie(meta) {
|
|
530
|
-
if (meta.status !==
|
|
530
|
+
if (meta.status !== "running") {
|
|
531
531
|
return false;
|
|
532
532
|
}
|
|
533
533
|
const reference = meta.startedAt ?? meta.createdAt;
|
|
@@ -546,11 +546,13 @@ async function isZombie(meta) {
|
|
|
546
546
|
}
|
|
547
547
|
function resolveZombieMaxAgeMs(meta) {
|
|
548
548
|
const explicit = meta.options?.zombieTimeoutMs;
|
|
549
|
-
const hasExplicit = typeof explicit ===
|
|
549
|
+
const hasExplicit = typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0;
|
|
550
550
|
let maxAgeMs = hasExplicit ? explicit : ZOMBIE_MAX_AGE_MS;
|
|
551
551
|
if (!hasExplicit) {
|
|
552
552
|
const timeoutSeconds = meta.options?.timeoutSeconds;
|
|
553
|
-
if (typeof timeoutSeconds ===
|
|
553
|
+
if (typeof timeoutSeconds === "number" &&
|
|
554
|
+
Number.isFinite(timeoutSeconds) &&
|
|
555
|
+
timeoutSeconds > 0) {
|
|
554
556
|
const timeoutMs = timeoutSeconds * 1000;
|
|
555
557
|
if (timeoutMs > maxAgeMs) {
|
|
556
558
|
maxAgeMs = timeoutMs;
|
|
@@ -563,7 +565,7 @@ async function getLastActivityMs(meta) {
|
|
|
563
565
|
const candidates = new Set();
|
|
564
566
|
candidates.add(logPath(meta.id));
|
|
565
567
|
const modelNames = new Set();
|
|
566
|
-
if (typeof meta.model ===
|
|
568
|
+
if (typeof meta.model === "string" && meta.model.length > 0) {
|
|
567
569
|
modelNames.add(meta.model);
|
|
568
570
|
}
|
|
569
571
|
if (Array.isArray(meta.models)) {
|
|
@@ -602,10 +604,10 @@ function isProcessAlive(pid) {
|
|
|
602
604
|
}
|
|
603
605
|
catch (error) {
|
|
604
606
|
const code = error instanceof Error ? error.code : undefined;
|
|
605
|
-
if (code ===
|
|
607
|
+
if (code === "ESRCH" || code === "EINVAL") {
|
|
606
608
|
return false;
|
|
607
609
|
}
|
|
608
|
-
if (code ===
|
|
610
|
+
if (code === "EPERM") {
|
|
609
611
|
return true;
|
|
610
612
|
}
|
|
611
613
|
return true;
|
|
@@ -629,11 +631,11 @@ async function isPortOpen(host, port) {
|
|
|
629
631
|
resolve(result);
|
|
630
632
|
};
|
|
631
633
|
const timer = setTimeout(() => cleanup(false), CHROME_RUNTIME_TIMEOUT_MS);
|
|
632
|
-
socket.once(
|
|
634
|
+
socket.once("connect", () => {
|
|
633
635
|
clearTimeout(timer);
|
|
634
636
|
cleanup(true);
|
|
635
637
|
});
|
|
636
|
-
socket.once(
|
|
638
|
+
socket.once("error", () => {
|
|
637
639
|
clearTimeout(timer);
|
|
638
640
|
cleanup(false);
|
|
639
641
|
});
|
package/dist/src/sessionStore.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensureSessionStorage, initializeSession, readSessionMetadata, updateSessionMetadata, createSessionLogWriter, readSessionLog, readModelLog, readSessionRequest, listSessionsMetadata, filterSessionsByRange, deleteSessionsOlderThan, updateModelRunMetadata, getSessionPaths, getSessionsDir, } from
|
|
1
|
+
import { ensureSessionStorage, initializeSession, readSessionMetadata, updateSessionMetadata, createSessionLogWriter, readSessionLog, readModelLog, readSessionRequest, listSessionsMetadata, filterSessionsByRange, deleteSessionsOlderThan, updateModelRunMetadata, getSessionPaths, getSessionsDir, } from "./sessionManager.js";
|
|
2
2
|
class FileSessionStore {
|
|
3
3
|
ensureStorage() {
|
|
4
4
|
return ensureSessionStorage();
|
|
@@ -44,9 +44,9 @@ class FileSessionStore {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
export const sessionStore = new FileSessionStore();
|
|
47
|
-
export { wait } from
|
|
47
|
+
export { wait } from "./sessionManager.js";
|
|
48
48
|
export async function pruneOldSessions(hours, log) {
|
|
49
|
-
if (typeof hours !==
|
|
49
|
+
if (typeof hours !== "number" || Number.isNaN(hours) || hours <= 0) {
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
const result = await sessionStore.deleteOlderThan({ hours });
|
package/dist/src/version.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
4
|
let cachedVersion = null;
|
|
5
5
|
export function getCliVersion() {
|
|
6
6
|
if (cachedVersion) {
|
|
@@ -15,18 +15,18 @@ function readVersionFromPackage() {
|
|
|
15
15
|
const filesystemRoot = path.parse(currentDir).root;
|
|
16
16
|
// biome-ignore lint/nursery/noUnnecessaryConditions: deliberate sentinel loop to walk up directories
|
|
17
17
|
while (true) {
|
|
18
|
-
const candidate = path.join(currentDir,
|
|
18
|
+
const candidate = path.join(currentDir, "package.json");
|
|
19
19
|
try {
|
|
20
|
-
const raw = readFileSync(candidate,
|
|
20
|
+
const raw = readFileSync(candidate, "utf8");
|
|
21
21
|
const parsed = JSON.parse(raw);
|
|
22
|
-
const version = typeof parsed.version ===
|
|
22
|
+
const version = typeof parsed.version === "string" && parsed.version.trim().length > 0
|
|
23
23
|
? parsed.version.trim()
|
|
24
|
-
:
|
|
24
|
+
: "0.0.0";
|
|
25
25
|
return version;
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
|
-
const code = error instanceof Error &&
|
|
29
|
-
if (code && code !==
|
|
28
|
+
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
29
|
+
if (code && code !== "ENOENT") {
|
|
30
30
|
break;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -35,5 +35,5 @@ function readVersionFromPackage() {
|
|
|
35
35
|
}
|
|
36
36
|
currentDir = path.dirname(currentDir);
|
|
37
37
|
}
|
|
38
|
-
return
|
|
38
|
+
return "0.0.0";
|
|
39
39
|
}
|
|
@@ -18,7 +18,9 @@ export APP_STORE_CONNECT_ISSUER_ID=YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY
|
|
|
18
18
|
- Output: `OracleNotifier.app` (arm64 only), bundled with `OracleIcon.icns`.
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
|
+
|
|
21
22
|
The CLI prefers this helper on macOS; if it fails or is missing, it falls back to toasted-notifier/terminal-notifier.
|
|
22
23
|
|
|
23
24
|
## Permissions
|
|
25
|
+
|
|
24
26
|
After first run, allow notifications for “Oracle Notifier” in System Settings → Notifications.
|