@steipete/oracle 0.8.6 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +130 -45
- package/dist/bin/oracle-cli.js +613 -379
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +314 -104
- package/dist/src/browser/actions/navigation.js +161 -136
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +452 -303
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
- package/dist/src/browser/providers/index.js +2 -0
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +65 -45
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +7 -4
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +11 -0
- 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 +12 -8
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +145 -87
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +37 -25
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +182 -79
- package/dist/src/cli/sessionLineage.js +60 -0
- package/dist/src/cli/sessionRunner.js +118 -90
- package/dist/src/cli/sessionTable.js +28 -24
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +140 -127
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +80 -0
- package/dist/src/gemini-web/client.js +81 -64
- package/dist/src/gemini-web/executionMode.js +16 -0
- package/dist/src/gemini-web/executor.js +327 -169
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +81 -64
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +84 -46
- package/dist/src/oracle/config.js +124 -58
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +69 -45
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -30
- 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 +23 -15
- package/dist/src/oracle/run.js +172 -140
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +81 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +69 -65
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- 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/types.js → src/gemini-web/executionClients.js} +0 -0
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
import { spawn } from
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import { randomBytes } from
|
|
5
|
-
import chalk from
|
|
6
|
-
import { getOracleHomeDir } from
|
|
7
|
-
import { parseHostPort, normalizeHostPort, formatBridgeConnectionString } from
|
|
8
|
-
import { serveRemote } from
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { getOracleHomeDir } from "../../oracleHome.js";
|
|
7
|
+
import { parseHostPort, normalizeHostPort, formatBridgeConnectionString, } from "../../bridge/connection.js";
|
|
8
|
+
import { serveRemote } from "../../remote/server.js";
|
|
9
9
|
export async function runBridgeHost(options) {
|
|
10
|
-
const bindRaw = options.bind?.trim() ||
|
|
10
|
+
const bindRaw = options.bind?.trim() || "127.0.0.1:9473";
|
|
11
11
|
const { hostname: bindHost, port: bindPort } = parseHostPort(bindRaw);
|
|
12
|
-
const tokenRaw = options.token?.trim() ||
|
|
13
|
-
const token = tokenRaw ===
|
|
12
|
+
const tokenRaw = options.token?.trim() || "auto";
|
|
13
|
+
const token = tokenRaw === "auto" ? randomBytes(16).toString("hex") : tokenRaw;
|
|
14
14
|
if (!token.trim()) {
|
|
15
|
-
throw new Error(
|
|
15
|
+
throw new Error("Token is required (use --token auto to generate one).");
|
|
16
16
|
}
|
|
17
|
-
const writeConnectionPath = options.writeConnection?.trim() || path.join(getOracleHomeDir(),
|
|
17
|
+
const writeConnectionPath = options.writeConnection?.trim() || path.join(getOracleHomeDir(), "bridge-connection.json");
|
|
18
18
|
const sshTarget = options.ssh?.trim();
|
|
19
|
-
const sshRemotePort = typeof options.sshRemotePort ===
|
|
19
|
+
const sshRemotePort = typeof options.sshRemotePort === "number" ? options.sshRemotePort : bindPort;
|
|
20
20
|
if (sshRemotePort <= 0 || sshRemotePort > 65_535) {
|
|
21
21
|
throw new Error(`Invalid --ssh-remote-port: ${sshRemotePort}. Expected 1-65535.`);
|
|
22
22
|
}
|
|
23
|
-
const connectionHostForClient = sshTarget
|
|
23
|
+
const connectionHostForClient = sshTarget
|
|
24
|
+
? normalizeHostPort("127.0.0.1", sshRemotePort)
|
|
25
|
+
: normalizeHostPort(bindHost === "0.0.0.0" || bindHost === "::" ? "127.0.0.1" : bindHost, bindPort);
|
|
24
26
|
const artifact = await upsertConnectionArtifact(writeConnectionPath, {
|
|
25
27
|
remoteHost: connectionHostForClient,
|
|
26
28
|
remoteToken: token,
|
|
@@ -52,11 +54,11 @@ export async function runBridgeHost(options) {
|
|
|
52
54
|
});
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
|
-
console.log(chalk.cyanBright(
|
|
57
|
+
console.log(chalk.cyanBright("Bridge host starting..."));
|
|
56
58
|
console.log(chalk.dim(`- Local bind: ${normalizeHostPort(bindHost, bindPort)}`));
|
|
57
59
|
console.log(chalk.dim(`- Connection artifact: ${writeConnectionPath}`));
|
|
58
60
|
console.log(chalk.dim(`- Client remoteHost: ${artifact.remoteHost}`));
|
|
59
|
-
console.log(chalk.dim(
|
|
61
|
+
console.log(chalk.dim("Token stored in connection artifact (not printed). Use --print or --print-token if needed."));
|
|
60
62
|
let tunnel = null;
|
|
61
63
|
if (sshTarget) {
|
|
62
64
|
tunnel = startReverseTunnel({
|
|
@@ -70,7 +72,7 @@ export async function runBridgeHost(options) {
|
|
|
70
72
|
console.log(chalk.dim(`Reverse SSH tunnel active (remote 127.0.0.1:${sshRemotePort} -> local 127.0.0.1:${bindPort})`));
|
|
71
73
|
}
|
|
72
74
|
const filteredServeLogger = (message) => {
|
|
73
|
-
if (message.includes(
|
|
75
|
+
if (message.includes("Access token:")) {
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
76
78
|
console.log(message);
|
|
@@ -91,12 +93,12 @@ async function upsertConnectionArtifact(filePath, input) {
|
|
|
91
93
|
const dir = path.dirname(filePath);
|
|
92
94
|
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
93
95
|
const now = new Date().toISOString();
|
|
94
|
-
const existing = await fs.readFile(filePath,
|
|
96
|
+
const existing = await fs.readFile(filePath, "utf8").catch(() => null);
|
|
95
97
|
let createdAt = now;
|
|
96
98
|
if (existing) {
|
|
97
99
|
try {
|
|
98
100
|
const parsed = JSON.parse(existing);
|
|
99
|
-
if (typeof parsed.createdAt ===
|
|
101
|
+
if (typeof parsed.createdAt === "string" && parsed.createdAt.trim().length > 0) {
|
|
100
102
|
createdAt = parsed.createdAt;
|
|
101
103
|
}
|
|
102
104
|
}
|
|
@@ -113,9 +115,9 @@ async function upsertConnectionArtifact(filePath, input) {
|
|
|
113
115
|
};
|
|
114
116
|
const contents = `${JSON.stringify(artifact, null, 2)}\n`;
|
|
115
117
|
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
116
|
-
await fs.writeFile(tempPath, contents, { encoding:
|
|
118
|
+
await fs.writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
|
|
117
119
|
await fs.rename(tempPath, filePath);
|
|
118
|
-
if (process.platform !==
|
|
120
|
+
if (process.platform !== "win32") {
|
|
119
121
|
await fs.chmod(filePath, 0o600).catch(() => undefined);
|
|
120
122
|
}
|
|
121
123
|
return artifact;
|
|
@@ -129,27 +131,27 @@ function startReverseTunnel({ sshTarget, remotePort, localPort, identity, extraA
|
|
|
129
131
|
if (stopped)
|
|
130
132
|
return;
|
|
131
133
|
const args = [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
"-N",
|
|
135
|
+
"-R",
|
|
134
136
|
`${remotePort}:127.0.0.1:${localPort}`,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
"-o",
|
|
138
|
+
"ExitOnForwardFailure=yes",
|
|
139
|
+
"-o",
|
|
140
|
+
"ServerAliveInterval=30",
|
|
141
|
+
"-o",
|
|
142
|
+
"ServerAliveCountMax=3",
|
|
141
143
|
];
|
|
142
144
|
if (identity) {
|
|
143
|
-
args.push(
|
|
145
|
+
args.push("-i", identity);
|
|
144
146
|
}
|
|
145
147
|
if (extraArgs) {
|
|
146
148
|
args.push(...splitArgs(extraArgs));
|
|
147
149
|
}
|
|
148
150
|
args.push(sshTarget);
|
|
149
|
-
child = spawn(
|
|
151
|
+
child = spawn("ssh", args, { stdio: "ignore" });
|
|
150
152
|
const pid = child.pid;
|
|
151
|
-
log(`[bridge host] ssh tunnel started${pid ? ` (pid ${pid})` :
|
|
152
|
-
child.once(
|
|
153
|
+
log(`[bridge host] ssh tunnel started${pid ? ` (pid ${pid})` : ""}: ${sshTarget}`);
|
|
154
|
+
child.once("exit", (code, signal) => {
|
|
153
155
|
child = null;
|
|
154
156
|
if (stopped)
|
|
155
157
|
return;
|
|
@@ -179,16 +181,16 @@ function startReverseTunnel({ sshTarget, remotePort, localPort, identity, extraA
|
|
|
179
181
|
}
|
|
180
182
|
function splitArgs(input) {
|
|
181
183
|
const args = [];
|
|
182
|
-
let current =
|
|
184
|
+
let current = "";
|
|
183
185
|
let quote = null;
|
|
184
186
|
const push = () => {
|
|
185
187
|
const trimmed = current.trim();
|
|
186
188
|
if (trimmed.length)
|
|
187
189
|
args.push(trimmed);
|
|
188
|
-
current =
|
|
190
|
+
current = "";
|
|
189
191
|
};
|
|
190
192
|
for (let i = 0; i < input.length; i += 1) {
|
|
191
|
-
const ch = input[i] ??
|
|
193
|
+
const ch = input[i] ?? "";
|
|
192
194
|
if (quote) {
|
|
193
195
|
if (ch === quote) {
|
|
194
196
|
quote = null;
|
|
@@ -214,46 +216,46 @@ function splitArgs(input) {
|
|
|
214
216
|
async function spawnBridgeHostInBackground({ bind, token, writeConnectionPath, sshTarget, sshRemotePort, sshIdentity, sshExtraArgs, }) {
|
|
215
217
|
const oracleHome = getOracleHomeDir();
|
|
216
218
|
await fs.mkdir(oracleHome, { recursive: true, mode: 0o700 });
|
|
217
|
-
const logPath = path.join(oracleHome,
|
|
218
|
-
const pidPath = path.join(oracleHome,
|
|
219
|
-
const logHandle = await fs.open(logPath,
|
|
220
|
-
const stdio = [
|
|
219
|
+
const logPath = path.join(oracleHome, "bridge-host.log");
|
|
220
|
+
const pidPath = path.join(oracleHome, "bridge-host.pid");
|
|
221
|
+
const logHandle = await fs.open(logPath, "a");
|
|
222
|
+
const stdio = ["ignore", logHandle.fd, logHandle.fd];
|
|
221
223
|
const scriptPath = process.argv[1];
|
|
222
224
|
if (!scriptPath) {
|
|
223
|
-
throw new Error(
|
|
225
|
+
throw new Error("Unable to determine CLI entrypoint for background mode.");
|
|
224
226
|
}
|
|
225
227
|
const args = [
|
|
226
228
|
scriptPath,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
"bridge",
|
|
230
|
+
"host",
|
|
231
|
+
"--foreground",
|
|
232
|
+
"--bind",
|
|
231
233
|
bind,
|
|
232
|
-
|
|
234
|
+
"--token",
|
|
233
235
|
token,
|
|
234
|
-
|
|
236
|
+
"--write-connection",
|
|
235
237
|
writeConnectionPath,
|
|
236
238
|
];
|
|
237
239
|
if (sshTarget) {
|
|
238
|
-
args.push(
|
|
240
|
+
args.push("--ssh", sshTarget);
|
|
239
241
|
}
|
|
240
|
-
if (typeof sshRemotePort ===
|
|
241
|
-
args.push(
|
|
242
|
+
if (typeof sshRemotePort === "number") {
|
|
243
|
+
args.push("--ssh-remote-port", String(sshRemotePort));
|
|
242
244
|
}
|
|
243
245
|
if (sshIdentity) {
|
|
244
|
-
args.push(
|
|
246
|
+
args.push("--ssh-identity", sshIdentity);
|
|
245
247
|
}
|
|
246
248
|
if (sshExtraArgs) {
|
|
247
|
-
args.push(
|
|
249
|
+
args.push("--ssh-extra-args", sshExtraArgs);
|
|
248
250
|
}
|
|
249
251
|
const child = spawn(process.execPath, args, { detached: true, stdio });
|
|
250
252
|
child.unref();
|
|
251
|
-
await fs.writeFile(pidPath, `${child.pid ??
|
|
252
|
-
if (process.platform !==
|
|
253
|
+
await fs.writeFile(pidPath, `${child.pid ?? ""}\n`, { encoding: "utf8", mode: 0o600 });
|
|
254
|
+
if (process.platform !== "win32") {
|
|
253
255
|
await fs.chmod(pidPath, 0o600).catch(() => undefined);
|
|
254
256
|
}
|
|
255
257
|
await logHandle.close();
|
|
256
|
-
console.log(chalk.green(`Bridge host running in background (pid ${child.pid ??
|
|
258
|
+
console.log(chalk.green(`Bridge host running in background (pid ${child.pid ?? "?"})`));
|
|
257
259
|
console.log(chalk.dim(`- Log: ${logPath}`));
|
|
258
260
|
console.log(chalk.dim(`- PID: ${pidPath}`));
|
|
259
261
|
}
|
|
@@ -1,51 +1,62 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, isTemporaryChatUrl, normalizeChatgptUrl, parseDuration } from
|
|
4
|
-
import { normalizeBrowserModelStrategy } from
|
|
5
|
-
import { getOracleHomeDir } from
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, isTemporaryChatUrl, normalizeChatgptUrl, parseDuration, } from "../browserMode.js";
|
|
4
|
+
import { normalizeBrowserModelStrategy } from "../browser/modelStrategy.js";
|
|
5
|
+
import { getOracleHomeDir } from "../oracleHome.js";
|
|
6
6
|
const DEFAULT_BROWSER_TIMEOUT_MS = 1_200_000;
|
|
7
7
|
const DEFAULT_BROWSER_INPUT_TIMEOUT_MS = 60_000;
|
|
8
8
|
const DEFAULT_BROWSER_RECHECK_TIMEOUT_MS = 120_000;
|
|
9
9
|
const DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS = 120_000;
|
|
10
|
-
const DEFAULT_CHROME_PROFILE =
|
|
10
|
+
const DEFAULT_CHROME_PROFILE = "Default";
|
|
11
11
|
// Ordered array: most specific models first to ensure correct selection.
|
|
12
12
|
// The browser label is passed to the model picker which fuzzy-matches against ChatGPT's UI.
|
|
13
13
|
const BROWSER_MODEL_LABELS = [
|
|
14
14
|
// Most specific first (e.g., "gpt-5.2-thinking" before "gpt-5.2")
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
15
|
+
["gpt-5.5-pro", "GPT-5.5 Pro"],
|
|
16
|
+
["gpt-5.5", "Thinking 5.5"],
|
|
17
|
+
["gpt-5.4-pro", "GPT-5.4 Pro"],
|
|
18
|
+
["gpt-5.2-thinking", "GPT-5.2 Thinking"],
|
|
19
|
+
["gpt-5.2-instant", "GPT-5.2 Instant"],
|
|
20
|
+
["gpt-5.2-pro", "GPT-5.5 Pro"],
|
|
21
|
+
["gpt-5.1-pro", "GPT-5.5 Pro"],
|
|
22
|
+
["gpt-5-pro", "GPT-5.5 Pro"],
|
|
20
23
|
// Base models last (least specific)
|
|
21
|
-
[
|
|
22
|
-
[
|
|
23
|
-
[
|
|
24
|
+
["gpt-5.4", "Thinking 5.4"],
|
|
25
|
+
["gpt-5.2", "GPT-5.2"], // Selects "Auto" in ChatGPT UI
|
|
26
|
+
["gpt-5.1", "GPT-5.2"], // Legacy alias → Auto
|
|
27
|
+
["gemini-3-pro", "Gemini 3 Pro"],
|
|
28
|
+
["gemini-3-pro-deep-think", "gemini-3-deep-think"],
|
|
24
29
|
];
|
|
25
30
|
export function normalizeChatGptModelForBrowser(model) {
|
|
26
31
|
const normalized = model.toLowerCase();
|
|
27
|
-
if (!normalized.startsWith(
|
|
32
|
+
if (!normalized.startsWith("gpt-") || normalized.includes("codex")) {
|
|
28
33
|
return model;
|
|
29
34
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
if (normalized === "gpt-5.5-pro" ||
|
|
36
|
+
normalized === "gpt-5.5" ||
|
|
37
|
+
normalized === "gpt-5.4-pro" ||
|
|
38
|
+
normalized === "gpt-5.4") {
|
|
39
|
+
return normalized;
|
|
40
|
+
}
|
|
41
|
+
// Pro variants: resolve to the latest Pro model in ChatGPT.
|
|
42
|
+
if (normalized === "gpt-5-pro" || normalized === "gpt-5.1-pro" || normalized === "gpt-5.2-pro") {
|
|
43
|
+
return "gpt-5.5-pro";
|
|
33
44
|
}
|
|
34
45
|
// Explicit model variants: keep as-is (they have their own browser labels)
|
|
35
|
-
if (normalized ===
|
|
46
|
+
if (normalized === "gpt-5.2-thinking" || normalized === "gpt-5.2-instant") {
|
|
36
47
|
return normalized;
|
|
37
48
|
}
|
|
38
49
|
// Legacy aliases: map to base GPT-5.2 (Auto)
|
|
39
|
-
if (normalized ===
|
|
40
|
-
return
|
|
50
|
+
if (normalized === "gpt-5.1") {
|
|
51
|
+
return "gpt-5.2";
|
|
41
52
|
}
|
|
42
53
|
return model;
|
|
43
54
|
}
|
|
44
55
|
export async function buildBrowserConfig(options) {
|
|
45
56
|
const desiredModelOverride = options.browserModelLabel?.trim();
|
|
46
|
-
const normalizedOverride = desiredModelOverride?.toLowerCase() ??
|
|
57
|
+
const normalizedOverride = desiredModelOverride?.toLowerCase() ?? "";
|
|
47
58
|
const baseModel = options.model.toLowerCase();
|
|
48
|
-
const isChatGptModel = baseModel.startsWith(
|
|
59
|
+
const isChatGptModel = baseModel.startsWith("gpt-") && !baseModel.includes("codex");
|
|
49
60
|
const shouldUseOverride = !isChatGptModel && normalizedOverride.length > 0 && normalizedOverride !== baseModel;
|
|
50
61
|
const modelStrategy = normalizeBrowserModelStrategy(options.browserModelStrategy) ?? DEFAULT_MODEL_STRATEGY;
|
|
51
62
|
const cookieNames = parseCookieNames(options.browserCookieNames ?? process.env.ORACLE_BROWSER_COOKIE_NAMES);
|
|
@@ -56,7 +67,7 @@ export async function buildBrowserConfig(options) {
|
|
|
56
67
|
envFile: process.env.ORACLE_BROWSER_COOKIES_FILE,
|
|
57
68
|
cwd: process.cwd(),
|
|
58
69
|
});
|
|
59
|
-
if (inline?.source?.startsWith(
|
|
70
|
+
if (inline?.source?.startsWith("home:") && options.browserNoCookieSync !== true) {
|
|
60
71
|
inline = undefined;
|
|
61
72
|
}
|
|
62
73
|
let remoteChrome;
|
|
@@ -70,8 +81,11 @@ export async function buildBrowserConfig(options) {
|
|
|
70
81
|
: shouldUseOverride
|
|
71
82
|
? desiredModelOverride
|
|
72
83
|
: mapModelToBrowserLabel(options.model);
|
|
73
|
-
if (modelStrategy ===
|
|
74
|
-
|
|
84
|
+
if (modelStrategy === "select" &&
|
|
85
|
+
url &&
|
|
86
|
+
isTemporaryChatUrl(url) &&
|
|
87
|
+
/\bpro\b/i.test(desiredModel ?? "")) {
|
|
88
|
+
throw new Error("Temporary Chat mode does not expose Pro models in the ChatGPT model picker. " +
|
|
75
89
|
'Remove "temporary-chat=true" from --chatgpt-url (or omit --chatgpt-url), or use a non-Pro model (e.g. --model gpt-5.2).');
|
|
76
90
|
}
|
|
77
91
|
return {
|
|
@@ -80,7 +94,9 @@ export async function buildBrowserConfig(options) {
|
|
|
80
94
|
chromeCookiePath: options.browserCookiePath ?? null,
|
|
81
95
|
url,
|
|
82
96
|
debugPort: selectBrowserPort(options),
|
|
83
|
-
timeoutMs: options.browserTimeout
|
|
97
|
+
timeoutMs: options.browserTimeout
|
|
98
|
+
? parseDuration(options.browserTimeout, DEFAULT_BROWSER_TIMEOUT_MS)
|
|
99
|
+
: undefined,
|
|
84
100
|
inputTimeoutMs: options.browserInputTimeout
|
|
85
101
|
? parseDuration(options.browserInputTimeout, DEFAULT_BROWSER_INPUT_TIMEOUT_MS)
|
|
86
102
|
: undefined,
|
|
@@ -90,7 +106,9 @@ export async function buildBrowserConfig(options) {
|
|
|
90
106
|
assistantRecheckTimeoutMs: options.browserRecheckTimeout
|
|
91
107
|
? parseDuration(options.browserRecheckTimeout, DEFAULT_BROWSER_RECHECK_TIMEOUT_MS)
|
|
92
108
|
: undefined,
|
|
93
|
-
reuseChromeWaitMs: options.browserReuseWait
|
|
109
|
+
reuseChromeWaitMs: options.browserReuseWait
|
|
110
|
+
? parseDuration(options.browserReuseWait, 0)
|
|
111
|
+
: undefined,
|
|
94
112
|
profileLockTimeoutMs: options.browserProfileLockTimeout
|
|
95
113
|
? parseDuration(options.browserProfileLockTimeout, 0)
|
|
96
114
|
: undefined,
|
|
@@ -103,7 +121,9 @@ export async function buildBrowserConfig(options) {
|
|
|
103
121
|
autoReattachTimeoutMs: options.browserAutoReattachTimeout
|
|
104
122
|
? parseDuration(options.browserAutoReattachTimeout, DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS)
|
|
105
123
|
: undefined,
|
|
106
|
-
cookieSyncWaitMs: options.browserCookieWait
|
|
124
|
+
cookieSyncWaitMs: options.browserCookieWait
|
|
125
|
+
? parseDuration(options.browserCookieWait, 0)
|
|
126
|
+
: undefined,
|
|
107
127
|
cookieSync: options.browserNoCookieSync ? false : undefined,
|
|
108
128
|
cookieNames,
|
|
109
129
|
inlineCookies: inline?.cookies,
|
|
@@ -142,7 +162,7 @@ export function mapModelToBrowserLabel(model) {
|
|
|
142
162
|
return DEFAULT_MODEL_TARGET;
|
|
143
163
|
}
|
|
144
164
|
export function resolveBrowserModelLabel(input, model) {
|
|
145
|
-
const trimmed = input?.trim?.() ??
|
|
165
|
+
const trimmed = input?.trim?.() ?? "";
|
|
146
166
|
if (!trimmed) {
|
|
147
167
|
return mapModelToBrowserLabel(model);
|
|
148
168
|
}
|
|
@@ -155,7 +175,7 @@ export function resolveBrowserModelLabel(input, model) {
|
|
|
155
175
|
function parseRemoteChromeTarget(raw) {
|
|
156
176
|
const target = raw.trim();
|
|
157
177
|
if (!target) {
|
|
158
|
-
throw new Error(
|
|
178
|
+
throw new Error("Invalid remote-chrome value: expected host:port but received an empty string.");
|
|
159
179
|
}
|
|
160
180
|
const ipv6Match = target.match(/^\[(.+)]:(\d+)$/);
|
|
161
181
|
let host;
|
|
@@ -165,22 +185,22 @@ function parseRemoteChromeTarget(raw) {
|
|
|
165
185
|
portSegment = ipv6Match[2]?.trim();
|
|
166
186
|
}
|
|
167
187
|
else {
|
|
168
|
-
const lastColon = target.lastIndexOf(
|
|
188
|
+
const lastColon = target.lastIndexOf(":");
|
|
169
189
|
if (lastColon === -1) {
|
|
170
190
|
throw new Error(`Invalid remote-chrome format: ${target}. Expected host:port (IPv6 must use [host]:port notation).`);
|
|
171
191
|
}
|
|
172
192
|
host = target.slice(0, lastColon).trim();
|
|
173
193
|
portSegment = target.slice(lastColon + 1).trim();
|
|
174
|
-
if (host.includes(
|
|
194
|
+
if (host.includes(":")) {
|
|
175
195
|
throw new Error(`Invalid remote-chrome format: ${target}. Wrap IPv6 addresses in brackets, e.g. --remote-chrome "[2001:db8::1]:9222".`);
|
|
176
196
|
}
|
|
177
197
|
}
|
|
178
198
|
if (!host) {
|
|
179
199
|
throw new Error(`Invalid remote-chrome format: ${target}. Host portion is missing; expected host:port.`);
|
|
180
200
|
}
|
|
181
|
-
const port = Number.parseInt(portSegment ??
|
|
201
|
+
const port = Number.parseInt(portSegment ?? "", 10);
|
|
182
202
|
if (!Number.isFinite(port) || port <= 0 || port > 65_535) {
|
|
183
|
-
throw new Error(`Invalid remote-chrome port: "${portSegment ??
|
|
203
|
+
throw new Error(`Invalid remote-chrome port: "${portSegment ?? ""}". Expected a number between 1 and 65535.`);
|
|
184
204
|
}
|
|
185
205
|
return { host, port };
|
|
186
206
|
}
|
|
@@ -188,7 +208,7 @@ function parseCookieNames(raw) {
|
|
|
188
208
|
if (!raw)
|
|
189
209
|
return undefined;
|
|
190
210
|
const names = raw
|
|
191
|
-
.split(
|
|
211
|
+
.split(",")
|
|
192
212
|
.map((entry) => entry.trim())
|
|
193
213
|
.filter(Boolean);
|
|
194
214
|
return names.length ? names : undefined;
|
|
@@ -205,7 +225,7 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
|
|
|
205
225
|
try {
|
|
206
226
|
const stat = await fs.stat(resolved);
|
|
207
227
|
if (stat.isFile()) {
|
|
208
|
-
const fileContent = await fs.readFile(resolved,
|
|
228
|
+
const fileContent = await fs.readFile(resolved, "utf8");
|
|
209
229
|
const parsed = parseInlineCookiesPayload(fileContent);
|
|
210
230
|
if (parsed)
|
|
211
231
|
return parsed;
|
|
@@ -218,10 +238,10 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
|
|
|
218
238
|
return parseInlineCookiesPayload(trimmed);
|
|
219
239
|
};
|
|
220
240
|
const sources = [
|
|
221
|
-
{ value: inlineFileArg, allowPath: true, source:
|
|
222
|
-
{ value: inlineArg, allowPath: true, source:
|
|
223
|
-
{ value: envFile, allowPath: true, source:
|
|
224
|
-
{ value: envPayload, allowPath: false, source:
|
|
241
|
+
{ value: inlineFileArg, allowPath: true, source: "inline-file" },
|
|
242
|
+
{ value: inlineArg, allowPath: true, source: "inline-arg" },
|
|
243
|
+
{ value: envFile, allowPath: true, source: "env-file" },
|
|
244
|
+
{ value: envPayload, allowPath: false, source: "env-payload" },
|
|
225
245
|
];
|
|
226
246
|
for (const { value, allowPath, source } of sources) {
|
|
227
247
|
const parsed = await tryLoad(value, allowPath);
|
|
@@ -230,14 +250,14 @@ async function resolveInlineCookies({ inlineArg, inlineFileArg, envPayload, envF
|
|
|
230
250
|
}
|
|
231
251
|
// fallback: ~/.oracle/cookies.{json,base64}
|
|
232
252
|
const oracleHome = getOracleHomeDir();
|
|
233
|
-
const candidates = [
|
|
253
|
+
const candidates = ["cookies.json", "cookies.base64"];
|
|
234
254
|
for (const file of candidates) {
|
|
235
255
|
const fullPath = path.join(oracleHome, file);
|
|
236
256
|
try {
|
|
237
257
|
const stat = await fs.stat(fullPath);
|
|
238
258
|
if (!stat.isFile())
|
|
239
259
|
continue;
|
|
240
|
-
const content = await fs.readFile(fullPath,
|
|
260
|
+
const content = await fs.readFile(fullPath, "utf8");
|
|
241
261
|
const parsed = parseInlineCookiesPayload(content);
|
|
242
262
|
if (parsed)
|
|
243
263
|
return { cookies: parsed, source: `home:${file}` };
|
|
@@ -257,8 +277,8 @@ function parseInlineCookiesPayload(raw) {
|
|
|
257
277
|
let jsonPayload = text;
|
|
258
278
|
// Attempt base64 decode first; fall back to raw text on failure.
|
|
259
279
|
try {
|
|
260
|
-
const decoded = Buffer.from(text,
|
|
261
|
-
if (decoded.trim().startsWith(
|
|
280
|
+
const decoded = Buffer.from(text, "base64").toString("utf8");
|
|
281
|
+
if (decoded.trim().startsWith("[")) {
|
|
262
282
|
jsonPayload = decoded;
|
|
263
283
|
}
|
|
264
284
|
}
|
|
@@ -1,81 +1,82 @@
|
|
|
1
|
-
import { normalizeChatgptUrl, CHATGPT_URL } from
|
|
1
|
+
import { normalizeChatgptUrl, CHATGPT_URL } from "../browserMode.js";
|
|
2
2
|
export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
3
3
|
const browser = config.browser;
|
|
4
4
|
if (!browser)
|
|
5
5
|
return;
|
|
6
6
|
const isUnset = (key) => {
|
|
7
7
|
const source = getSource(key);
|
|
8
|
-
return source === undefined || source ===
|
|
8
|
+
return source === undefined || source === "default";
|
|
9
9
|
};
|
|
10
10
|
const configuredChatgptUrl = browser.chatgptUrl ?? browser.url;
|
|
11
11
|
const cliChatgptSet = options.chatgptUrl !== undefined || options.browserUrl !== undefined;
|
|
12
|
-
if (isUnset(
|
|
13
|
-
options.chatgptUrl = normalizeChatgptUrl(configuredChatgptUrl ??
|
|
12
|
+
if (isUnset("chatgptUrl") && !cliChatgptSet && configuredChatgptUrl !== undefined) {
|
|
13
|
+
options.chatgptUrl = normalizeChatgptUrl(configuredChatgptUrl ?? "", CHATGPT_URL);
|
|
14
14
|
}
|
|
15
|
-
if (isUnset(
|
|
15
|
+
if (isUnset("browserChromeProfile") && browser.chromeProfile !== undefined) {
|
|
16
16
|
options.browserChromeProfile = browser.chromeProfile ?? undefined;
|
|
17
17
|
}
|
|
18
|
-
if (isUnset(
|
|
18
|
+
if (isUnset("browserChromePath") && browser.chromePath !== undefined) {
|
|
19
19
|
options.browserChromePath = browser.chromePath ?? undefined;
|
|
20
20
|
}
|
|
21
|
-
if (isUnset(
|
|
21
|
+
if (isUnset("browserCookiePath") && browser.chromeCookiePath !== undefined) {
|
|
22
22
|
options.browserCookiePath = browser.chromeCookiePath ?? undefined;
|
|
23
23
|
}
|
|
24
|
-
if (isUnset(
|
|
24
|
+
if (isUnset("browserUrl") && options.browserUrl === undefined && browser.url !== undefined) {
|
|
25
25
|
options.browserUrl = browser.url;
|
|
26
26
|
}
|
|
27
|
-
if (isUnset(
|
|
27
|
+
if (isUnset("browserTimeout") && typeof browser.timeoutMs === "number") {
|
|
28
28
|
options.browserTimeout = String(browser.timeoutMs);
|
|
29
29
|
}
|
|
30
|
-
if (isUnset(
|
|
30
|
+
if (isUnset("browserPort") && typeof browser.debugPort === "number") {
|
|
31
31
|
options.browserPort = browser.debugPort;
|
|
32
32
|
}
|
|
33
|
-
if (isUnset(
|
|
33
|
+
if (isUnset("browserInputTimeout") && typeof browser.inputTimeoutMs === "number") {
|
|
34
34
|
options.browserInputTimeout = String(browser.inputTimeoutMs);
|
|
35
35
|
}
|
|
36
|
-
if (isUnset(
|
|
36
|
+
if (isUnset("browserRecheckDelay") && typeof browser.assistantRecheckDelayMs === "number") {
|
|
37
37
|
options.browserRecheckDelay = String(browser.assistantRecheckDelayMs);
|
|
38
38
|
}
|
|
39
|
-
if (isUnset(
|
|
39
|
+
if (isUnset("browserRecheckTimeout") && typeof browser.assistantRecheckTimeoutMs === "number") {
|
|
40
40
|
options.browserRecheckTimeout = String(browser.assistantRecheckTimeoutMs);
|
|
41
41
|
}
|
|
42
|
-
if (isUnset(
|
|
42
|
+
if (isUnset("browserReuseWait") && typeof browser.reuseChromeWaitMs === "number") {
|
|
43
43
|
options.browserReuseWait = String(browser.reuseChromeWaitMs);
|
|
44
44
|
}
|
|
45
|
-
if (isUnset(
|
|
45
|
+
if (isUnset("browserProfileLockTimeout") && typeof browser.profileLockTimeoutMs === "number") {
|
|
46
46
|
options.browserProfileLockTimeout = String(browser.profileLockTimeoutMs);
|
|
47
47
|
}
|
|
48
|
-
if (isUnset(
|
|
48
|
+
if (isUnset("browserAutoReattachDelay") && typeof browser.autoReattachDelayMs === "number") {
|
|
49
49
|
options.browserAutoReattachDelay = String(browser.autoReattachDelayMs);
|
|
50
50
|
}
|
|
51
|
-
if (isUnset(
|
|
51
|
+
if (isUnset("browserAutoReattachInterval") &&
|
|
52
|
+
typeof browser.autoReattachIntervalMs === "number") {
|
|
52
53
|
options.browserAutoReattachInterval = String(browser.autoReattachIntervalMs);
|
|
53
54
|
}
|
|
54
|
-
if (isUnset(
|
|
55
|
+
if (isUnset("browserAutoReattachTimeout") && typeof browser.autoReattachTimeoutMs === "number") {
|
|
55
56
|
options.browserAutoReattachTimeout = String(browser.autoReattachTimeoutMs);
|
|
56
57
|
}
|
|
57
|
-
if (isUnset(
|
|
58
|
+
if (isUnset("browserCookieWait") && typeof browser.cookieSyncWaitMs === "number") {
|
|
58
59
|
options.browserCookieWait = String(browser.cookieSyncWaitMs);
|
|
59
60
|
}
|
|
60
|
-
if (isUnset(
|
|
61
|
+
if (isUnset("browserHeadless") && browser.headless !== undefined) {
|
|
61
62
|
options.browserHeadless = browser.headless;
|
|
62
63
|
}
|
|
63
|
-
if (isUnset(
|
|
64
|
+
if (isUnset("browserHideWindow") && browser.hideWindow !== undefined) {
|
|
64
65
|
options.browserHideWindow = browser.hideWindow;
|
|
65
66
|
}
|
|
66
|
-
if (isUnset(
|
|
67
|
+
if (isUnset("browserKeepBrowser") && browser.keepBrowser !== undefined) {
|
|
67
68
|
options.browserKeepBrowser = browser.keepBrowser;
|
|
68
69
|
}
|
|
69
|
-
if (isUnset(
|
|
70
|
+
if (isUnset("browserModelStrategy") && browser.modelStrategy !== undefined) {
|
|
70
71
|
options.browserModelStrategy = browser.modelStrategy;
|
|
71
72
|
}
|
|
72
|
-
if (isUnset(
|
|
73
|
+
if (isUnset("browserThinkingTime") && browser.thinkingTime !== undefined) {
|
|
73
74
|
options.browserThinkingTime = browser.thinkingTime;
|
|
74
75
|
}
|
|
75
|
-
if (isUnset(
|
|
76
|
+
if (isUnset("browserManualLogin") && browser.manualLogin !== undefined) {
|
|
76
77
|
options.browserManualLogin = browser.manualLogin;
|
|
77
78
|
}
|
|
78
|
-
if (isUnset(
|
|
79
|
+
if (isUnset("browserManualLoginProfileDir") && browser.manualLoginProfileDir !== undefined) {
|
|
79
80
|
options.browserManualLoginProfileDir = browser.manualLoginProfileDir;
|
|
80
81
|
}
|
|
81
82
|
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
async function loadClipboard() {
|
|
2
|
+
if (process.platform === "darwin" && process.arch === "x64") {
|
|
3
|
+
const paths = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
4
|
+
if (!paths.includes("/usr/sbin")) {
|
|
5
|
+
process.env.PATH = ["/usr/sbin", ...paths].join(":");
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return (await import("clipboardy")).default;
|
|
9
|
+
}
|
|
2
10
|
export async function copyToClipboard(text) {
|
|
3
11
|
try {
|
|
12
|
+
const clipboard = await loadClipboard();
|
|
4
13
|
await clipboard.write(text);
|
|
5
|
-
return { success: true, command:
|
|
14
|
+
return { success: true, command: "clipboardy" };
|
|
6
15
|
}
|
|
7
16
|
catch (error) {
|
|
8
17
|
return { success: false, error };
|
package/dist/src/cli/detach.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { isProModel } from
|
|
1
|
+
import { isProModel } from "../oracle/modelResolver.js";
|
|
2
2
|
export function shouldDetachSession({
|
|
3
|
-
// Params kept for
|
|
4
|
-
engine, model, waitPreference
|
|
3
|
+
// Params kept for policy tweaks.
|
|
4
|
+
engine, model, waitPreference, disableDetachEnv, }) {
|
|
5
5
|
if (disableDetachEnv)
|
|
6
6
|
return false;
|
|
7
|
+
// Explicit --wait means "stay attached", regardless of model defaults.
|
|
8
|
+
if (waitPreference)
|
|
9
|
+
return false;
|
|
7
10
|
// Only Pro-tier API runs should start detached by default; browser runs stay inline so failures surface.
|
|
8
|
-
if (isProModel(model) && engine ===
|
|
11
|
+
if (isProModel(model) && engine === "api")
|
|
9
12
|
return true;
|
|
10
13
|
return false;
|
|
11
14
|
}
|