@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.
Files changed (177) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +61 -48
  3. package/dist/bin/oracle-cli.js +455 -402
  4. package/dist/bin/oracle-mcp.js +2 -2
  5. package/dist/bin/oracle.js +165 -279
  6. package/dist/scripts/agent-send.js +31 -31
  7. package/dist/scripts/check.js +6 -6
  8. package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
  9. package/dist/scripts/docs-list.js +30 -30
  10. package/dist/scripts/git-policy.js +25 -23
  11. package/dist/scripts/run-cli.js +8 -8
  12. package/dist/scripts/runner.js +203 -195
  13. package/dist/scripts/test-browser.js +21 -18
  14. package/dist/scripts/test-remote-chrome.js +20 -20
  15. package/dist/src/bridge/connection.js +18 -18
  16. package/dist/src/bridge/userConfigFile.js +7 -7
  17. package/dist/src/browser/actions/assistantResponse.js +149 -101
  18. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  19. package/dist/src/browser/actions/attachments.js +246 -150
  20. package/dist/src/browser/actions/domEvents.js +2 -2
  21. package/dist/src/browser/actions/modelSelection.js +275 -117
  22. package/dist/src/browser/actions/navigation.js +161 -137
  23. package/dist/src/browser/actions/promptComposer.js +100 -64
  24. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  25. package/dist/src/browser/actions/thinkingTime.js +207 -110
  26. package/dist/src/browser/chromeLifecycle.js +62 -60
  27. package/dist/src/browser/config.js +34 -15
  28. package/dist/src/browser/constants.js +17 -12
  29. package/dist/src/browser/cookies.js +19 -19
  30. package/dist/src/browser/detect.js +62 -62
  31. package/dist/src/browser/domDebug.js +1 -1
  32. package/dist/src/browser/index.js +390 -295
  33. package/dist/src/browser/modelStrategy.js +1 -1
  34. package/dist/src/browser/pageActions.js +5 -5
  35. package/dist/src/browser/policies.js +16 -13
  36. package/dist/src/browser/profileState.js +44 -39
  37. package/dist/src/browser/prompt.js +72 -42
  38. package/dist/src/browser/promptSummary.js +5 -5
  39. package/dist/src/browser/providerDomFlow.js +1 -1
  40. package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
  41. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
  42. package/dist/src/browser/providers/index.js +2 -2
  43. package/dist/src/browser/reattach.js +67 -34
  44. package/dist/src/browser/reattachHelpers.js +31 -26
  45. package/dist/src/browser/sessionRunner.js +37 -25
  46. package/dist/src/browser/utils.js +9 -9
  47. package/dist/src/browserMode.js +1 -1
  48. package/dist/src/cli/bridge/claudeConfig.js +16 -16
  49. package/dist/src/cli/bridge/client.js +28 -20
  50. package/dist/src/cli/bridge/codexConfig.js +16 -16
  51. package/dist/src/cli/bridge/doctor.js +47 -39
  52. package/dist/src/cli/bridge/host.js +58 -56
  53. package/dist/src/cli/browserConfig.js +62 -48
  54. package/dist/src/cli/browserDefaults.js +27 -26
  55. package/dist/src/cli/bundleWarnings.js +1 -1
  56. package/dist/src/cli/clipboard.js +11 -2
  57. package/dist/src/cli/detach.js +2 -2
  58. package/dist/src/cli/dryRun.js +29 -25
  59. package/dist/src/cli/duplicatePromptGuard.js +3 -3
  60. package/dist/src/cli/engine.js +9 -9
  61. package/dist/src/cli/errorUtils.js +1 -1
  62. package/dist/src/cli/fileSize.js +3 -3
  63. package/dist/src/cli/format.js +2 -2
  64. package/dist/src/cli/help.js +28 -28
  65. package/dist/src/cli/hiddenAliases.js +3 -3
  66. package/dist/src/cli/markdownBundle.js +7 -7
  67. package/dist/src/cli/markdownRenderer.js +15 -15
  68. package/dist/src/cli/notifier.js +77 -67
  69. package/dist/src/cli/options.js +127 -106
  70. package/dist/src/cli/oscUtils.js +1 -1
  71. package/dist/src/cli/promptRequirement.js +2 -2
  72. package/dist/src/cli/renderOutput.js +1 -1
  73. package/dist/src/cli/rootAlias.js +1 -1
  74. package/dist/src/cli/runOptions.js +32 -28
  75. package/dist/src/cli/sessionCommand.js +31 -21
  76. package/dist/src/cli/sessionDisplay.js +95 -81
  77. package/dist/src/cli/sessionLineage.js +6 -2
  78. package/dist/src/cli/sessionRunner.js +103 -93
  79. package/dist/src/cli/sessionTable.js +26 -23
  80. package/dist/src/cli/stdin.js +22 -0
  81. package/dist/src/cli/tagline.js +121 -124
  82. package/dist/src/cli/tui/index.js +139 -128
  83. package/dist/src/cli/writeOutputPath.js +5 -5
  84. package/dist/src/config.js +7 -7
  85. package/dist/src/gemini-web/browserSessionManager.js +19 -15
  86. package/dist/src/gemini-web/client.js +76 -70
  87. package/dist/src/gemini-web/executionMode.js +6 -8
  88. package/dist/src/gemini-web/executor.js +98 -93
  89. package/dist/src/gemini-web/index.js +1 -1
  90. package/dist/src/mcp/server.js +16 -12
  91. package/dist/src/mcp/tools/consult.js +51 -47
  92. package/dist/src/mcp/tools/sessionResources.js +12 -12
  93. package/dist/src/mcp/tools/sessions.js +26 -17
  94. package/dist/src/mcp/types.js +5 -5
  95. package/dist/src/mcp/utils.js +15 -7
  96. package/dist/src/oracle/background.js +15 -15
  97. package/dist/src/oracle/claude.js +53 -25
  98. package/dist/src/oracle/client.js +50 -41
  99. package/dist/src/oracle/config.js +96 -66
  100. package/dist/src/oracle/errors.js +38 -38
  101. package/dist/src/oracle/files.js +55 -46
  102. package/dist/src/oracle/finishLine.js +10 -8
  103. package/dist/src/oracle/format.js +3 -3
  104. package/dist/src/oracle/gemini.js +37 -33
  105. package/dist/src/oracle/logging.js +7 -7
  106. package/dist/src/oracle/markdown.js +28 -28
  107. package/dist/src/oracle/modelResolver.js +16 -16
  108. package/dist/src/oracle/multiModelRunner.js +12 -12
  109. package/dist/src/oracle/oscProgress.js +8 -8
  110. package/dist/src/oracle/promptAssembly.js +6 -3
  111. package/dist/src/oracle/request.js +16 -13
  112. package/dist/src/oracle/run.js +156 -134
  113. package/dist/src/oracle/runUtils.js +8 -5
  114. package/dist/src/oracle/tokenEstimate.js +6 -6
  115. package/dist/src/oracle/tokenStats.js +5 -5
  116. package/dist/src/oracle/tokenStringifier.js +5 -5
  117. package/dist/src/oracle.js +12 -12
  118. package/dist/src/oracleHome.js +3 -3
  119. package/dist/src/remote/client.js +25 -25
  120. package/dist/src/remote/health.js +20 -20
  121. package/dist/src/remote/remoteServiceConfig.js +9 -9
  122. package/dist/src/remote/server.js +129 -118
  123. package/dist/src/sessionManager.js +77 -75
  124. package/dist/src/sessionStore.js +3 -3
  125. package/dist/src/version.js +10 -10
  126. package/dist/vendor/oracle-notifier/README.md +2 -0
  127. package/package.json +66 -62
  128. package/vendor/oracle-notifier/README.md +2 -0
  129. package/dist/markdansi/types/index.js +0 -4
  130. package/dist/oracle/bin/oracle-cli.js +0 -472
  131. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  132. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  133. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  134. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  135. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  136. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  137. package/dist/oracle/src/browser/config.js +0 -33
  138. package/dist/oracle/src/browser/constants.js +0 -40
  139. package/dist/oracle/src/browser/cookies.js +0 -210
  140. package/dist/oracle/src/browser/domDebug.js +0 -36
  141. package/dist/oracle/src/browser/index.js +0 -331
  142. package/dist/oracle/src/browser/pageActions.js +0 -5
  143. package/dist/oracle/src/browser/prompt.js +0 -88
  144. package/dist/oracle/src/browser/promptSummary.js +0 -20
  145. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  146. package/dist/oracle/src/browser/types.js +0 -1
  147. package/dist/oracle/src/browser/utils.js +0 -62
  148. package/dist/oracle/src/browserMode.js +0 -1
  149. package/dist/oracle/src/cli/browserConfig.js +0 -44
  150. package/dist/oracle/src/cli/dryRun.js +0 -59
  151. package/dist/oracle/src/cli/engine.js +0 -17
  152. package/dist/oracle/src/cli/errorUtils.js +0 -9
  153. package/dist/oracle/src/cli/help.js +0 -70
  154. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  155. package/dist/oracle/src/cli/options.js +0 -103
  156. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  157. package/dist/oracle/src/cli/rootAlias.js +0 -30
  158. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  159. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  160. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  161. package/dist/oracle/src/heartbeat.js +0 -43
  162. package/dist/oracle/src/oracle/client.js +0 -48
  163. package/dist/oracle/src/oracle/config.js +0 -29
  164. package/dist/oracle/src/oracle/errors.js +0 -101
  165. package/dist/oracle/src/oracle/files.js +0 -220
  166. package/dist/oracle/src/oracle/format.js +0 -33
  167. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  168. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  169. package/dist/oracle/src/oracle/request.js +0 -48
  170. package/dist/oracle/src/oracle/run.js +0 -444
  171. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  172. package/dist/oracle/src/oracle/types.js +0 -1
  173. package/dist/oracle/src/oracle.js +0 -9
  174. package/dist/oracle/src/sessionManager.js +0 -205
  175. package/dist/oracle/src/version.js +0 -39
  176. package/dist/scripts/chrome/browser-tools.js +0 -295
  177. package/dist/src/browser/profileSync.js +0 -141
