@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.
Files changed (194) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +107 -49
  3. package/dist/bin/oracle-cli.js +551 -410
  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/archiveConversation.js +224 -0
  18. package/dist/src/browser/actions/assistantResponse.js +175 -101
  19. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  20. package/dist/src/browser/actions/attachments.js +246 -150
  21. package/dist/src/browser/actions/deepResearch.js +662 -0
  22. package/dist/src/browser/actions/domEvents.js +2 -2
  23. package/dist/src/browser/actions/modelSelection.js +342 -119
  24. package/dist/src/browser/actions/navigation.js +183 -137
  25. package/dist/src/browser/actions/projectSources.js +491 -0
  26. package/dist/src/browser/actions/promptComposer.js +152 -91
  27. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  28. package/dist/src/browser/actions/thinkingStatus.js +391 -0
  29. package/dist/src/browser/actions/thinkingTime.js +207 -110
  30. package/dist/src/browser/artifacts.js +150 -0
  31. package/dist/src/browser/attachRunning.js +31 -0
  32. package/dist/src/browser/chatgptImages.js +315 -0
  33. package/dist/src/browser/chromeLifecycle.js +276 -63
  34. package/dist/src/browser/config.js +59 -16
  35. package/dist/src/browser/constants.js +25 -12
  36. package/dist/src/browser/controlPlan.js +81 -0
  37. package/dist/src/browser/cookies.js +19 -19
  38. package/dist/src/browser/detect.js +250 -77
  39. package/dist/src/browser/domDebug.js +50 -1
  40. package/dist/src/browser/index.js +1559 -692
  41. package/dist/src/browser/liveTabs.js +434 -0
  42. package/dist/src/browser/modelStrategy.js +1 -1
  43. package/dist/src/browser/pageActions.js +5 -5
  44. package/dist/src/browser/policies.js +16 -13
  45. package/dist/src/browser/profileState.js +127 -42
  46. package/dist/src/browser/projectSourcesRunner.js +366 -0
  47. package/dist/src/browser/prompt.js +72 -42
  48. package/dist/src/browser/promptSummary.js +5 -5
  49. package/dist/src/browser/providerDomFlow.js +1 -1
  50. package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
  51. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
  52. package/dist/src/browser/providers/index.js +2 -2
  53. package/dist/src/browser/reattach.js +178 -73
  54. package/dist/src/browser/reattachHelpers.js +32 -27
  55. package/dist/src/browser/sessionRunner.js +89 -25
  56. package/dist/src/browser/tabLeaseRegistry.js +182 -0
  57. package/dist/src/browser/utils.js +9 -9
  58. package/dist/src/browserMode.js +1 -1
  59. package/dist/src/cli/bridge/claudeConfig.js +24 -20
  60. package/dist/src/cli/bridge/client.js +28 -20
  61. package/dist/src/cli/bridge/codexConfig.js +16 -16
  62. package/dist/src/cli/bridge/doctor.js +47 -39
  63. package/dist/src/cli/bridge/host.js +58 -56
  64. package/dist/src/cli/browserConfig.js +102 -48
  65. package/dist/src/cli/browserDefaults.js +51 -26
  66. package/dist/src/cli/browserTabs.js +228 -0
  67. package/dist/src/cli/bundleWarnings.js +1 -1
  68. package/dist/src/cli/clipboard.js +11 -2
  69. package/dist/src/cli/detach.js +2 -2
  70. package/dist/src/cli/dryRun.js +62 -26
  71. package/dist/src/cli/duplicatePromptGuard.js +12 -4
  72. package/dist/src/cli/engine.js +9 -9
  73. package/dist/src/cli/errorUtils.js +1 -1
  74. package/dist/src/cli/fileSize.js +3 -3
  75. package/dist/src/cli/format.js +2 -2
  76. package/dist/src/cli/help.js +28 -28
  77. package/dist/src/cli/hiddenAliases.js +3 -3
  78. package/dist/src/cli/markdownBundle.js +7 -7
  79. package/dist/src/cli/markdownRenderer.js +15 -15
  80. package/dist/src/cli/notifier.js +77 -67
  81. package/dist/src/cli/options.js +131 -106
  82. package/dist/src/cli/oscUtils.js +1 -1
  83. package/dist/src/cli/projectSources.js +116 -0
  84. package/dist/src/cli/promptRequirement.js +2 -2
  85. package/dist/src/cli/renderOutput.js +1 -1
  86. package/dist/src/cli/rootAlias.js +1 -1
  87. package/dist/src/cli/runOptions.js +32 -28
  88. package/dist/src/cli/sessionCommand.js +82 -21
  89. package/dist/src/cli/sessionDisplay.js +213 -87
  90. package/dist/src/cli/sessionLineage.js +6 -2
  91. package/dist/src/cli/sessionRunner.js +149 -95
  92. package/dist/src/cli/sessionTable.js +26 -23
  93. package/dist/src/cli/stdin.js +22 -0
  94. package/dist/src/cli/tagline.js +121 -124
  95. package/dist/src/cli/tui/index.js +139 -128
  96. package/dist/src/cli/writeOutputPath.js +5 -5
  97. package/dist/src/config.js +7 -7
  98. package/dist/src/gemini-web/browserSessionManager.js +19 -15
  99. package/dist/src/gemini-web/client.js +76 -70
  100. package/dist/src/gemini-web/executionMode.js +6 -8
  101. package/dist/src/gemini-web/executor.js +98 -93
  102. package/dist/src/gemini-web/index.js +1 -1
  103. package/dist/src/mcp/consultPresets.js +19 -0
  104. package/dist/src/mcp/server.js +18 -12
  105. package/dist/src/mcp/tools/consult.js +246 -67
  106. package/dist/src/mcp/tools/projectSources.js +123 -0
  107. package/dist/src/mcp/tools/sessionResources.js +12 -12
  108. package/dist/src/mcp/tools/sessions.js +26 -17
  109. package/dist/src/mcp/types.js +12 -5
  110. package/dist/src/mcp/utils.js +21 -8
  111. package/dist/src/oracle/background.js +15 -15
  112. package/dist/src/oracle/claude.js +53 -25
  113. package/dist/src/oracle/client.js +50 -41
  114. package/dist/src/oracle/config.js +96 -66
  115. package/dist/src/oracle/errors.js +38 -38
  116. package/dist/src/oracle/files.js +55 -46
  117. package/dist/src/oracle/finishLine.js +10 -8
  118. package/dist/src/oracle/format.js +3 -3
  119. package/dist/src/oracle/gemini.js +37 -33
  120. package/dist/src/oracle/logging.js +7 -7
  121. package/dist/src/oracle/markdown.js +28 -28
  122. package/dist/src/oracle/modelResolver.js +16 -16
  123. package/dist/src/oracle/multiModelRunner.js +12 -12
  124. package/dist/src/oracle/oscProgress.js +8 -8
  125. package/dist/src/oracle/promptAssembly.js +6 -3
  126. package/dist/src/oracle/request.js +16 -13
  127. package/dist/src/oracle/run.js +160 -135
  128. package/dist/src/oracle/runUtils.js +8 -5
  129. package/dist/src/oracle/tokenEstimate.js +6 -6
  130. package/dist/src/oracle/tokenStats.js +5 -5
  131. package/dist/src/oracle/tokenStringifier.js +5 -5
  132. package/dist/src/oracle.js +12 -12
  133. package/dist/src/oracleHome.js +3 -3
  134. package/dist/src/projectSources/plan.js +27 -0
  135. package/dist/src/projectSources/url.js +23 -0
  136. package/dist/src/remote/client.js +25 -25
  137. package/dist/src/remote/health.js +20 -20
  138. package/dist/src/remote/remoteServiceConfig.js +9 -9
  139. package/dist/src/remote/server.js +129 -118
  140. package/dist/src/sessionManager.js +78 -75
  141. package/dist/src/sessionStore.js +3 -3
  142. package/dist/src/version.js +10 -10
  143. package/dist/vendor/oracle-notifier/README.md +2 -0
  144. package/package.json +67 -62
  145. package/vendor/oracle-notifier/README.md +2 -0
  146. package/dist/markdansi/types/index.js +0 -4
  147. package/dist/oracle/bin/oracle-cli.js +0 -472
  148. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  149. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  150. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  151. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  152. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  153. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  154. package/dist/oracle/src/browser/config.js +0 -33
  155. package/dist/oracle/src/browser/constants.js +0 -40
  156. package/dist/oracle/src/browser/cookies.js +0 -210
  157. package/dist/oracle/src/browser/domDebug.js +0 -36
  158. package/dist/oracle/src/browser/index.js +0 -331
  159. package/dist/oracle/src/browser/pageActions.js +0 -5
  160. package/dist/oracle/src/browser/prompt.js +0 -88
  161. package/dist/oracle/src/browser/promptSummary.js +0 -20
  162. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  163. package/dist/oracle/src/browser/utils.js +0 -62
  164. package/dist/oracle/src/browserMode.js +0 -1
  165. package/dist/oracle/src/cli/browserConfig.js +0 -44
  166. package/dist/oracle/src/cli/dryRun.js +0 -59
  167. package/dist/oracle/src/cli/engine.js +0 -17
  168. package/dist/oracle/src/cli/errorUtils.js +0 -9
  169. package/dist/oracle/src/cli/help.js +0 -70
  170. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  171. package/dist/oracle/src/cli/options.js +0 -103
  172. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  173. package/dist/oracle/src/cli/rootAlias.js +0 -30
  174. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  175. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  176. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  177. package/dist/oracle/src/heartbeat.js +0 -43
  178. package/dist/oracle/src/oracle/client.js +0 -48
  179. package/dist/oracle/src/oracle/config.js +0 -29
  180. package/dist/oracle/src/oracle/errors.js +0 -101
  181. package/dist/oracle/src/oracle/files.js +0 -220
  182. package/dist/oracle/src/oracle/format.js +0 -33
  183. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  184. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  185. package/dist/oracle/src/oracle/request.js +0 -48
  186. package/dist/oracle/src/oracle/run.js +0 -444
  187. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  188. package/dist/oracle/src/oracle/types.js +0 -1
  189. package/dist/oracle/src/oracle.js +0 -9
  190. package/dist/oracle/src/sessionManager.js +0 -205
  191. package/dist/oracle/src/version.js +0 -39
  192. package/dist/scripts/chrome/browser-tools.js +0 -295
  193. package/dist/src/browser/profileSync.js +0 -141
  194. /package/dist/{oracle/src/browser → src/projectSources}/types.js +0 -0
