@emseepea/testing 0.23.5 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -1
- package/chatgpt/run.mjs +57 -0
- package/chatgpt/setup.mjs +81 -0
- package/dist/chatgpt.d.ts +132 -0
- package/dist/chatgpt.d.ts.map +1 -0
- package/dist/chatgpt.js +296 -0
- package/dist/chatgpt.js.map +1 -0
- package/package.json +10 -3
- package/semantic/provider.mjs +0 -14
package/README.md
CHANGED
|
@@ -109,10 +109,14 @@ EMSEEPEA_CODEX_MODEL=gpt-5.5 emseepea-test --provider codex-local eval
|
|
|
109
109
|
|
|
110
110
|
`codex-local` uses the existing Codex CLI sign-in without copying it.
|
|
111
111
|
`codex-ci` requires `OPENAI_API_KEY` from the CI secret store. Both providers
|
|
112
|
-
|
|
112
|
+
record the installed CLI version as provenance, run `codex exec --json`, resume the actual
|
|
113
113
|
Codex session for follow-ups, ignore user configuration, and pre-approve only
|
|
114
114
|
the discovered tools from the test server.
|
|
115
115
|
|
|
116
|
+
CLI eligibility is established by the invocation and event contracts rather
|
|
117
|
+
than a version cutoff. An unsupported invocation or invalid, incomplete or
|
|
118
|
+
forbidden event still fails qualification.
|
|
119
|
+
|
|
116
120
|
An isolated command policy denies shell launches and spawned shell environments
|
|
117
121
|
inherit no provider credentials. The runner also disables Codex account apps,
|
|
118
122
|
plugins, remote plugins, multi-agent execution, web search, shell tools, and
|
|
@@ -611,3 +615,116 @@ retained. Known configured authentication values are scrubbed from elicitation
|
|
|
611
615
|
evidence. Arbitrary secrets placed inside test content are not detected.
|
|
612
616
|
Local evidence is written with mode `0600`; repository CI retains it for 14
|
|
613
617
|
days.
|
|
618
|
+
|
|
619
|
+
## Opt In to Native ChatGPT Browser Journeys
|
|
620
|
+
|
|
621
|
+
Import `@emseepea/testing/chatgpt` only when you want to test the real ChatGPT
|
|
622
|
+
web experience. Ordinary framework tests do not enable this adapter or install
|
|
623
|
+
Playwright. This is an experimental browser
|
|
624
|
+
adapter: ChatGPT controls can change, and live host qualification is pending.
|
|
625
|
+
|
|
626
|
+
On your own machine, install the optional browser tooling:
|
|
627
|
+
|
|
628
|
+
```sh
|
|
629
|
+
npm install --save-dev @emseepea/testing playwright@1.62.1
|
|
630
|
+
npx playwright install chromium
|
|
631
|
+
npx emseepea-chatgpt-setup --enable-native-chatgpt --account-owner '<test-account-owner>'
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Before publication, run the source checkout with
|
|
635
|
+
`node packages/testing/chatgpt/setup.mjs` using the same arguments.
|
|
636
|
+
|
|
637
|
+
The setup command opens a dedicated headed browser. Sign in to the authorized
|
|
638
|
+
ChatGPT test account, complete MFA, check the account, and type `READY` in the
|
|
639
|
+
terminal. Codex CLI login does not sign in to ChatGPT.
|
|
640
|
+
|
|
641
|
+
If bundled Chromium
|
|
642
|
+
cannot complete your account login, use `--browser-channel chrome` with an
|
|
643
|
+
installed Chrome browser. The profile defaults to `~/.emseepea/chatgpt-browser`;
|
|
644
|
+
keep it outside the repository and CI artifacts.
|
|
645
|
+
|
|
646
|
+
The command verifies a
|
|
647
|
+
signed-in control, not the account's identity; the human confirms that identity.
|
|
648
|
+
If that control changes, pass an account-specific `--authenticated-selector`.
|
|
649
|
+
|
|
650
|
+
Connect your authorized HTTPS MCP endpoint through ChatGPT's current plugin
|
|
651
|
+
installation flow. Complete account consent yourself. A local server requires
|
|
652
|
+
a supported HTTPS tunnel before ChatGPT can reach it. See the
|
|
653
|
+
[official ChatGPT connection instructions](https://developers.openai.com/plugins/deploy/connect-chatgpt).
|
|
654
|
+
|
|
655
|
+
Use synthetic fixtures and a dedicated account; starting a fresh conversation
|
|
656
|
+
does not disable account memory. This package does not create an endpoint,
|
|
657
|
+
install a plugin, grant consent, or infer client identity from a user agent.
|
|
658
|
+
|
|
659
|
+
Create an adopter-owned `.mjs` scenario module that exports `authorization` and
|
|
660
|
+
`createJourney({ page, runId })`. The authorization declares `approved: true`,
|
|
661
|
+
`fixturePolicy: "synthetic"`, an HTTPS `endpoint: new URL(...)`, and the
|
|
662
|
+
`accountOwner`, `endpointOwner`, `fixtureOwner`, and `cleanupOwner` strings.
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
Use the same account owner as setup. `createJourney` returns `checkpoints` and
|
|
666
|
+
an asynchronous `cleanup(signal)` callback. The callback must confirm both
|
|
667
|
+
`fixturesRemoved: true` and `conversationsRemoved: true` after observing cleanup.
|
|
668
|
+
|
|
669
|
+
The CLI supplies the explicit enable flag, authorized page, run ID, and ownership.
|
|
670
|
+
|
|
671
|
+
```sh
|
|
672
|
+
npx emseepea-chatgpt --enable-native-chatgpt --scenario ./native-chatgpt.mjs
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Before publication, use `node packages/testing/chatgpt/run.mjs` after building
|
|
676
|
+
the package. The command reuses the private profile and writes evidence to
|
|
677
|
+
`artifacts/native-chatgpt/evidence.json`. Exit code 0 means passed, 1 means an
|
|
678
|
+
assertion failed, and 2 means blocked or incomplete.
|
|
679
|
+
|
|
680
|
+
The exported `NativeChatGptJourneyModule` type describes the scenario contract.
|
|
681
|
+
For direct integration, use `defineNativeChatGptJourney` and
|
|
682
|
+
`runNativeChatGptJourney(journey, outputPath)` with your own authenticated
|
|
683
|
+
Playwright page. The runner never launches a browser by itself. Use these
|
|
684
|
+
checkpoint operations in the order your application requires:
|
|
685
|
+
|
|
686
|
+
- `human`: wait for the account owner to finish connection or consent.
|
|
687
|
+
- `attach`: upload a synthetic file and observe its ready control.
|
|
688
|
+
- `send`: submit a prompt and assert the completed assistant response.
|
|
689
|
+
- `server-observation`: assert MCP calls after every submitted turn.
|
|
690
|
+
- `visible`, `click`, and `image`: inspect or interact with an application card,
|
|
691
|
+
including locators inside its iframe. Image assertions receive PNG bytes.
|
|
692
|
+
- `application-state`: compare a declared SHA-256 with independently read state.
|
|
693
|
+
- `fresh-conversation`: start a new conversation, then submit and observe retrieval.
|
|
694
|
+
|
|
695
|
+
Default controls use ChatGPT's composer and send/stop buttons. Override
|
|
696
|
+
`controls` with locators from the authorized page when the UI changes.
|
|
697
|
+
|
|
698
|
+
`timeoutMs` bounds each browser operation or callback (default 30 seconds),
|
|
699
|
+
and `clockSkewMs` allows 0–5000 ms between browser and server clocks (default
|
|
700
|
+
1000 ms). A callback must respect its abort signal; timing it out cannot undo
|
|
701
|
+
an operation already sent to a server.
|
|
702
|
+
|
|
703
|
+
A server collector receives `runId`, `afterSequence`, `since`, and `signal`.
|
|
704
|
+
Return ordered, run-scoped calls matching the declared method, target, outcome,
|
|
705
|
+
and optional result hash. Each call includes request/result SHA-256 values,
|
|
706
|
+
endpoint SHA-256, sequence, timestamp, and `nativeClient`.
|
|
707
|
+
|
|
708
|
+
Set
|
|
709
|
+
`nativeClient: "verified-chatgpt"` only when your authenticated server boundary
|
|
710
|
+
establishes that the request came through the dedicated ChatGPT connection;
|
|
711
|
+
use `"unverified"` otherwise.
|
|
712
|
+
|
|
713
|
+
A user agent, browser answer, or simulator record
|
|
714
|
+
cannot establish this. Define your run correlation and authenticated boundary
|
|
715
|
+
before attempting live qualification. An unavailable collector or unverified
|
|
716
|
+
client produces incomplete evidence.
|
|
717
|
+
|
|
718
|
+
Evidence retains bounded hashes and fixed failure categories. It omits raw
|
|
719
|
+
prompts, replies, endpoint addresses, ownership strings, cookies, and arbitrary
|
|
720
|
+
observer metadata. Explicit `image` checkpoints save scoped PNGs alongside
|
|
721
|
+
the evidence; review the selected card for private content before enabling one.
|
|
722
|
+
|
|
723
|
+
Files use mode `0600`. Store evidence privately and choose your own retention
|
|
724
|
+
policy. Cleanup is required even after an assertion fails.
|
|
725
|
+
|
|
726
|
+
The package's scripted-page and intercepted Chromium tests verify the adapter
|
|
727
|
+
contract. They do not prove compatibility with live ChatGPT. Qualify connection,
|
|
728
|
+
real MCP invocation, visible card, durable application state, fresh retrieval,
|
|
729
|
+
and cleanup against your owned account and endpoint before claiming that host
|
|
730
|
+
journey works.
|
package/chatgpt/run.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
|
|
8
|
+
const help = `Usage: emseepea-chatgpt --enable-native-chatgpt --scenario <owned-module.mjs>
|
|
9
|
+
[--profile <dedicated-directory>] [--output <private-evidence.json>]
|
|
10
|
+
|
|
11
|
+
The module exports authorization and createJourney({ page, runId }). It owns
|
|
12
|
+
its assertions, server observations, and cleanup. Complete human setup first.
|
|
13
|
+
This command never logs in, grants consent, or exports authentication state.
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
if (args.includes("--help")) { console.log(help); return; }
|
|
19
|
+
if (args.filter((arg) => arg === "--enable-native-chatgpt").length !== 1) throw new Error("explicit-opt-in-required");
|
|
20
|
+
const values = new Map();
|
|
21
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
22
|
+
const arg = args[index];
|
|
23
|
+
if (arg === "--enable-native-chatgpt") continue;
|
|
24
|
+
if (!["--scenario", "--profile", "--output"].includes(arg) || values.has(arg) || !args[index + 1] || args[index + 1].startsWith("--")) throw new Error("invalid-run-options");
|
|
25
|
+
values.set(arg, args[++index]);
|
|
26
|
+
}
|
|
27
|
+
if (!values.get("--scenario")) throw new Error("owned-scenario-required");
|
|
28
|
+
const scenario = await import(pathToFileURL(resolve(values.get("--scenario"))).href);
|
|
29
|
+
if (typeof scenario.createJourney !== "function" || scenario.authorization?.approved !== true || typeof scenario.authorization.accountOwner !== "string") throw new Error("owned-scenario-required");
|
|
30
|
+
const profile = resolve(values.get("--profile") ?? join(homedir(), ".emseepea", "chatgpt-browser"));
|
|
31
|
+
const record = await readFile(join(profile, "emseepea-profile.json"), "utf8").then(JSON.parse).catch(() => undefined);
|
|
32
|
+
const ownerSha256 = createHash("sha256").update(scenario.authorization.accountOwner).digest("hex");
|
|
33
|
+
if (record?.schemaVersion !== 1 || record?.humanConfirmed !== true || record?.ownerSha256 !== ownerSha256 || !["chromium", "chrome", "msedge"].includes(record?.channel)) throw new Error("human-setup-required");
|
|
34
|
+
let chromium;
|
|
35
|
+
try { ({ chromium } = await import("playwright")); }
|
|
36
|
+
catch { throw new Error("optional-playwright-dependency-required"); }
|
|
37
|
+
const { runNativeChatGptJourney } = await import("../dist/chatgpt.js");
|
|
38
|
+
let context;
|
|
39
|
+
try { context = await chromium.launchPersistentContext(profile, { headless: false, ...(record.channel === "chromium" ? {} : { channel: record.channel }) }); }
|
|
40
|
+
catch { throw new Error("headed-browser-unavailable"); }
|
|
41
|
+
try {
|
|
42
|
+
const page = context.pages()[0] ?? await context.newPage();
|
|
43
|
+
await page.goto("https://chatgpt.com/", { waitUntil: "domcontentloaded" });
|
|
44
|
+
const runId = randomUUID();
|
|
45
|
+
const declared = await scenario.createJourney({ page, runId });
|
|
46
|
+
const evidence = await runNativeChatGptJourney({ ...declared, enabled: true, page, runId, authorization: scenario.authorization },
|
|
47
|
+
resolve(values.get("--output") ?? "artifacts/native-chatgpt/evidence.json"));
|
|
48
|
+
console.log(`Native ChatGPT journey ${evidence.status}. Evidence written privately.`);
|
|
49
|
+
if (evidence.status !== "passed") process.exitCode = evidence.status === "failed" ? 1 : 2;
|
|
50
|
+
} finally { await context.close(); }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
main().catch((error) => {
|
|
54
|
+
const categories = new Set(["explicit-opt-in-required", "invalid-run-options", "owned-scenario-required", "human-setup-required", "optional-playwright-dependency-required", "headed-browser-unavailable"]);
|
|
55
|
+
console.error(`Native ChatGPT journey blocked: ${categories.has(error.message) ? error.message : "journey-unavailable"}`);
|
|
56
|
+
process.exitCode = 2;
|
|
57
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { chmod, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { createInterface } from "node:readline/promises";
|
|
7
|
+
|
|
8
|
+
const help = `Usage: emseepea-chatgpt-setup --enable-native-chatgpt --account-owner <owner>
|
|
9
|
+
[--profile <dedicated-directory>] [--authenticated-selector <CSS selector>]
|
|
10
|
+
[--browser-channel chromium|chrome|msedge]
|
|
11
|
+
|
|
12
|
+
Opens a headed ChatGPT browser for human login and consent. Authentication stays
|
|
13
|
+
in the private local profile; it is never exported to evidence or a repository.
|
|
14
|
+
Install the optional playwright@1.62.1 dependency and its Chromium browser first.
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.includes("--help")) { console.log(help); return; }
|
|
20
|
+
if (args.filter((arg) => arg === "--enable-native-chatgpt").length !== 1) throw new Error("explicit-opt-in-required");
|
|
21
|
+
const values = new Map();
|
|
22
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
23
|
+
const arg = args[index];
|
|
24
|
+
if (arg === "--enable-native-chatgpt") continue;
|
|
25
|
+
if (!["--account-owner", "--profile", "--authenticated-selector", "--browser-channel"].includes(arg) || values.has(arg) || !args[index + 1] || args[index + 1].startsWith("--")) throw new Error("invalid-setup-options");
|
|
26
|
+
values.set(arg, args[++index]);
|
|
27
|
+
}
|
|
28
|
+
const owner = values.get("--account-owner");
|
|
29
|
+
if (!owner?.trim() || owner.length > 256) throw new Error("account-owner-required");
|
|
30
|
+
if (!process.stdin.isTTY) throw new Error("interactive-local-terminal-required");
|
|
31
|
+
const selector = values.get("--authenticated-selector") ?? '[data-testid="accounts-profile-button"]';
|
|
32
|
+
if (selector.length > 1024) throw new Error("invalid-setup-options");
|
|
33
|
+
const channel = values.get("--browser-channel") ?? "chromium";
|
|
34
|
+
if (!["chromium", "chrome", "msedge"].includes(channel)) throw new Error("invalid-setup-options");
|
|
35
|
+
let chromium;
|
|
36
|
+
try { ({ chromium } = await import("playwright")); }
|
|
37
|
+
catch { throw new Error("optional-playwright-dependency-required"); }
|
|
38
|
+
const profile = resolve(values.get("--profile") ?? join(homedir(), ".emseepea", "chatgpt-browser"));
|
|
39
|
+
const marker = join(profile, "emseepea-profile.json");
|
|
40
|
+
const ownerSha256 = createHash("sha256").update(owner).digest("hex");
|
|
41
|
+
await mkdir(profile, { recursive: true, mode: 0o700 });
|
|
42
|
+
const existing = await readdir(profile);
|
|
43
|
+
if (existing.length) {
|
|
44
|
+
const record = await readFile(marker, "utf8").then(JSON.parse).catch(() => undefined);
|
|
45
|
+
if (record?.schemaVersion !== 1 || record?.ownerSha256 !== ownerSha256) throw new Error("dedicated-owned-profile-required");
|
|
46
|
+
}
|
|
47
|
+
{
|
|
48
|
+
await writeFile(marker, `${JSON.stringify({ schemaVersion: 1, ownerSha256, humanConfirmed: false, channel })}\n`, { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
await chmod(profile, 0o700);
|
|
51
|
+
let context;
|
|
52
|
+
try { context = await chromium.launchPersistentContext(profile, { headless: false, ...(channel === "chromium" ? {} : { channel }) }); }
|
|
53
|
+
catch { throw new Error("headed-browser-unavailable"); }
|
|
54
|
+
const terminal = createInterface({ input: process.stdin, output: process.stdout });
|
|
55
|
+
try {
|
|
56
|
+
const page = context.pages()[0] ?? await context.newPage();
|
|
57
|
+
await page.goto("https://chatgpt.com/", { waitUntil: "domcontentloaded" });
|
|
58
|
+
console.log("Sign in to the authorized test account in the browser. Complete MFA and any consent yourself.");
|
|
59
|
+
const confirmation = await terminal.question("After checking the account, type READY to verify the signed-in control: ");
|
|
60
|
+
if (confirmation !== "READY") throw new Error("human-confirmation-required");
|
|
61
|
+
if (new URL(page.url()).origin !== "https://chatgpt.com") throw new Error("native-chatgpt-page-required");
|
|
62
|
+
try { await page.locator(selector).waitFor({ state: "visible", timeout: 10_000 }); }
|
|
63
|
+
catch { throw new Error("signed-in-control-unobserved"); }
|
|
64
|
+
await writeFile(marker, `${JSON.stringify({ schemaVersion: 1, ownerSha256, humanConfirmed: true, channel })}\n`, { mode: 0o600 });
|
|
65
|
+
await chmod(marker, 0o600);
|
|
66
|
+
console.log("Signed-in control observed. The dedicated local profile is ready for explicitly enabled journeys.");
|
|
67
|
+
} finally {
|
|
68
|
+
terminal.close();
|
|
69
|
+
await context.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
main().catch((error) => {
|
|
74
|
+
// Never print browser errors, headers, cookies, account metadata, or credentials.
|
|
75
|
+
const categories = new Set(["explicit-opt-in-required", "invalid-setup-options", "account-owner-required",
|
|
76
|
+
"interactive-local-terminal-required", "optional-playwright-dependency-required", "dedicated-owned-profile-required",
|
|
77
|
+
"headed-browser-unavailable", "human-confirmation-required", "native-chatgpt-page-required", "signed-in-control-unobserved"]);
|
|
78
|
+
console.error(`ChatGPT setup blocked: ${categories.has(error.message) ? error.message : "setup-unavailable"}`);
|
|
79
|
+
console.error(help);
|
|
80
|
+
process.exitCode = 2;
|
|
81
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Locator, Page } from "playwright";
|
|
2
|
+
export type NativeChatGptStatus = "passed" | "failed" | "blocked" | "incomplete";
|
|
3
|
+
export type NativeChatGptMethod = "initialize" | "tools/list" | "tools/call" | "resources/list" | "resources/templates/list" | "resources/read" | "prompts/list" | "prompts/get" | "notifications/cancelled";
|
|
4
|
+
export interface NativeChatGptAuthorization {
|
|
5
|
+
readonly approved: true;
|
|
6
|
+
readonly accountOwner: string;
|
|
7
|
+
readonly endpointOwner: string;
|
|
8
|
+
readonly fixtureOwner: string;
|
|
9
|
+
readonly cleanupOwner: string;
|
|
10
|
+
readonly fixturePolicy: "synthetic";
|
|
11
|
+
readonly endpoint: URL;
|
|
12
|
+
}
|
|
13
|
+
export interface NativeChatGptControls {
|
|
14
|
+
readonly composer: Locator;
|
|
15
|
+
readonly submit: Locator;
|
|
16
|
+
readonly assistantMessages: Locator;
|
|
17
|
+
readonly conversationMessages: Locator;
|
|
18
|
+
readonly generating: Locator;
|
|
19
|
+
readonly fileInput: Locator;
|
|
20
|
+
}
|
|
21
|
+
export interface NativeChatGptServerCall {
|
|
22
|
+
readonly sequence: number;
|
|
23
|
+
readonly runId: string;
|
|
24
|
+
readonly endpointSha256: string;
|
|
25
|
+
/** Established by the server's authenticated client boundary, never user-agent text. */
|
|
26
|
+
readonly nativeClient: "verified-chatgpt" | "unverified";
|
|
27
|
+
readonly observedAt: string;
|
|
28
|
+
readonly method: NativeChatGptMethod;
|
|
29
|
+
readonly target: string;
|
|
30
|
+
readonly requestSha256: string;
|
|
31
|
+
readonly responseSha256: string;
|
|
32
|
+
readonly outcome: "success" | "denied" | "cancelled";
|
|
33
|
+
}
|
|
34
|
+
export interface NativeChatGptObservationContext {
|
|
35
|
+
readonly runId: string;
|
|
36
|
+
readonly afterSequence: number;
|
|
37
|
+
readonly since: string;
|
|
38
|
+
readonly signal: AbortSignal;
|
|
39
|
+
}
|
|
40
|
+
interface CheckpointBase {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
}
|
|
43
|
+
export type NativeChatGptCheckpoint = (CheckpointBase & {
|
|
44
|
+
readonly operation: "human";
|
|
45
|
+
readonly ready: (signal: AbortSignal) => Promise<boolean>;
|
|
46
|
+
}) | (CheckpointBase & {
|
|
47
|
+
readonly operation: "fresh-conversation";
|
|
48
|
+
}) | (CheckpointBase & {
|
|
49
|
+
readonly operation: "click";
|
|
50
|
+
readonly target: Locator;
|
|
51
|
+
}) | (CheckpointBase & {
|
|
52
|
+
readonly operation: "attach";
|
|
53
|
+
readonly file: string;
|
|
54
|
+
readonly mimeType: string;
|
|
55
|
+
readonly ready: Locator;
|
|
56
|
+
}) | (CheckpointBase & {
|
|
57
|
+
readonly operation: "send";
|
|
58
|
+
readonly prompt: string;
|
|
59
|
+
readonly expectResponse: (text: string) => boolean | Promise<boolean>;
|
|
60
|
+
}) | (CheckpointBase & {
|
|
61
|
+
readonly operation: "visible";
|
|
62
|
+
readonly target: Locator;
|
|
63
|
+
}) | (CheckpointBase & {
|
|
64
|
+
readonly operation: "image";
|
|
65
|
+
readonly target: Locator;
|
|
66
|
+
readonly assertImage: (png: Buffer) => boolean | Promise<boolean>;
|
|
67
|
+
}) | (CheckpointBase & {
|
|
68
|
+
readonly operation: "application-state";
|
|
69
|
+
readonly expectedSha256: string;
|
|
70
|
+
readonly readState: (signal: AbortSignal) => Promise<string>;
|
|
71
|
+
}) | (CheckpointBase & {
|
|
72
|
+
readonly operation: "server-observation";
|
|
73
|
+
readonly expectedCalls: readonly {
|
|
74
|
+
readonly method: NativeChatGptMethod;
|
|
75
|
+
readonly target: string;
|
|
76
|
+
readonly outcome: NativeChatGptServerCall["outcome"];
|
|
77
|
+
readonly responseSha256?: string;
|
|
78
|
+
}[];
|
|
79
|
+
readonly collect: (context: NativeChatGptObservationContext) => Promise<readonly NativeChatGptServerCall[]>;
|
|
80
|
+
});
|
|
81
|
+
export interface NativeChatGptJourney {
|
|
82
|
+
/** Importing the opt-in entry point alone does not authorize execution. */
|
|
83
|
+
readonly enabled: true;
|
|
84
|
+
readonly runId: string;
|
|
85
|
+
/** The adopter supplies an already authenticated, dedicated browser page. */
|
|
86
|
+
readonly page: Page;
|
|
87
|
+
readonly authorization: NativeChatGptAuthorization;
|
|
88
|
+
readonly controls?: NativeChatGptControls;
|
|
89
|
+
readonly checkpoints: readonly NativeChatGptCheckpoint[];
|
|
90
|
+
readonly timeoutMs?: number;
|
|
91
|
+
/** Bounded allowance for independently clocked server observations. Defaults to 1000 ms. */
|
|
92
|
+
readonly clockSkewMs?: number;
|
|
93
|
+
/** Must remove fixture data and test conversations; profile ownership stays with the adopter. */
|
|
94
|
+
readonly cleanup: (signal: AbortSignal) => Promise<{
|
|
95
|
+
readonly fixturesRemoved: true;
|
|
96
|
+
readonly conversationsRemoved: true;
|
|
97
|
+
}>;
|
|
98
|
+
}
|
|
99
|
+
export interface NativeChatGptJourneyModule {
|
|
100
|
+
readonly authorization: NativeChatGptAuthorization;
|
|
101
|
+
readonly createJourney: (context: {
|
|
102
|
+
readonly page: Page;
|
|
103
|
+
readonly runId: string;
|
|
104
|
+
}) => Promise<Omit<NativeChatGptJourney, "enabled" | "page" | "runId" | "authorization">> | Omit<NativeChatGptJourney, "enabled" | "page" | "runId" | "authorization">;
|
|
105
|
+
}
|
|
106
|
+
export interface NativeChatGptCheckpointEvidence {
|
|
107
|
+
readonly id: string;
|
|
108
|
+
readonly operation: NativeChatGptCheckpoint["operation"] | "cleanup";
|
|
109
|
+
readonly status: NativeChatGptStatus;
|
|
110
|
+
readonly category: string;
|
|
111
|
+
readonly observation?: Readonly<Record<string, unknown>>;
|
|
112
|
+
}
|
|
113
|
+
export interface NativeChatGptEvidence {
|
|
114
|
+
readonly schemaVersion: 1;
|
|
115
|
+
readonly evidenceKind: "native-chatgpt-browser";
|
|
116
|
+
readonly status: NativeChatGptStatus;
|
|
117
|
+
readonly runId: string;
|
|
118
|
+
readonly endpointSha256: string;
|
|
119
|
+
readonly ownershipSha256: string;
|
|
120
|
+
readonly clockSkewMs: number;
|
|
121
|
+
readonly startedAt: string;
|
|
122
|
+
readonly finishedAt: string;
|
|
123
|
+
readonly checkpoints: readonly NativeChatGptCheckpointEvidence[];
|
|
124
|
+
}
|
|
125
|
+
/** Use native DOM controls; callers can replace locators when ChatGPT's UI changes. */
|
|
126
|
+
export declare function nativeChatGptControls(page: Page): NativeChatGptControls;
|
|
127
|
+
/** Preserve type checking for an explicitly enabled, adopter-owned native journey. */
|
|
128
|
+
export declare function defineNativeChatGptJourney(journey: NativeChatGptJourney): NativeChatGptJourney;
|
|
129
|
+
/** No browser is launched, account connected, or authentication state exported by this runner. */
|
|
130
|
+
export declare function runNativeChatGptJourney(journey: NativeChatGptJourney, output: string): Promise<NativeChatGptEvidence>;
|
|
131
|
+
export {};
|
|
132
|
+
//# sourceMappingURL=chatgpt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatgpt.d.ts","sourceRoot":"","sources":["../src/chatgpt.ts"],"names":[],"mappings":"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"}
|
package/dist/chatgpt.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
|
|
5
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
6
|
+
const METHODS = new Set(["initialize", "tools/list", "tools/call", "resources/list", "resources/templates/list", "resources/read", "prompts/list", "prompts/get", "notifications/cancelled"]);
|
|
7
|
+
const MAX_BYTES = 16 * 1024 * 1024;
|
|
8
|
+
const MAX_TEXT = 64 * 1024;
|
|
9
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
10
|
+
/** Use native DOM controls; callers can replace locators when ChatGPT's UI changes. */
|
|
11
|
+
export function nativeChatGptControls(page) {
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
composer: page.locator("#prompt-textarea"),
|
|
14
|
+
submit: page.getByTestId("send-button"),
|
|
15
|
+
assistantMessages: page.locator('[data-message-author-role="assistant"]'),
|
|
16
|
+
conversationMessages: page.locator('[data-message-author-role="user"], [data-message-author-role="assistant"]'),
|
|
17
|
+
generating: page.getByTestId("stop-button"),
|
|
18
|
+
fileInput: page.locator('input[type="file"][multiple]'),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Preserve type checking for an explicitly enabled, adopter-owned native journey. */
|
|
22
|
+
export function defineNativeChatGptJourney(journey) {
|
|
23
|
+
return journey;
|
|
24
|
+
}
|
|
25
|
+
/** No browser is launched, account connected, or authentication state exported by this runner. */
|
|
26
|
+
export async function runNativeChatGptJourney(journey, output) {
|
|
27
|
+
validate(journey, output);
|
|
28
|
+
const controls = journey.controls ?? nativeChatGptControls(journey.page);
|
|
29
|
+
for (const control of Object.values(controls))
|
|
30
|
+
requireOwnedLocator(control, journey.page);
|
|
31
|
+
const timeout = journey.timeoutMs ?? 30_000;
|
|
32
|
+
const endpointSha256 = digest(journey.authorization.endpoint.href);
|
|
33
|
+
const ownershipSha256 = digest(JSON.stringify([journey.authorization.accountOwner, journey.authorization.endpointOwner, journey.authorization.fixtureOwner, journey.authorization.cleanupOwner]));
|
|
34
|
+
// Snapshot declared expectations before a collector can observe actual calls.
|
|
35
|
+
const steps = journey.checkpoints.map((checkpoint) => checkpoint.operation === "server-observation"
|
|
36
|
+
? { ...checkpoint, expectedCalls: checkpoint.expectedCalls.map((call) => ({ method: call.method, target: call.target, outcome: call.outcome, ...(call.responseSha256 ? { responseSha256: call.responseSha256 } : {}) })) }
|
|
37
|
+
: { ...checkpoint });
|
|
38
|
+
const startedAt = new Date().toISOString();
|
|
39
|
+
const clockSkewMs = journey.clockSkewMs ?? 1_000;
|
|
40
|
+
const checkpoints = [];
|
|
41
|
+
let sequence = 0;
|
|
42
|
+
let pendingObservation = false;
|
|
43
|
+
let priorConversation;
|
|
44
|
+
let freshRequired = false;
|
|
45
|
+
let observedCalls = 0;
|
|
46
|
+
let sentTurns = 0;
|
|
47
|
+
let since = startedAt;
|
|
48
|
+
try {
|
|
49
|
+
for (const checkpoint of steps) {
|
|
50
|
+
let observation;
|
|
51
|
+
try {
|
|
52
|
+
if (checkpoint.operation !== "human")
|
|
53
|
+
assertNativeHost(journey.page);
|
|
54
|
+
switch (checkpoint.operation) {
|
|
55
|
+
case "human":
|
|
56
|
+
if (await bounded(checkpoint.ready, timeout) !== true)
|
|
57
|
+
throw new Outcome("blocked", "human-required");
|
|
58
|
+
assertNativeHost(journey.page);
|
|
59
|
+
break;
|
|
60
|
+
case "fresh-conversation":
|
|
61
|
+
if (pendingObservation)
|
|
62
|
+
throw new Outcome("incomplete", "unobserved-turn");
|
|
63
|
+
priorConversation = conversationId(journey.page.url()) ?? priorConversation;
|
|
64
|
+
await journey.page.goto("https://chatgpt.com/", { waitUntil: "domcontentloaded", timeout });
|
|
65
|
+
await controls.composer.waitFor({ state: "visible", timeout });
|
|
66
|
+
if ((await controls.composer.innerText()).trim())
|
|
67
|
+
throw new Outcome("incomplete", "composer-not-empty");
|
|
68
|
+
if (await controls.conversationMessages.count() !== 0)
|
|
69
|
+
throw new Outcome("incomplete", "fresh-conversation-unconfirmed");
|
|
70
|
+
freshRequired = priorConversation !== undefined;
|
|
71
|
+
break;
|
|
72
|
+
case "click":
|
|
73
|
+
since = new Date().toISOString();
|
|
74
|
+
await checkpoint.target.click({ timeout });
|
|
75
|
+
assertNativeHost(journey.page);
|
|
76
|
+
break;
|
|
77
|
+
case "attach": {
|
|
78
|
+
const info = await stat(checkpoint.file);
|
|
79
|
+
if (!info.isFile() || info.size > MAX_BYTES)
|
|
80
|
+
throw new Outcome("failed", "fixture-too-large");
|
|
81
|
+
const bytes = await readFile(checkpoint.file);
|
|
82
|
+
if (bytes.length > MAX_BYTES)
|
|
83
|
+
throw new Outcome("failed", "fixture-too-large");
|
|
84
|
+
await controls.fileInput.setInputFiles({ name: basename(checkpoint.file), mimeType: checkpoint.mimeType, buffer: bytes }, { timeout });
|
|
85
|
+
await checkpoint.ready.waitFor({ state: "visible", timeout });
|
|
86
|
+
observation = { fileSha256: digest(bytes), bytes: bytes.length };
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "send": {
|
|
90
|
+
if (pendingObservation)
|
|
91
|
+
throw new Outcome("incomplete", "unobserved-turn");
|
|
92
|
+
const before = await controls.assistantMessages.count();
|
|
93
|
+
since = new Date().toISOString();
|
|
94
|
+
await controls.composer.fill(checkpoint.prompt, { timeout });
|
|
95
|
+
await controls.submit.click({ timeout });
|
|
96
|
+
const reply = controls.assistantMessages.nth(before);
|
|
97
|
+
await reply.waitFor({ state: "visible", timeout });
|
|
98
|
+
const text = await completedReply(reply, controls.generating, timeout);
|
|
99
|
+
assertNativeHost(journey.page);
|
|
100
|
+
const conversation = conversationId(journey.page.url());
|
|
101
|
+
if (!conversation || (freshRequired && conversation === priorConversation))
|
|
102
|
+
throw new Outcome("incomplete", "fresh-conversation-unconfirmed");
|
|
103
|
+
if (await bounded(() => Promise.resolve(checkpoint.expectResponse(text)), timeout) !== true)
|
|
104
|
+
throw new Outcome("failed", "response-assertion");
|
|
105
|
+
priorConversation = conversation;
|
|
106
|
+
freshRequired = false;
|
|
107
|
+
pendingObservation = true;
|
|
108
|
+
sentTurns += 1;
|
|
109
|
+
observation = { promptSha256: digest(checkpoint.prompt), responseSha256: digest(text), conversationSha256: digest(conversation) };
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "visible":
|
|
113
|
+
await checkpoint.target.waitFor({ state: "visible", timeout });
|
|
114
|
+
break;
|
|
115
|
+
case "image": {
|
|
116
|
+
const box = await checkpoint.target.boundingBox();
|
|
117
|
+
if (!box || box.width * box.height > 4_000_000)
|
|
118
|
+
throw new Outcome("incomplete", "image-unavailable");
|
|
119
|
+
const png = await checkpoint.target.screenshot({ type: "png", scale: "css", animations: "disabled", timeout });
|
|
120
|
+
if (png.length > MAX_BYTES)
|
|
121
|
+
throw new Outcome("failed", "image-too-large");
|
|
122
|
+
if (await bounded(() => Promise.resolve(checkpoint.assertImage(Buffer.from(png))), timeout) !== true)
|
|
123
|
+
throw new Outcome("failed", "image-assertion");
|
|
124
|
+
const artifact = join(dirname(output), `${checkpoint.id}.png`);
|
|
125
|
+
await privateFile(artifact, png);
|
|
126
|
+
observation = { imageSha256: digest(png), bytes: png.length };
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "server-observation": {
|
|
130
|
+
const calls = await bounded((signal) => checkpoint.collect({ runId: journey.runId, afterSequence: sequence, since, signal }), timeout);
|
|
131
|
+
if (!Array.isArray(calls) || calls.length !== checkpoint.expectedCalls.length)
|
|
132
|
+
throw new Outcome("incomplete", "server-observation-missing");
|
|
133
|
+
const retained = calls.map((call, index) => {
|
|
134
|
+
const expected = checkpoint.expectedCalls[index];
|
|
135
|
+
if (call.nativeClient === "unverified")
|
|
136
|
+
throw new Outcome("incomplete", "native-client-unverified");
|
|
137
|
+
const observed = Date.parse(call.observedAt);
|
|
138
|
+
if (call.nativeClient !== "verified-chatgpt" || !Number.isFinite(observed) || observed < Date.parse(since) - clockSkewMs || observed > Date.now() + clockSkewMs || call.runId !== journey.runId || call.endpointSha256 !== endpointSha256 || !Number.isSafeInteger(call.sequence) || call.sequence <= sequence)
|
|
139
|
+
throw new Outcome("failed", "server-observation-scope");
|
|
140
|
+
if (call.method !== expected.method || call.target !== expected.target || call.outcome !== expected.outcome || !SHA256.test(call.requestSha256) || !SHA256.test(call.responseSha256) || (expected.responseSha256 !== undefined && call.responseSha256 !== expected.responseSha256))
|
|
141
|
+
throw new Outcome("failed", "server-assertion");
|
|
142
|
+
sequence = call.sequence;
|
|
143
|
+
return { method: call.method, targetSha256: digest(call.target), requestSha256: call.requestSha256, responseSha256: call.responseSha256, outcome: call.outcome };
|
|
144
|
+
});
|
|
145
|
+
observedCalls += calls.length;
|
|
146
|
+
pendingObservation = false;
|
|
147
|
+
observation = { calls: retained, contractSha256: digest(JSON.stringify(checkpoint.expectedCalls)) };
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "application-state": {
|
|
151
|
+
const state = await bounded(checkpoint.readState, timeout);
|
|
152
|
+
if (state !== checkpoint.expectedSha256)
|
|
153
|
+
throw new Outcome("failed", "application-state-assertion");
|
|
154
|
+
observation = { stateSha256: state };
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
assertNativeHost(journey.page);
|
|
159
|
+
checkpoints.push(Object.freeze({ id: checkpoint.id, operation: checkpoint.operation, status: "passed", category: "verified", ...(observation ? { observation } : {}) }));
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const outcome = error instanceof Outcome ? error : new Outcome("incomplete", "browser-or-observer-unavailable");
|
|
163
|
+
checkpoints.push(Object.freeze({ id: checkpoint.id, operation: checkpoint.operation, status: outcome.status, category: outcome.category }));
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (checkpoints.every((checkpoint) => checkpoint.status === "passed") && (pendingObservation || observedCalls === 0 || sentTurns === 0)) {
|
|
168
|
+
checkpoints.push(Object.freeze({ id: "server-evidence", operation: "server-observation", status: "incomplete", category: "unobserved-turn" }));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
try {
|
|
173
|
+
const result = await bounded(journey.cleanup, timeout);
|
|
174
|
+
const confirmed = result?.fixturesRemoved === true && result?.conversationsRemoved === true;
|
|
175
|
+
checkpoints.push(Object.freeze({ id: "cleanup", operation: "cleanup", status: confirmed ? "passed" : "incomplete", category: confirmed ? "verified" : "cleanup-unconfirmed" }));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
checkpoints.push(Object.freeze({ id: "cleanup", operation: "cleanup", status: "incomplete", category: "cleanup-unconfirmed" }));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const status = ["failed", "blocked", "incomplete"].find((candidate) => checkpoints.some((checkpoint) => checkpoint.status === candidate)) ?? "passed";
|
|
182
|
+
const evidence = Object.freeze({
|
|
183
|
+
schemaVersion: 1, evidenceKind: "native-chatgpt-browser", status, runId: journey.runId, endpointSha256,
|
|
184
|
+
ownershipSha256, clockSkewMs,
|
|
185
|
+
startedAt, finishedAt: new Date().toISOString(), checkpoints: Object.freeze(checkpoints),
|
|
186
|
+
});
|
|
187
|
+
await privateFile(output, `${JSON.stringify(evidence, null, 2)}\n`);
|
|
188
|
+
return evidence;
|
|
189
|
+
}
|
|
190
|
+
class Outcome extends Error {
|
|
191
|
+
status;
|
|
192
|
+
category;
|
|
193
|
+
constructor(status, category) {
|
|
194
|
+
super(category);
|
|
195
|
+
this.status = status;
|
|
196
|
+
this.category = category;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function assertNativeHost(page) {
|
|
200
|
+
try {
|
|
201
|
+
if (new URL(page.url()).origin === "https://chatgpt.com")
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
catch { /* No native host is observable. */ }
|
|
205
|
+
throw new Outcome("blocked", "native-host-required");
|
|
206
|
+
}
|
|
207
|
+
function conversationId(url) { return new URL(url).pathname.match(/\/c\/([A-Za-z0-9-]+)(?:\/|$)/)?.[1]; }
|
|
208
|
+
async function completedReply(reply, generating, timeout) {
|
|
209
|
+
const end = Date.now() + timeout;
|
|
210
|
+
let previous = "";
|
|
211
|
+
let stableSince = Date.now();
|
|
212
|
+
while (Date.now() < end) {
|
|
213
|
+
const text = await reply.innerText({ timeout: Math.max(1, end - Date.now()) });
|
|
214
|
+
if (Buffer.byteLength(text) > MAX_TEXT)
|
|
215
|
+
throw new Outcome("incomplete", "response-too-large");
|
|
216
|
+
if (text !== previous) {
|
|
217
|
+
previous = text;
|
|
218
|
+
stableSince = Date.now();
|
|
219
|
+
}
|
|
220
|
+
if (text.trim() && !(await generating.isVisible()) && Date.now() - stableSince >= 1_000)
|
|
221
|
+
return text;
|
|
222
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
223
|
+
}
|
|
224
|
+
throw new Outcome("incomplete", "response-not-complete");
|
|
225
|
+
}
|
|
226
|
+
async function bounded(operation, timeout) {
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
let timer;
|
|
229
|
+
const expired = new Promise((_, reject) => { timer = setTimeout(() => { controller.abort(); reject(new Outcome("incomplete", "observation-timeout")); }, timeout); });
|
|
230
|
+
try {
|
|
231
|
+
return await Promise.race([Promise.resolve().then(() => operation(controller.signal)), expired]);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
clearTimeout(timer);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function privateFile(path, data) {
|
|
238
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
239
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
240
|
+
try {
|
|
241
|
+
await writeFile(temporary, data, { mode: 0o600, flag: "wx" });
|
|
242
|
+
await rename(temporary, path);
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await rm(temporary, { force: true });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function validate(journey, output) {
|
|
249
|
+
if (journey.enabled !== true)
|
|
250
|
+
throw new TypeError("native ChatGPT browser testing must be explicitly enabled");
|
|
251
|
+
const auth = journey.authorization;
|
|
252
|
+
if (auth?.approved !== true || auth.fixturePolicy !== "synthetic" || [auth.accountOwner, auth.endpointOwner, auth.fixtureOwner, auth.cleanupOwner].some((owner) => typeof owner !== "string" || !owner.trim() || owner.length > 256))
|
|
253
|
+
throw new TypeError("approved account, endpoint, fixture, and cleanup ownership is required");
|
|
254
|
+
if (!(auth.endpoint instanceof URL) || auth.endpoint.protocol !== "https:" || auth.endpoint.username || auth.endpoint.password || auth.endpoint.search || auth.endpoint.hash)
|
|
255
|
+
throw new TypeError("an authorized HTTPS MCP endpoint without credentials is required");
|
|
256
|
+
if (!ID.test(journey.runId) || typeof output !== "string" || !output || typeof journey.cleanup !== "function" || !journey.page || !Array.isArray(journey.checkpoints) || journey.checkpoints.length === 0 || journey.checkpoints.length > 64)
|
|
257
|
+
throw new TypeError("a bounded native journey, private output, and cleanup are required");
|
|
258
|
+
if (journey.timeoutMs !== undefined && (!Number.isSafeInteger(journey.timeoutMs) || journey.timeoutMs < 100 || journey.timeoutMs > 300_000))
|
|
259
|
+
throw new TypeError("native journey timeout must be between 100 and 300000 milliseconds");
|
|
260
|
+
if (journey.clockSkewMs !== undefined && (!Number.isSafeInteger(journey.clockSkewMs) || journey.clockSkewMs < 0 || journey.clockSkewMs > 5_000))
|
|
261
|
+
throw new TypeError("native observation clock skew must be between 0 and 5000 milliseconds");
|
|
262
|
+
const ids = new Set(["cleanup", "server-evidence"]);
|
|
263
|
+
for (const checkpoint of journey.checkpoints) {
|
|
264
|
+
if (!ID.test(checkpoint.id) || ids.has(checkpoint.id))
|
|
265
|
+
throw new TypeError("checkpoint IDs must be unique bounded identifiers");
|
|
266
|
+
ids.add(checkpoint.id);
|
|
267
|
+
if (checkpoint.operation === "click" || checkpoint.operation === "visible" || checkpoint.operation === "image")
|
|
268
|
+
requireOwnedLocator(checkpoint.target, journey.page);
|
|
269
|
+
if (checkpoint.operation === "attach")
|
|
270
|
+
requireOwnedLocator(checkpoint.ready, journey.page);
|
|
271
|
+
if (checkpoint.operation === "attach" && (typeof checkpoint.file !== "string" || !checkpoint.file || typeof checkpoint.mimeType !== "string" || !/^[a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+$/.test(checkpoint.mimeType)))
|
|
272
|
+
throw new TypeError("a fixture file and MIME type are required");
|
|
273
|
+
if (checkpoint.operation === "send" && (typeof checkpoint.prompt !== "string" || !checkpoint.prompt.trim() || Buffer.byteLength(checkpoint.prompt) > MAX_TEXT || typeof checkpoint.expectResponse !== "function"))
|
|
274
|
+
throw new TypeError("a bounded prompt and response assertion are required");
|
|
275
|
+
if (checkpoint.operation === "human" && typeof checkpoint.ready !== "function")
|
|
276
|
+
throw new TypeError("human readiness observation is required");
|
|
277
|
+
if (checkpoint.operation === "image" && typeof checkpoint.assertImage !== "function")
|
|
278
|
+
throw new TypeError("an image assertion is required");
|
|
279
|
+
if (checkpoint.operation === "application-state" && (!SHA256.test(checkpoint.expectedSha256) || typeof checkpoint.readState !== "function"))
|
|
280
|
+
throw new TypeError("declared application state and an independent reader are required");
|
|
281
|
+
if (checkpoint.operation === "server-observation") {
|
|
282
|
+
if (typeof checkpoint.collect !== "function" || !Array.isArray(checkpoint.expectedCalls) || !checkpoint.expectedCalls.length || checkpoint.expectedCalls.length > 64)
|
|
283
|
+
throw new TypeError("bounded server observations and declared expectations are required");
|
|
284
|
+
for (const call of checkpoint.expectedCalls)
|
|
285
|
+
if (!METHODS.has(call.method) || typeof call.target !== "string" || call.target.length > 4096 || !["success", "denied", "cancelled"].includes(call.outcome) || (call.responseSha256 !== undefined && !SHA256.test(call.responseSha256)))
|
|
286
|
+
throw new TypeError("invalid server observation expectation");
|
|
287
|
+
}
|
|
288
|
+
if (!["human", "fresh-conversation", "click", "attach", "send", "visible", "image", "application-state", "server-observation"].includes(checkpoint.operation))
|
|
289
|
+
throw new TypeError("unknown native checkpoint operation");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function requireOwnedLocator(locator, page) {
|
|
293
|
+
if (!locator || typeof locator.page !== "function" || locator.page() !== page)
|
|
294
|
+
throw new TypeError("native controls must belong to the authorized page");
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=chatgpt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatgpt.js","sourceRoot":"","sources":["../src/chatgpt.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "MCP integration and semantic testing helpers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,12 +27,18 @@
|
|
|
27
27
|
"./semantic": {
|
|
28
28
|
"types": "./semantic/test.d.mts",
|
|
29
29
|
"import": "./semantic/test.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./chatgpt": {
|
|
32
|
+
"types": "./dist/chatgpt.d.ts",
|
|
33
|
+
"import": "./dist/chatgpt.js"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
"bin": {
|
|
33
37
|
"emseepea-contract": "./published-contract/cli.mjs",
|
|
34
38
|
"emseepea-qualify": "./mcp-cli/cli.mjs",
|
|
35
|
-
"emseepea-test": "./semantic/cli.mjs"
|
|
39
|
+
"emseepea-test": "./semantic/cli.mjs",
|
|
40
|
+
"emseepea-chatgpt-setup": "./chatgpt/setup.mjs",
|
|
41
|
+
"emseepea-chatgpt": "./chatgpt/run.mjs"
|
|
36
42
|
},
|
|
37
43
|
"files": [
|
|
38
44
|
"dist",
|
|
@@ -40,7 +46,8 @@
|
|
|
40
46
|
"published-contract",
|
|
41
47
|
"semantic",
|
|
42
48
|
"LICENSE",
|
|
43
|
-
"README.md"
|
|
49
|
+
"README.md",
|
|
50
|
+
"chatgpt"
|
|
44
51
|
],
|
|
45
52
|
"scripts": {
|
|
46
53
|
"build": "tsc -p tsconfig.json && tsc -p tsconfig.type-tests.json",
|
package/semantic/provider.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import { scriptedElicitations } from "./elicitation.mjs";
|
|
|
6
6
|
|
|
7
7
|
const claudeModel = "claude-sonnet-4-6";
|
|
8
8
|
const mcpServerName = "emseepea_eval";
|
|
9
|
-
const minimumCodexCliVersion = "0.162.0";
|
|
10
9
|
const codexEmptyDiscoveryTools = new Map([
|
|
11
10
|
["list_mcp_resources", "resources"],
|
|
12
11
|
["list_mcp_resource_templates", "resourceTemplates"],
|
|
@@ -44,9 +43,6 @@ export async function modelVersion(provider = "claude-local") {
|
|
|
44
43
|
const result = await runProcess(command, ["--version"], { env: modelEnvironment({}) });
|
|
45
44
|
const version = result.stdout.trim().match(/(?:codex-cli\s+)?(\d+\.\d+\.\d+)\b/)?.[1];
|
|
46
45
|
if (result.code !== 0 || !version) throw new Error(`Could not verify ${codex ? "Codex" : "Claude"} CLI version`);
|
|
47
|
-
if (codex && compareVersions(version, minimumCodexCliVersion) < 0) {
|
|
48
|
-
throw new Error(`Codex CLI ${version} is unsupported; use ${minimumCodexCliVersion} or later`);
|
|
49
|
-
}
|
|
50
46
|
return version;
|
|
51
47
|
}
|
|
52
48
|
|
|
@@ -677,16 +673,6 @@ function classifyCodexFailure(stderr) {
|
|
|
677
673
|
return "unknown";
|
|
678
674
|
}
|
|
679
675
|
|
|
680
|
-
function compareVersions(left, right) {
|
|
681
|
-
const a = left.split(".").map(Number);
|
|
682
|
-
const b = right.split(".").map(Number);
|
|
683
|
-
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
684
|
-
const difference = (a[index] ?? 0) - (b[index] ?? 0);
|
|
685
|
-
if (difference !== 0) return difference;
|
|
686
|
-
}
|
|
687
|
-
return 0;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
676
|
function codexEvidenceError(message, category) {
|
|
691
677
|
return Object.assign(new Error(message), {
|
|
692
678
|
providerFailureCategory: codexFailureCategories.has(category) ? category : "unknown",
|