@@ -1,23 +1,23 @@
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';
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(), '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';
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 = 'session';
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
- .slice(0, maxWords)
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('-').filter(Boolean).length;
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), 'output.log');
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, 'utf8');
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), 'utf8');
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: 'pending',
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), 'utf8');
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 ?? 'api';
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: 'pending',
171
- promptPreview: (options.prompt || '').slice(0, 160),
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: 'pending',
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), 'utf8');
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: 'pending',
227
+ status: "pending",
230
228
  log: { path: path.relative(sessionDir(sessionId), logFilePath) },
231
229
  };
232
- await fs.writeFile(jsonPath, JSON.stringify(modelRecord, null, 2), 'utf8');
233
- await fs.writeFile(logFilePath, '', 'utf8');
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), '', 'utf8');
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), 'utf8');
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), 'utf8');
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), 'utf8');
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 === 'string' && value.status);
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: 'a' });
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), 'utf8');
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) => (a.startedAt && b.startedAt ? a.startedAt.localeCompare(b.startedAt) : a.model.localeCompare(b.model)));
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, 'utf8');
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), 'utf8');
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('\n\n');
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), 'utf8');
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), 'utf8');
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 === 'browser') {
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 ?? '127.0.0.1';
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: 'error',
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), 'utf8');
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 !== 'running' || meta.mode !== 'browser') {
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 ?? '127.0.0.1';
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: 'error',
514
- incompleteReason: meta.response.incompleteReason ?? 'chrome-disconnected',
513
+ status: "error",
514
+ incompleteReason: meta.response.incompleteReason ?? "chrome-disconnected",
515
515
  }