@@ -1,4 +1,4 @@
1
- import { TOKENIZER_OPTIONS } from './config.js';
1
+ import { TOKENIZER_OPTIONS } from "./config.js";
2
2
  /**
3
3
  * Estimate input tokens from the full request body instead of just system/user text.
4
4
  * This is a conservative approximation: we tokenize the key textual fields and add a fixed buffer
@@ -12,7 +12,7 @@ export function estimateRequestTokens(requestBody, modelConfig, bufferTokens = 2
12
12
  }
13
13
  for (const turn of requestBody.input ?? []) {
14
14
  for (const content of turn.content ?? []) {
15
- if (typeof content.text === 'string') {
15
+ if (typeof content.text === "string") {
16
16
  parts.push(content.text);
17
17
  }
18
18
  }
@@ -24,14 +24,14 @@ export function estimateRequestTokens(requestBody, modelConfig, bufferTokens = 2
24
24
  parts.push(JSON.stringify(requestBody.reasoning));
25
25
  }
26
26
  if (requestBody.background) {
27
- parts.push('background:true');
27
+ parts.push("background:true");
28
28
  }
29
29
  if (requestBody.store) {
30
- parts.push('store:true');
30
+ parts.push("store:true");
31
31
  }
32
- const concatenated = parts.join('\n');
32
+ const concatenated = parts.join("\n");
33
33
  const baseEstimate = modelConfig.tokenizer(concatenated, TOKENIZER_OPTIONS);
34
- const hasWebSearch = requestBody.tools?.some((tool) => tool?.type === 'web_search_preview');
34
+ const hasWebSearch = requestBody.tools?.some((tool) => tool?.type === "web_search_preview");
35
35
  const searchBuffer = hasWebSearch ? SEARCH_RESULT_BUFFER_TOKENS : 0;
36
36
  return baseEstimate + bufferTokens + searchBuffer;
37
37
  }
@@ -1,5 +1,5 @@
1
- import chalk from 'chalk';
2
- import { createFileSections } from './files.js';
1
+ import chalk from "chalk";
2
+ import { createFileSections } from "./files.js";
3
3
  export function getFileTokenStats(files, { cwd = process.cwd(), tokenizer, tokenizerOptions, inputTokenBudget, }) {
4
4
  if (!files.length) {
5
5
  return { stats: [], totalTokens: 0 };
@@ -20,13 +20,13 @@ export function getFileTokenStats(files, { cwd = process.cwd(), tokenizer, token
20
20
  const totalTokens = stats.reduce((sum, entry) => sum + entry.tokens, 0);
21
21
  return { stats, totalTokens };
22
22
  }
23
- export function printFileTokenStats({ stats, totalTokens }, { inputTokenBudget, log = console.log }) {
23
+ export function printFileTokenStats({ stats, totalTokens }, { inputTokenBudget, log = console.log, }) {
24
24
  if (!stats.length) {
25
25
  return;
26
26
  }
27
- log(chalk.bold('File Token Usage'));
27
+ log(chalk.bold("File Token Usage"));
28
28
  for (const entry of stats) {
29
- const percentLabel = inputTokenBudget && entry.percent != null ? `${entry.percent.toFixed(2)}%` : 'n/a';
29
+ const percentLabel = inputTokenBudget && entry.percent != null ? `${entry.percent.toFixed(2)}%` : "n/a";
30
30
  log(`${entry.tokens.toLocaleString().padStart(10)} ${percentLabel.padStart(8)} ${entry.displayPath}`);
31
31
  }
32
32
  if (inputTokenBudget) {
@@ -1,14 +1,14 @@
1
1
  // Minimal helper to stringify arbitrary input for tokenizer consumption.
2
2
  // Anthropic's tokenizer expects a string; we accept unknown and coerce safely.
3
3
  export function stringifyTokenizerInput(input) {
4
- if (typeof input === 'string')
4
+ if (typeof input === "string")
5
5
  return input;
6
6
  if (input === null || input === undefined)
7
- return '';
8
- if (typeof input === 'number' || typeof input === 'boolean' || typeof input === 'bigint') {
7
+ return "";
8
+ if (typeof input === "number" || typeof input === "boolean" || typeof input === "bigint") {
9
9
  return String(input);
10
10
  }
11
- if (typeof input === 'object') {
11
+ if (typeof input === "object") {
12
12
  try {
13
13
  return JSON.stringify(input);
14
14
  }
@@ -16,7 +16,7 @@ export function stringifyTokenizerInput(input) {
16
16
  // fall through to generic stringification
17
17
  }
18
18
  }
19
- if (typeof input === 'function') {
19
+ if (typeof input === "function") {
20
20
  return input.toString();
21
21
  }
22
22
  return String(input);
@@ -1,12 +1,12 @@
1
- export * from './oracle/types.js';
2
- export { MODEL_CONFIGS, DEFAULT_MODEL, PRO_MODELS, DEFAULT_SYSTEM_PROMPT, TOKENIZER_OPTIONS, } from './oracle/config.js';
3
- export { readFiles, createFileSections } from './oracle/files.js';
4
- export { buildPrompt, buildRequestBody, renderPromptMarkdown } from './oracle/request.js';
5
- export { estimateRequestTokens } from './oracle/tokenEstimate.js';
6
- export { formatUSD, formatNumber, formatElapsed } from './oracle/format.js';
7
- export { formatFileSection } from './oracle/markdown.js';
8
- export { getFileTokenStats, printFileTokenStats } from './oracle/tokenStats.js';
9
- export { OracleResponseError, OracleTransportError, OracleUserError, FileValidationError, BrowserAutomationError, PromptValidationError, describeTransportError, extractResponseMetadata, asOracleUserError, toTransportError, } from './oracle/errors.js';
10
- export { createDefaultClientFactory } from './oracle/client.js';
11
- export { runOracle, extractTextOutput } from './oracle/run.js';
12
- export { resolveGeminiModelId } from './oracle/gemini.js';
1
+ export * from "./oracle/types.js";
2
+ export { MODEL_CONFIGS, DEFAULT_MODEL, PRO_MODELS, DEFAULT_SYSTEM_PROMPT, TOKENIZER_OPTIONS, } from "./oracle/config.js";
3
+ export { readFiles, createFileSections } from "./oracle/files.js";
4
+ export { buildPrompt, buildRequestBody, renderPromptMarkdown } from "./oracle/request.js";
5
+ export { estimateRequestTokens } from "./oracle/tokenEstimate.js";
6
+ export { formatUSD, formatNumber, formatElapsed } from "./oracle/format.js";
7
+ export { formatFileSection } from "./oracle/markdown.js";
8
+ export { getFileTokenStats, printFileTokenStats } from "./oracle/tokenStats.js";
9
+ export { OracleResponseError, OracleTransportError, OracleUserError, FileValidationError, BrowserAutomationError, PromptValidationError, describeTransportError, extractResponseMetadata, asOracleUserError, toTransportError, } from "./oracle/errors.js";
10
+ export { createDefaultClientFactory } from "./oracle/client.js";
11
+ export { runOracle, extractTextOutput } from "./oracle/run.js";
12
+ export { resolveGeminiModelId } from "./oracle/gemini.js";
@@ -1,5 +1,5 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
1
+ import os from "node:os";
2
+ import path from "node:path";
3
3
  let oracleHomeDirOverride = null;
4
4
  /**
5
5
  * Test-only hook: avoid mutating process.env (shared across Vitest worker threads).
@@ -9,5 +9,5 @@ export function setOracleHomeDirOverrideForTest(dir) {
9
9
  oracleHomeDirOverride = dir;
10
10
  }
11
11
  export function getOracleHomeDir() {
12
- return oracleHomeDirOverride ?? process.env.ORACLE_HOME_DIR ?? path.join(os.homedir(), '.oracle');
12
+ return oracleHomeDirOverride ?? process.env.ORACLE_HOME_DIR ?? path.join(os.homedir(), ".oracle");
13
13
  }
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ export const PROJECT_SOURCES_MAX_UPLOAD_BATCH = 10;
3
+ export function buildProjectSourcesUploadPlan(files) {
4
+ return files.map((file, index) => ({
5
+ path: file.path,
6
+ displayPath: file.displayPath,
7
+ name: path.basename(file.path),
8
+ sizeBytes: file.sizeBytes,
9
+ batch: Math.floor(index / PROJECT_SOURCES_MAX_UPLOAD_BATCH) + 1,
10
+ }));
11
+ }
12
+ export function diffAddedProjectSources(before, after) {
13
+ const remainingBefore = new Map();
14
+ for (const source of before) {
15
+ remainingBefore.set(source.name, (remainingBefore.get(source.name) ?? 0) + 1);
16
+ }
17
+ const added = [];
18
+ for (const source of after) {
19
+ const count = remainingBefore.get(source.name) ?? 0;
20
+ if (count > 0) {
21
+ remainingBefore.set(source.name, count - 1);
22
+ continue;
23
+ }
24
+ added.push(source);
25
+ }
26
+ return added;
27
+ }
@@ -0,0 +1,23 @@
1
+ export function normalizeProjectSourcesUrl(rawUrl) {
2
+ let url;
3
+ try {
4
+ url = new URL(rawUrl);
5
+ }
6
+ catch (error) {
7
+ throw new Error(`Invalid ChatGPT project URL: ${rawUrl} (${error instanceof Error ? error.message : String(error)})`);
8
+ }
9
+ const hostname = url.hostname.toLowerCase();
10
+ if (hostname !== "chatgpt.com" && hostname !== "chat.openai.com") {
11
+ throw new Error(`Project Sources require a ChatGPT URL, received: ${rawUrl}`);
12
+ }
13
+ if (!/\/project\/?$/u.test(url.pathname)) {
14
+ throw new Error(`Project Sources require a ChatGPT project URL ending in /project, received: ${rawUrl}`);
15
+ }
16
+ const existingParams = Array.from(url.searchParams.entries()).filter(([key]) => key !== "tab");
17
+ url.search = "";
18
+ url.searchParams.set("tab", "sources");
19
+ for (const [key, value] of existingParams) {
20
+ url.searchParams.append(key, value);
21
+ }
22
+ return url.toString();
23
+ }
@@ -1,7 +1,7 @@
1
- import http from 'node:http';
2
- import path from 'node:path';
3
- import { readFile } from 'node:fs/promises';
4
- import { parseHostPort } from '../bridge/connection.js';
1
+ import http from "node:http";
2
+ import path from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { parseHostPort } from "../bridge/connection.js";
5
5
  export function createRemoteBrowserExecutor({ host, token }) {
6
6
  // Return a drop-in replacement for runBrowserMode so the browser session runner can stay unchanged.
7
7
  return async function remoteBrowserExecutor(options) {
@@ -20,11 +20,11 @@ export function createRemoteBrowserExecutor({ host, token }) {
20
20
  const req = http.request({
21
21
  hostname,
22
22
  port,
23
- path: '/runs',
24
- method: 'POST',
23
+ path: "/runs",
24
+ method: "POST",
25
25
  headers: {
26
- 'Content-Type': 'application/json',
27
- 'Content-Length': body.length,
26
+ "Content-Type": "application/json",
27
+ "Content-Length": body.length,
28
28
  ...(token ? { authorization: `Bearer ${token}` } : {}),
29
29
  },
30
30
  }, (res) => {
@@ -34,12 +34,12 @@ export function createRemoteBrowserExecutor({ host, token }) {
34
34
  .catch(reject);
35
35
  return;
36
36
  }
37
- res.setEncoding('utf8');
38
- let buffer = '';
37
+ res.setEncoding("utf8");
38
+ let buffer = "";
39
39
  let resolved = null;
40
- res.on('data', (chunk) => {
40
+ res.on("data", (chunk) => {
41
41
  buffer += chunk;
42
- let newlineIndex = buffer.indexOf('\n');
42
+ let newlineIndex = buffer.indexOf("\n");
43
43
  while (newlineIndex !== -1) {
44
44
  const line = buffer.slice(0, newlineIndex).trim();
45
45
  buffer = buffer.slice(newlineIndex + 1);
@@ -48,18 +48,18 @@ export function createRemoteBrowserExecutor({ host, token }) {
48
48
  resolved = result;
49
49
  }, reject);
50
50
  }
51
- newlineIndex = buffer.indexOf('\n');
51
+ newlineIndex = buffer.indexOf("\n");
52
52
  }
53
53
  });
54
- res.on('end', () => {
54
+ res.on("end", () => {
55
55
  if (resolved) {
56
56
  resolve(resolved);
57
57
  return;
58
58
  }
59
- reject(new Error('Remote browser run completed without a result.'));
59
+ reject(new Error("Remote browser run completed without a result."));
60
60
  });
61
61
  });
62
- req.on('error', reject);
62
+ req.on("error", reject);
63
63
  req.write(body);
64
64
  req.end();
65
65
  });
@@ -74,7 +74,7 @@ async function serializeAttachments(attachments) {
74
74
  fileName: path.basename(attachment.path),
75
75
  displayPath: attachment.displayPath,
76
76
  sizeBytes: attachment.sizeBytes,
77
- contentBase64: content.toString('base64'),
77
+ contentBase64: content.toString("base64"),
78
78
  });
79
79
  }
80
80
  return serialized;
@@ -96,26 +96,26 @@ function handleEvent(line, options, onResult, onError) {
96
96
  onError(new Error(`Failed to parse remote event: ${error instanceof Error ? error.message : String(error)}`));
97
97
  return;
98
98
  }
99
- if (event.type === 'log') {
99
+ if (event.type === "log") {
100
100
  options.log?.(event.message);
101
101
  return;
102
102
  }
103
- if (event.type === 'error') {
103
+ if (event.type === "error") {
104
104
  onError(new Error(event.message));
105
105
  return;
106
106
  }
107
- if (event.type === 'result') {
107
+ if (event.type === "result") {
108
108
  onResult(event.result);
109
109
  }
110
110
  }
111
111
  function collectError(res) {
112
112
  return new Promise((resolve, reject) => {
113
113
  const chunks = [];
114
- res.on('data', (chunk) => {
115
- chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
114
+ res.on("data", (chunk) => {
115
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
116
116
  });
117
- res.on('end', () => {
118
- const raw = Buffer.concat(chunks).toString('utf8');
117
+ res.on("end", () => {
118
+ const raw = Buffer.concat(chunks).toString("utf8");
119
119
  try {
120
120
  const parsed = JSON.parse(raw);
121
121
  resolve(parsed.error ?? `Remote host responded with status ${res.statusCode}`);
@@ -124,6 +124,6 @@ function collectError(res) {
124
124
  resolve(raw || `Remote host responded with status ${res.statusCode}`);
125
125
  }
126
126
  });
127
- res.on('error', reject);
127
+ res.on("error", reject);
128
128
  });
129
129
  }
@@ -1,6 +1,6 @@
1
- import http from 'node:http';
2
- import net from 'node:net';
3
- import { parseHostPort } from '../bridge/connection.js';
1
+ import http from "node:http";
2
+ import net from "node:net";
3
+ import { parseHostPort } from "../bridge/connection.js";
4
4
  export async function checkTcpConnection(host, timeoutMs = 2000) {
5
5
  const { hostname, port } = parseHostPort(host);
6
6
  return await new Promise((resolve) => {
@@ -24,14 +24,14 @@ export async function checkTcpConnection(host, timeoutMs = 2000) {
24
24
  socket.unref();
25
25
  };
26
26
  socket.setTimeout(timeoutMs);
27
- socket.once('error', onError);
28
- socket.once('connect', onConnect);
29
- socket.once('timeout', onTimeout);
27
+ socket.once("error", onError);
28
+ socket.once("connect", onConnect);
29
+ socket.once("timeout", onTimeout);
30
30
  });
31
31
  }
32
32
  export async function checkRemoteHealth({ host, token, timeoutMs = 5000, }) {
33
33
  const { hostname, port } = parseHostPort(host);
34
- const headers = { accept: 'application/json' };
34
+ const headers = { accept: "application/json" };
35
35
  if (token) {
36
36
  headers.authorization = `Bearer ${token}`;
37
37
  }
@@ -39,26 +39,26 @@ export async function checkRemoteHealth({ host, token, timeoutMs = 5000, }) {
39
39
  const response = await requestJson({
40
40
  hostname,
41
41
  port,
42
- path: '/health',
42
+ path: "/health",
43
43
  headers,
44
44
  timeoutMs,
45
45
  });
46
- if (response.statusCode === 200 && typeof response.json === 'object' && response.json) {
46
+ if (response.statusCode === 200 && typeof response.json === "object" && response.json) {
47
47
  const ok = response.json.ok === true;
48
48
  const version = response.json.version;
49
49
  const uptimeSeconds = response.json.uptimeSeconds;
50
50
  return {
51
51
  ok,
52
52
  statusCode: response.statusCode,
53
- version: typeof version === 'string' ? version : undefined,
54
- uptimeSeconds: typeof uptimeSeconds === 'number' ? uptimeSeconds : undefined,
53
+ version: typeof version === "string" ? version : undefined,
54
+ uptimeSeconds: typeof uptimeSeconds === "number" ? uptimeSeconds : undefined,
55
55
  };
56
56
  }
57
57
  if (response.statusCode === 404) {
58
58
  return {
59
59
  ok: false,
60
60
  statusCode: response.statusCode,
61
- error: 'remote host does not expose /health (upgrade oracle on the host and retry)',
61
+ error: "remote host does not expose /health (upgrade oracle on the host and retry)",
62
62
  };
63
63
  }
64
64
  const error = extractErrorMessage(response.json, response.bodyText) ?? `HTTP ${response.statusCode}`;
@@ -69,9 +69,9 @@ export async function checkRemoteHealth({ host, token, timeoutMs = 5000, }) {
69
69
  }
70
70
  }
71
71
  function extractErrorMessage(json, bodyText) {
72
- if (json && typeof json === 'object') {
72
+ if (json && typeof json === "object") {
73
73
  const err = json.error;
74
- if (typeof err === 'string' && err.trim().length > 0) {
74
+ if (typeof err === "string" && err.trim().length > 0) {
75
75
  return err.trim();
76
76
  }
77
77
  }
@@ -84,15 +84,15 @@ async function requestJson({ hostname, port, path, headers, timeoutMs, }) {
84
84
  hostname,
85
85
  port,
86
86
  path,
87
- method: 'GET',
87
+ method: "GET",
88
88
  headers,
89
89
  }, (res) => {
90
- res.setEncoding('utf8');
91
- let body = '';
92
- res.on('data', (chunk) => {
90
+ res.setEncoding("utf8");
91
+ let body = "";
92
+ res.on("data", (chunk) => {
93
93
  body += chunk;
94
94
  });
95
- res.on('end', () => {
95
+ res.on("end", () => {
96
96
  const statusCode = res.statusCode ?? 0;
97
97
  let json = null;
98
98
  try {
@@ -107,7 +107,7 @@ async function requestJson({ hostname, port, path, headers, timeoutMs, }) {
107
107
  req.setTimeout(timeoutMs, () => {
108
108
  req.destroy(new Error(`timeout after ${timeoutMs}ms`));
109
109
  });
110
- req.on('error', reject);
110
+ req.on("error", reject);
111
111
  req.end();
112
112
  });
113
113
  }
@@ -1,5 +1,5 @@
1
1
  function normalizeString(value) {
2
- if (typeof value !== 'string')
2
+ if (typeof value !== "string")
3
3
  return undefined;
4
4
  const trimmed = value.trim();
5
5
  return trimmed.length ? trimmed : undefined;
@@ -14,18 +14,18 @@ export function resolveRemoteServiceConfig({ cliHost, cliToken, userConfig, env
14
14
  const host = cliHostValue ?? configBrowserHost ?? envHost;
15
15
  const token = cliTokenValue ?? configBrowserToken ?? envToken;
16
16
  const hostSource = cliHostValue
17
- ? 'cli'
17
+ ? "cli"
18
18
  : configBrowserHost
19
- ? 'config.browser'
19
+ ? "config.browser"
20
20
  : envHost
21
- ? 'env'
22
- : 'unset';
21
+ ? "env"
22
+ : "unset";
23
23
  const tokenSource = cliTokenValue
24
- ? 'cli'
24
+ ? "cli"
25
25
  : configBrowserToken
26
- ? 'config.browser'
26
+ ? "config.browser"
27
27
  : envToken
28
- ? 'env'
29
- : 'unset';
28
+ ? "env"
29
+ : "unset";
30
30
  return { host, token, sources: { host: hostSource, token: tokenSource } };
31
31
  }