codex-chatgpt-control 0.3.0-alpha.1 → 0.5.1-alpha.1

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 (224) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +42 -2
  3. package/contracts/v1/fixtures/backend-capabilities.json +10 -0
  4. package/contracts/v1/fixtures/command-descriptors.json +265 -8
  5. package/contracts/v1/fixtures/describe-runner-run.json +5 -2
  6. package/contracts/v1/fixtures/doctor-scenario-preflight.json +34 -2
  7. package/contracts/v1/fixtures/help-root.json +1 -1
  8. package/contracts/v1/fixtures/output-json-parse-success.json +9 -0
  9. package/contracts/v1/fixtures/run-timeout-partial.json +15 -4
  10. package/contracts/v1/fixtures/stream-in-progress.ndjson +3 -0
  11. package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
  12. package/contracts/v1/fixtures/surface-chat-legacy.json +35 -0
  13. package/contracts/v1/fixtures/surface-chat-simplified.json +37 -0
  14. package/contracts/v1/fixtures/surface-sidebar-false-positive.json +29 -0
  15. package/contracts/v1/fixtures/surface-work-advanced.json +45 -0
  16. package/contracts/v1/fixtures/surface-work-basic.json +38 -0
  17. package/contracts/v1/fixtures/workflow-ask-success.json +8 -0
  18. package/contracts/v1/manifest.json +32 -1
  19. package/contracts/v1/parity-suite.json +124 -0
  20. package/contracts/v1/schemas/backend-request.schema.json +15 -0
  21. package/contracts/v1/schemas/manifest.schema.json +6 -3
  22. package/contracts/v1/schemas/surface-profile.schema.json +166 -0
  23. package/dist/codex-chatgpt-control-backend.mjs +3475 -367
  24. package/dist/codex-chatgpt-control.bundle.mjs +5249 -2102
  25. package/dist/src/backend/client.d.ts +26 -1
  26. package/dist/src/backend/client.js +23 -1
  27. package/dist/src/backend/protocol.d.ts +1 -1
  28. package/dist/src/backend/protocol.js +10 -0
  29. package/dist/src/backend/session.js +20 -0
  30. package/dist/src/browser/attach.d.ts +1 -0
  31. package/dist/src/browser/attach.js +3 -3
  32. package/dist/src/browser/downloads.d.ts +1 -1
  33. package/dist/src/browser/downloads.js +13 -4
  34. package/dist/src/browser/page-state.js +5 -2
  35. package/dist/src/client.d.ts +31 -1
  36. package/dist/src/client.js +69 -14
  37. package/dist/src/commands/artifacts.js +1 -7
  38. package/dist/src/commands/configuration.d.ts +15 -0
  39. package/dist/src/commands/configuration.js +897 -0
  40. package/dist/src/commands/doctor.js +38 -1
  41. package/dist/src/commands/experience.d.ts +13 -0
  42. package/dist/src/commands/experience.js +421 -0
  43. package/dist/src/commands/files.js +160 -10
  44. package/dist/src/commands/messages.d.ts +2 -1
  45. package/dist/src/commands/messages.js +138 -16
  46. package/dist/src/commands/modes.js +28 -14
  47. package/dist/src/commands/registry.js +74 -7
  48. package/dist/src/commands/response-actions.js +1 -7
  49. package/dist/src/commands/sequence.js +24 -1
  50. package/dist/src/commands/session.d.ts +2 -0
  51. package/dist/src/commands/session.js +50 -1
  52. package/dist/src/commands/threads.js +1 -7
  53. package/dist/src/commands/work.d.ts +6 -0
  54. package/dist/src/commands/work.js +338 -0
  55. package/dist/src/dom/locale/am.d.ts +15 -1
  56. package/dist/src/dom/locale/am.js +15 -1
  57. package/dist/src/dom/locale/ar.d.ts +15 -1
  58. package/dist/src/dom/locale/ar.js +18 -4
  59. package/dist/src/dom/locale/bg.d.ts +15 -1
  60. package/dist/src/dom/locale/bg.js +18 -4
  61. package/dist/src/dom/locale/bn.d.ts +15 -1
  62. package/dist/src/dom/locale/bn.js +15 -1
  63. package/dist/src/dom/locale/bs.d.ts +14 -1
  64. package/dist/src/dom/locale/bs.js +18 -5
  65. package/dist/src/dom/locale/ca.d.ts +14 -1
  66. package/dist/src/dom/locale/ca.js +19 -6
  67. package/dist/src/dom/locale/cs.d.ts +14 -1
  68. package/dist/src/dom/locale/cs.js +14 -1
  69. package/dist/src/dom/locale/da.d.ts +14 -1
  70. package/dist/src/dom/locale/da.js +15 -2
  71. package/dist/src/dom/locale/de.d.ts +16 -3
  72. package/dist/src/dom/locale/de.js +18 -5
  73. package/dist/src/dom/locale/el.d.ts +15 -1
  74. package/dist/src/dom/locale/el.js +18 -4
  75. package/dist/src/dom/locale/en.d.ts +27 -2
  76. package/dist/src/dom/locale/en.js +28 -2
  77. package/dist/src/dom/locale/es-419.d.ts +15 -1
  78. package/dist/src/dom/locale/es-419.js +15 -1
  79. package/dist/src/dom/locale/es-ES.d.ts +15 -1
  80. package/dist/src/dom/locale/es-ES.js +15 -1
  81. package/dist/src/dom/locale/et.d.ts +14 -1
  82. package/dist/src/dom/locale/et.js +14 -1
  83. package/dist/src/dom/locale/fa.d.ts +14 -1
  84. package/dist/src/dom/locale/fa.js +16 -3
  85. package/dist/src/dom/locale/fi.d.ts +15 -1
  86. package/dist/src/dom/locale/fi.js +15 -1
  87. package/dist/src/dom/locale/fr-CA.d.ts +13 -1
  88. package/dist/src/dom/locale/fr-CA.js +13 -1
  89. package/dist/src/dom/locale/fr-FR.d.ts +16 -3
  90. package/dist/src/dom/locale/fr-FR.js +20 -7
  91. package/dist/src/dom/locale/gu.d.ts +15 -1
  92. package/dist/src/dom/locale/gu.js +19 -5
  93. package/dist/src/dom/locale/hi.d.ts +15 -1
  94. package/dist/src/dom/locale/hi.js +17 -3
  95. package/dist/src/dom/locale/hr.d.ts +14 -1
  96. package/dist/src/dom/locale/hr.js +17 -4
  97. package/dist/src/dom/locale/hu.d.ts +15 -1
  98. package/dist/src/dom/locale/hu.js +18 -4
  99. package/dist/src/dom/locale/hy.d.ts +15 -1
  100. package/dist/src/dom/locale/hy.js +18 -4
  101. package/dist/src/dom/locale/id.d.ts +14 -1
  102. package/dist/src/dom/locale/id.js +16 -3
  103. package/dist/src/dom/locale/index.d.ts +17 -2
  104. package/dist/src/dom/locale/index.js +65 -0
  105. package/dist/src/dom/locale/is.d.ts +14 -1
  106. package/dist/src/dom/locale/is.js +14 -1
  107. package/dist/src/dom/locale/it.d.ts +13 -1
  108. package/dist/src/dom/locale/it.js +17 -5
  109. package/dist/src/dom/locale/ja.d.ts +15 -1
  110. package/dist/src/dom/locale/ja.js +19 -5
  111. package/dist/src/dom/locale/ka.d.ts +15 -1
  112. package/dist/src/dom/locale/ka.js +15 -1
  113. package/dist/src/dom/locale/kk.d.ts +15 -1
  114. package/dist/src/dom/locale/kk.js +17 -3
  115. package/dist/src/dom/locale/kn.d.ts +15 -1
  116. package/dist/src/dom/locale/kn.js +19 -5
  117. package/dist/src/dom/locale/ko.d.ts +13 -1
  118. package/dist/src/dom/locale/ko.js +13 -1
  119. package/dist/src/dom/locale/lt.d.ts +15 -1
  120. package/dist/src/dom/locale/lt.js +17 -3
  121. package/dist/src/dom/locale/lv.d.ts +15 -1
  122. package/dist/src/dom/locale/lv.js +15 -1
  123. package/dist/src/dom/locale/mk.d.ts +15 -1
  124. package/dist/src/dom/locale/mk.js +19 -5
  125. package/dist/src/dom/locale/ml.d.ts +15 -1
  126. package/dist/src/dom/locale/ml.js +17 -3
  127. package/dist/src/dom/locale/mn.d.ts +15 -1
  128. package/dist/src/dom/locale/mn.js +15 -1
  129. package/dist/src/dom/locale/mr.d.ts +15 -1
  130. package/dist/src/dom/locale/mr.js +17 -3
  131. package/dist/src/dom/locale/ms.d.ts +13 -1
  132. package/dist/src/dom/locale/ms.js +13 -1
  133. package/dist/src/dom/locale/my.d.ts +15 -1
  134. package/dist/src/dom/locale/my.js +17 -3
  135. package/dist/src/dom/locale/nb.d.ts +14 -1
  136. package/dist/src/dom/locale/nb.js +14 -1
  137. package/dist/src/dom/locale/nl.d.ts +14 -1
  138. package/dist/src/dom/locale/nl.js +16 -3
  139. package/dist/src/dom/locale/pa.d.ts +15 -1
  140. package/dist/src/dom/locale/pa.js +19 -5
  141. package/dist/src/dom/locale/pl.d.ts +14 -1
  142. package/dist/src/dom/locale/pl.js +16 -3
  143. package/dist/src/dom/locale/pt-BR.d.ts +15 -1
  144. package/dist/src/dom/locale/pt-BR.js +17 -3
  145. package/dist/src/dom/locale/pt-PT.d.ts +15 -1
  146. package/dist/src/dom/locale/pt-PT.js +19 -5
  147. package/dist/src/dom/locale/ro.d.ts +13 -1
  148. package/dist/src/dom/locale/ro.js +16 -4
  149. package/dist/src/dom/locale/ru.d.ts +17 -1
  150. package/dist/src/dom/locale/ru.js +18 -2
  151. package/dist/src/dom/locale/sk.d.ts +14 -1
  152. package/dist/src/dom/locale/sk.js +16 -3
  153. package/dist/src/dom/locale/sl.d.ts +14 -1
  154. package/dist/src/dom/locale/sl.js +17 -4
  155. package/dist/src/dom/locale/so.d.ts +15 -1
  156. package/dist/src/dom/locale/so.js +17 -3
  157. package/dist/src/dom/locale/sq.d.ts +13 -1
  158. package/dist/src/dom/locale/sq.js +17 -5
  159. package/dist/src/dom/locale/sr.d.ts +17 -1
  160. package/dist/src/dom/locale/sr.js +18 -2
  161. package/dist/src/dom/locale/sv.d.ts +14 -1
  162. package/dist/src/dom/locale/sv.js +14 -1
  163. package/dist/src/dom/locale/sw.d.ts +15 -1
  164. package/dist/src/dom/locale/sw.js +15 -1
  165. package/dist/src/dom/locale/ta.d.ts +15 -1
  166. package/dist/src/dom/locale/ta.js +19 -5
  167. package/dist/src/dom/locale/te.d.ts +15 -1
  168. package/dist/src/dom/locale/te.js +19 -5
  169. package/dist/src/dom/locale/th.d.ts +7 -1
  170. package/dist/src/dom/locale/th.js +7 -1
  171. package/dist/src/dom/locale/tl.d.ts +11 -5
  172. package/dist/src/dom/locale/tl.js +11 -5
  173. package/dist/src/dom/locale/tr.d.ts +14 -1
  174. package/dist/src/dom/locale/tr.js +16 -3
  175. package/dist/src/dom/locale/types.d.ts +18 -1
  176. package/dist/src/dom/locale/uk.d.ts +15 -1
  177. package/dist/src/dom/locale/uk.js +17 -3
  178. package/dist/src/dom/locale/ur.d.ts +15 -1
  179. package/dist/src/dom/locale/ur.js +18 -4
  180. package/dist/src/dom/locale/vi.d.ts +15 -1
  181. package/dist/src/dom/locale/vi.js +18 -4
  182. package/dist/src/dom/locale/zh-HK.d.ts +15 -1
  183. package/dist/src/dom/locale/zh-HK.js +17 -3
  184. package/dist/src/dom/locale/zh-Hans.d.ts +15 -1
  185. package/dist/src/dom/locale/zh-Hans.js +19 -5
  186. package/dist/src/dom/locale/zh-TW.d.ts +15 -1
  187. package/dist/src/dom/locale/zh-TW.js +17 -3
  188. package/dist/src/dom/menus.js +26 -2
  189. package/dist/src/dom/selectors.js +6 -1
  190. package/dist/src/index.d.ts +3 -0
  191. package/dist/src/index.js +3 -0
  192. package/dist/src/platform/local-paths.d.ts +1 -0
  193. package/dist/src/platform/local-paths.js +7 -3
  194. package/dist/src/runner/responses.d.ts +3 -1
  195. package/dist/src/runner/responses.js +16 -1
  196. package/dist/src/runner/result.js +129 -4
  197. package/dist/src/runner/stream.d.ts +1 -1
  198. package/dist/src/runner/stream.js +6 -0
  199. package/dist/src/runner/types.d.ts +35 -1
  200. package/dist/src/safety/risk.d.ts +10 -0
  201. package/dist/src/safety/risk.js +10 -0
  202. package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +15 -0
  203. package/dist/src/scripts/apply-intelligence-locale-captures.js +225 -7
  204. package/dist/src/scripts/capture-intelligence-locales.d.ts +8 -0
  205. package/dist/src/scripts/capture-intelligence-locales.js +663 -6
  206. package/dist/src/scripts/capture-surface-profile.d.ts +25 -0
  207. package/dist/src/scripts/capture-surface-profile.js +354 -0
  208. package/dist/src/scripts/live-smoke/scenarios.d.ts +2 -0
  209. package/dist/src/scripts/live-smoke/scenarios.js +347 -5
  210. package/dist/src/scripts/live-smoke.js +2 -0
  211. package/dist/src/scripts/locale-capture/surface-graph.d.ts +26 -0
  212. package/dist/src/scripts/locale-capture/surface-graph.js +43 -0
  213. package/dist/src/scripts/release-canary-module.d.ts +20 -0
  214. package/dist/src/scripts/release-canary-module.js +86 -0
  215. package/dist/src/types.d.ts +248 -2
  216. package/package.json +9 -3
  217. package/references/2026-07-16-chat-work-surfaces.md +100 -0
  218. package/references/agents-runner.md +13 -2
  219. package/references/backend-protocol.md +26 -9
  220. package/references/language-coverage.md +51 -3
  221. package/references/localization.md +52 -6
  222. package/references/python-parity.md +12 -2
  223. package/references/responses-adapter.md +12 -1
  224. package/references/troubleshooting.md +38 -4
