@jevitate/cli 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 +21 -0
- package/dist/ai-cli.d.ts +31 -0
- package/dist/ai-cli.js +238 -0
- package/dist/ai-cli.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +50663 -0
- package/dist/bin.js.map +1 -0
- package/dist/data-dir.d.ts +9 -0
- package/dist/data-dir.js +13 -0
- package/dist/data-dir.js.map +1 -0
- package/dist/envelope.d.ts +11 -0
- package/dist/envelope.js +7 -0
- package/dist/envelope.js.map +1 -0
- package/dist/explore-api.d.ts +187 -0
- package/dist/explore-api.js +302 -0
- package/dist/explore-api.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/init-keys.d.ts +14 -0
- package/dist/init-keys.js +18 -0
- package/dist/init-keys.js.map +1 -0
- package/dist/init-mcp.d.ts +87 -0
- package/dist/init-mcp.js +198 -0
- package/dist/init-mcp.js.map +1 -0
- package/dist/init-skills.d.ts +80 -0
- package/dist/init-skills.js +256 -0
- package/dist/init-skills.js.map +1 -0
- package/dist/journey-api.d.ts +37 -0
- package/dist/journey-api.js +55 -0
- package/dist/journey-api.js.map +1 -0
- package/dist/load-api.d.ts +48 -0
- package/dist/load-api.js +84 -0
- package/dist/load-api.js.map +1 -0
- package/dist/mcp-api.d.ts +116 -0
- package/dist/mcp-api.js +359 -0
- package/dist/mcp-api.js.map +1 -0
- package/dist/mission-api.d.ts +56 -0
- package/dist/mission-api.js +65 -0
- package/dist/mission-api.js.map +1 -0
- package/dist/program.d.ts +77 -0
- package/dist/program.js +1938 -0
- package/dist/program.js.map +1 -0
- package/dist/record-api.d.ts +74 -0
- package/dist/record-api.js +93 -0
- package/dist/record-api.js.map +1 -0
- package/dist/regression-api.d.ts +17 -0
- package/dist/regression-api.js +16 -0
- package/dist/regression-api.js.map +1 -0
- package/dist/self-heal-adapter.d.ts +31 -0
- package/dist/self-heal-adapter.js +70 -0
- package/dist/self-heal-adapter.js.map +1 -0
- package/dist/source-api.d.ts +147 -0
- package/dist/source-api.js +208 -0
- package/dist/source-api.js.map +1 -0
- package/dist/source-run-api.d.ts +57 -0
- package/dist/source-run-api.js +91 -0
- package/dist/source-run-api.js.map +1 -0
- package/dist/ui-api.d.ts +35 -0
- package/dist/ui-api.js +369 -0
- package/dist/ui-api.js.map +1 -0
- package/dist/ux-api.d.ts +75 -0
- package/dist/ux-api.js +231 -0
- package/dist/ux-api.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +37 -0
- package/dist/version.js.map +1 -0
- package/package.json +85 -0
- package/skills/jevitate-explore/SKILL.md +108 -0
- package/skills/jevitate-load-test/SKILL.md +42 -0
- package/skills/jevitate-mission-scope/SKILL.md +107 -0
- package/skills/jevitate-record/SKILL.md +78 -0
- package/skills/jevitate-run-journey/SKILL.md +72 -0
- package/skills/jevitate-sources/SKILL.md +88 -0
- package/skills/jevitate-ux-review/SKILL.md +68 -0
- package/ui/app.js +538 -0
- package/ui/index.html +546 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { FsJourneyStore, JourneyRegistry } from "@jevitate/journey";
|
|
5
|
+
import { GitSourceManager, RemoteSource, UnknownSourceError, collectNavigateOrigins, loadManifest, publishJourney, readLock, surfaceForAck, writeLock, } from "@jevitate/sources";
|
|
6
|
+
import { UnknownJourneyError } from "./journey-api.js";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
/**
|
|
9
|
+
* A publish was requested for a Journey that exists locally but has not been
|
|
10
|
+
* promoted. Publishing shares a Journey with the world — only reviewed,
|
|
11
|
+
* promoted Journeys may leave this machine (spec §7). Kept distinct from
|
|
12
|
+
* `UnknownJourneyError` so the CLI can emit a precise error code without
|
|
13
|
+
* string-matching.
|
|
14
|
+
*/
|
|
15
|
+
export class NotPromotedError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "NotPromotedError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A publish request resolved to zero declared origins — the Journey has no
|
|
23
|
+
* absolute `navigate` origin and none were supplied with `--declare-origin`.
|
|
24
|
+
* A `SharedJourneyFile` MUST declare at least one origin, so this fails
|
|
25
|
+
* closed BEFORE `validateForPublish` (whose Zod error would be opaque).
|
|
26
|
+
*/
|
|
27
|
+
export class NoDeclaredOriginsError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "NoDeclaredOriginsError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function mgrOf(deps) {
|
|
34
|
+
return new GitSourceManager(deps.sourcesDir, deps.git);
|
|
35
|
+
}
|
|
36
|
+
function nowIso(deps) {
|
|
37
|
+
return (deps.now ?? (() => new Date().toISOString()))();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a source `name` to its recorded `SourceEntry`, or throws
|
|
41
|
+
* `UnknownSourceError` (fail-closed, FMECA #5) — a source the lock does not
|
|
42
|
+
* know about is never silently treated as empty/absent. Reused by every
|
|
43
|
+
* command that operates on an existing source (pull/update/remove/trust/
|
|
44
|
+
* publish).
|
|
45
|
+
*/
|
|
46
|
+
async function requireEntry(lockPath, name) {
|
|
47
|
+
const lock = await readLock(lockPath);
|
|
48
|
+
const entry = lock.sources.find((s) => s.name === name);
|
|
49
|
+
if (!entry) {
|
|
50
|
+
throw new UnknownSourceError(`source '${name}' is not registered (run 'jevitate source add' first)`);
|
|
51
|
+
}
|
|
52
|
+
return entry;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clones a remote Journey source, pins it at its current HEAD, and records the
|
|
56
|
+
* pin in the lock. Adding a source NEVER trusts its Journeys (trust is a
|
|
57
|
+
* separate, explicit `source trust` act) — it only registers the source.
|
|
58
|
+
* The source's Terms of Use are surfaced for the human; a `TouAck` is
|
|
59
|
+
* recorded ONLY when `acceptTou` is set, so the run-gate stays fail-closed
|
|
60
|
+
* until the user has explicitly accepted.
|
|
61
|
+
*/
|
|
62
|
+
export async function addSource(deps, req) {
|
|
63
|
+
const mgr = mgrOf(deps);
|
|
64
|
+
await mkdir(deps.sourcesDir, { recursive: true });
|
|
65
|
+
const pinnedCommit = await mgr.add(req.name, req.gitUrl);
|
|
66
|
+
const manifest = await loadManifest(mgr.resolveDir(req.name));
|
|
67
|
+
const touSurface = surfaceForAck(manifest, req.gitUrl);
|
|
68
|
+
let touAccepted = false;
|
|
69
|
+
if (req.acceptTou) {
|
|
70
|
+
await deps.ack.put({
|
|
71
|
+
sourceName: req.name,
|
|
72
|
+
gitUrl: req.gitUrl,
|
|
73
|
+
origins: manifest.sites.map((s) => s.origin),
|
|
74
|
+
ackedBy: req.ackedBy ?? "local",
|
|
75
|
+
ackedAtIso: nowIso(deps),
|
|
76
|
+
});
|
|
77
|
+
touAccepted = true;
|
|
78
|
+
}
|
|
79
|
+
const lock = await readLock(deps.lockPath);
|
|
80
|
+
const sources = lock.sources.filter((s) => s.name !== req.name);
|
|
81
|
+
sources.push({ name: req.name, gitUrl: req.gitUrl, pinnedCommit });
|
|
82
|
+
await writeLock(deps.lockPath, { version: 1, sources });
|
|
83
|
+
return { name: req.name, gitUrl: req.gitUrl, pinnedCommit, touSurface, touAccepted };
|
|
84
|
+
}
|
|
85
|
+
/** Lists every registered source (from the lock), annotated with which of its
|
|
86
|
+
* Journeys the local user has explicitly trusted. */
|
|
87
|
+
export async function listSources(deps) {
|
|
88
|
+
const lock = await readLock(deps.lockPath);
|
|
89
|
+
const trustRecords = await deps.trust.list();
|
|
90
|
+
return lock.sources.map((s) => ({
|
|
91
|
+
...s,
|
|
92
|
+
trustedJourneys: trustRecords
|
|
93
|
+
.filter((t) => t.sourceId === s.name)
|
|
94
|
+
.map((t) => t.journeyId)
|
|
95
|
+
.sort(),
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
/** Fetches new refs for a registered source without moving its pin. */
|
|
99
|
+
export async function pullSource(deps, name) {
|
|
100
|
+
const entry = await requireEntry(deps.lockPath, name);
|
|
101
|
+
await mgrOf(deps).pull(name);
|
|
102
|
+
return { name, pulled: true, pinnedCommit: entry.pinnedCommit, pinAdvanced: false };
|
|
103
|
+
}
|
|
104
|
+
/** The ONLY way a pin advances: fetch + fast-forward, then record the new pin
|
|
105
|
+
* in the lock. Explicit and per-source — never automatic. */
|
|
106
|
+
export async function updateSource(deps, name) {
|
|
107
|
+
await requireEntry(deps.lockPath, name);
|
|
108
|
+
const pinnedCommit = await mgrOf(deps).update(name);
|
|
109
|
+
const lock = await readLock(deps.lockPath);
|
|
110
|
+
const sources = lock.sources.map((s) => (s.name === name ? { ...s, pinnedCommit } : s));
|
|
111
|
+
await writeLock(deps.lockPath, { version: 1, sources });
|
|
112
|
+
return { name, pinnedCommit };
|
|
113
|
+
}
|
|
114
|
+
/** Removes a source's clone and its lock entry. Local `TrustRecord`s are left
|
|
115
|
+
* intact (they are hash-bound and harmless; re-adding the same content
|
|
116
|
+
* re-uses them, a content change invalidates them). */
|
|
117
|
+
export async function removeSource(deps, name) {
|
|
118
|
+
await requireEntry(deps.lockPath, name);
|
|
119
|
+
await mgrOf(deps).remove(name);
|
|
120
|
+
const lock = await readLock(deps.lockPath);
|
|
121
|
+
const sources = lock.sources.filter((s) => s.name !== name);
|
|
122
|
+
await writeLock(deps.lockPath, { version: 1, sources });
|
|
123
|
+
return { name, removed: true };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Records an explicit, human trust decision for one Journey in a source,
|
|
127
|
+
* BOUND to the Journey's CURRENT content hash at the recorded pin (spec §5,
|
|
128
|
+
* FMECA #2 TOCTOU). Trust is the single explicit user act that lets a source's
|
|
129
|
+
* risky Journey run: it never happens implicitly on `add`/`pull`/`update`, and
|
|
130
|
+
* a later content change invalidates it (the run-gate recomputes the hash).
|
|
131
|
+
*/
|
|
132
|
+
export async function trustJourney(deps, req) {
|
|
133
|
+
const entry = await requireEntry(deps.lockPath, req.sourceName);
|
|
134
|
+
const mgr = mgrOf(deps);
|
|
135
|
+
// Pin the clone to the exact recorded commit before reading, so the hash we
|
|
136
|
+
// bind trust to is the content AT the pin, not a drifted working tree.
|
|
137
|
+
await mgr.checkout(req.sourceName, entry.pinnedCommit);
|
|
138
|
+
const remote = new RemoteSource(req.sourceName, mgr.resolveDir(req.sourceName), entry.pinnedCommit);
|
|
139
|
+
const sourced = await remote.get(req.journeyId);
|
|
140
|
+
if (!sourced) {
|
|
141
|
+
throw new UnknownJourneyError(`source '${req.sourceName}' has no journey '${req.journeyId}'`);
|
|
142
|
+
}
|
|
143
|
+
const record = {
|
|
144
|
+
sourceId: req.sourceName,
|
|
145
|
+
journeyId: req.journeyId,
|
|
146
|
+
contentHash: sourced.meta.contentHash,
|
|
147
|
+
approvedBy: req.approvedBy,
|
|
148
|
+
approvedAtIso: nowIso(deps),
|
|
149
|
+
};
|
|
150
|
+
await deps.trust.put(record);
|
|
151
|
+
return record;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Publishes a promoted local Journey to a registered distributed source via
|
|
155
|
+
* `@jevitate/sources`' `publishJourney`. Preserves every publish-side guard:
|
|
156
|
+
* - the target source must be REGISTERED (`UnknownSourceError` otherwise);
|
|
157
|
+
* - the Journey must exist and be PROMOTED (`NotPromotedError`);
|
|
158
|
+
* - `publishJourney`/`validateForPublish` then hard-block a materialized
|
|
159
|
+
* (non-redacted) secret value (`EmbeddedSecretError`) and any origin the
|
|
160
|
+
* steps touch but `declaredOrigins` does not cover (`UndeclaredOriginError`).
|
|
161
|
+
* The write always lands on a NEW `publish/<id>` branch — never a default
|
|
162
|
+
* branch — and degrades gracefully to instructions when `gh` is absent.
|
|
163
|
+
*/
|
|
164
|
+
export async function publishJourneyToSource(deps, req) {
|
|
165
|
+
await requireEntry(deps.lockPath, req.toSource);
|
|
166
|
+
const registry = new JourneyRegistry(new FsJourneyStore(req.journeysDir));
|
|
167
|
+
const journey = await registry.get(req.id);
|
|
168
|
+
if (!journey) {
|
|
169
|
+
throw new UnknownJourneyError(`unknown journey '${req.id}'`);
|
|
170
|
+
}
|
|
171
|
+
if (!journey.metadata.promoted) {
|
|
172
|
+
throw new NotPromotedError(`journey '${req.id}' is not promoted; promote it before publishing`);
|
|
173
|
+
}
|
|
174
|
+
const declaredOrigins = req.declareOrigins && req.declareOrigins.length > 0
|
|
175
|
+
? req.declareOrigins
|
|
176
|
+
: collectNavigateOrigins(journey.recording);
|
|
177
|
+
if (declaredOrigins.length === 0) {
|
|
178
|
+
throw new NoDeclaredOriginsError(`journey '${req.id}' declares no origins and navigates to no absolute origin; pass --declare-origin <origin>`);
|
|
179
|
+
}
|
|
180
|
+
return publishJourney(mgrOf(deps), deps.gh, {
|
|
181
|
+
journey,
|
|
182
|
+
declaredOrigins,
|
|
183
|
+
toSource: req.toSource,
|
|
184
|
+
asId: req.asId,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Real `gh` CLI port for `publishJourney`. Degrades gracefully: `available()`
|
|
189
|
+
* is false when `gh` isn't installed/authenticated, so publish falls back to
|
|
190
|
+
* printed branch+push instructions instead of failing (spec §14.3). Shells via
|
|
191
|
+
* `execFile` with an ARGS ARRAY only — never a shell string.
|
|
192
|
+
*/
|
|
193
|
+
export const realGhPort = {
|
|
194
|
+
async available() {
|
|
195
|
+
try {
|
|
196
|
+
await execFileAsync("gh", ["auth", "status"]);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
async createPr(cwd, branch, title) {
|
|
204
|
+
const { stdout } = await execFileAsync("gh", ["pr", "create", "--head", branch, "--title", title, "--body", title], { cwd });
|
|
205
|
+
return stdout.trim();
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=source-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-api.js","sourceRoot":"","sources":["../src/source-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type RunPolicy } from "@jevitate/domain";
|
|
2
|
+
import { type JourneyRunResult } from "@jevitate/runtime";
|
|
3
|
+
import { type SharedJourneyFile } from "@jevitate/sources";
|
|
4
|
+
import type { SourceApiDeps } from "./source-api.js";
|
|
5
|
+
/**
|
|
6
|
+
* The runner seam for a source-resolved Journey. Takes the ALREADY-GATED
|
|
7
|
+
* `SharedJourneyFile` (`resolveForRun`'s output) — never a raw address or a
|
|
8
|
+
* store `dir` — so the security gate always runs BEFORE anything reaches this
|
|
9
|
+
* function. Injected as a fake in tests so unit tests never launch a real
|
|
10
|
+
* browser; production wires `realResolvedJourneyRunner`.
|
|
11
|
+
*/
|
|
12
|
+
export type RunResolvedJourney = (file: SharedJourneyFile, params: Record<string, string>, policy: RunPolicy) => Promise<JourneyRunResult>;
|
|
13
|
+
/**
|
|
14
|
+
* The real, Playwright-backed runner for a gated source Journey. Mirrors
|
|
15
|
+
* `journey-api.ts`'s `runJourneyProgrammatically` exactly:
|
|
16
|
+
* - validates params UP FRONT (`deriveParamSchema`/`validateParams`) before
|
|
17
|
+
* any browser launch, so bad params never pay the cost/risk of a browser;
|
|
18
|
+
* - opens a real `PlaywrightBrowserPort` scoped to the Journey's authorized
|
|
19
|
+
* origins (the gate has already proven `declaredOrigins ⊆ manifest`);
|
|
20
|
+
* - ALWAYS closes the session and removes the temp profile dir in `finally`
|
|
21
|
+
* (matches the just-fixed profile-dir-leak pattern).
|
|
22
|
+
* A `SharedJourneyFile` IS a `Journey` (+ `declaredOrigins`), so it runs
|
|
23
|
+
* through the standard `JourneyRunner` unchanged.
|
|
24
|
+
*/
|
|
25
|
+
export declare const realResolvedJourneyRunner: RunResolvedJourney;
|
|
26
|
+
/**
|
|
27
|
+
* Injected seams for `jevitate source run`. Reuses the distributed-sources
|
|
28
|
+
* `SourceApiDeps` (sources clone dir + lock + local trust/ack stores + the
|
|
29
|
+
* injectable `git` port) and adds a runner seam. Every field is injectable so
|
|
30
|
+
* unit tests never touch the network, the real home dir, `git`, or a real
|
|
31
|
+
* browser.
|
|
32
|
+
*/
|
|
33
|
+
export interface SourceRunApiDeps extends SourceApiDeps {
|
|
34
|
+
/** Runner seam; defaults to `realResolvedJourneyRunner` in production. */
|
|
35
|
+
runJourney?: RunResolvedJourney;
|
|
36
|
+
}
|
|
37
|
+
export interface RunSourceJourneyRequest {
|
|
38
|
+
sourceName: string;
|
|
39
|
+
journeyId: string;
|
|
40
|
+
params: Record<string, string>;
|
|
41
|
+
/** Defaults to `safeRunPolicy()` (Slice 1 fail-closed secret mode). */
|
|
42
|
+
policy?: RunPolicy;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Runs a Journey that lives in a trusted remote source — the ONE programmatic
|
|
46
|
+
* surface for `jevitate source run`. It NEVER bypasses the gate: it resolves
|
|
47
|
+
* the Journey through `@jevitate/sources`' real `resolveForRun`, which runs the
|
|
48
|
+
* full §9.8 "reviewed, pinned, in-origin, or refuse" ladder IN ORDER and THROWS
|
|
49
|
+
* a typed error on any failure (unknown source/id, hash mismatch/TOCTOU,
|
|
50
|
+
* untrusted-risky, undeclared origin, unacknowledged ToU, embedded secret).
|
|
51
|
+
* Only a `SharedJourneyFile` that passes EVERY gate reaches the runner seam.
|
|
52
|
+
*
|
|
53
|
+
* The clone is checked out to the EXACT recorded pin before the gate reads it,
|
|
54
|
+
* so the content the gate hashes is the content at the pin — not a drifted
|
|
55
|
+
* working tree (matches `trustJourney`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function runSourceJourney(deps: SourceRunApiDeps, req: RunSourceJourneyRequest): Promise<JourneyRunResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { deriveParamSchema, validateParams } from "@jevitate/journey";
|
|
5
|
+
import { safeRunPolicy } from "@jevitate/domain";
|
|
6
|
+
import { PlaywrightBrowserPort } from "@jevitate/playwright";
|
|
7
|
+
import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
|
|
8
|
+
import { RecordingInterpreter } from "@jevitate/interpreter";
|
|
9
|
+
import { JourneyRunner } from "@jevitate/runtime";
|
|
10
|
+
import { FederatedJourneyRegistry, GitSourceManager, RemoteSource, UnknownSourceError, loadManifest, readLock, resolveForRun, } from "@jevitate/sources";
|
|
11
|
+
/**
|
|
12
|
+
* The real, Playwright-backed runner for a gated source Journey. Mirrors
|
|
13
|
+
* `journey-api.ts`'s `runJourneyProgrammatically` exactly:
|
|
14
|
+
* - validates params UP FRONT (`deriveParamSchema`/`validateParams`) before
|
|
15
|
+
* any browser launch, so bad params never pay the cost/risk of a browser;
|
|
16
|
+
* - opens a real `PlaywrightBrowserPort` scoped to the Journey's authorized
|
|
17
|
+
* origins (the gate has already proven `declaredOrigins ⊆ manifest`);
|
|
18
|
+
* - ALWAYS closes the session and removes the temp profile dir in `finally`
|
|
19
|
+
* (matches the just-fixed profile-dir-leak pattern).
|
|
20
|
+
* A `SharedJourneyFile` IS a `Journey` (+ `declaredOrigins`), so it runs
|
|
21
|
+
* through the standard `JourneyRunner` unchanged.
|
|
22
|
+
*/
|
|
23
|
+
export const realResolvedJourneyRunner = async (file, params, policy) => {
|
|
24
|
+
validateParams(deriveParamSchema(file.recording), params);
|
|
25
|
+
// The browser allowlist is the Journey's authorized origin set — the base
|
|
26
|
+
// site plus every gate-approved declared origin.
|
|
27
|
+
const allowedOrigins = [...new Set([file.recording.site, ...file.declaredOrigins])];
|
|
28
|
+
const profileDir = await mkdtemp(join(tmpdir(), "jevitate-source-run-"));
|
|
29
|
+
const port = new PlaywrightBrowserPort();
|
|
30
|
+
const session = await port.open({
|
|
31
|
+
profileDir,
|
|
32
|
+
headless: true,
|
|
33
|
+
allowedOrigins,
|
|
34
|
+
baseUrl: file.recording.site,
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
const actor = CastActor.named("source-runner").whoCan(new BrowseTheWeb(session, allowedOrigins));
|
|
38
|
+
const runner = new JourneyRunner(actor, new RecordingInterpreter());
|
|
39
|
+
return await runner.run({ journey: file, params, policy });
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await session.close();
|
|
43
|
+
await rm(profileDir, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/** Resolves a registered source's recorded pin, or throws `UnknownSourceError`
|
|
47
|
+
* (fail-closed) — a source the lock does not know about is never treated as
|
|
48
|
+
* empty/absent. Mirrors `source-api.ts`'s private `requireEntry`. */
|
|
49
|
+
async function requireEntry(lockPath, name) {
|
|
50
|
+
const lock = await readLock(lockPath);
|
|
51
|
+
const entry = lock.sources.find((s) => s.name === name);
|
|
52
|
+
if (!entry) {
|
|
53
|
+
throw new UnknownSourceError(`source '${name}' is not registered (run 'jevitate source add' first)`);
|
|
54
|
+
}
|
|
55
|
+
return entry;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Runs a Journey that lives in a trusted remote source — the ONE programmatic
|
|
59
|
+
* surface for `jevitate source run`. It NEVER bypasses the gate: it resolves
|
|
60
|
+
* the Journey through `@jevitate/sources`' real `resolveForRun`, which runs the
|
|
61
|
+
* full §9.8 "reviewed, pinned, in-origin, or refuse" ladder IN ORDER and THROWS
|
|
62
|
+
* a typed error on any failure (unknown source/id, hash mismatch/TOCTOU,
|
|
63
|
+
* untrusted-risky, undeclared origin, unacknowledged ToU, embedded secret).
|
|
64
|
+
* Only a `SharedJourneyFile` that passes EVERY gate reaches the runner seam.
|
|
65
|
+
*
|
|
66
|
+
* The clone is checked out to the EXACT recorded pin before the gate reads it,
|
|
67
|
+
* so the content the gate hashes is the content at the pin — not a drifted
|
|
68
|
+
* working tree (matches `trustJourney`).
|
|
69
|
+
*/
|
|
70
|
+
export async function runSourceJourney(deps, req) {
|
|
71
|
+
// Unknown/unregistered source -> UnknownSourceError, before any run.
|
|
72
|
+
const entry = await requireEntry(deps.lockPath, req.sourceName);
|
|
73
|
+
const mgr = new GitSourceManager(deps.sourcesDir, deps.git);
|
|
74
|
+
// Pin the clone to the recorded commit before the gate reads it (fail-closed:
|
|
75
|
+
// `checkout` propagates if the pinned commit is absent — never HEAD).
|
|
76
|
+
await mgr.checkout(req.sourceName, entry.pinnedCommit);
|
|
77
|
+
const remote = new RemoteSource(req.sourceName, mgr.resolveDir(req.sourceName), entry.pinnedCommit);
|
|
78
|
+
const fed = new FederatedJourneyRegistry([remote], deps.trust);
|
|
79
|
+
const gateDeps = {
|
|
80
|
+
fed,
|
|
81
|
+
trust: deps.trust,
|
|
82
|
+
manifestFor: (source) => loadManifest(mgr.resolveDir(source)),
|
|
83
|
+
ackFor: (source) => deps.ack.get(source),
|
|
84
|
+
};
|
|
85
|
+
// The full trust boundary: throws on any refusal, returns the validated,
|
|
86
|
+
// ready-to-run file only when every gate passes.
|
|
87
|
+
const file = await resolveForRun(gateDeps, `${req.sourceName}/${req.journeyId}`);
|
|
88
|
+
const run = deps.runJourney ?? realResolvedJourneyRunner;
|
|
89
|
+
return run(file, req.params, req.policy ?? safeRunPolicy());
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=source-run-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-run-api.js","sourceRoot":"","sources":["../src/source-run-api.ts"],"names":[],"mappings":"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"}
|
package/dist/ui-api.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `jevitate ui` local HTTP server — the human-facing side of the HITL
|
|
3
|
+
* inbox. SM4 (loopback is NOT a trust boundary) is load-bearing here:
|
|
4
|
+
* - bind 127.0.0.1 ONLY, on every path including the port-retry;
|
|
5
|
+
* - every request is Host-checked (S-A), then every `/api/*` request is
|
|
6
|
+
* capability-token-gated (S-B), before any routing happens;
|
|
7
|
+
* - `GET /` sets a strict CSP (`default-src 'self'`) so the SPA MUST load
|
|
8
|
+
* its JS from a same-origin `<script src="/app.js">` — no inline script;
|
|
9
|
+
* - never logs `req.url` (it carries `?t=<token>`), request bodies, or
|
|
10
|
+
* query strings — only `method <pathname> status`.
|
|
11
|
+
*/
|
|
12
|
+
export interface StartUiServerDeps {
|
|
13
|
+
/** Inbox store directory (`~/.jevitate/inbox` in production). Screenshots
|
|
14
|
+
* are read from `<inboxDir>/screenshots/<id>.png`. */
|
|
15
|
+
inboxDir: string;
|
|
16
|
+
/** Accepted but ignored for the actual bind (SM4: 127.0.0.1 ONLY on every
|
|
17
|
+
* path); kept in the signature for forward compatibility. */
|
|
18
|
+
host?: string;
|
|
19
|
+
/** Explicit port. When set, `EADDRINUSE` REJECTS the promise (no drift).
|
|
20
|
+
* When unset, defaults to 4180 and retries the next port on conflict. */
|
|
21
|
+
port?: number;
|
|
22
|
+
/** Open the URL in the default browser once bound. Defaults to `true`.
|
|
23
|
+
* Tests should always pass `false`. Fire-and-forget: a failed opener
|
|
24
|
+
* (e.g. no `xdg-open` under WSL) never rejects `startUiServer`. */
|
|
25
|
+
open?: boolean;
|
|
26
|
+
/** Structured log sink: `method <pathname> status` lines only. */
|
|
27
|
+
logger?: (line: string) => void;
|
|
28
|
+
}
|
|
29
|
+
export interface UiServerHandle {
|
|
30
|
+
url: string;
|
|
31
|
+
token: string;
|
|
32
|
+
port: number;
|
|
33
|
+
close: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export declare function startUiServer(deps: StartUiServerDeps): Promise<UiServerHandle>;
|