@steipete/oracle 0.9.0 → 0.11.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 +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- 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/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- 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 +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- 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 +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- 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 +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- 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 +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- 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 +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- 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 +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- 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/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- 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 +160 -135
- 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/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- 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 +78 -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 +67 -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/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
- /package/dist/{oracle/src/browser → src/projectSources}/types.js +0 -0
|
@@ -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,
|
|
@@ -215,24 +213,25 @@ export async function initializeSession(options, cwd, notifications, baseSlugOve
|
|
|
215
213
|
generateImage: options.generateImage,
|
|
216
214
|
editImage: options.editImage,
|
|
217
215
|
outputPath: options.outputPath,
|
|
216
|
+
browserFollowUps: options.browserFollowUps,
|
|
218
217
|
aspectRatio: options.aspectRatio,
|
|
219
218
|
geminiShowThoughts: options.geminiShowThoughts,
|
|
220
219
|
},
|
|
221
220
|
};
|
|
222
221
|
await ensureDir(modelsDir(sessionId));
|
|
223
|
-
await fs.writeFile(metaPath(sessionId), JSON.stringify(metadata, null, 2),
|
|
222
|
+
await fs.writeFile(metaPath(sessionId), JSON.stringify(metadata, null, 2), "utf8");
|
|
224
223
|
await Promise.all((modelList.length > 0 ? modelList : [metadata.model ?? DEFAULT_MODEL]).map(async (modelName) => {
|
|
225
224
|
const jsonPath = modelJsonPath(sessionId, modelName);
|
|
226
225
|
const logFilePath = modelLogPath(sessionId, modelName);
|
|
227
226
|
const modelRecord = {
|
|
228
227
|
model: modelName,
|
|
229
|
-
status:
|
|
228
|
+
status: "pending",
|
|
230
229
|
log: { path: path.relative(sessionDir(sessionId), logFilePath) },
|
|
231
230
|
};
|
|
232
|
-
await fs.writeFile(jsonPath, JSON.stringify(modelRecord, null, 2),
|
|
233
|
-
await fs.writeFile(logFilePath,
|
|
231
|
+
await fs.writeFile(jsonPath, JSON.stringify(modelRecord, null, 2), "utf8");
|
|
232
|
+
await fs.writeFile(logFilePath, "", "utf8");
|
|
234
233
|
}));
|
|
235
|
-
await fs.writeFile(logPath(sessionId),
|
|
234
|
+
await fs.writeFile(logPath(sessionId), "", "utf8");
|
|
236
235
|
return metadata;
|
|
237
236
|
}
|
|
238
237
|
export async function readSessionMetadata(sessionId) {
|
|
@@ -251,12 +250,12 @@ export async function updateSessionMetadata(sessionId, updates) {
|
|
|
251
250
|
(await readLegacySessionMetadata(sessionId)) ??
|
|
252
251
|
{ id: sessionId };
|
|
253
252
|
const next = { ...existing, ...updates };
|
|
254
|
-
await fs.writeFile(metaPath(sessionId), JSON.stringify(next, null, 2),
|
|
253
|
+
await fs.writeFile(metaPath(sessionId), JSON.stringify(next, null, 2), "utf8");
|
|
255
254
|
return next;
|
|
256
255
|
}
|
|
257
256
|
async function readModernSessionMetadata(sessionId) {
|
|
258
257
|
try {
|
|
259
|
-
const raw = await fs.readFile(metaPath(sessionId),
|
|
258
|
+
const raw = await fs.readFile(metaPath(sessionId), "utf8");
|
|
260
259
|
const parsed = JSON.parse(raw);
|
|
261
260
|
if (!isSessionMetadataRecord(parsed)) {
|
|
262
261
|
return null;
|
|
@@ -271,7 +270,7 @@ async function readModernSessionMetadata(sessionId) {
|
|
|
271
270
|
}
|
|
272
271
|
async function readLegacySessionMetadata(sessionId) {
|
|
273
272
|
try {
|
|
274
|
-
const raw = await fs.readFile(legacySessionPath(sessionId),
|
|
273
|
+
const raw = await fs.readFile(legacySessionPath(sessionId), "utf8");
|
|
275
274
|
const parsed = JSON.parse(raw);
|
|
276
275
|
const enriched = await attachModelRuns(parsed, sessionId);
|
|
277
276
|
const runtimeChecked = await markDeadBrowser(enriched, { persist: false });
|
|
@@ -282,7 +281,7 @@ async function readLegacySessionMetadata(sessionId) {
|
|
|
282
281
|
}
|
|
283
282
|
}
|
|
284
283
|
function isSessionMetadataRecord(value) {
|
|
285
|
-
return Boolean(value && typeof value.id ===
|
|
284
|
+
return Boolean(value && typeof value.id === "string" && value.status);
|
|
286
285
|
}
|
|
287
286
|
async function attachModelRuns(meta, sessionId) {
|
|
288
287
|
const runs = await listModelRunFiles(sessionId);
|
|
@@ -296,8 +295,8 @@ export function createSessionLogWriter(sessionId, model) {
|
|
|
296
295
|
if (model) {
|
|
297
296
|
void ensureDir(modelsDir(sessionId));
|
|
298
297
|
}
|
|
299
|
-
const stream = createWriteStream(targetPath, { flags:
|
|
300
|
-
const logLine = (line =
|
|
298
|
+
const stream = createWriteStream(targetPath, { flags: "a" });
|
|
299
|
+
const logLine = (line = "") => {
|
|
301
300
|
stream.write(`${line}\n`);
|
|
302
301
|
};
|
|
303
302
|
const writeChunk = (chunk) => {
|
|
@@ -320,7 +319,7 @@ export async function listSessionsMetadata() {
|
|
|
320
319
|
}
|
|
321
320
|
return metas.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
322
321
|
}
|
|
323
|
-
export function filterSessionsByRange(metas, { hours = 24, includeAll = false, limit = 100 }) {
|
|
322
|
+
export function filterSessionsByRange(metas, { hours = 24, includeAll = false, limit = 100, }) {
|
|
324
323
|
const maxLimit = Math.min(limit, MAX_STATUS_LIMIT);
|
|
325
324
|
let filtered = metas;
|
|
326
325
|
if (!includeAll) {
|
|
@@ -335,29 +334,31 @@ export async function readSessionLog(sessionId) {
|
|
|
335
334
|
const runs = await listModelRunFiles(sessionId);
|
|
336
335
|
if (runs.length === 0) {
|
|
337
336
|
try {
|
|
338
|
-
return await fs.readFile(logPath(sessionId),
|
|
337
|
+
return await fs.readFile(logPath(sessionId), "utf8");
|
|
339
338
|
}
|
|
340
339
|
catch {
|
|
341
|
-
return
|
|
340
|
+
return "";
|
|
342
341
|
}
|
|
343
342
|
}
|
|
344
343
|
const sections = [];
|
|
345
344
|
let hasContent = false;
|
|
346
345
|
const ordered = runs
|
|
347
346
|
.slice()
|
|
348
|
-
.sort((a, b) =>
|
|
347
|
+
.sort((a, b) => a.startedAt && b.startedAt
|
|
348
|
+
? a.startedAt.localeCompare(b.startedAt)
|
|
349
|
+
: a.model.localeCompare(b.model));
|
|
349
350
|
for (const run of ordered) {
|
|
350
351
|
const logFile = run.log?.path
|
|
351
352
|
? path.isAbsolute(run.log.path)
|
|
352
353
|
? run.log.path
|
|
353
354
|
: path.join(sessionDir(sessionId), run.log.path)
|
|
354
355
|
: modelLogPath(sessionId, run.model);
|
|
355
|
-
let body =
|
|
356
|
+
let body = "";
|
|
356
357
|
try {
|
|
357
|
-
body = await fs.readFile(logFile,
|
|
358
|
+
body = await fs.readFile(logFile, "utf8");
|
|
358
359
|
}
|
|
359
360
|
catch {
|
|
360
|
-
body =
|
|
361
|
+
body = "";
|
|
361
362
|
}
|
|
362
363
|
if (body.length > 0) {
|
|
363
364
|
hasContent = true;
|
|
@@ -366,20 +367,20 @@ export async function readSessionLog(sessionId) {
|
|
|
366
367
|
}
|
|
367
368
|
if (!hasContent) {
|
|
368
369
|
try {
|
|
369
|
-
return await fs.readFile(logPath(sessionId),
|
|
370
|
+
return await fs.readFile(logPath(sessionId), "utf8");
|
|
370
371
|
}
|
|
371
372
|
catch {
|
|
372
373
|
// ignore and return structured header-only log
|
|
373
374
|
}
|
|
374
375
|
}
|
|
375
|
-
return sections.join(
|
|
376
|
+
return sections.join("\n\n");
|
|
376
377
|
}
|
|
377
378
|
export async function readModelLog(sessionId, model) {
|
|
378
379
|
try {
|
|
379
|
-
return await fs.readFile(modelLogPath(sessionId, model),
|
|
380
|
+
return await fs.readFile(modelLogPath(sessionId, model), "utf8");
|
|
380
381
|
}
|
|
381
382
|
catch {
|
|
382
|
-
return
|
|
383
|
+
return "";
|
|
383
384
|
}
|
|
384
385
|
}
|
|
385
386
|
export async function readSessionRequest(sessionId) {
|
|
@@ -388,7 +389,7 @@ export async function readSessionRequest(sessionId) {
|
|
|
388
389
|
return modern.options;
|
|
389
390
|
}
|
|
390
391
|
try {
|
|
391
|
-
const raw = await fs.readFile(requestPath(sessionId),
|
|
392
|
+
const raw = await fs.readFile(requestPath(sessionId), "utf8");
|
|
392
393
|
const parsed = JSON.parse(raw);
|
|
393
394
|
if (isSessionMetadataRecord(parsed)) {
|
|
394
395
|
return parsed.options ?? null;
|
|
@@ -452,7 +453,7 @@ export async function getSessionPaths(sessionId) {
|
|
|
452
453
|
}
|
|
453
454
|
}
|
|
454
455
|
if (missing.length > 0) {
|
|
455
|
-
throw new Error(`Session "${sessionId}" is missing: ${missing.join(
|
|
456
|
+
throw new Error(`Session "${sessionId}" is missing: ${missing.join(", ")}`);
|
|
456
457
|
}
|
|
457
458
|
return { dir, metadata, log, request };
|
|
458
459
|
}
|
|
@@ -460,7 +461,7 @@ async function markZombie(meta, { persist }) {
|
|
|
460
461
|
if (!(await isZombie(meta))) {
|
|
461
462
|
return meta;
|
|
462
463
|
}
|
|
463
|
-
if (meta.mode ===
|
|
464
|
+
if (meta.mode === "browser") {
|
|
464
465
|
const runtime = meta.browser?.runtime;
|
|
465
466
|
if (runtime) {
|
|
466
467
|
const signals = [];
|
|
@@ -468,7 +469,7 @@ async function markZombie(meta, { persist }) {
|
|
|
468
469
|
signals.push(isProcessAlive(runtime.chromePid));
|
|
469
470
|
}
|
|
470
471
|
if (runtime.chromePort) {
|
|
471
|
-
const host = runtime.chromeHost ??
|
|
472
|
+
const host = runtime.chromeHost ?? "127.0.0.1";
|
|
472
473
|
signals.push(await isPortOpen(host, runtime.chromePort));
|
|
473
474
|
}
|
|
474
475
|
if (signals.some(Boolean)) {
|
|
@@ -479,17 +480,17 @@ async function markZombie(meta, { persist }) {
|
|
|
479
480
|
const maxAgeMs = resolveZombieMaxAgeMs(meta);
|
|
480
481
|
const updated = {
|
|
481
482
|
...meta,
|
|
482
|
-
status:
|
|
483
|
+
status: "error",
|
|
483
484
|
errorMessage: `Session marked as zombie (> ${formatElapsed(maxAgeMs)} stale)`,
|
|
484
485
|
completedAt: new Date().toISOString(),
|
|
485
486
|
};
|
|
486
487
|
if (persist) {
|
|
487
|
-
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2),
|
|
488
|
+
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2), "utf8");
|
|
488
489
|
}
|
|
489
490
|
return updated;
|
|
490
491
|
}
|
|
491
492
|
async function markDeadBrowser(meta, { persist }) {
|
|
492
|
-
if (meta.status !==
|
|
493
|
+
if (meta.status !== "running" || meta.mode !== "browser") {
|
|
493
494
|
return meta;
|
|
494
495
|
}
|
|
495
496
|
const runtime = meta.browser?.runtime;
|
|
@@ -501,7 +502,7 @@ async function markDeadBrowser(meta, { persist }) {
|
|
|
501
502
|
signals.push(isProcessAlive(runtime.chromePid));
|
|
502
503
|
}
|
|
503
504
|
if (runtime.chromePort) {
|
|
504
|
-
const host = runtime.chromeHost ??
|
|
505
|
+
const host = runtime.chromeHost ?? "127.0.0.1";
|
|
505
506
|
signals.push(await isPortOpen(host, runtime.chromePort));
|
|
506
507
|
}
|
|
507
508
|
if (signals.length === 0 || signals.some(Boolean)) {
|
|
@@ -510,24 +511,24 @@ async function markDeadBrowser(meta, { persist }) {
|
|
|
510
511
|
const response = meta.response
|
|
511
512
|
? {
|
|
512
513
|
...meta.response,
|
|
513
|
-
status:
|
|
514
|
-
incompleteReason: meta.response.incompleteReason ??
|
|
514
|
+
status: "error",
|
|
515
|
+
incompleteReason: meta.response.incompleteReason ?? "chrome-disconnected",
|
|
515
516
|
}
|
|
516
|
-
: { status:
|
|
517
|
+
: { status: "error", incompleteReason: "chrome-disconnected" };
|
|
517
518
|
const updated = {
|
|
518
519
|
...meta,
|
|
519
|
-
status:
|
|
520
|
-
errorMessage:
|
|
520
|
+
status: "error",
|
|
521
|
+
errorMessage: "Browser session ended (Chrome is no longer reachable)",
|
|
521
522
|
completedAt: new Date().toISOString(),
|
|
522
523
|
response,
|
|
523
524
|
};
|
|
524
525
|
if (persist) {
|
|
525
|
-
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2),
|
|
526
|
+
await fs.writeFile(metaPath(meta.id), JSON.stringify(updated, null, 2), "utf8");
|
|
526
527
|
}
|
|
527
528
|
return updated;
|
|
528
529
|
}
|
|
529
530
|
async function isZombie(meta) {
|
|
530
|
-
if (meta.status !==
|
|
531
|
+
if (meta.status !== "running") {
|
|
531
532
|
return false;
|
|
532
533
|
}
|
|
533
534
|
const reference = meta.startedAt ?? meta.createdAt;
|
|
@@ -546,11 +547,13 @@ async function isZombie(meta) {
|
|
|
546
547
|
}
|
|
547
548
|
function resolveZombieMaxAgeMs(meta) {
|
|
548
549
|
const explicit = meta.options?.zombieTimeoutMs;
|
|
549
|
-
const hasExplicit = typeof explicit ===
|
|
550
|
+
const hasExplicit = typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0;
|
|
550
551
|
let maxAgeMs = hasExplicit ? explicit : ZOMBIE_MAX_AGE_MS;
|
|
551
552
|
if (!hasExplicit) {
|
|
552
553
|
const timeoutSeconds = meta.options?.timeoutSeconds;
|
|
553
|
-
if (typeof timeoutSeconds ===
|
|
554
|
+
if (typeof timeoutSeconds === "number" &&
|
|
555
|
+
Number.isFinite(timeoutSeconds) &&
|
|
556
|
+
timeoutSeconds > 0) {
|
|
554
557
|
const timeoutMs = timeoutSeconds * 1000;
|
|
555
558
|
if (timeoutMs > maxAgeMs) {
|
|
556
559
|
maxAgeMs = timeoutMs;
|
|
@@ -563,7 +566,7 @@ async function getLastActivityMs(meta) {
|
|
|
563
566
|
const candidates = new Set();
|
|
564
567
|
candidates.add(logPath(meta.id));
|
|
565
568
|
const modelNames = new Set();
|
|
566
|
-
if (typeof meta.model ===
|
|
569
|
+
if (typeof meta.model === "string" && meta.model.length > 0) {
|
|
567
570
|
modelNames.add(meta.model);
|
|
568
571
|
}
|
|
569
572
|
if (Array.isArray(meta.models)) {
|
|
@@ -602,10 +605,10 @@ function isProcessAlive(pid) {
|
|
|
602
605
|
}
|
|
603
606
|
catch (error) {
|
|
604
607
|
const code = error instanceof Error ? error.code : undefined;
|
|
605
|
-
if (code ===
|
|
608
|
+
if (code === "ESRCH" || code === "EINVAL") {
|
|
606
609
|
return false;
|
|
607
610
|
}
|
|
608
|
-
if (code ===
|
|
611
|
+
if (code === "EPERM") {
|
|
609
612
|
return true;
|
|
610
613
|
}
|
|
611
614
|
return true;
|
|
@@ -629,11 +632,11 @@ async function isPortOpen(host, port) {
|
|
|
629
632
|
resolve(result);
|
|
630
633
|
};
|
|
631
634
|
const timer = setTimeout(() => cleanup(false), CHROME_RUNTIME_TIMEOUT_MS);
|
|
632
|
-
socket.once(
|
|
635
|
+
socket.once("connect", () => {
|
|
633
636
|
clearTimeout(timer);
|
|
634
637
|
cleanup(true);
|
|
635
638
|
});
|
|
636
|
-
socket.once(
|
|
639
|
+
socket.once("error", () => {
|
|
637
640
|
clearTimeout(timer);
|
|
638
641
|
cleanup(false);
|
|
639
642
|
});
|
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.
|