@@ -0,0 +1,25 @@
1
+ import { detectExperienceFromSnapshot, readSurfaceSnapshot } from "../commands/experience.js";
2
+ import type { BrowserLike, ChatGPTExperience, ConfigurationInspectionData, ExistingTabPolicy, SurfaceProfileFixture, SurfaceProfileSupportState } from "../types.js";
3
+ type CaptureRuntime = {
4
+ agent?: unknown;
5
+ browser?: BrowserLike;
6
+ };
7
+ type CaptureOptions = {
8
+ id: string;
9
+ out: string;
10
+ locale?: string;
11
+ region: string;
12
+ accountScope: string;
13
+ planScope: string;
14
+ workspaceScope: string;
15
+ supportState: SurfaceProfileSupportState;
16
+ provenance: string;
17
+ tabId?: string;
18
+ ifMissing: NonNullable<ExistingTabPolicy["ifMissing"]>;
19
+ experience?: Exclude<ChatGPTExperience, "unknown">;
20
+ restoreExperience: boolean;
21
+ };
22
+ export declare function main(argv?: string[], runtime?: CaptureRuntime): Promise<number>;
23
+ export declare function buildSurfaceProfileDraft(options: Pick<CaptureOptions, "id" | "region" | "accountScope" | "planScope" | "workspaceScope" | "supportState" | "provenance">, locale: string, snapshot: Awaited<ReturnType<typeof readSurfaceSnapshot>>, detected: ReturnType<typeof detectExperienceFromSnapshot>, inspection: ConfigurationInspectionData, observedAt?: string): SurfaceProfileFixture;
24
+ export declare function parseArgs(argv: readonly string[]): CaptureOptions;
25
+ export {};
@@ -0,0 +1,354 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { attachChatGPTBrowser } from "../browser/attach.js";
4
+ import { inspectConfiguration } from "../commands/configuration.js";
5
+ import { detectExperienceFromSnapshot, openExperience, readSurfaceSnapshot } from "../commands/experience.js";
6
+ import { BROWSER_BRIDGE_REMEDIATION, BROWSER_BRIDGE_UNAVAILABLE_MESSAGE, toCommandResult } from "../errors.js";
7
+ const CHATGPT_HOME = "https://chatgpt.com/";
8
+ const DEFAULT_PROVENANCE = "Read-only sanitized capture from visible ChatGPT controls; review before committing.";
9
+ class UsageError extends Error {
10
+ exitCode;
11
+ constructor(message, exitCode = 2) {
12
+ super(message);
13
+ this.exitCode = exitCode;
14
+ this.name = "UsageError";
15
+ }
16
+ }
17
+ const USAGE = [
18
+ "Usage:",
19
+ " npm run capture:surface-profile -- --id work-basic-en",
20
+ " npm run capture:surface-profile -- --id chat-simplified-de --locale de-DE --out ../../outputs/surface-profiles/chat-simplified-de.json",
21
+ "",
22
+ "Options:",
23
+ " --id Required normalized profile id.",
24
+ " --out Draft JSON path. Defaults under ../../outputs/surface-profiles/.",
25
+ " --locale Known BCP47 locale; otherwise reads documentElement.lang.",
26
+ " --region Normalized supplied region group. Default: not-recorded.",
27
+ " --account-scope Normalized supplied account group. Default: not-recorded.",
28
+ " --plan-scope Normalized supplied plan group. Default: not-recorded.",
29
+ " --workspace-scope Normalized supplied workspace group. Default: not-recorded.",
30
+ " --support-state current|compatibility|unverified|retired. Default: unverified.",
31
+ " --provenance Non-private provenance note.",
32
+ " --tab-id Claim an exact already-open ChatGPT tab.",
33
+ " --if-missing block|open|create. Default: block.",
34
+ " --experience Capture chat|work, switching visibly when needed.",
35
+ " --no-restore-experience Leave the requested surface selected. Default: restore.",
36
+ "",
37
+ "The capture does not mutate model configuration. With --experience it visibly",
38
+ "switches panes and restores the prior pane by default. It writes a sanitized draft",
39
+ "locally and never records",
40
+ "prompts, responses, sidebar titles, account names, cookies, or network data."
41
+ ].join("\n");
42
+ export async function main(argv = process.argv.slice(2), runtime = globalThis) {
43
+ let options;
44
+ try {
45
+ options = parseArgs(argv);
46
+ }
47
+ catch (error) {
48
+ if (error instanceof UsageError) {
49
+ console.log(error.message);
50
+ return error.exitCode;
51
+ }
52
+ throw error;
53
+ }
54
+ if ((runtime.agent === undefined || runtime.agent === null) && runtime.browser === undefined) {
55
+ console.log(JSON.stringify({
56
+ ok: false,
57
+ status: "blocked",
58
+ blocker: {
59
+ kind: "browser_bridge_unavailable",
60
+ code: "codex_chrome_bridge_unavailable",
61
+ message: BROWSER_BRIDGE_UNAVAILABLE_MESSAGE,
62
+ remediation: BROWSER_BRIDGE_REMEDIATION
63
+ }
64
+ }, null, 2));
65
+ return 2;
66
+ }
67
+ try {
68
+ const env = {};
69
+ if (runtime.agent !== undefined && runtime.agent !== null)
70
+ env.agent = runtime.agent;
71
+ if (runtime.browser !== undefined)
72
+ env.browser = runtime.browser;
73
+ const attached = await attachChatGPTBrowser(env, {
74
+ existingTab: {
75
+ target: options.tabId === undefined
76
+ ? { type: "selected", host: "chatgpt" }
77
+ : { type: "tabId", tabId: options.tabId },
78
+ ifMissing: options.ifMissing,
79
+ ifMultiple: "first",
80
+ requireChatGPT: true
81
+ },
82
+ url: CHATGPT_HOME
83
+ });
84
+ env.page = attached.page;
85
+ const initial = detectExperienceFromSnapshot(await readSurfaceSnapshot(attached.page));
86
+ const previousExperience = initial.experience === "unknown" ? undefined : initial.experience;
87
+ if (options.experience !== undefined && previousExperience === undefined) {
88
+ throw new Error("Cannot safely switch surfaces because the initial Chat/Work pane could not be identified.");
89
+ }
90
+ try {
91
+ if (options.experience !== undefined) {
92
+ const opened = await openExperience(env, { experience: options.experience, timeoutMs: 60_000 });
93
+ if (!opened.ok || opened.data?.experience !== options.experience) {
94
+ console.log(JSON.stringify(opened, null, 2));
95
+ return 1;
96
+ }
97
+ }
98
+ const snapshot = await readSurfaceSnapshot(attached.page);
99
+ const detected = detectExperienceFromSnapshot(snapshot);
100
+ const inspectionResult = await inspectConfiguration(env, { includeOptions: true });
101
+ if (!inspectionResult.ok || inspectionResult.data === undefined) {
102
+ console.log(JSON.stringify(inspectionResult, null, 2));
103
+ return 1;
104
+ }
105
+ const locale = options.locale ?? await readDocumentLocale(attached.page) ?? "und";
106
+ const profile = buildSurfaceProfileDraft(options, locale, snapshot, detected, inspectionResult.data);
107
+ await mkdir(dirname(options.out), { recursive: true });
108
+ await writeFile(options.out, `${JSON.stringify(profile, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
109
+ console.log(JSON.stringify({
110
+ ok: true,
111
+ status: "ok",
112
+ path: options.out,
113
+ id: profile.id,
114
+ experience: profile.expected.experience,
115
+ selectorProfile: profile.expected.selectorProfile,
116
+ supportState: profile.supportState,
117
+ restoreExperience: options.restoreExperience
118
+ }, null, 2));
119
+ return 0;
120
+ }
121
+ finally {
122
+ if (options.experience !== undefined
123
+ && options.restoreExperience
124
+ && previousExperience !== undefined
125
+ && previousExperience !== options.experience) {
126
+ const restored = await openExperience(env, { experience: previousExperience, timeoutMs: 60_000 });
127
+ if (!restored.ok || restored.data?.experience !== previousExperience) {
128
+ throw new Error(`Captured ${options.experience}, but failed to restore ${previousExperience}.`);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ catch (error) {
134
+ console.log(JSON.stringify(toCommandResult(error), null, 2));
135
+ return 1;
136
+ }
137
+ }
138
+ export function buildSurfaceProfileDraft(options, locale, snapshot, detected, inspection, observedAt = new Date().toISOString().slice(0, 10)) {
139
+ const activeEntries = Object.entries(inspection.active)
140
+ .filter((entry) => typeof entry[1] === "string");
141
+ const axisRows = inspection.experience === "work"
142
+ ? activeEntries.map(([axis, value]) => ({
143
+ axis,
144
+ label: `${titleCase(axis)} ${value}`,
145
+ value
146
+ }))
147
+ : [];
148
+ const openerLabel = inspection.experience === "chat"
149
+ ? inspection.active.intelligence ?? inspection.active.effort
150
+ : undefined;
151
+ const menuItems = Object.values(inspection.options)
152
+ .flatMap(optionsForAxis => optionsForAxis ?? [])
153
+ .map(option => ({
154
+ label: option.label,
155
+ normalized: option.id,
156
+ role: option.hasSubmenu === true ? "menuitem" : "menuitemradio",
157
+ checked: option.selected,
158
+ ...(option.hasSubmenu === undefined ? {} : { hasPopup: option.hasSubmenu })
159
+ }));
160
+ const safeComposerLabels = dedupeBounded(detected.evidence
161
+ .filter(item => item.source === "composer")
162
+ .map(item => item.label), 16);
163
+ const safeMainControls = dedupeBounded([
164
+ ...axisRows.map(row => row.label),
165
+ ...(openerLabel === undefined ? [] : [openerLabel]),
166
+ ...menuItems.map(item => item.label),
167
+ ...(inspection.selectorProfile === "work_advanced_v1" ? ["Advanced"] : [])
168
+ ], 80);
169
+ return {
170
+ schemaVersion: "chatgpt.browser_control.surface_profile.v1",
171
+ id: options.id,
172
+ observedAt,
173
+ provenance: options.provenance,
174
+ locale,
175
+ region: options.region,
176
+ accountScope: options.accountScope,
177
+ planScope: options.planScope,
178
+ workspaceScope: options.workspaceScope,
179
+ supportState: options.supportState,
180
+ snapshot: {
181
+ url: sanitizeChatGPTUrl(snapshot.url),
182
+ composerLabels: safeComposerLabels,
183
+ mainControls: safeMainControls,
184
+ mainText: detected.evidence
185
+ .filter(item => item.source === "heading")
186
+ .map(item => item.label)
187
+ .join(" ")
188
+ .slice(0, 2000)
189
+ },
190
+ panel: {
191
+ ...(openerLabel === undefined ? {} : { openerLabel }),
192
+ axisRows,
193
+ advancedVisible: inspection.selectorProfile === "work_advanced_v1"
194
+ },
195
+ menuItems,
196
+ expected: {
197
+ experience: detected.experience,
198
+ selectorProfile: inspection.selectorProfile,
199
+ availableAxes: inspection.availableAxes,
200
+ active: inspection.active
201
+ }
202
+ };
203
+ }
204
+ export function parseArgs(argv) {
205
+ let id;
206
+ let out;
207
+ let locale;
208
+ let region = "not-recorded";
209
+ let accountScope = "not-recorded";
210
+ let planScope = "not-recorded";
211
+ let workspaceScope = "not-recorded";
212
+ let supportState = "unverified";
213
+ let provenance = DEFAULT_PROVENANCE;
214
+ let tabId;
215
+ let ifMissing = "block";
216
+ let experience;
217
+ let restoreExperience = true;
218
+ for (let index = 0; index < argv.length; index += 1) {
219
+ const arg = argv[index];
220
+ if (arg === undefined)
221
+ continue;
222
+ switch (arg) {
223
+ case "--help":
224
+ case "-h":
225
+ throw new UsageError(USAGE, 0);
226
+ case "--id":
227
+ id = requiredValue(argv, ++index, arg);
228
+ break;
229
+ case "--out":
230
+ out = requiredValue(argv, ++index, arg);
231
+ break;
232
+ case "--locale":
233
+ locale = requiredValue(argv, ++index, arg);
234
+ break;
235
+ case "--region":
236
+ region = requiredValue(argv, ++index, arg);
237
+ break;
238
+ case "--account-scope":
239
+ accountScope = requiredValue(argv, ++index, arg);
240
+ break;
241
+ case "--plan-scope":
242
+ planScope = requiredValue(argv, ++index, arg);
243
+ break;
244
+ case "--workspace-scope":
245
+ workspaceScope = requiredValue(argv, ++index, arg);
246
+ break;
247
+ case "--support-state":
248
+ supportState = parseSupportState(requiredValue(argv, ++index, arg));
249
+ break;
250
+ case "--provenance":
251
+ provenance = requiredValue(argv, ++index, arg);
252
+ break;
253
+ case "--tab-id":
254
+ tabId = requiredValue(argv, ++index, arg);
255
+ break;
256
+ case "--if-missing":
257
+ ifMissing = parseIfMissing(requiredValue(argv, ++index, arg));
258
+ break;
259
+ case "--experience":
260
+ experience = parseExperience(requiredValue(argv, ++index, arg));
261
+ break;
262
+ case "--restore-experience":
263
+ restoreExperience = true;
264
+ break;
265
+ case "--no-restore-experience":
266
+ restoreExperience = false;
267
+ break;
268
+ default:
269
+ throw new UsageError(`Unknown argument: ${arg}\n\n${USAGE}`);
270
+ }
271
+ }
272
+ if (id === undefined) {
273
+ throw new UsageError(`--id is required.\n\n${USAGE}`);
274
+ }
275
+ for (const [field, value] of Object.entries({
276
+ id,
277
+ region,
278
+ accountScope,
279
+ planScope,
280
+ workspaceScope
281
+ })) {
282
+ assertNormalizedSlug(value, `--${field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)}`);
283
+ }
284
+ const defaultOut = resolve(process.cwd(), "..", "..", "outputs", "surface-profiles", `${new Date().toISOString().slice(0, 10)}-${id}.json`);
285
+ return {
286
+ id,
287
+ out: resolve(out ?? defaultOut),
288
+ ...(locale === undefined ? {} : { locale }),
289
+ region,
290
+ accountScope,
291
+ planScope,
292
+ workspaceScope,
293
+ supportState,
294
+ provenance,
295
+ ...(tabId === undefined ? {} : { tabId }),
296
+ ifMissing,
297
+ ...(experience === undefined ? {} : { experience }),
298
+ restoreExperience
299
+ };
300
+ }
301
+ async function readDocumentLocale(page) {
302
+ if (page.evaluate === undefined)
303
+ return undefined;
304
+ return page.evaluate(() => document.documentElement.lang || undefined).catch(() => undefined);
305
+ }
306
+ function sanitizeChatGPTUrl(value) {
307
+ const url = new URL(value || CHATGPT_HOME, CHATGPT_HOME);
308
+ url.search = "";
309
+ url.hash = "";
310
+ if (/^\/c\/[^/]+/.test(url.pathname)) {
311
+ url.pathname = url.pathname.replace(/^\/c\/[^/]+/, "/c/sanitized");
312
+ }
313
+ return `${url.origin}${url.pathname}`;
314
+ }
315
+ function titleCase(value) {
316
+ return value.length === 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
317
+ }
318
+ function dedupeBounded(values, limit) {
319
+ return Array.from(new Set(values.map(value => value.trim()).filter(Boolean))).slice(0, limit);
320
+ }
321
+ function requiredValue(argv, index, flag) {
322
+ const value = argv[index];
323
+ if (value === undefined || value.startsWith("--")) {
324
+ throw new UsageError(`${flag} requires a value.`);
325
+ }
326
+ return value;
327
+ }
328
+ function parseSupportState(value) {
329
+ if (["current", "compatibility", "unverified", "retired"].includes(value)) {
330
+ return value;
331
+ }
332
+ throw new UsageError(`Invalid --support-state: ${value}`);
333
+ }
334
+ function parseExperience(value) {
335
+ if (value === "chat" || value === "work")
336
+ return value;
337
+ throw new UsageError("--experience must be chat or work.");
338
+ }
339
+ function parseIfMissing(value) {
340
+ if (value === "block" || value === "open" || value === "create")
341
+ return value;
342
+ throw new UsageError(`Invalid --if-missing: ${value}`);
343
+ }
344
+ function assertNormalizedSlug(value, flag) {
345
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(value)) {
346
+ throw new UsageError(`${flag} must be a normalized lowercase slug without private names.`);
347
+ }
348
+ }
349
+ if (typeof process !== "undefined"
350
+ && Array.isArray(process.argv)
351
+ && typeof process.argv[1] === "string"
352
+ && import.meta.url === `file://${process.argv[1]}`) {
353
+ process.exitCode = await main();
354
+ }
@@ -1,3 +1,5 @@
1
+ import type { AskReadData, CommandResult } from "../../types.js";
1
2
  import type { LiveSmokeScenario } from "./types.js";
2
3
  export declare const requiredScenarios: LiveSmokeScenario[];
3
4
  export declare const optionalScenarios: LiveSmokeScenario[];
5
+ export declare function generatedFileAskCanProceed(result: CommandResult<AskReadData>): boolean;