@gate-forge/pack-playwright 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/bin/gateforge-witness.js +40 -0
- package/dist/attestation/proxy.d.ts +40 -0
- package/dist/attestation/proxy.d.ts.map +1 -0
- package/dist/attestation/proxy.js +78 -0
- package/dist/attestation/proxy.js.map +1 -0
- package/dist/constants.d.ts +130 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +138 -0
- package/dist/constants.js.map +1 -0
- package/dist/discovery/adapters.d.ts +167 -0
- package/dist/discovery/adapters.d.ts.map +1 -0
- package/dist/discovery/adapters.js +276 -0
- package/dist/discovery/adapters.js.map +1 -0
- package/dist/discovery/discover.d.ts +57 -0
- package/dist/discovery/discover.d.ts.map +1 -0
- package/dist/discovery/discover.js +544 -0
- package/dist/discovery/discover.js.map +1 -0
- package/dist/discovery/index.d.ts +15 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +15 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/inference.d.ts +63 -0
- package/dist/discovery/inference.d.ts.map +1 -0
- package/dist/discovery/inference.js +173 -0
- package/dist/discovery/inference.js.map +1 -0
- package/dist/discovery/pytest-adapter.d.ts +205 -0
- package/dist/discovery/pytest-adapter.d.ts.map +1 -0
- package/dist/discovery/pytest-adapter.js +548 -0
- package/dist/discovery/pytest-adapter.js.map +1 -0
- package/dist/discovery/reconcile.d.ts +106 -0
- package/dist/discovery/reconcile.d.ts.map +1 -0
- package/dist/discovery/reconcile.js +268 -0
- package/dist/discovery/reconcile.js.map +1 -0
- package/dist/discovery/runner-env.d.ts +80 -0
- package/dist/discovery/runner-env.d.ts.map +1 -0
- package/dist/discovery/runner-env.js +139 -0
- package/dist/discovery/runner-env.js.map +1 -0
- package/dist/discovery/static-discovery.d.ts +121 -0
- package/dist/discovery/static-discovery.d.ts.map +1 -0
- package/dist/discovery/static-discovery.js +1028 -0
- package/dist/discovery/static-discovery.js.map +1 -0
- package/dist/discovery/supervised-run.d.ts +110 -0
- package/dist/discovery/supervised-run.d.ts.map +1 -0
- package/dist/discovery/supervised-run.js +328 -0
- package/dist/discovery/supervised-run.js.map +1 -0
- package/dist/discovery/trusted-config.d.ts +103 -0
- package/dist/discovery/trusted-config.d.ts.map +1 -0
- package/dist/discovery/trusted-config.js +117 -0
- package/dist/discovery/trusted-config.js.map +1 -0
- package/dist/fixture/evidence.d.ts +191 -0
- package/dist/fixture/evidence.d.ts.map +1 -0
- package/dist/fixture/evidence.js +366 -0
- package/dist/fixture/evidence.js.map +1 -0
- package/dist/fixture/fixture.d.ts +37 -0
- package/dist/fixture/fixture.d.ts.map +1 -0
- package/dist/fixture/fixture.js +38 -0
- package/dist/fixture/fixture.js.map +1 -0
- package/dist/fixture/witness-client.d.ts +164 -0
- package/dist/fixture/witness-client.d.ts.map +1 -0
- package/dist/fixture/witness-client.js +253 -0
- package/dist/fixture/witness-client.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +16 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +21 -0
- package/dist/json.js.map +1 -0
- package/dist/reporter/ledger.d.ts +105 -0
- package/dist/reporter/ledger.d.ts.map +1 -0
- package/dist/reporter/ledger.js +197 -0
- package/dist/reporter/ledger.js.map +1 -0
- package/dist/reporter/reporter.cjs +105 -0
- package/dist/reporter/reporter.cjs.map +1 -0
- package/dist/reporter/reporter.d.cts +35 -0
- package/dist/reporter/reporter.d.cts.map +1 -0
- package/dist/reporter/reporter.d.ts +167 -0
- package/dist/reporter/reporter.d.ts.map +1 -0
- package/dist/reporter/reporter.js +673 -0
- package/dist/reporter/reporter.js.map +1 -0
- package/dist/setup.d.ts +49 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +167 -0
- package/dist/setup.js.map +1 -0
- package/dist/supervisor/client.d.ts +73 -0
- package/dist/supervisor/client.d.ts.map +1 -0
- package/dist/supervisor/client.js +164 -0
- package/dist/supervisor/client.js.map +1 -0
- package/dist/supervisor/drain.d.ts +45 -0
- package/dist/supervisor/drain.d.ts.map +1 -0
- package/dist/supervisor/drain.js +235 -0
- package/dist/supervisor/drain.js.map +1 -0
- package/dist/supervisor/index.d.ts +16 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +13 -0
- package/dist/supervisor/index.js.map +1 -0
- package/dist/supervisor/spool.d.ts +137 -0
- package/dist/supervisor/spool.d.ts.map +1 -0
- package/dist/supervisor/spool.js +263 -0
- package/dist/supervisor/spool.js.map +1 -0
- package/dist/surface.d.ts +134 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +139 -0
- package/dist/surface.js.map +1 -0
- package/dist/witness/adapter-registry.d.ts +38 -0
- package/dist/witness/adapter-registry.d.ts.map +1 -0
- package/dist/witness/adapter-registry.js +158 -0
- package/dist/witness/adapter-registry.js.map +1 -0
- package/dist/witness/bin.d.ts +21 -0
- package/dist/witness/bin.d.ts.map +1 -0
- package/dist/witness/bin.js +172 -0
- package/dist/witness/bin.js.map +1 -0
- package/dist/witness/browser.d.ts +173 -0
- package/dist/witness/browser.d.ts.map +1 -0
- package/dist/witness/browser.js +580 -0
- package/dist/witness/browser.js.map +1 -0
- package/dist/witness/classifications.d.ts +42 -0
- package/dist/witness/classifications.d.ts.map +1 -0
- package/dist/witness/classifications.js +87 -0
- package/dist/witness/classifications.js.map +1 -0
- package/dist/witness/env-attestation.d.ts +98 -0
- package/dist/witness/env-attestation.d.ts.map +1 -0
- package/dist/witness/env-attestation.js +202 -0
- package/dist/witness/env-attestation.js.map +1 -0
- package/dist/witness/loopback-pins.d.ts +79 -0
- package/dist/witness/loopback-pins.d.ts.map +1 -0
- package/dist/witness/loopback-pins.js +244 -0
- package/dist/witness/loopback-pins.js.map +1 -0
- package/dist/witness/server.d.ts +34 -0
- package/dist/witness/server.d.ts.map +1 -0
- package/dist/witness/server.js +2539 -0
- package/dist/witness/server.js.map +1 -0
- package/dist/witness/types.d.ts +643 -0
- package/dist/witness/types.d.ts.map +1 -0
- package/dist/witness/types.js +2 -0
- package/dist/witness/types.js.map +1 -0
- package/package.json +64 -0
- package/python/gateforge_persistence_intents.py +100 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter loading + registry (pin #8, plan §5.4).
|
|
3
|
+
*
|
|
4
|
+
* Reviewed evidence adapters load from `.gateforge/adapters/<name>.mjs`
|
|
5
|
+
* at WITNESS START (engine-side — the test process never imports them).
|
|
6
|
+
* Default export contract:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* export default {
|
|
10
|
+
* read: async (ctx, id) => body | null, // GET-only; ctx.get(path) is the transport
|
|
11
|
+
* normalize: (body) => ({ entityId, fields }), // stamped from the RESPONSE, never caller args
|
|
12
|
+
* deletion: 'hard' | 'archive',
|
|
13
|
+
* environmentFingerprint: '…', // must match the target's marker header
|
|
14
|
+
* baseUrl: 'http://…', // optional override of the witness adapter base
|
|
15
|
+
* probeServer: async (ctx, subject) => ({ found, fields }), // optional: the
|
|
16
|
+
* // SERVER-WITNESSED persistence channel probe — executed
|
|
17
|
+
* // ONLY in this trusted witness process
|
|
18
|
+
* };
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The registry loads fail-closed: a missing/invalid module, a missing
|
|
22
|
+
* `read`/`normalize`/`deletion`/`environmentFingerprint`, an unknown
|
|
23
|
+
* deletion value, or a duplicate name aborts witness startup with an
|
|
24
|
+
* actionable diagnostic naming the file. The registry is frozen after
|
|
25
|
+
* load — there is no registration path a test could reach (invariant 6,
|
|
26
|
+
* GF-11 "registration path does not exist").
|
|
27
|
+
*/
|
|
28
|
+
import { readdirSync } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { pathToFileURL } from 'node:url';
|
|
31
|
+
/**
|
|
32
|
+
* Loads every `.mjs` adapter in `dir` into a frozen registry.
|
|
33
|
+
*
|
|
34
|
+
* Args:
|
|
35
|
+
* dir: the configured adapters directory (absolute).
|
|
36
|
+
*
|
|
37
|
+
* Returns:
|
|
38
|
+
* Map<string, EvidenceAdapter>: keyed by the file basename sans `.mjs`.
|
|
39
|
+
*
|
|
40
|
+
* Throws:
|
|
41
|
+
* AdapterRegistryError: fail-closed load problems — unreadable dir,
|
|
42
|
+
* unimportable module, or a module missing any contract member.
|
|
43
|
+
*/
|
|
44
|
+
export async function loadAdapters(dir) {
|
|
45
|
+
const registry = new Map();
|
|
46
|
+
let entries;
|
|
47
|
+
try {
|
|
48
|
+
entries = readdirSync(dir);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error.code === 'ENOENT') {
|
|
52
|
+
// No adapters directory at all: the registry stays empty; the
|
|
53
|
+
// witness then answers 400 for every persistence request (a run
|
|
54
|
+
// with persistence obligations and no adapters is misconfigured).
|
|
55
|
+
return registry;
|
|
56
|
+
}
|
|
57
|
+
throw new AdapterRegistryError(`cannot read adapters directory '${dir}': ${error.message}`);
|
|
58
|
+
}
|
|
59
|
+
for (const entry of entries.sort()) {
|
|
60
|
+
if (!entry.endsWith('.mjs'))
|
|
61
|
+
continue;
|
|
62
|
+
const name = entry.slice(0, -'.mjs'.length);
|
|
63
|
+
if (registry.has(name)) {
|
|
64
|
+
throw new AdapterRegistryError(`duplicate adapter '${name}' (multiple '${entry}' files is a config error)`);
|
|
65
|
+
}
|
|
66
|
+
const module = await importAdapter(dir, entry, name);
|
|
67
|
+
const adapter = validateAdapter(module, name);
|
|
68
|
+
registry.set(name, adapter);
|
|
69
|
+
}
|
|
70
|
+
return Object.freeze(registry);
|
|
71
|
+
}
|
|
72
|
+
/** Imports one adapter module; import errors carry an actionable message. */
|
|
73
|
+
async function importAdapter(dir, entry, name) {
|
|
74
|
+
try {
|
|
75
|
+
const loaded = (await import(pathToFileURL(join(dir, entry)).href));
|
|
76
|
+
return loaded.default;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new AdapterRegistryError(`adapter '${name}' (${join(dir, entry)}) could not be imported: ` +
|
|
80
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validates one adapter module against the pin-#8 contract.
|
|
85
|
+
*
|
|
86
|
+
* Args:
|
|
87
|
+
* module: the default export of the adapter file.
|
|
88
|
+
* name: adapter name, used in diagnostics.
|
|
89
|
+
*
|
|
90
|
+
* Returns:
|
|
91
|
+
* EvidenceAdapter: the validated adapter.
|
|
92
|
+
*
|
|
93
|
+
* Throws:
|
|
94
|
+
* AdapterRegistryError: when any required member is missing or shaped
|
|
95
|
+
* wrong. Optional `baseUrl` (a string) overrides the witness's
|
|
96
|
+
* configured adapter base for this adapter.
|
|
97
|
+
*/
|
|
98
|
+
export function validateAdapter(module, name) {
|
|
99
|
+
if (typeof module !== 'object' || module === null) {
|
|
100
|
+
throw new AdapterRegistryError(`adapter '${name}' must default-export an object {read, normalize, deletion, environmentFingerprint}`);
|
|
101
|
+
}
|
|
102
|
+
const adapter = module;
|
|
103
|
+
const problems = [];
|
|
104
|
+
if (typeof adapter['read'] !== 'function')
|
|
105
|
+
problems.push('read must be an async function (ctx, id)');
|
|
106
|
+
if (typeof adapter['normalize'] !== 'function')
|
|
107
|
+
problems.push('normalize must be a function (body) => {entityId, fields}');
|
|
108
|
+
if (adapter['deletion'] !== 'hard' && adapter['deletion'] !== 'archive') {
|
|
109
|
+
problems.push("deletion must be 'hard' or 'archive'");
|
|
110
|
+
}
|
|
111
|
+
if (typeof adapter['environmentFingerprint'] !== 'string') {
|
|
112
|
+
problems.push('environmentFingerprint must be a string');
|
|
113
|
+
}
|
|
114
|
+
if (adapter['baseUrl'] !== undefined &&
|
|
115
|
+
(typeof adapter['baseUrl'] !== 'string' || adapter['baseUrl'].length === 0)) {
|
|
116
|
+
problems.push('baseUrl must be a non-empty string when present');
|
|
117
|
+
}
|
|
118
|
+
if (adapter['list'] !== undefined && typeof adapter['list'] !== 'function') {
|
|
119
|
+
problems.push('list must be a function (ctx) => entity[] when present');
|
|
120
|
+
}
|
|
121
|
+
// Server probe (server-witnessed persistence channel): OPTIONAL — an
|
|
122
|
+
// adapter without it simply cannot serve the channel and every server
|
|
123
|
+
// intent for the resource resolves to a typed
|
|
124
|
+
// SERVER_PROBE_UNAVAILABLE failure. A PRESENT but malformed export is
|
|
125
|
+
// a contract violation caught here at load, fail-closed, not at
|
|
126
|
+
// intent time.
|
|
127
|
+
if (adapter['probeServer'] !== undefined && typeof adapter['probeServer'] !== 'function') {
|
|
128
|
+
problems.push('probeServer must be an async function (ctx, subject) => {found, fields} when present');
|
|
129
|
+
}
|
|
130
|
+
if (problems.length > 0) {
|
|
131
|
+
throw new AdapterRegistryError(`adapter '${name}' violates the adapter contract: ${problems.join('; ')}`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
read: adapter['read'],
|
|
135
|
+
normalize: adapter['normalize'],
|
|
136
|
+
deletion: adapter['deletion'],
|
|
137
|
+
environmentFingerprint: adapter['environmentFingerprint'],
|
|
138
|
+
baseUrl: adapter['baseUrl'],
|
|
139
|
+
...(adapter['list'] !== undefined
|
|
140
|
+
? { list: adapter['list'] }
|
|
141
|
+
: {}),
|
|
142
|
+
...(adapter['probeServer'] !== undefined
|
|
143
|
+
? { probeServer: adapter['probeServer'] }
|
|
144
|
+
: {}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** Fail-closed adapter-loading error (witness startup aborts). */
|
|
148
|
+
export class AdapterRegistryError extends Error {
|
|
149
|
+
constructor(message) {
|
|
150
|
+
super(message);
|
|
151
|
+
this.name = 'AdapterRegistryError';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** The transport the witness hands to adapter `read` calls (GET-only). */
|
|
155
|
+
export function makeAdapterContext(baseUrl, resourceId, get, headers) {
|
|
156
|
+
return Object.freeze({ baseUrl, resourceId, get, ...(headers ? { headers } : {}) });
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=adapter-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-registry.js","sourceRoot":"","sources":["../../src/witness/adapter-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WitnessHandle } from './types.js';
|
|
2
|
+
/** One-line usage (the header comment above is the long form). */
|
|
3
|
+
export declare const WITNESS_BIN_USAGE: string;
|
|
4
|
+
/**
|
|
5
|
+
* Reads env + argv and starts the witness.
|
|
6
|
+
*
|
|
7
|
+
* Args:
|
|
8
|
+
* argv: CLI flags (see the module header; `--help` prints usage and
|
|
9
|
+
* returns a never-started handle).
|
|
10
|
+
* env: process environment.
|
|
11
|
+
*
|
|
12
|
+
* Returns:
|
|
13
|
+
* WitnessHandle once listening (or a handle with an empty `url` for
|
|
14
|
+
* `--help`; the wrapper skips its URL banner in that case).
|
|
15
|
+
*
|
|
16
|
+
* Throws:
|
|
17
|
+
* WitnessStartupError / AttestationError / AdapterRegistryError:
|
|
18
|
+
* fail-closed startup problems (exit 2 via the wrapper).
|
|
19
|
+
*/
|
|
20
|
+
export declare function main(argv: readonly string[], env: NodeJS.ProcessEnv): Promise<WitnessHandle>;
|
|
21
|
+
//# sourceMappingURL=bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/witness/bin.ts"],"names":[],"mappings":"AA0DA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,QAKK,CAAC;AAkFpC;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,IAAI,CACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,OAAO,CAAC,aAAa,CAAC,CAiCxB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Witness process entry (`gateforge-witness`): derives configuration
|
|
3
|
+
* from CLI flags and the environment and starts the loopback witness
|
|
4
|
+
* service.
|
|
5
|
+
*
|
|
6
|
+
* Usage (every flag has an env fallback; the flag wins when both are
|
|
7
|
+
* given):
|
|
8
|
+
*
|
|
9
|
+
* gateforge-witness [--help]
|
|
10
|
+
* [--proxy-target <url>] start the observation proxy against
|
|
11
|
+
* this loopback base URL (ADR 0004 D7)
|
|
12
|
+
* [--mount-path <prefix>] browser-facing mount prefix the proxy
|
|
13
|
+
* strips before forwarding AND recording
|
|
14
|
+
* (e.g. /api; requires --proxy-target)
|
|
15
|
+
* [--state-dir <dir>] manifest append at shutdown
|
|
16
|
+
* [--classifications <path>] classifications YAML input
|
|
17
|
+
* [--adapters-dir <dir>] reviewed adapters (default .gateforge/adapters)
|
|
18
|
+
* [--target-base-url <url>] attestation subject (GF-10, loopback)
|
|
19
|
+
* [--target-fingerprint <m>] expected marker (GF-13)
|
|
20
|
+
* [--adapter-base-url <url>] default adapter read base
|
|
21
|
+
*
|
|
22
|
+
* Env surface:
|
|
23
|
+
* GATEFORGE_RUN_ID, GATEFORGE_RUN_TOKEN (required; CLI contract)
|
|
24
|
+
* GATEFORGE_PROXY_TARGET, GATEFORGE_MOUNT_PATH (observation proxy)
|
|
25
|
+
* GATEFORGE_STATE_DIR (manifest append at shutdown)
|
|
26
|
+
* GATEFORGE_ADAPTERS_DIR (default .gateforge/adapters)
|
|
27
|
+
* GATEFORGE_CLASSIFICATIONS (classifications YAML path)
|
|
28
|
+
* GATEFORGE_TARGET_BASE_URL (attestation subject, GF-10)
|
|
29
|
+
* GATEFORGE_TARGET_FINGERPRINT (expected marker, GF-13)
|
|
30
|
+
* GATEFORGE_ADAPTER_BASE_URL (default adapter read base)
|
|
31
|
+
* GATEFORGE_WITNESS_VERIFIER_KEY (attestation verifier key)
|
|
32
|
+
*
|
|
33
|
+
* SECRETS NEVER TRAVEL ON ARGV (a process's cmdline is world-readable
|
|
34
|
+
* via /proc — the same rule `gateforge test-gates` applies): the run
|
|
35
|
+
* id/token are env-only (set by 'gateforge test-gates'), and the
|
|
36
|
+
* verifier key is read from GATEFORGE_WITNESS_VERIFIER_KEY only. With a
|
|
37
|
+
* verifier key the witness serves the MAC-attested ledger surface;
|
|
38
|
+
* without one it stays unauthenticated and downstream evaluation fails
|
|
39
|
+
* closed.
|
|
40
|
+
*
|
|
41
|
+
* The wrapper prints `GATEFORGE_WITNESS_URL=<url>` once listening — and
|
|
42
|
+
* `GATEFORGE_WITNESS_PROXY_URL=<url>` when an observation proxy is
|
|
43
|
+
* active; the process serves until SIGTERM/SIGINT.
|
|
44
|
+
*/
|
|
45
|
+
import { startWitness, WitnessStartupError } from './server.js';
|
|
46
|
+
import { ENV_ADAPTER_BASE_URL, ENV_ADAPTERS_DIR, ENV_CLASSIFICATIONS, ENV_MOUNT_PATH, ENV_PROXY_TARGET, ENV_RUN_ID, ENV_RUN_TOKEN, ENV_STATE_DIR, ENV_TARGET_BASE_URL, ENV_TARGET_FINGERPRINT, ENV_WITNESS_VERIFIER_KEY, } from '../constants.js';
|
|
47
|
+
/** One-line usage (the header comment above is the long form). */
|
|
48
|
+
export const WITNESS_BIN_USAGE = 'usage: gateforge-witness [--proxy-target <url>] [--mount-path <prefix>] ' +
|
|
49
|
+
'[--state-dir <dir>] [--classifications <path>] [--adapters-dir <dir>] ' +
|
|
50
|
+
'[--target-base-url <url>] [--target-fingerprint <marker>] [--adapter-base-url <url>] ' +
|
|
51
|
+
'(run id/token via GATEFORGE_RUN_ID/GATEFORGE_RUN_TOKEN; verifier key via ' +
|
|
52
|
+
'GATEFORGE_WITNESS_VERIFIER_KEY)';
|
|
53
|
+
/** Flags this bin accepts (values; `--help` is handled separately). */
|
|
54
|
+
const VALUE_FLAGS = new Set([
|
|
55
|
+
'proxy-target',
|
|
56
|
+
'mount-path',
|
|
57
|
+
'state-dir',
|
|
58
|
+
'classifications',
|
|
59
|
+
'adapters-dir',
|
|
60
|
+
'target-base-url',
|
|
61
|
+
'target-fingerprint',
|
|
62
|
+
'adapter-base-url',
|
|
63
|
+
'adapter-read-authorization',
|
|
64
|
+
]);
|
|
65
|
+
/**
|
|
66
|
+
* Parses the bin argv (`--flag value` and `--flag=value` shapes).
|
|
67
|
+
* Unknown flags and missing values fail closed with the usage line —
|
|
68
|
+
* a mis-wired harness must never silently start a half-configured
|
|
69
|
+
* witness.
|
|
70
|
+
*
|
|
71
|
+
* Args:
|
|
72
|
+
* argv: arguments after node/script.
|
|
73
|
+
*
|
|
74
|
+
* Returns:
|
|
75
|
+
* WitnessBinFlags: parsed flag values.
|
|
76
|
+
*
|
|
77
|
+
* Throws:
|
|
78
|
+
* WitnessStartupError: on any malformed argv.
|
|
79
|
+
*/
|
|
80
|
+
function parseFlags(argv) {
|
|
81
|
+
const values = new Map();
|
|
82
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
83
|
+
const token = argv[index] ?? '';
|
|
84
|
+
if (!token.startsWith('--')) {
|
|
85
|
+
throw new WitnessStartupError(`unexpected argument '${token}'\n${WITNESS_BIN_USAGE}`);
|
|
86
|
+
}
|
|
87
|
+
const body = token.slice(2);
|
|
88
|
+
const eq = body.indexOf('=');
|
|
89
|
+
const name = eq >= 0 ? body.slice(0, eq) : body;
|
|
90
|
+
if (name.length === 0) {
|
|
91
|
+
throw new WitnessStartupError(`invalid flag '${token}'\n${WITNESS_BIN_USAGE}`);
|
|
92
|
+
}
|
|
93
|
+
if (name === 'help') {
|
|
94
|
+
continue; // handled by the caller before parsing matters
|
|
95
|
+
}
|
|
96
|
+
if (!VALUE_FLAGS.has(name)) {
|
|
97
|
+
throw new WitnessStartupError(`unknown flag '--${name}'\n${WITNESS_BIN_USAGE}`);
|
|
98
|
+
}
|
|
99
|
+
let value = eq >= 0 ? body.slice(eq + 1) : undefined;
|
|
100
|
+
if (value === undefined) {
|
|
101
|
+
const next = argv[index + 1];
|
|
102
|
+
if (next === undefined || next.startsWith('--')) {
|
|
103
|
+
throw new WitnessStartupError(`flag '--${name}' requires a value\n${WITNESS_BIN_USAGE}`);
|
|
104
|
+
}
|
|
105
|
+
value = next;
|
|
106
|
+
index += 1;
|
|
107
|
+
}
|
|
108
|
+
if (values.has(name)) {
|
|
109
|
+
throw new WitnessStartupError(`flag '--${name}' may only be given once\n${WITNESS_BIN_USAGE}`);
|
|
110
|
+
}
|
|
111
|
+
values.set(name, value);
|
|
112
|
+
}
|
|
113
|
+
return { values };
|
|
114
|
+
}
|
|
115
|
+
/** Flag value or its env fallback (undefined when neither is set). */
|
|
116
|
+
function flagOrEnv(flags, name, envValue) {
|
|
117
|
+
const fromFlags = flags.values.get(name);
|
|
118
|
+
if (fromFlags !== undefined && fromFlags !== '')
|
|
119
|
+
return fromFlags;
|
|
120
|
+
if (envValue !== undefined && envValue !== '')
|
|
121
|
+
return envValue;
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Reads env + argv and starts the witness.
|
|
126
|
+
*
|
|
127
|
+
* Args:
|
|
128
|
+
* argv: CLI flags (see the module header; `--help` prints usage and
|
|
129
|
+
* returns a never-started handle).
|
|
130
|
+
* env: process environment.
|
|
131
|
+
*
|
|
132
|
+
* Returns:
|
|
133
|
+
* WitnessHandle once listening (or a handle with an empty `url` for
|
|
134
|
+
* `--help`; the wrapper skips its URL banner in that case).
|
|
135
|
+
*
|
|
136
|
+
* Throws:
|
|
137
|
+
* WitnessStartupError / AttestationError / AdapterRegistryError:
|
|
138
|
+
* fail-closed startup problems (exit 2 via the wrapper).
|
|
139
|
+
*/
|
|
140
|
+
export async function main(argv, env) {
|
|
141
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
142
|
+
process.stdout.write(`${WITNESS_BIN_USAGE}\n`);
|
|
143
|
+
return Object.freeze({ url: '', proxyUrl: null, stop: async () => { } });
|
|
144
|
+
}
|
|
145
|
+
const flags = parseFlags(argv);
|
|
146
|
+
const runId = env[ENV_RUN_ID];
|
|
147
|
+
const token = env[ENV_RUN_TOKEN];
|
|
148
|
+
if (runId === undefined || runId === '') {
|
|
149
|
+
throw new WitnessStartupError(`${ENV_RUN_ID} is required (set by 'gateforge test-gates')`);
|
|
150
|
+
}
|
|
151
|
+
if (token === undefined || token === '') {
|
|
152
|
+
throw new WitnessStartupError(`${ENV_RUN_TOKEN} is required (set by 'gateforge test-gates')`);
|
|
153
|
+
}
|
|
154
|
+
// Environment-only, deliberately absent from argv (world-readable
|
|
155
|
+
// /proc cmdline): the suite must never see the verifier key either way.
|
|
156
|
+
const verifierKey = env[ENV_WITNESS_VERIFIER_KEY] ?? null;
|
|
157
|
+
return startWitness({
|
|
158
|
+
runId,
|
|
159
|
+
token,
|
|
160
|
+
proxyTarget: flagOrEnv(flags, 'proxy-target', env[ENV_PROXY_TARGET]),
|
|
161
|
+
mountPath: flagOrEnv(flags, 'mount-path', env[ENV_MOUNT_PATH]),
|
|
162
|
+
stateDir: flagOrEnv(flags, 'state-dir', env[ENV_STATE_DIR]),
|
|
163
|
+
classificationsPath: flagOrEnv(flags, 'classifications', env[ENV_CLASSIFICATIONS]),
|
|
164
|
+
adaptersDir: flagOrEnv(flags, 'adapters-dir', env[ENV_ADAPTERS_DIR] ?? '.gateforge/adapters'),
|
|
165
|
+
targetBaseUrl: flagOrEnv(flags, 'target-base-url', env[ENV_TARGET_BASE_URL]),
|
|
166
|
+
targetFingerprint: flagOrEnv(flags, 'target-fingerprint', env[ENV_TARGET_FINGERPRINT]),
|
|
167
|
+
adapterBaseUrl: flagOrEnv(flags, 'adapter-base-url', env[ENV_ADAPTER_BASE_URL]),
|
|
168
|
+
adapterReadAuthorization: flagOrEnv(flags, 'adapter-read-authorization', env['GATEFORGE_ADAPTER_READ_AUTHORIZATION']) ?? null,
|
|
169
|
+
verifierKey,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/witness/bin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AAGzB,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAC5B,0EAA0E;IAC1E,wEAAwE;IACxE,uFAAuF;IACvF,2EAA2E;IAC3E,iCAAiC,CAAC;AAEpC,uEAAuE;AACvE,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAC;IAC/C,cAAc;IACd,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,oBAAoB;IACpB,kBAAkB;IAClB,4BAA4B;CAC7B,CAAC,CAAC;AAMH;;;;;;;;;;;;;;GAcG;AACH,SAAS,UAAU,CAAC,IAAuB;IACzC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,mBAAmB,CAAC,wBAAwB,KAAK,MAAM,iBAAiB,EAAE,CAAC,CAAC;QACxF,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,KAAK,MAAM,iBAAiB,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,SAAS,CAAC,+CAA+C;QAC3D,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,IAAI,MAAM,iBAAiB,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,KAAK,GAAuB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,MAAM,IAAI,mBAAmB,CAAC,WAAW,IAAI,uBAAuB,iBAAiB,EAAE,CAAC,CAAC;YAC3F,CAAC;YACD,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QACD,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,mBAAmB,CAAC,WAAW,IAAI,6BAA6B,iBAAiB,EAAE,CAAC,CAAC;QACjG,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,sEAAsE;AACtE,SAAS,SAAS,CAChB,KAAsB,EACtB,IAAY,EACZ,QAA4B;IAE5B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAClE,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,IAAuB,EACvB,GAAsB;IAEtB,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,iBAAiB,IAAI,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,IAAmB,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,mBAAmB,CAAC,GAAG,UAAU,8CAA8C,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,mBAAmB,CAAC,GAAG,aAAa,8CAA8C,CAAC,CAAC;IAChG,CAAC;IACD,kEAAkE;IAClE,wEAAwE;IACxE,MAAM,WAAW,GAAG,GAAG,CAAC,wBAAwB,CAAC,IAAI,IAAI,CAAC;IAE1D,OAAO,YAAY,CAAC;QAClB,KAAK;QACL,KAAK;QACL,WAAW,EAAE,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACpE,SAAS,EAAE,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;QAC9D,QAAQ,EAAE,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAC3D,mBAAmB,EAAE,SAAS,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAClF,WAAW,EAAE,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,CAAC,gBAAgB,CAAC,IAAI,qBAAqB,CAAC;QAC7F,aAAa,EAAE,SAAS,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC5E,iBAAiB,EAAE,SAAS,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtF,cAAc,EAAE,SAAS,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAC/E,wBAAwB,EACtB,SAAS,CAAC,KAAK,EAAE,4BAA4B,EAAE,GAAG,CAAC,sCAAsC,CAAC,CAAC,IAAI,IAAI;QACrG,WAAW;KACZ,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-owned browser (plan Phase 1 item 4): Chromium contexts the
|
|
3
|
+
* WITNESS process creates and drives itself — one isolated context per
|
|
4
|
+
* supervisor-opened test session, closed when the session seals.
|
|
5
|
+
*
|
|
6
|
+
* Test code NEVER touches these pages: the worker holds no handle, no
|
|
7
|
+
* URL, and no capability over them. The engine executes the constrained
|
|
8
|
+
* surface operations (create/read/update/archive) with the
|
|
9
|
+
* consumer-declared locators, and observes each step itself:
|
|
10
|
+
* - the rendered control it clicked and the values it typed;
|
|
11
|
+
* - the actual application request the action caused (captured from the
|
|
12
|
+
* engine page's own network, app-origin only);
|
|
13
|
+
* - the resulting navigation and the rendered row/form it read back.
|
|
14
|
+
*
|
|
15
|
+
* Forgery resistance (why this channel is proof):
|
|
16
|
+
* - fabricated DOM (`page.evaluate` in the worker page) is irrelevant:
|
|
17
|
+
* the engine reads its OWN page;
|
|
18
|
+
* - mocked responses (`page.route` in the worker) are irrelevant: the
|
|
19
|
+
* worker cannot route the engine's traffic;
|
|
20
|
+
* - direct API/Node mutations bypass the engine page entirely: no
|
|
21
|
+
* engine action runs, so no engine-observed action/visible records
|
|
22
|
+
* exist for them — invented suite-submitted records never satisfy;
|
|
23
|
+
* - HTTP-200-while-error: the engine asserts the rendered status
|
|
24
|
+
* itself (created/archived values) AND captures the app response
|
|
25
|
+
* status — a displayed error fails the action fail-closed.
|
|
26
|
+
*
|
|
27
|
+
* Surface selectors are LOCATORS, never proof: a lying descriptor only
|
|
28
|
+
* makes the engine fail to find elements (fail closed).
|
|
29
|
+
*/
|
|
30
|
+
import { type Browser, type Page } from 'playwright';
|
|
31
|
+
import { type SurfaceDescriptor } from '../surface.js';
|
|
32
|
+
/** The UI operations the engine can perform (constrained subset). */
|
|
33
|
+
export type EngineOperation = 'create' | 'read' | 'update' | 'delete';
|
|
34
|
+
/** One app-origin exchange the engine captured during an action. */
|
|
35
|
+
export interface EngineCapturedExchange {
|
|
36
|
+
/** Uppercase method (GET, POST, ...). */
|
|
37
|
+
method: string;
|
|
38
|
+
/** Canonical observed path (query/fragment stripped). */
|
|
39
|
+
path: string;
|
|
40
|
+
/** Response status the app answered. */
|
|
41
|
+
status: number;
|
|
42
|
+
/** Response body bytes (bounded snapshot for hashing). */
|
|
43
|
+
body: Buffer;
|
|
44
|
+
}
|
|
45
|
+
/** What the engine observed performing one action. */
|
|
46
|
+
export interface EngineActionObservation {
|
|
47
|
+
/** Entity id OBSERVED from the rendered list (never declared). */
|
|
48
|
+
entityId: string;
|
|
49
|
+
/** The exact input values the engine typed (exact-value echo source). */
|
|
50
|
+
enteredFields: Record<string, string>;
|
|
51
|
+
/** The rendered fields the engine read back after the action. */
|
|
52
|
+
renderedFields: Record<string, string>;
|
|
53
|
+
/** App-origin exchanges captured inside the action window. */
|
|
54
|
+
exchanges: EngineCapturedExchange[];
|
|
55
|
+
/** Engine-side pre-observation marker (filled by the witness layer). */
|
|
56
|
+
preObservationId?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Fail-closed engine browser error (surfaced as 409/503 downstream). */
|
|
59
|
+
export declare class EngineBrowserError extends Error {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
/** The browser launcher the engine drives (default: pinned Chromium). */
|
|
63
|
+
export interface EngineBrowserLauncher {
|
|
64
|
+
launch(options?: Record<string, unknown>): Promise<Browser>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Owns the engine Chromium instance and its per-session contexts.
|
|
68
|
+
*
|
|
69
|
+
* Args:
|
|
70
|
+
* launcher: browser launcher (default: the pinned Chromium). Tests
|
|
71
|
+
* inject a failing launcher to prove the fail-closed path; the
|
|
72
|
+
* driving and observation below stay engine code regardless of
|
|
73
|
+
* which executable launches.
|
|
74
|
+
*/
|
|
75
|
+
export declare class EngineBrowserManager {
|
|
76
|
+
private readonly launcher;
|
|
77
|
+
private browser;
|
|
78
|
+
private launching;
|
|
79
|
+
private readonly sessions;
|
|
80
|
+
/**
|
|
81
|
+
* Pinned `--host-resolver-rules` value binding every attested hostname
|
|
82
|
+
* to its startup-approved loopback IPs. Set once at witness startup
|
|
83
|
+
* (before any launch); the browser is then incapable of resolving
|
|
84
|
+
* those names anywhere but loopback, regardless of later DNS changes.
|
|
85
|
+
*/
|
|
86
|
+
private dnsPinRules;
|
|
87
|
+
constructor(launcher?: EngineBrowserLauncher);
|
|
88
|
+
/**
|
|
89
|
+
* Installs the DNS pin rules for the next (first) launch.
|
|
90
|
+
*
|
|
91
|
+
* Args:
|
|
92
|
+
* rules: the `--host-resolver-rules` value, or null when nothing is
|
|
93
|
+
* pinned (plain launch, previous behavior).
|
|
94
|
+
*
|
|
95
|
+
* Throws:
|
|
96
|
+
* EngineBrowserError: when the browser already launched — pins must
|
|
97
|
+
* precede every navigation (fail closed, never silently unbound).
|
|
98
|
+
*/
|
|
99
|
+
setDnsPinRules(rules: string | null): void;
|
|
100
|
+
/**
|
|
101
|
+
* Returns the engine page for one session, creating the isolated
|
|
102
|
+
* context on first use.
|
|
103
|
+
*
|
|
104
|
+
* Args:
|
|
105
|
+
* sessionId: the supervisor-opened session the context belongs to.
|
|
106
|
+
*
|
|
107
|
+
* Returns:
|
|
108
|
+
* Promise<Page>: the session's engine-owned page.
|
|
109
|
+
*
|
|
110
|
+
* Throws:
|
|
111
|
+
* EngineBrowserError: when Chromium cannot launch (fail closed —
|
|
112
|
+
* the capability is honestly unavailable at runtime).
|
|
113
|
+
*/
|
|
114
|
+
pageFor(sessionId: string): Promise<Page>;
|
|
115
|
+
/**
|
|
116
|
+
* Closes and forgets one session's engine context (session seal).
|
|
117
|
+
* Late engine calls for the session then recreate nothing — callers
|
|
118
|
+
* must refuse sealed sessions before driving (they do: every browser
|
|
119
|
+
* endpoint requires an OPEN session first).
|
|
120
|
+
*
|
|
121
|
+
* Args:
|
|
122
|
+
* sessionId: the sealed session.
|
|
123
|
+
*/
|
|
124
|
+
closeSession(sessionId: string): Promise<void>;
|
|
125
|
+
/** Closes every engine context and the browser (witness stop). */
|
|
126
|
+
closeAll(): Promise<void>;
|
|
127
|
+
/** Lazy Chromium launch (single flight; fail closed with the cause). */
|
|
128
|
+
private ensureBrowser;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Drives one constrained surface operation on the engine page and
|
|
132
|
+
* observes every step itself.
|
|
133
|
+
*
|
|
134
|
+
* Args:
|
|
135
|
+
* page: the session's engine-owned page.
|
|
136
|
+
* appBase: loopback app base the engine navigates (validated upstream).
|
|
137
|
+
* surface: the validated consumer descriptor (locators only).
|
|
138
|
+
* operation: the constrained operation to perform.
|
|
139
|
+
* input: entered fields (create/update) and/or the target entity id.
|
|
140
|
+
*
|
|
141
|
+
* Returns:
|
|
142
|
+
* Promise<EngineActionObservation>: the engine's own observation —
|
|
143
|
+
* observed entity id, entered + rendered fields, captured exchanges.
|
|
144
|
+
*
|
|
145
|
+
* Throws:
|
|
146
|
+
* EngineBrowserError: fail-closed on every untrusted outcome —
|
|
147
|
+
* missing rows, ambiguous creation, wrong rendered status, no
|
|
148
|
+
* application request, or an application error status.
|
|
149
|
+
*/
|
|
150
|
+
export declare function driveEngineAction(page: Page, appBase: string, surface: SurfaceDescriptor, operation: EngineOperation, input: {
|
|
151
|
+
fields?: Record<string, string>;
|
|
152
|
+
entityId?: string;
|
|
153
|
+
}): Promise<EngineActionObservation>;
|
|
154
|
+
/**
|
|
155
|
+
* Re-reads the rendered result for one entity on the engine page
|
|
156
|
+
* (the `visible.confirm` counterpart): row read for mutations, form
|
|
157
|
+
* read for reads.
|
|
158
|
+
*
|
|
159
|
+
* Args:
|
|
160
|
+
* page: the session's engine-owned page.
|
|
161
|
+
* appBase: loopback app base.
|
|
162
|
+
* surface: the validated consumer descriptor.
|
|
163
|
+
* operation: the original action (decides row vs form readback).
|
|
164
|
+
* entityId: the engine-observed entity id.
|
|
165
|
+
*
|
|
166
|
+
* Returns:
|
|
167
|
+
* Promise<Record<string, string>>: the rendered fields.
|
|
168
|
+
*
|
|
169
|
+
* Throws:
|
|
170
|
+
* EngineBrowserError: when the UI no longer exposes the entity.
|
|
171
|
+
*/
|
|
172
|
+
export declare function readEngineVisible(page: Page, appBase: string, surface: SurfaceDescriptor, operation: EngineOperation, entityId: string): Promise<Record<string, string>>;
|
|
173
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/witness/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAY,KAAK,OAAO,EAAqC,KAAK,IAAI,EAAiB,MAAM,YAAY,CAAC;AACjH,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAC;AAEvB,qEAAqE;AACrE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEtE,oEAAoE;AACpE,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,8DAA8D;IAC9D,SAAS,EAAE,sBAAsB,EAAE,CAAC;IACpC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,yEAAyE;AACzE,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,yEAAyE;AACzE,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AAKD;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAYnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAXrC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;IACvF;;;;;OAKG;IACH,OAAO,CAAC,WAAW,CAAuB;gBAEb,QAAQ,GAAE,qBAAgC;IAEvE;;;;;;;;;;OAUG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAS1C;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C;;;;;;;;OAQG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD,kEAAkE;IAC5D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B,wEAAwE;YAC1D,aAAa;CAuB5B;AAgND;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,OAAO,CAAC,uBAAuB,CAAC,CAWlC;AAkLD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmBjC"}
|