516
- : { status: 'error', incompleteReason: 'chrome-disconnected' };
516
+ : { status: "error", incompleteReason: "chrome-disconnected" };
517
517
  const updated = {
518
518
  ...meta,
519
- status: 'error',
520
- errorMessage: 'Browser session ended (Chrome is no longer reachable)',
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), 'utf8');
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 !== 'running') {
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 === 'number' && Number.isFinite(explicit) && explicit > 0;
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 === 'number' && Number.isFinite(timeoutSeconds) && timeoutSeconds > 0) {
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 === 'string' && meta.model.length > 0) {
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 === 'ESRCH' || code === 'EINVAL') {
607
+ if (code === "ESRCH" || code === "EINVAL") {
606
608
  return false;
607
609
  }
608
- if (code === 'EPERM') {
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('connect', () => {
634
+ socket.once("connect", () => {
633
635
  clearTimeout(timer);
634
636
  cleanup(true);
635
637
  });
636
- socket.once('error', () => {
638
+ socket.once("error", () => {
637
639
  clearTimeout(timer);
638
640
  cleanup(false);
639
641
  });
@@ -1,4 +1,4 @@
1
- import { ensureSessionStorage, initializeSession, readSessionMetadata, updateSessionMetadata, createSessionLogWriter, readSessionLog, readModelLog, readSessionRequest, listSessionsMetadata, filterSessionsByRange, deleteSessionsOlderThan, updateModelRunMetadata, getSessionPaths, getSessionsDir, } from './sessionManager.js';
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 './sessionManager.js';
47
+ export { wait } from "./sessionManager.js";
48
48
  export async function pruneOldSessions(hours, log) {
49
- if (typeof hours !== 'number' || Number.isNaN(hours) || hours <= 0) {
49
+ if (typeof hours !== "number" || Number.isNaN(hours) || hours <= 0) {
50
50
  return;
51
51
  }
52
52
  const result = await sessionStore.deleteOlderThan({ hours });
@@ -1,6 +1,6 @@
1
- import { readFileSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
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, 'package.json');
18
+ const candidate = path.join(currentDir, "package.json");
19
19
  try {
20
- const raw = readFileSync(candidate, 'utf8');
20
+ const raw = readFileSync(candidate, "utf8");
21
21
  const parsed = JSON.parse(raw);
22
- const version = typeof parsed.version === 'string' && parsed.version.trim().length > 0
22
+ const version = typeof parsed.version === "string" && parsed.version.trim().length > 0
23
23
  ? parsed.version.trim()
24
- : '0.0.0';
24
+ : "0.0.0";
25
25
  return version;
26
26
  }
27
27
  catch (error) {
28
- const code = error instanceof Error && 'code' in error ? error.code : undefined;
29
- if (code && code !== 'ENOENT') {
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 '0.0.0';
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.