codex-chatgpt-control 0.2.0-alpha.1 → 0.5.0-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.
- package/CHANGELOG.md +15 -0
- package/README.md +42 -2
- package/contracts/v1/fixtures/backend-capabilities.json +11 -0
- package/contracts/v1/fixtures/command-descriptors.json +289 -7
- package/contracts/v1/fixtures/describe-runner-run.json +5 -2
- package/contracts/v1/fixtures/doctor-scenario-preflight.json +45 -2
- package/contracts/v1/fixtures/help-root.json +1 -1
- package/contracts/v1/fixtures/output-json-parse-success.json +9 -0
- package/contracts/v1/fixtures/reports-create-redacted.json +1 -1
- package/contracts/v1/fixtures/run-timeout-partial.json +15 -4
- package/contracts/v1/fixtures/stream-in-progress.ndjson +3 -0
- package/contracts/v1/fixtures/stream-submitted-completed.ndjson +1 -1
- package/contracts/v1/fixtures/surface-chat-legacy.json +35 -0
- package/contracts/v1/fixtures/surface-chat-simplified.json +37 -0
- package/contracts/v1/fixtures/surface-sidebar-false-positive.json +29 -0
- package/contracts/v1/fixtures/surface-work-advanced.json +45 -0
- package/contracts/v1/fixtures/surface-work-basic.json +38 -0
- package/contracts/v1/fixtures/workflow-ask-success.json +8 -0
- package/contracts/v1/manifest.json +32 -1
- package/contracts/v1/parity-suite.json +121 -1
- package/contracts/v1/schemas/backend-request.schema.json +15 -0
- package/contracts/v1/schemas/manifest.schema.json +6 -3
- package/contracts/v1/schemas/surface-profile.schema.json +166 -0
- package/dist/codex-chatgpt-control-backend.mjs +3198 -340
- package/dist/codex-chatgpt-control.bundle.mjs +4454 -1555
- package/dist/src/backend/client.d.ts +26 -1
- package/dist/src/backend/client.js +23 -1
- package/dist/src/backend/protocol.d.ts +1 -1
- package/dist/src/backend/protocol.js +11 -0
- package/dist/src/backend/session.js +22 -0
- package/dist/src/browser/attach.d.ts +1 -0
- package/dist/src/browser/attach.js +3 -3
- package/dist/src/browser/clipboard.d.ts +11 -0
- package/dist/src/browser/clipboard.js +34 -7
- package/dist/src/browser/page-state.js +17 -4
- package/dist/src/client.d.ts +32 -1
- package/dist/src/client.js +72 -16
- package/dist/src/commands/artifacts.js +1 -7
- package/dist/src/commands/configuration.d.ts +15 -0
- package/dist/src/commands/configuration.js +674 -0
- package/dist/src/commands/context.js +3 -1
- package/dist/src/commands/conversation.d.ts +15 -0
- package/dist/src/commands/conversation.js +44 -0
- package/dist/src/commands/deadline.d.ts +8 -0
- package/dist/src/commands/deadline.js +14 -0
- package/dist/src/commands/doctor.js +47 -3
- package/dist/src/commands/experience.d.ts +12 -0
- package/dist/src/commands/experience.js +288 -0
- package/dist/src/commands/files.js +61 -13
- package/dist/src/commands/messages.d.ts +2 -1
- package/dist/src/commands/messages.js +349 -87
- package/dist/src/commands/modes.d.ts +4 -1
- package/dist/src/commands/modes.js +243 -44
- package/dist/src/commands/probes.d.ts +15 -0
- package/dist/src/commands/probes.js +64 -0
- package/dist/src/commands/registry.js +102 -8
- package/dist/src/commands/reports.js +1 -1
- package/dist/src/commands/response-actions.js +1 -7
- package/dist/src/commands/sequence.js +24 -1
- package/dist/src/commands/session.d.ts +2 -0
- package/dist/src/commands/session.js +50 -1
- package/dist/src/commands/threads.js +3 -31
- package/dist/src/commands/work.d.ts +6 -0
- package/dist/src/commands/work.js +338 -0
- package/dist/src/dom/generation-state.d.ts +7 -0
- package/dist/src/dom/generation-state.js +7 -1
- package/dist/src/dom/label-match.d.ts +3 -0
- package/dist/src/dom/label-match.js +25 -0
- package/dist/src/dom/locale/am.d.ts +8 -1
- package/dist/src/dom/locale/am.js +8 -1
- package/dist/src/dom/locale/ar.d.ts +9 -1
- package/dist/src/dom/locale/ar.js +9 -1
- package/dist/src/dom/locale/bg.d.ts +9 -1
- package/dist/src/dom/locale/bg.js +9 -1
- package/dist/src/dom/locale/bn.d.ts +9 -1
- package/dist/src/dom/locale/bn.js +9 -1
- package/dist/src/dom/locale/bs.d.ts +8 -1
- package/dist/src/dom/locale/bs.js +8 -1
- package/dist/src/dom/locale/ca.d.ts +8 -1
- package/dist/src/dom/locale/ca.js +8 -1
- package/dist/src/dom/locale/cs.d.ts +8 -1
- package/dist/src/dom/locale/cs.js +8 -1
- package/dist/src/dom/locale/da.d.ts +7 -1
- package/dist/src/dom/locale/da.js +7 -1
- package/dist/src/dom/locale/de.d.ts +10 -3
- package/dist/src/dom/locale/de.js +10 -3
- package/dist/src/dom/locale/el.d.ts +8 -1
- package/dist/src/dom/locale/el.js +8 -1
- package/dist/src/dom/locale/en.d.ts +40 -1
- package/dist/src/dom/locale/en.js +42 -1
- package/dist/src/dom/locale/es-419.d.ts +8 -1
- package/dist/src/dom/locale/es-419.js +8 -1
- package/dist/src/dom/locale/es-ES.d.ts +8 -1
- package/dist/src/dom/locale/es-ES.js +8 -1
- package/dist/src/dom/locale/et.d.ts +8 -1
- package/dist/src/dom/locale/et.js +8 -1
- package/dist/src/dom/locale/fa.d.ts +9 -1
- package/dist/src/dom/locale/fa.js +9 -1
- package/dist/src/dom/locale/fi.d.ts +8 -1
- package/dist/src/dom/locale/fi.js +8 -1
- package/dist/src/dom/locale/fr-CA.d.ts +8 -1
- package/dist/src/dom/locale/fr-CA.js +8 -1
- package/dist/src/dom/locale/fr-FR.d.ts +8 -3
- package/dist/src/dom/locale/fr-FR.js +8 -3
- package/dist/src/dom/locale/gu.d.ts +8 -1
- package/dist/src/dom/locale/gu.js +8 -1
- package/dist/src/dom/locale/hi.d.ts +8 -1
- package/dist/src/dom/locale/hi.js +8 -1
- package/dist/src/dom/locale/hr.d.ts +7 -1
- package/dist/src/dom/locale/hr.js +7 -1
- package/dist/src/dom/locale/hu.d.ts +8 -1
- package/dist/src/dom/locale/hu.js +8 -1
- package/dist/src/dom/locale/hy.d.ts +9 -1
- package/dist/src/dom/locale/hy.js +9 -1
- package/dist/src/dom/locale/id.d.ts +8 -1
- package/dist/src/dom/locale/id.js +8 -1
- package/dist/src/dom/locale/index.d.ts +20 -1
- package/dist/src/dom/locale/index.js +102 -0
- package/dist/src/dom/locale/is.d.ts +8 -1
- package/dist/src/dom/locale/is.js +8 -1
- package/dist/src/dom/locale/it.d.ts +8 -1
- package/dist/src/dom/locale/it.js +8 -1
- package/dist/src/dom/locale/ja.d.ts +8 -1
- package/dist/src/dom/locale/ja.js +8 -1
- package/dist/src/dom/locale/ka.d.ts +8 -1
- package/dist/src/dom/locale/ka.js +8 -1
- package/dist/src/dom/locale/kk.d.ts +8 -1
- package/dist/src/dom/locale/kk.js +8 -1
- package/dist/src/dom/locale/kn.d.ts +9 -1
- package/dist/src/dom/locale/kn.js +9 -1
- package/dist/src/dom/locale/ko.d.ts +8 -1
- package/dist/src/dom/locale/ko.js +8 -1
- package/dist/src/dom/locale/lt.d.ts +9 -1
- package/dist/src/dom/locale/lt.js +9 -1
- package/dist/src/dom/locale/lv.d.ts +8 -1
- package/dist/src/dom/locale/lv.js +8 -1
- package/dist/src/dom/locale/mk.d.ts +7 -1
- package/dist/src/dom/locale/mk.js +7 -1
- package/dist/src/dom/locale/ml.d.ts +9 -1
- package/dist/src/dom/locale/ml.js +9 -1
- package/dist/src/dom/locale/mn.d.ts +9 -1
- package/dist/src/dom/locale/mn.js +9 -1
- package/dist/src/dom/locale/mr.d.ts +9 -1
- package/dist/src/dom/locale/mr.js +9 -1
- package/dist/src/dom/locale/ms.d.ts +8 -1
- package/dist/src/dom/locale/ms.js +8 -1
- package/dist/src/dom/locale/my.d.ts +8 -1
- package/dist/src/dom/locale/my.js +8 -1
- package/dist/src/dom/locale/nb.d.ts +8 -1
- package/dist/src/dom/locale/nb.js +8 -1
- package/dist/src/dom/locale/nl.d.ts +8 -1
- package/dist/src/dom/locale/nl.js +8 -1
- package/dist/src/dom/locale/pa.d.ts +9 -1
- package/dist/src/dom/locale/pa.js +9 -1
- package/dist/src/dom/locale/pl.d.ts +8 -1
- package/dist/src/dom/locale/pl.js +8 -1
- package/dist/src/dom/locale/pt-BR.d.ts +8 -1
- package/dist/src/dom/locale/pt-BR.js +8 -1
- package/dist/src/dom/locale/pt-PT.d.ts +8 -1
- package/dist/src/dom/locale/pt-PT.js +8 -1
- package/dist/src/dom/locale/ro.d.ts +7 -1
- package/dist/src/dom/locale/ro.js +7 -1
- package/dist/src/dom/locale/ru.d.ts +5 -1
- package/dist/src/dom/locale/ru.js +5 -1
- package/dist/src/dom/locale/sk.d.ts +8 -1
- package/dist/src/dom/locale/sk.js +8 -1
- package/dist/src/dom/locale/sl.d.ts +8 -1
- package/dist/src/dom/locale/sl.js +8 -1
- package/dist/src/dom/locale/so.d.ts +8 -1
- package/dist/src/dom/locale/so.js +8 -1
- package/dist/src/dom/locale/sq.d.ts +8 -1
- package/dist/src/dom/locale/sq.js +8 -1
- package/dist/src/dom/locale/sr.d.ts +5 -1
- package/dist/src/dom/locale/sr.js +5 -1
- package/dist/src/dom/locale/sv.d.ts +8 -1
- package/dist/src/dom/locale/sv.js +8 -1
- package/dist/src/dom/locale/sw.d.ts +8 -1
- package/dist/src/dom/locale/sw.js +8 -1
- package/dist/src/dom/locale/ta.d.ts +9 -1
- package/dist/src/dom/locale/ta.js +9 -1
- package/dist/src/dom/locale/te.d.ts +9 -1
- package/dist/src/dom/locale/te.js +9 -1
- package/dist/src/dom/locale/th.d.ts +8 -1
- package/dist/src/dom/locale/th.js +8 -1
- package/dist/src/dom/locale/tl.d.ts +4 -5
- package/dist/src/dom/locale/tl.js +4 -5
- package/dist/src/dom/locale/tr.d.ts +8 -1
- package/dist/src/dom/locale/tr.js +8 -1
- package/dist/src/dom/locale/types.d.ts +25 -1
- package/dist/src/dom/locale/uk.d.ts +8 -1
- package/dist/src/dom/locale/uk.js +8 -1
- package/dist/src/dom/locale/ur.d.ts +8 -1
- package/dist/src/dom/locale/ur.js +8 -1
- package/dist/src/dom/locale/vi.d.ts +8 -1
- package/dist/src/dom/locale/vi.js +8 -1
- package/dist/src/dom/locale/zh-HK.d.ts +9 -1
- package/dist/src/dom/locale/zh-HK.js +10 -2
- package/dist/src/dom/locale/zh-Hans.d.ts +9 -1
- package/dist/src/dom/locale/zh-Hans.js +9 -1
- package/dist/src/dom/locale/zh-TW.d.ts +9 -1
- package/dist/src/dom/locale/zh-TW.js +9 -1
- package/dist/src/dom/menus.js +13 -2
- package/dist/src/dom/selectors.js +6 -1
- package/dist/src/dom/wait-snapshot.d.ts +43 -0
- package/dist/src/dom/wait-snapshot.js +127 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/runner/responses.d.ts +3 -1
- package/dist/src/runner/responses.js +16 -1
- package/dist/src/runner/result.js +129 -4
- package/dist/src/runner/stream.d.ts +1 -1
- package/dist/src/runner/stream.js +6 -0
- package/dist/src/runner/types.d.ts +35 -1
- package/dist/src/safety/risk.d.ts +11 -0
- package/dist/src/safety/risk.js +11 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.d.ts +6 -0
- package/dist/src/scripts/apply-intelligence-locale-captures.js +147 -15
- package/dist/src/scripts/capture-intelligence-locales.d.ts +3 -0
- package/dist/src/scripts/capture-intelligence-locales.js +372 -3
- package/dist/src/scripts/capture-surface-profile.d.ts +23 -0
- package/dist/src/scripts/capture-surface-profile.js +304 -0
- package/dist/src/scripts/live-smoke/scenarios.js +34 -1
- package/dist/src/scripts/live-smoke.js +1 -0
- package/dist/src/types.d.ts +273 -2
- package/package.json +4 -3
- package/references/2026-07-16-chat-work-surfaces.md +87 -0
- package/references/agents-runner.md +13 -2
- package/references/backend-protocol.md +19 -7
- package/references/language-coverage.md +40 -3
- package/references/localization.md +48 -10
- package/references/python-parity.md +16 -4
- package/references/responses-adapter.md +12 -1
- package/references/troubleshooting.md +49 -6
|
@@ -0,0 +1,304 @@
|
|
|
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, 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
|
+
"",
|
|
35
|
+
"The capture is read-only with respect to ChatGPT configuration. It opens and",
|
|
36
|
+
"closes visible menus, writes a sanitized draft locally, and never records",
|
|
37
|
+
"prompts, responses, sidebar titles, account names, cookies, or network data."
|
|
38
|
+
].join("\n");
|
|
39
|
+
export async function main(argv = process.argv.slice(2), runtime = globalThis) {
|
|
40
|
+
let options;
|
|
41
|
+
try {
|
|
42
|
+
options = parseArgs(argv);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof UsageError) {
|
|
46
|
+
console.log(error.message);
|
|
47
|
+
return error.exitCode;
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
if ((runtime.agent === undefined || runtime.agent === null) && runtime.browser === undefined) {
|
|
52
|
+
console.log(JSON.stringify({
|
|
53
|
+
ok: false,
|
|
54
|
+
status: "blocked",
|
|
55
|
+
blocker: {
|
|
56
|
+
kind: "browser_bridge_unavailable",
|
|
57
|
+
code: "codex_chrome_bridge_unavailable",
|
|
58
|
+
message: BROWSER_BRIDGE_UNAVAILABLE_MESSAGE,
|
|
59
|
+
remediation: BROWSER_BRIDGE_REMEDIATION
|
|
60
|
+
}
|
|
61
|
+
}, null, 2));
|
|
62
|
+
return 2;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const env = {};
|
|
66
|
+
if (runtime.agent !== undefined && runtime.agent !== null)
|
|
67
|
+
env.agent = runtime.agent;
|
|
68
|
+
if (runtime.browser !== undefined)
|
|
69
|
+
env.browser = runtime.browser;
|
|
70
|
+
const attached = await attachChatGPTBrowser(env, {
|
|
71
|
+
existingTab: {
|
|
72
|
+
target: options.tabId === undefined
|
|
73
|
+
? { type: "selected", host: "chatgpt" }
|
|
74
|
+
: { type: "tabId", tabId: options.tabId },
|
|
75
|
+
ifMissing: options.ifMissing,
|
|
76
|
+
ifMultiple: "first",
|
|
77
|
+
requireChatGPT: true
|
|
78
|
+
},
|
|
79
|
+
url: CHATGPT_HOME
|
|
80
|
+
});
|
|
81
|
+
env.page = attached.page;
|
|
82
|
+
const snapshot = await readSurfaceSnapshot(attached.page);
|
|
83
|
+
const detected = detectExperienceFromSnapshot(snapshot);
|
|
84
|
+
const inspectionResult = await inspectConfiguration(env, { includeOptions: true });
|
|
85
|
+
if (!inspectionResult.ok || inspectionResult.data === undefined) {
|
|
86
|
+
console.log(JSON.stringify(inspectionResult, null, 2));
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
const locale = options.locale ?? await readDocumentLocale(attached.page) ?? "und";
|
|
90
|
+
const profile = buildSurfaceProfileDraft(options, locale, snapshot, detected, inspectionResult.data);
|
|
91
|
+
await mkdir(dirname(options.out), { recursive: true });
|
|
92
|
+
await writeFile(options.out, `${JSON.stringify(profile, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
93
|
+
console.log(JSON.stringify({
|
|
94
|
+
ok: true,
|
|
95
|
+
status: "ok",
|
|
96
|
+
path: options.out,
|
|
97
|
+
id: profile.id,
|
|
98
|
+
experience: profile.expected.experience,
|
|
99
|
+
selectorProfile: profile.expected.selectorProfile,
|
|
100
|
+
supportState: profile.supportState
|
|
101
|
+
}, null, 2));
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.log(JSON.stringify(toCommandResult(error), null, 2));
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function buildSurfaceProfileDraft(options, locale, snapshot, detected, inspection, observedAt = new Date().toISOString().slice(0, 10)) {
|
|
110
|
+
const activeEntries = Object.entries(inspection.active)
|
|
111
|
+
.filter((entry) => typeof entry[1] === "string");
|
|
112
|
+
const axisRows = inspection.experience === "work"
|
|
113
|
+
? activeEntries.map(([axis, value]) => ({
|
|
114
|
+
axis,
|
|
115
|
+
label: `${titleCase(axis)} ${value}`,
|
|
116
|
+
value
|
|
117
|
+
}))
|
|
118
|
+
: [];
|
|
119
|
+
const openerLabel = inspection.experience === "chat"
|
|
120
|
+
? inspection.active.intelligence ?? inspection.active.effort
|
|
121
|
+
: undefined;
|
|
122
|
+
const menuItems = Object.values(inspection.options)
|
|
123
|
+
.flatMap(optionsForAxis => optionsForAxis ?? [])
|
|
124
|
+
.map(option => ({
|
|
125
|
+
label: option.label,
|
|
126
|
+
normalized: option.id,
|
|
127
|
+
role: option.hasSubmenu === true ? "menuitem" : "menuitemradio",
|
|
128
|
+
checked: option.selected,
|
|
129
|
+
...(option.hasSubmenu === undefined ? {} : { hasPopup: option.hasSubmenu })
|
|
130
|
+
}));
|
|
131
|
+
const safeComposerLabels = dedupeBounded(detected.evidence
|
|
132
|
+
.filter(item => item.source === "composer")
|
|
133
|
+
.map(item => item.label), 16);
|
|
134
|
+
const safeMainControls = dedupeBounded([
|
|
135
|
+
...axisRows.map(row => row.label),
|
|
136
|
+
...(openerLabel === undefined ? [] : [openerLabel]),
|
|
137
|
+
...menuItems.map(item => item.label),
|
|
138
|
+
...(inspection.selectorProfile === "work_advanced_v1" ? ["Advanced"] : [])
|
|
139
|
+
], 80);
|
|
140
|
+
return {
|
|
141
|
+
schemaVersion: "chatgpt.browser_control.surface_profile.v1",
|
|
142
|
+
id: options.id,
|
|
143
|
+
observedAt,
|
|
144
|
+
provenance: options.provenance,
|
|
145
|
+
locale,
|
|
146
|
+
region: options.region,
|
|
147
|
+
accountScope: options.accountScope,
|
|
148
|
+
planScope: options.planScope,
|
|
149
|
+
workspaceScope: options.workspaceScope,
|
|
150
|
+
supportState: options.supportState,
|
|
151
|
+
snapshot: {
|
|
152
|
+
url: sanitizeChatGPTUrl(snapshot.url),
|
|
153
|
+
composerLabels: safeComposerLabels,
|
|
154
|
+
mainControls: safeMainControls,
|
|
155
|
+
mainText: detected.evidence
|
|
156
|
+
.filter(item => item.source === "heading")
|
|
157
|
+
.map(item => item.label)
|
|
158
|
+
.join(" ")
|
|
159
|
+
.slice(0, 2000)
|
|
160
|
+
},
|
|
161
|
+
panel: {
|
|
162
|
+
...(openerLabel === undefined ? {} : { openerLabel }),
|
|
163
|
+
axisRows,
|
|
164
|
+
advancedVisible: inspection.selectorProfile === "work_advanced_v1"
|
|
165
|
+
},
|
|
166
|
+
menuItems,
|
|
167
|
+
expected: {
|
|
168
|
+
experience: detected.experience,
|
|
169
|
+
selectorProfile: inspection.selectorProfile,
|
|
170
|
+
availableAxes: inspection.availableAxes,
|
|
171
|
+
active: inspection.active
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export function parseArgs(argv) {
|
|
176
|
+
let id;
|
|
177
|
+
let out;
|
|
178
|
+
let locale;
|
|
179
|
+
let region = "not-recorded";
|
|
180
|
+
let accountScope = "not-recorded";
|
|
181
|
+
let planScope = "not-recorded";
|
|
182
|
+
let workspaceScope = "not-recorded";
|
|
183
|
+
let supportState = "unverified";
|
|
184
|
+
let provenance = DEFAULT_PROVENANCE;
|
|
185
|
+
let tabId;
|
|
186
|
+
let ifMissing = "block";
|
|
187
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
188
|
+
const arg = argv[index];
|
|
189
|
+
if (arg === undefined)
|
|
190
|
+
continue;
|
|
191
|
+
switch (arg) {
|
|
192
|
+
case "--help":
|
|
193
|
+
case "-h":
|
|
194
|
+
throw new UsageError(USAGE, 0);
|
|
195
|
+
case "--id":
|
|
196
|
+
id = requiredValue(argv, ++index, arg);
|
|
197
|
+
break;
|
|
198
|
+
case "--out":
|
|
199
|
+
out = requiredValue(argv, ++index, arg);
|
|
200
|
+
break;
|
|
201
|
+
case "--locale":
|
|
202
|
+
locale = requiredValue(argv, ++index, arg);
|
|
203
|
+
break;
|
|
204
|
+
case "--region":
|
|
205
|
+
region = requiredValue(argv, ++index, arg);
|
|
206
|
+
break;
|
|
207
|
+
case "--account-scope":
|
|
208
|
+
accountScope = requiredValue(argv, ++index, arg);
|
|
209
|
+
break;
|
|
210
|
+
case "--plan-scope":
|
|
211
|
+
planScope = requiredValue(argv, ++index, arg);
|
|
212
|
+
break;
|
|
213
|
+
case "--workspace-scope":
|
|
214
|
+
workspaceScope = requiredValue(argv, ++index, arg);
|
|
215
|
+
break;
|
|
216
|
+
case "--support-state":
|
|
217
|
+
supportState = parseSupportState(requiredValue(argv, ++index, arg));
|
|
218
|
+
break;
|
|
219
|
+
case "--provenance":
|
|
220
|
+
provenance = requiredValue(argv, ++index, arg);
|
|
221
|
+
break;
|
|
222
|
+
case "--tab-id":
|
|
223
|
+
tabId = requiredValue(argv, ++index, arg);
|
|
224
|
+
break;
|
|
225
|
+
case "--if-missing":
|
|
226
|
+
ifMissing = parseIfMissing(requiredValue(argv, ++index, arg));
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
throw new UsageError(`Unknown argument: ${arg}\n\n${USAGE}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (id === undefined) {
|
|
233
|
+
throw new UsageError(`--id is required.\n\n${USAGE}`);
|
|
234
|
+
}
|
|
235
|
+
for (const [field, value] of Object.entries({
|
|
236
|
+
id,
|
|
237
|
+
region,
|
|
238
|
+
accountScope,
|
|
239
|
+
planScope,
|
|
240
|
+
workspaceScope
|
|
241
|
+
})) {
|
|
242
|
+
assertNormalizedSlug(value, `--${field.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)}`);
|
|
243
|
+
}
|
|
244
|
+
const defaultOut = resolve(process.cwd(), "..", "..", "outputs", "surface-profiles", `${new Date().toISOString().slice(0, 10)}-${id}.json`);
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
out: resolve(out ?? defaultOut),
|
|
248
|
+
...(locale === undefined ? {} : { locale }),
|
|
249
|
+
region,
|
|
250
|
+
accountScope,
|
|
251
|
+
planScope,
|
|
252
|
+
workspaceScope,
|
|
253
|
+
supportState,
|
|
254
|
+
provenance,
|
|
255
|
+
...(tabId === undefined ? {} : { tabId }),
|
|
256
|
+
ifMissing
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
async function readDocumentLocale(page) {
|
|
260
|
+
if (page.evaluate === undefined)
|
|
261
|
+
return undefined;
|
|
262
|
+
return page.evaluate(() => document.documentElement.lang || undefined).catch(() => undefined);
|
|
263
|
+
}
|
|
264
|
+
function sanitizeChatGPTUrl(value) {
|
|
265
|
+
const url = new URL(value || CHATGPT_HOME, CHATGPT_HOME);
|
|
266
|
+
url.search = "";
|
|
267
|
+
url.hash = "";
|
|
268
|
+
if (/^\/c\/[^/]+/.test(url.pathname)) {
|
|
269
|
+
url.pathname = url.pathname.replace(/^\/c\/[^/]+/, "/c/sanitized");
|
|
270
|
+
}
|
|
271
|
+
return `${url.origin}${url.pathname}`;
|
|
272
|
+
}
|
|
273
|
+
function titleCase(value) {
|
|
274
|
+
return value.length === 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
|
|
275
|
+
}
|
|
276
|
+
function dedupeBounded(values, limit) {
|
|
277
|
+
return Array.from(new Set(values.map(value => value.trim()).filter(Boolean))).slice(0, limit);
|
|
278
|
+
}
|
|
279
|
+
function requiredValue(argv, index, flag) {
|
|
280
|
+
const value = argv[index];
|
|
281
|
+
if (value === undefined || value.startsWith("--")) {
|
|
282
|
+
throw new UsageError(`${flag} requires a value.`);
|
|
283
|
+
}
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
function parseSupportState(value) {
|
|
287
|
+
if (["current", "compatibility", "unverified", "retired"].includes(value)) {
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
throw new UsageError(`Invalid --support-state: ${value}`);
|
|
291
|
+
}
|
|
292
|
+
function parseIfMissing(value) {
|
|
293
|
+
if (value === "block" || value === "open" || value === "create")
|
|
294
|
+
return value;
|
|
295
|
+
throw new UsageError(`Invalid --if-missing: ${value}`);
|
|
296
|
+
}
|
|
297
|
+
function assertNormalizedSlug(value, flag) {
|
|
298
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(value)) {
|
|
299
|
+
throw new UsageError(`${flag} must be a normalized lowercase slug without private names.`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
303
|
+
process.exitCode = await main();
|
|
304
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdtemp, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { attachAskRead, attachFiles, askMessage, bootstrap, composeMessage, createChatGPT, copyResponse, downloadLatestAttachment, newThread, openThread, readLatest, runSequence, searchThreads, selectTool, setMode, submitMessage, twoTurnExchange, waitAndRead, waitForMessage } from "../../index.js";
|
|
4
|
+
import { attachAskRead, attachFiles, askMessage, bootstrap, composeMessage, createChatGPT, copyResponse, downloadLatestAttachment, messageStatus, newThread, openThread, readLatest, runSequence, searchThreads, selectTool, setMode, submitMessage, twoTurnExchange, waitAndRead, waitForMessage } from "../../index.js";
|
|
5
5
|
import { readAssistantGenerationState } from "../../dom/generation-state.js";
|
|
6
6
|
import { contextEnvFlag, contextEnvText } from "./harness.js";
|
|
7
7
|
export const requiredScenarios = [
|
|
@@ -496,6 +496,39 @@ export const optionalScenarios = [
|
|
|
496
496
|
? pass(meta, waited, details)
|
|
497
497
|
: fail(meta, waited, details);
|
|
498
498
|
}),
|
|
499
|
+
scenario("running-status-detection", false, context => contextEnvFlag(context, "CHATGPT_E2E_RUNNING_STATUS"), async (context, meta) => {
|
|
500
|
+
const env = await bootNewThread(context, meta);
|
|
501
|
+
if ("status" in env)
|
|
502
|
+
return env;
|
|
503
|
+
let status;
|
|
504
|
+
try {
|
|
505
|
+
const asked = await askMessage(env, {
|
|
506
|
+
text: [
|
|
507
|
+
"Live running-status stress test. Write exactly 500 numbered items.",
|
|
508
|
+
"Each item should be a complete sentence. Continue until item 500."
|
|
509
|
+
].join("\n"),
|
|
510
|
+
wait: false,
|
|
511
|
+
read: false
|
|
512
|
+
});
|
|
513
|
+
if (!asked.ok)
|
|
514
|
+
return fail(meta, asked);
|
|
515
|
+
await waitForGenerationSignal(env, 30000);
|
|
516
|
+
status = await messageStatus(env, { maxPreviewChars: 400 });
|
|
517
|
+
const data = status.data;
|
|
518
|
+
const details = {
|
|
519
|
+
completionState: data?.completionState,
|
|
520
|
+
generationActive: data?.generationActive,
|
|
521
|
+
latestAssistantTextLength: data?.latestAssistantTextLength,
|
|
522
|
+
status: status.status
|
|
523
|
+
};
|
|
524
|
+
return status.ok && (data?.completionState === "generating" || data?.generationActive === true)
|
|
525
|
+
? pass(meta, status, details)
|
|
526
|
+
: fail(meta, status, details);
|
|
527
|
+
}
|
|
528
|
+
finally {
|
|
529
|
+
await stopGenerationIfVisible(env);
|
|
530
|
+
}
|
|
531
|
+
}),
|
|
499
532
|
scenario("download-generated-file", false, context => contextEnvFlag(context, "CHATGPT_E2E_DOWNLOAD"), async (context, meta) => {
|
|
500
533
|
const env = await bootNewThread(context, meta);
|
|
501
534
|
if ("status" in env)
|
|
@@ -44,6 +44,7 @@ else {
|
|
|
44
44
|
CHATGPT_E2E_DOWNLOAD: envText("CHATGPT_E2E_DOWNLOAD"),
|
|
45
45
|
CHATGPT_E2E_LOGIN_PROFILE: envText("CHATGPT_E2E_LOGIN_PROFILE"),
|
|
46
46
|
CHATGPT_E2E_MODE_LABEL: envText("CHATGPT_E2E_MODE_LABEL"),
|
|
47
|
+
CHATGPT_E2E_RUNNING_STATUS: envText("CHATGPT_E2E_RUNNING_STATUS"),
|
|
47
48
|
CHATGPT_E2E_STREAM: envText("CHATGPT_E2E_STREAM"),
|
|
48
49
|
CHATGPT_E2E_UPLOAD_PERMISSION_MANUAL: envText("CHATGPT_E2E_UPLOAD_PERMISSION_MANUAL"),
|
|
49
50
|
CHATGPT_E2E_WEB_SEARCH: envText("CHATGPT_E2E_WEB_SEARCH")
|