@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,14 +1,17 @@
1
- import { InvalidArgumentError } from 'commander';
2
- import { parseDuration } from '../browserMode.js';
3
- import path from 'node:path';
4
- import fg from 'fast-glob';
5
- import { DEFAULT_MODEL, MODEL_CONFIGS } from '../oracle.js';
1
+ import { InvalidArgumentError } from "commander";
2
+ import { parseDuration } from "../browserMode.js";
3
+ import path from "node:path";
4
+ import fg from "fast-glob";
5
+ import { DEFAULT_MODEL, MODEL_CONFIGS } from "../oracle.js";
6
6
  export function collectPaths(value, previous = []) {
7
7
  if (!value) {
8
8
  return previous;
9
9
  }
10
10
  const nextValues = Array.isArray(value) ? value : [value];
11
- return previous.concat(nextValues.flatMap((entry) => entry.split(',')).map((entry) => entry.trim()).filter(Boolean));
11
+ return previous.concat(nextValues
12
+ .flatMap((entry) => entry.split(","))
13
+ .map((entry) => entry.trim())
14
+ .filter(Boolean));
12
15
  }
13
16
  /**
14
17
  * Merge all path-like CLI inputs (file/include aliases) into a single list, preserving order.
@@ -29,7 +32,7 @@ export function dedupePathInputs(inputs, { cwd = process.cwd() } = {}) {
29
32
  if (!raw)
30
33
  continue;
31
34
  let key = raw;
32
- if (!raw.startsWith('!') && !fg.isDynamicPattern(raw)) {
35
+ if (!raw.startsWith("!") && !fg.isDynamicPattern(raw)) {
33
36
  const absolute = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
34
37
  key = `path:${path.normalize(absolute)}`;
35
38
  }
@@ -50,15 +53,19 @@ export function collectModelList(value, previous = []) {
50
53
  return previous;
51
54
  }
52
55
  const entries = value
53
- .split(',')
56
+ .split(",")
54
57
  .map((entry) => entry.trim())
55
58
  .filter((entry) => entry.length > 0);
56
59
  return previous.concat(entries);
57
60
  }
61
+ export function collectTextValues(value, previous = []) {
62
+ const trimmed = value.trim();
63
+ return trimmed ? previous.concat(trimmed) : previous;
64
+ }
58
65
  export function parseFloatOption(value) {
59
66
  const parsed = Number.parseFloat(value);
60
67
  if (Number.isNaN(parsed)) {
61
- throw new InvalidArgumentError('Value must be a number.');
68
+ throw new InvalidArgumentError("Value must be a number.");
62
69
  }
63
70
  return parsed;
64
71
  }
@@ -68,7 +75,7 @@ export function parseIntOption(value) {
68
75
  }
69
76
  const parsed = Number.parseInt(value, 10);
70
77
  if (Number.isNaN(parsed)) {
71
- throw new InvalidArgumentError('Value must be an integer.');
78
+ throw new InvalidArgumentError("Value must be an integer.");
72
79
  }
73
80
  return parsed;
74
81
  }
@@ -76,9 +83,9 @@ export function parseHeartbeatOption(value) {
76
83
  if (value == null) {
77
84
  return 30;
78
85
  }
79
- if (typeof value === 'number') {
86
+ if (typeof value === "number") {
80
87
  if (Number.isNaN(value) || value < 0) {
81
- throw new InvalidArgumentError('Heartbeat interval must be zero or a positive number.');
88
+ throw new InvalidArgumentError("Heartbeat interval must be zero or a positive number.");
82
89
  }
83
90
  return value;
84
91
  }
@@ -86,42 +93,42 @@ export function parseHeartbeatOption(value) {
86
93
  if (normalized.length === 0) {
87
94
  return 30;
88
95
  }
89
- if (normalized === 'false' || normalized === 'off') {
96
+ if (normalized === "false" || normalized === "off") {
90
97
  return 0;
91
98
  }
92
99
  const parsed = Number.parseFloat(normalized);
93
100
  if (Number.isNaN(parsed) || parsed < 0) {
94
- throw new InvalidArgumentError('Heartbeat interval must be zero or a positive number.');
101
+ throw new InvalidArgumentError("Heartbeat interval must be zero or a positive number.");
95
102
  }
96
103
  return parsed;
97
104
  }
98
105
  export function usesDefaultStatusFilters(cmd) {
99
- const hoursSource = cmd.getOptionValueSource?.('hours') ?? 'default';
100
- const limitSource = cmd.getOptionValueSource?.('limit') ?? 'default';
101
- const allSource = cmd.getOptionValueSource?.('all') ?? 'default';
102
- return hoursSource === 'default' && limitSource === 'default' && allSource === 'default';
106
+ const hoursSource = cmd.getOptionValueSource?.("hours") ?? "default";
107
+ const limitSource = cmd.getOptionValueSource?.("limit") ?? "default";
108
+ const allSource = cmd.getOptionValueSource?.("all") ?? "default";
109
+ return hoursSource === "default" && limitSource === "default" && allSource === "default";
103
110
  }
104
111
  export function resolvePreviewMode(value) {
105
- if (typeof value === 'string' && value.length > 0) {
112
+ if (typeof value === "string" && value.length > 0) {
106
113
  return value;
107
114
  }
108
115
  if (value === true) {
109
- return 'summary';
116
+ return "summary";
110
117
  }
111
118
  return undefined;
112
119
  }
113
120
  export function parseSearchOption(value) {
114
121
  const normalized = value.trim().toLowerCase();
115
- if (['on', 'true', '1', 'yes'].includes(normalized)) {
122
+ if (["on", "true", "1", "yes"].includes(normalized)) {
116
123
  return true;
117
124
  }
118
- if (['off', 'false', '0', 'no'].includes(normalized)) {
125
+ if (["off", "false", "0", "no"].includes(normalized)) {
119
126
  return false;
120
127
  }
121
128
  throw new InvalidArgumentError('Search mode must be "on" or "off".');
122
129
  }
123
130
  export function normalizeModelOption(value) {
124
- return (value ?? '').trim();
131
+ return (value ?? "").trim();
125
132
  }
126
133
  export function normalizeBaseUrl(value) {
127
134
  const trimmed = value?.trim();
@@ -131,8 +138,8 @@ export function parseTimeoutOption(value) {
131
138
  if (value == null)
132
139
  return undefined;
133
140
  const normalized = value.trim().toLowerCase();
134
- if (normalized === 'auto')
135
- return 'auto';
141
+ if (normalized === "auto")
142
+ return "auto";
136
143
  const parsed = Number.parseFloat(normalized);
137
144
  if (Number.isNaN(parsed) || parsed <= 0) {
138
145
  throw new InvalidArgumentError('Timeout must be a positive number of seconds or "auto".');
@@ -153,68 +160,74 @@ export function parseDurationOption(value, label) {
153
160
  return parsed;
154
161
  }
155
162
  function isGeminiDeepThinkAlias(normalized) {
156
- return ((normalized.includes('gemini') && normalized.includes('deep')) ||
157
- normalized.includes('deep-think') ||
158
- normalized.includes('deep_think') ||
159
- normalized.includes('deepthink'));
163
+ return ((normalized.includes("gemini") && normalized.includes("deep")) ||
164
+ normalized.includes("deep-think") ||
165
+ normalized.includes("deep_think") ||
166
+ normalized.includes("deepthink"));
160
167
  }
161
168
  export function resolveApiModel(modelValue) {
162
169
  const normalized = normalizeModelOption(modelValue).toLowerCase();
163
170
  if (normalized in MODEL_CONFIGS) {
164
171
  return normalized;
165
172
  }
166
- if (normalized.includes('/')) {
173
+ if (normalized.includes("/")) {
167
174
  return normalized;
168
175
  }
169
- if (normalized.includes('grok')) {
170
- return 'grok-4.1';
176
+ if (normalized.includes("grok")) {
177
+ return "grok-4.1";
178
+ }
179
+ if (normalized.includes("claude") && normalized.includes("sonnet")) {
180
+ return "claude-4.6-sonnet";
171
181
  }
172
- if (normalized.includes('claude') && normalized.includes('sonnet')) {
173
- return 'claude-4.5-sonnet';
182
+ if (normalized.includes("claude") && normalized.includes("opus")) {
183
+ return "claude-4.1-opus";
174
184
  }
175
- if (normalized.includes('claude') && normalized.includes('opus')) {
176
- return 'claude-4.1-opus';
185
+ if (normalized.includes("5.5") && normalized.includes("pro")) {
186
+ return "gpt-5.5-pro";
177
187
  }
178
- if (normalized.includes('5.4') && normalized.includes('pro')) {
179
- return 'gpt-5.4-pro';
188
+ if (normalized.includes("5.5")) {
189
+ return "gpt-5.5";
180
190
  }
181
- if (normalized.includes('5.4')) {
182
- return 'gpt-5.4';
191
+ if (normalized.includes("5.4") && normalized.includes("pro")) {
192
+ return "gpt-5.4-pro";
183
193
  }
184
- if (normalized === 'claude' || normalized === 'sonnet' || /(^|\b)sonnet(\b|$)/.test(normalized)) {
185
- return 'claude-4.5-sonnet';
194
+ if (normalized.includes("5.4")) {
195
+ return "gpt-5.4";
186
196
  }
187
- if (normalized === 'opus' || normalized === 'claude-4.1') {
188
- return 'claude-4.1-opus';
197
+ if (normalized === "claude" || normalized === "sonnet" || /(^|\b)sonnet(\b|$)/.test(normalized)) {
198
+ return "claude-4.6-sonnet";
189
199
  }
190
- if (normalized.includes('5.0') || normalized === 'gpt-5-pro' || normalized === 'gpt-5') {
191
- return 'gpt-5-pro';
200
+ if (normalized === "opus" || normalized === "claude-4.1") {
201
+ return "claude-4.1-opus";
192
202
  }
193
- if (normalized.includes('5-pro') && !normalized.includes('5.1')) {
194
- return 'gpt-5-pro';
203
+ if (normalized.includes("5.0") || normalized === "gpt-5-pro" || normalized === "gpt-5") {
204
+ return "gpt-5-pro";
195
205
  }
196
- if (normalized.includes('5.2') && normalized.includes('pro')) {
197
- return 'gpt-5.2-pro';
206
+ if (normalized.includes("5-pro") && !normalized.includes("5.1")) {
207
+ return "gpt-5-pro";
198
208
  }
199
- if (normalized.includes('5.1') && normalized.includes('pro')) {
200
- return 'gpt-5.1-pro';
209
+ if (normalized.includes("5.2") && normalized.includes("pro")) {
210
+ return "gpt-5.2-pro";
201
211
  }
202
- if (normalized.includes('codex')) {
203
- if (normalized.includes('max')) {
204
- throw new InvalidArgumentError('gpt-5.1-codex-max is not available yet. OpenAI has not released the API.');
212
+ if (normalized.includes("5.1") && normalized.includes("pro")) {
213
+ return "gpt-5.1-pro";
214
+ }
215
+ if (normalized.includes("codex")) {
216
+ if (normalized.includes("max")) {
217
+ throw new InvalidArgumentError("gpt-5.1-codex-max is not available yet. OpenAI has not released the API.");
205
218
  }
206
- return 'gpt-5.1-codex';
219
+ return "gpt-5.1-codex";
207
220
  }
208
221
  if (isGeminiDeepThinkAlias(normalized)) {
209
- throw new InvalidArgumentError('Gemini Deep Think is browser-only today. Use --engine browser --model gemini-3-deep-think.');
222
+ throw new InvalidArgumentError("Gemini Deep Think is browser-only today. Use --engine browser --model gemini-3-deep-think.");
210
223
  }
211
- if (normalized.includes('gemini')) {
212
- if (normalized.includes('3.1') || normalized.includes('3_1')) {
213
- return 'gemini-3.1-pro';
224
+ if (normalized.includes("gemini")) {
225
+ if (normalized.includes("3.1") || normalized.includes("3_1")) {
226
+ return "gemini-3.1-pro";
214
227
  }
215
- return 'gemini-3-pro';
228
+ return "gemini-3-pro";
216
229
  }
217
- if (normalized.includes('pro')) {
230
+ if (normalized.includes("pro")) {
218
231
  return DEFAULT_MODEL;
219
232
  }
220
233
  // Passthrough for custom/OpenRouter model IDs.
@@ -228,73 +241,85 @@ export function inferModelFromLabel(modelValue) {
228
241
  if (normalized in MODEL_CONFIGS) {
229
242
  return normalized;
230
243
  }
231
- if (normalized.includes('/')) {
244
+ if (normalized.includes("/")) {
232
245
  return normalized;
233
246
  }
234
- if (normalized.includes('grok')) {
235
- return 'grok-4.1';
247
+ if (normalized.includes("grok")) {
248
+ return "grok-4.1";
236
249
  }
237
- if (normalized.includes('claude') && normalized.includes('sonnet')) {
238
- return 'claude-4.5-sonnet';
250
+ if (normalized.includes("claude") && normalized.includes("sonnet")) {
251
+ return "claude-4.6-sonnet";
239
252
  }
240
- if (normalized.includes('claude') && normalized.includes('opus')) {
241
- return 'claude-4.1-opus';
253
+ if (normalized.includes("claude") && normalized.includes("opus")) {
254
+ return "claude-4.1-opus";
242
255
  }
243
- if (normalized.includes('codex')) {
244
- return 'gpt-5.1-codex';
256
+ if (normalized.includes("codex")) {
257
+ return "gpt-5.1-codex";
245
258
  }
246
259
  if (isGeminiDeepThinkAlias(normalized)) {
247
- return 'gemini-3-pro-deep-think';
260
+ return "gemini-3-pro-deep-think";
248
261
  }
249
- if (normalized.includes('gemini')) {
250
- if (normalized.includes('3.1') || normalized.includes('3_1')) {
251
- return 'gemini-3.1-pro';
262
+ if (normalized.includes("gemini")) {
263
+ if (normalized.includes("3.1") || normalized.includes("3_1")) {
264
+ return "gemini-3.1-pro";
252
265
  }
253
- return 'gemini-3-pro';
266
+ return "gemini-3-pro";
267
+ }
268
+ if (normalized.includes("classic")) {
269
+ return "gpt-5-pro";
270
+ }
271
+ if (normalized.includes("thinking") && normalized.includes("heavy")) {
272
+ return "gpt-5.5";
273
+ }
274
+ if ((normalized.includes("5.5") || normalized.includes("5_5")) && normalized.includes("pro")) {
275
+ return "gpt-5.5-pro";
254
276
  }
255
- if (normalized.includes('classic')) {
256
- return 'gpt-5-pro';
277
+ if (normalized.includes("5.5") || normalized.includes("5_5")) {
278
+ return "gpt-5.5";
257
279
  }
258
- if ((normalized.includes('5.4') || normalized.includes('5_4')) && normalized.includes('pro')) {
259
- return 'gpt-5.4-pro';
280
+ if ((normalized.includes("5.4") || normalized.includes("5_4")) && normalized.includes("pro")) {
281
+ return "gpt-5.4-pro";
260
282
  }
261
- if (normalized.includes('5.4') || normalized.includes('5_4')) {
262
- return 'gpt-5.4';
283
+ if (normalized.includes("5.4") || normalized.includes("5_4")) {
284
+ return "gpt-5.4";
263
285
  }
264
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('pro')) {
265
- return 'gpt-5.2-pro';
286
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) && normalized.includes("pro")) {
287
+ return "gpt-5.2-pro";
266
288
  }
267
289
  // Browser-only: pass through 5.2 thinking/instant variants for browser label mapping
268
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('thinking')) {
269
- return 'gpt-5.2-thinking';
290
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) &&
291
+ normalized.includes("thinking")) {
292
+ return "gpt-5.2-thinking";
270
293
  }
271
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('instant')) {
272
- return 'gpt-5.2-instant';
294
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) &&
295
+ normalized.includes("instant")) {
296
+ return "gpt-5.2-instant";
273
297
  }
274
- if (normalized.includes('5.0') || normalized.includes('5-pro')) {
275
- return 'gpt-5-pro';
298
+ if (normalized.includes("5.0") || normalized.includes("5-pro")) {
299
+ return "gpt-5-pro";
276
300
  }
277
- if (normalized.includes('gpt-5') &&
278
- normalized.includes('pro') &&
279
- !normalized.includes('5.1') &&
280
- !normalized.includes('5.2') &&
281
- !normalized.includes('5.4')) {
282
- return 'gpt-5-pro';
301
+ if (normalized.includes("gpt-5") &&
302
+ normalized.includes("pro") &&
303
+ !normalized.includes("5.1") &&
304
+ !normalized.includes("5.2") &&
305
+ !normalized.includes("5.5") &&
306
+ !normalized.includes("5.4")) {
307
+ return "gpt-5-pro";
283
308
  }
284
- if ((normalized.includes('5.1') || normalized.includes('5_1')) && normalized.includes('pro')) {
285
- return 'gpt-5.1-pro';
309
+ if ((normalized.includes("5.1") || normalized.includes("5_1")) && normalized.includes("pro")) {
310
+ return "gpt-5.1-pro";
286
311
  }
287
- if (normalized.includes('pro')) {
312
+ if (normalized.includes("pro")) {
288
313
  return DEFAULT_MODEL;
289
314
  }
290
- if (normalized.includes('5.1') || normalized.includes('5_1')) {
291
- return 'gpt-5.1';
315
+ if (normalized.includes("5.1") || normalized.includes("5_1")) {
316
+ return "gpt-5.1";
292
317
  }
293
- if (normalized.includes('thinking')) {
294
- return 'gpt-5.2-thinking';
318
+ if (normalized.includes("thinking")) {
319
+ return "gpt-5.2-thinking";
295
320
  }
296
- if (normalized.includes('instant') || normalized.includes('fast')) {
297
- return 'gpt-5.2-instant';
321
+ if (normalized.includes("instant") || normalized.includes("fast")) {
322
+ return "gpt-5.2-instant";
298
323
  }
299
- return 'gpt-5.2';
324
+ return "gpt-5.2";
300
325
  }
@@ -1,2 +1,2 @@
1
1
  // Utilities for handling OSC progress codes embedded in stored logs.
2
- export { sanitizeOscProgress } from 'osc-progress';
2
+ export { sanitizeOscProgress } from "osc-progress";
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import chalk from "chalk";
4
+ import { buildBrowserConfig } from "./browserConfig.js";
5
+ import { readFiles } from "../oracle/files.js";
6
+ import { loadUserConfig } from "../config.js";
7
+ import { resolveConfiguredMaxFileSizeBytes } from "./fileSize.js";
8
+ import { runBrowserProjectSources } from "../browser/projectSourcesRunner.js";
9
+ import { normalizeProjectSourcesUrl } from "../projectSources/url.js";
10
+ export async function runProjectSourcesCliCommand(operation, options) {
11
+ const { config: userConfig } = await loadUserConfig();
12
+ const configuredUrl = userConfig.browser?.chatgptUrl ?? userConfig.browser?.url;
13
+ const projectUrl = normalizeProjectSourcesUrl(options.chatgptUrl ?? configuredUrl ?? "");
14
+ const maxFileSizeBytes = options.maxFileSizeBytes ?? resolveConfiguredMaxFileSizeBytes(userConfig, process.env);
15
+ const files = operation === "add"
16
+ ? await resolveProjectSourceFiles(options.file ?? [], {
17
+ cwd: process.cwd(),
18
+ maxFileSizeBytes,
19
+ })
20
+ : [];
21
+ if (operation === "add" && files.length === 0) {
22
+ throw new Error("project-sources add requires at least one --file.");
23
+ }
24
+ const browserConfig = await buildProjectSourcesBrowserConfig({
25
+ options,
26
+ projectUrl,
27
+ configuredBrowser: userConfig.browser ?? {},
28
+ });
29
+ const result = await runBrowserProjectSources({
30
+ operation,
31
+ chatgptUrl: projectUrl,
32
+ files,
33
+ dryRun: options.dryRun,
34
+ config: browserConfig,
35
+ log: (message) => {
36
+ if (options.verbose || !message.startsWith("[debug]")) {
37
+ console.log(chalk.dim(message));
38
+ }
39
+ },
40
+ });
41
+ printProjectSourcesResult(result, Boolean(options.json));
42
+ }
43
+ export async function resolveProjectSourceFiles(fileInputs, options) {
44
+ const files = await readFiles(fileInputs, {
45
+ cwd: options.cwd,
46
+ maxFileSizeBytes: options.maxFileSizeBytes,
47
+ readContents: false,
48
+ });
49
+ const attachments = [];
50
+ for (const file of files) {
51
+ const stats = await fs.stat(file.path);
52
+ attachments.push({
53
+ path: file.path,
54
+ displayPath: path.relative(options.cwd, file.path) || path.basename(file.path),
55
+ sizeBytes: stats.size,
56
+ });
57
+ }
58
+ return attachments;
59
+ }
60
+ export async function buildProjectSourcesBrowserConfig({ options, projectUrl, configuredBrowser, }) {
61
+ const flagConfig = removeUndefined(await buildBrowserConfig({
62
+ ...options,
63
+ model: "gpt-5.5-pro",
64
+ chatgptUrl: projectUrl,
65
+ }));
66
+ const envProfileDir = process.env.ORACLE_BROWSER_PROFILE_DIR?.trim();
67
+ const manualLogin = flagConfig.manualLogin ?? configuredBrowser.manualLogin ?? (envProfileDir ? true : undefined);
68
+ const manualLoginProfileDir = manualLogin === true
69
+ ? (flagConfig.manualLoginProfileDir ??
70
+ configuredBrowser.manualLoginProfileDir ??
71
+ envProfileDir ??
72
+ null)
73
+ : null;
74
+ return {
75
+ ...configuredBrowser,
76
+ ...flagConfig,
77
+ url: projectUrl,
78
+ chatgptUrl: projectUrl,
79
+ cookieSync: manualLogin ? false : (flagConfig.cookieSync ?? configuredBrowser.cookieSync),
80
+ manualLogin,
81
+ manualLoginProfileDir,
82
+ desiredModel: null,
83
+ modelStrategy: "ignore",
84
+ researchMode: "off",
85
+ };
86
+ }
87
+ function removeUndefined(value) {
88
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
89
+ }
90
+ function printProjectSourcesResult(result, json) {
91
+ if (json) {
92
+ console.log(JSON.stringify(result, null, 2));
93
+ return;
94
+ }
95
+ if (result.status === "dry-run") {
96
+ console.log(chalk.bold(`Project Sources ${result.operation} dry run`));
97
+ console.log(`Project: ${result.projectUrl}`);
98
+ const plan = result.plannedUploads ?? [];
99
+ if (plan.length > 0) {
100
+ console.log(`Planned uploads: ${plan.length}`);
101
+ for (const upload of plan) {
102
+ console.log(` batch ${upload.batch}: ${upload.displayPath}`);
103
+ }
104
+ }
105
+ return;
106
+ }
107
+ console.log(chalk.bold(`Project Sources ${result.operation} completed`));
108
+ console.log(`Project: ${result.projectUrl}`);
109
+ const before = result.sourcesBefore?.length ?? 0;
110
+ const after = result.sourcesAfter?.length ?? 0;
111
+ console.log(`Before: ${before}`);
112
+ console.log(`After: ${after}`);
113
+ if (result.added && result.added.length > 0) {
114
+ console.log(`Added: ${result.added.map((source) => source.name).join(", ")}`);
115
+ }
116
+ }
@@ -10,8 +10,8 @@ export function shouldRequirePrompt(rawArgs, options) {
10
10
  options.execSession ||
11
11
  options.status ||
12
12
  options.debugHelp ||
13
- firstArg === 'status' ||
14
- firstArg === 'session');
13
+ firstArg === "status" ||
14
+ firstArg === "session");
15
15
  const requiresPrompt = options.renderMarkdown || Boolean(options.preview) || Boolean(options.dryRun) || !bypassPrompt;
16
16
  return requiresPrompt && !options.prompt;
17
17
  }
@@ -1,4 +1,4 @@
1
- import { ensureShikiReady, renderMarkdownAnsi } from './markdownRenderer.js';
1
+ import { ensureShikiReady, renderMarkdownAnsi } from "./markdownRenderer.js";
2
2
  export function shouldRenderRich(options = {}) {
3
3
  return options.richTty ?? Boolean(process.stdout.isTTY);
4
4
  }
@@ -1,4 +1,4 @@
1
- import { attachSession, showStatus } from './sessionDisplay.js';
1
+ import { attachSession, showStatus } from "./sessionDisplay.js";
2
2
  const defaultDeps = {
3
3
  attachSession,
4
4
  showStatus,
@@ -1,51 +1,55 @@
1
- import { DEFAULT_MODEL, MODEL_CONFIGS } from '../oracle.js';
2
- import { resolveEngine } from './engine.js';
3
- import { normalizeModelOption, inferModelFromLabel, resolveApiModel, normalizeBaseUrl } from './options.js';
4
- import { resolveGeminiModelId } from '../oracle/gemini.js';
5
- import { PromptValidationError } from '../oracle/errors.js';
6
- import { normalizeChatGptModelForBrowser } from './browserConfig.js';
7
- import { resolveConfiguredMaxFileSizeBytes } from './fileSize.js';
1
+ import { DEFAULT_MODEL, MODEL_CONFIGS } from "../oracle.js";
2
+ import { resolveEngine } from "./engine.js";
3
+ import { normalizeModelOption, inferModelFromLabel, resolveApiModel, normalizeBaseUrl, } from "./options.js";
4
+ import { resolveGeminiModelId } from "../oracle/gemini.js";
5
+ import { PromptValidationError } from "../oracle/errors.js";
6
+ import { normalizeChatGptModelForBrowser } from "./browserConfig.js";
7
+ import { resolveConfiguredMaxFileSizeBytes } from "./fileSize.js";
8
8
  export function resolveRunOptionsFromConfig({ prompt, files = [], model, models, engine, userConfig, env = process.env, }) {
9
9
  const resolvedEngine = resolveEngineWithConfig({ engine, configEngine: userConfig?.engine, env });
10
- const browserRequested = engine === 'browser';
11
- const browserConfigured = userConfig?.engine === 'browser';
10
+ const browserRequested = engine === "browser";
11
+ const browserConfigured = userConfig?.engine === "browser";
12
12
  const requestedModelList = Array.isArray(models) ? models : [];
13
- const normalizedRequestedModels = requestedModelList.map((entry) => normalizeModelOption(entry)).filter(Boolean);
13
+ const normalizedRequestedModels = requestedModelList
14
+ .map((entry) => normalizeModelOption(entry))
15
+ .filter(Boolean);
14
16
  const cliModelArg = normalizeModelOption(model ?? userConfig?.model) || DEFAULT_MODEL;
15
- const inferredModel = resolvedEngine === 'browser' && normalizedRequestedModels.length === 0
17
+ const inferredModel = resolvedEngine === "browser" && normalizedRequestedModels.length === 0
16
18
  ? inferModelFromLabel(cliModelArg)
17
19
  : resolveApiModel(cliModelArg);
18
- // Browser engine maps Pro/legacy aliases to the latest ChatGPT picker targets (GPT-5.4 / GPT-5.4 Pro).
19
- const resolvedModel = resolvedEngine === 'browser' ? normalizeChatGptModelForBrowser(inferredModel) : inferredModel;
20
- const isCodex = resolvedModel.startsWith('gpt-5.1-codex');
21
- const isClaude = resolvedModel.startsWith('claude');
22
- const isGrok = resolvedModel.startsWith('grok');
23
- const isGeminiApiOnly = resolvedModel === 'gemini-3.1-pro';
24
- const engineWasBrowser = resolvedEngine === 'browser';
20
+ // Browser engine maps Pro/legacy aliases to the latest ChatGPT picker targets.
21
+ const resolvedModel = resolvedEngine === "browser" ? normalizeChatGptModelForBrowser(inferredModel) : inferredModel;
22
+ const isCodex = resolvedModel.startsWith("gpt-5.1-codex");
23
+ const isClaude = resolvedModel.startsWith("claude");
24
+ const isGrok = resolvedModel.startsWith("grok");
25
+ const isGeminiApiOnly = resolvedModel === "gemini-3.1-pro";
26
+ const engineWasBrowser = resolvedEngine === "browser";
25
27
  const allModels = normalizedRequestedModels.length > 0
26
28
  ? Array.from(new Set(normalizedRequestedModels.map((entry) => resolveApiModel(entry))))
27
29
  : [resolvedModel];
28
- const includesGeminiApiOnly = allModels.some((m) => m === 'gemini-3.1-pro');
30
+ const includesGeminiApiOnly = allModels.some((m) => m === "gemini-3.1-pro");
29
31
  if ((browserRequested || browserConfigured) && includesGeminiApiOnly) {
30
- throw new PromptValidationError('gemini-3.1-pro is API-only today. Use --engine api or switch to gemini-3-pro for Gemini web.', { engine: 'browser', models: allModels });
32
+ throw new PromptValidationError("gemini-3.1-pro is API-only today. Use --engine api or switch to gemini-3-pro for Gemini web.", { engine: "browser", models: allModels });
31
33
  }
32
- const isBrowserCompatible = (m) => m.startsWith('gpt-') || m.startsWith('gemini');
34
+ const isBrowserCompatible = (m) => m.startsWith("gpt-") || m.startsWith("gemini");
33
35
  const hasNonBrowserCompatibleTarget = (browserRequested || browserConfigured) && allModels.some((m) => !isBrowserCompatible(m));
34
36
  if (hasNonBrowserCompatibleTarget) {
35
- throw new PromptValidationError('Browser engine only supports GPT and Gemini models. Re-run with --engine api for Grok, Claude, or other models.', { engine: 'browser', models: allModels });
37
+ throw new PromptValidationError("Browser engine only supports GPT and Gemini models. Re-run with --engine api for Grok, Claude, or other models.", { engine: "browser", models: allModels });
36
38
  }
37
39
  const engineCoercedToApi = engineWasBrowser && (isCodex || isClaude || isGrok || isGeminiApiOnly);
38
- const fixedEngine = isCodex || isClaude || isGrok || isGeminiApiOnly || normalizedRequestedModels.length > 0 ? 'api' : resolvedEngine;
40
+ const fixedEngine = isCodex || isClaude || isGrok || isGeminiApiOnly || normalizedRequestedModels.length > 0
41
+ ? "api"
42
+ : resolvedEngine;
39
43
  const promptWithSuffix = userConfig?.promptSuffix && userConfig.promptSuffix.trim().length > 0
40
44
  ? `${prompt.trim()}\n${userConfig.promptSuffix}`
41
45
  : prompt;
42
- const search = userConfig?.search !== 'off';
46
+ const search = userConfig?.search !== "off";
43
47
  const heartbeatIntervalMs = userConfig?.heartbeatSeconds !== undefined ? userConfig.heartbeatSeconds * 1000 : 30_000;
44
48
  const maxFileSizeBytes = resolveConfiguredMaxFileSizeBytes(userConfig, env);
45
49
  const baseUrl = normalizeBaseUrl(userConfig?.apiBaseUrl ??
46
50
  (isClaude ? env.ANTHROPIC_BASE_URL : isGrok ? env.XAI_BASE_URL : env.OPENAI_BASE_URL));
47
51
  const uniqueMultiModels = normalizedRequestedModels.length > 0 ? allModels : [];
48
- const includesCodexMultiModel = uniqueMultiModels.some((entry) => entry.startsWith('gpt-5.1-codex'));
52
+ const includesCodexMultiModel = uniqueMultiModels.some((entry) => entry.startsWith("gpt-5.1-codex"));
49
53
  if (includesCodexMultiModel && browserRequested) {
50
54
  // Silent coerce; multi-model still forces API.
51
55
  }
@@ -69,8 +73,8 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
69
73
  function resolveEngineWithConfig({ engine, configEngine, env, }) {
70
74
  if (engine)
71
75
  return engine;
72
- const envOverride = (env.ORACLE_ENGINE ?? '').trim().toLowerCase();
73
- if (envOverride === 'api' || envOverride === 'browser') {
76
+ const envOverride = (env.ORACLE_ENGINE ?? "").trim().toLowerCase();
77
+ if (envOverride === "api" || envOverride === "browser") {
74
78
  return envOverride;
75
79
  }
76
80
  if (configEngine)
@@ -78,7 +82,7 @@ function resolveEngineWithConfig({ engine, configEngine, env, }) {
78
82
  return resolveEngine({ engine: undefined, env });
79
83
  }
80
84
  function resolveEffectiveModelId(model) {
81
- if (typeof model === 'string' && model.startsWith('gemini')) {
85
+ if (typeof model === "string" && model.startsWith("gemini")) {
82
86
  return resolveGeminiModelId(model);
83
87
  }
84
88
  const config = MODEL_CONFIGS[model];