wicked-crew 0.7.47 → 0.7.48
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/dist/api/chat-citations.d.ts +10 -1
- package/dist/api/chat-citations.d.ts.map +1 -1
- package/dist/api/chat-citations.js +28 -0
- package/dist/api/chat-citations.js.map +1 -1
- package/dist/api/chat-scope.d.ts +4 -2
- package/dist/api/chat-scope.d.ts.map +1 -1
- package/dist/api/chat-scope.js +35 -3
- package/dist/api/chat-scope.js.map +1 -1
- package/dist/api/chat-transcripts.d.ts +23 -1
- package/dist/api/chat-transcripts.d.ts.map +1 -1
- package/dist/api/chat-transcripts.js +61 -6
- package/dist/api/chat-transcripts.js.map +1 -1
- package/dist/api/eval-sample.d.ts +6 -6
- package/dist/api/routes.d.ts +11 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +99 -4
- package/dist/api/routes.js.map +1 -1
- package/dist/api/server.d.ts +15 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +94 -3
- package/dist/api/server.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +16 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/adapter.d.ts +16 -1
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +16 -0
- package/dist/core/adapter.js.map +1 -1
- package/dist/decisions/chat-recorder.d.ts +129 -0
- package/dist/decisions/chat-recorder.d.ts.map +1 -0
- package/dist/decisions/chat-recorder.js +367 -0
- package/dist/decisions/chat-recorder.js.map +1 -0
- package/dist/decisions/consider.d.ts +102 -0
- package/dist/decisions/consider.d.ts.map +1 -0
- package/dist/decisions/consider.js +207 -0
- package/dist/decisions/consider.js.map +1 -0
- package/dist/decisions/events.d.ts +2 -0
- package/dist/decisions/events.d.ts.map +1 -1
- package/dist/decisions/events.js +2 -0
- package/dist/decisions/events.js.map +1 -1
- package/dist/decisions/ingest.d.ts +28 -5
- package/dist/decisions/ingest.d.ts.map +1 -1
- package/dist/decisions/ingest.js +14 -6
- package/dist/decisions/ingest.js.map +1 -1
- package/dist/decisions/rules-text.d.ts +21 -0
- package/dist/decisions/rules-text.d.ts.map +1 -0
- package/dist/decisions/rules-text.js +50 -0
- package/dist/decisions/rules-text.js.map +1 -0
- package/dist/editors/csp.d.ts +31 -0
- package/dist/editors/csp.d.ts.map +1 -0
- package/dist/editors/csp.js +46 -0
- package/dist/editors/csp.js.map +1 -0
- package/dist/editors/pack-install.d.ts +28 -0
- package/dist/editors/pack-install.d.ts.map +1 -0
- package/dist/editors/pack-install.js +153 -0
- package/dist/editors/pack-install.js.map +1 -0
- package/dist/editors/registry.d.ts +142 -0
- package/dist/editors/registry.d.ts.map +1 -0
- package/dist/editors/registry.js +255 -0
- package/dist/editors/registry.js.map +1 -0
- package/dist/editors/routes.d.ts +46 -0
- package/dist/editors/routes.d.ts.map +1 -0
- package/dist/editors/routes.js +166 -0
- package/dist/editors/routes.js.map +1 -0
- package/dist/interactive/bridge-pool.d.ts +7 -2
- package/dist/interactive/bridge-pool.d.ts.map +1 -1
- package/dist/interactive/bridge-pool.js +6 -1
- package/dist/interactive/bridge-pool.js.map +1 -1
- package/dist/interactive/theme-events.d.ts +119 -0
- package/dist/interactive/theme-events.d.ts.map +1 -0
- package/dist/interactive/theme-events.js +606 -0
- package/dist/interactive/theme-events.js.map +1 -0
- package/dist/projects/routes.d.ts +2 -2
- package/dist/standing-orders/routes.d.ts +21 -21
- package/dist/standing-orders/store.d.ts +9 -9
- package/dist/watch/checks/claim-vs-evidence.d.ts +30 -0
- package/dist/watch/checks/claim-vs-evidence.d.ts.map +1 -0
- package/dist/watch/checks/claim-vs-evidence.js +152 -0
- package/dist/watch/checks/claim-vs-evidence.js.map +1 -0
- package/dist/watch/checks/index.d.ts.map +1 -1
- package/dist/watch/checks/index.js +5 -0
- package/dist/watch/checks/index.js.map +1 -1
- package/dist/watch/checks/warned-rule.d.ts +26 -0
- package/dist/watch/checks/warned-rule.d.ts.map +1 -0
- package/dist/watch/checks/warned-rule.js +126 -0
- package/dist/watch/checks/warned-rule.js.map +1 -0
- package/dist/watch/registry.d.ts +29 -0
- package/dist/watch/registry.d.ts.map +1 -1
- package/dist/watch/registry.js +60 -1
- package/dist/watch/registry.js.map +1 -1
- package/dist/watch/types.d.ts +19 -0
- package/dist/watch/types.d.ts.map +1 -1
- package/endpoint-manifest.json +95 -1
- package/package.json +1 -1
- package/watch/entries/claim-vs-evidence.json +13 -0
- package/watch/entries/risky-call.json +12 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pack install, as far as it goes before EP-C8 (DES-artifact-editor-plugins §5.2, §9.2; EP-C1).
|
|
3
|
+
*
|
|
4
|
+
* Install is human-only (§6.4) and runs garden's fail-closed gate FIRST: `pack check --json` on the
|
|
5
|
+
* pack root (schema, every editor's entry inside the pack, its hash, its size, nothing loaded from
|
|
6
|
+
* outside). Then crew reads the spec-2 manifest and lists what the operator would be asked to
|
|
7
|
+
* approve — the editors, the blocks and the pack's skills (§3.2 "Add these") — and REFUSES with 501
|
|
8
|
+
* `third_party_editors_not_available`: an unverified editor must pass the install-time conformance
|
|
9
|
+
* run (cases 1-12 against the bundle, headless Chrome) before it may be installed, and that run is
|
|
10
|
+
* EP-C8. Nothing is written. The refusal carries the check verdict and the approval list, so the
|
|
11
|
+
* operator sees exactly what will be asked once installs open.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
14
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
15
|
+
import { execCapped } from '../core/exec.js';
|
|
16
|
+
/** The verdict shape garden's `scripts/pack/check.py --json` prints. */
|
|
17
|
+
function parseCheckOutput(stdout) {
|
|
18
|
+
try {
|
|
19
|
+
const raw = JSON.parse(stdout);
|
|
20
|
+
if (typeof raw !== 'object' || raw === null || typeof raw.ok !== 'boolean')
|
|
21
|
+
return null;
|
|
22
|
+
const findings = (list) => Array.isArray(list)
|
|
23
|
+
? list.flatMap((f) => (typeof f === 'object' && f !== null ? [f] : []))
|
|
24
|
+
: [];
|
|
25
|
+
return { ok: raw.ok, errors: findings(raw.errors), warnings: findings(raw.warnings) };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Garden's gate, run as a child: `python3 <garden>/scripts/pack/check.py <pack> --json --garden-root <garden>`
|
|
33
|
+
* (`python` when `python3` is not on PATH). A missing garden or an unparseable verdict is a FAILED
|
|
34
|
+
* check with the reason — never a pass.
|
|
35
|
+
*/
|
|
36
|
+
export function gardenPackCheck(gardenRoot) {
|
|
37
|
+
return async (packRoot) => {
|
|
38
|
+
if (gardenRoot === null) {
|
|
39
|
+
return { ok: false, errors: [{ level: 'error', code: 'garden-missing', message: 'no wicked-garden plugin found to run `pack check`' }], warnings: [] };
|
|
40
|
+
}
|
|
41
|
+
const script = join(gardenRoot, 'scripts', 'pack', 'check.py');
|
|
42
|
+
if (!existsSync(script)) {
|
|
43
|
+
return { ok: false, errors: [{ level: 'error', code: 'garden-too-old', message: `this wicked-garden has no scripts/pack/check.py (needs >= 12.40.0)` }], warnings: [] };
|
|
44
|
+
}
|
|
45
|
+
const args = [script, packRoot, '--json', '--garden-root', gardenRoot];
|
|
46
|
+
let stdout = '';
|
|
47
|
+
for (const bin of ['python3', 'python']) {
|
|
48
|
+
try {
|
|
49
|
+
const out = await execCapped(bin, args, { timeout: 60_000, windowsHide: true });
|
|
50
|
+
stdout = out.stdout;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const e = err;
|
|
55
|
+
if (e.code === 'ENOENT')
|
|
56
|
+
continue; // no such interpreter: try the next spelling
|
|
57
|
+
// A failing check exits 1 with the JSON verdict on stdout.
|
|
58
|
+
if (typeof e.stdout === 'string' && e.stdout.trim() !== '') {
|
|
59
|
+
stdout = e.stdout;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return { ok: false, errors: [{ level: 'error', code: 'check-failed', message: `pack check did not run: ${e.message}` }], warnings: [] };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const parsed = parseCheckOutput(stdout);
|
|
66
|
+
if (parsed === null) {
|
|
67
|
+
return { ok: false, errors: [{ level: 'error', code: 'check-unreadable', message: stdout === '' ? 'no python interpreter found (python3 / python)' : 'pack check printed no readable verdict' }], warnings: [] };
|
|
68
|
+
}
|
|
69
|
+
return parsed;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export class PackInstallError extends Error {
|
|
73
|
+
status;
|
|
74
|
+
check;
|
|
75
|
+
constructor(status, message, check) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.status = status;
|
|
78
|
+
this.check = check;
|
|
79
|
+
this.name = 'PackInstallError';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** What `POST /editors` reads of a pack before refusing: the approval list (§3.2). */
|
|
83
|
+
export async function previewPackInstall(packRootRaw, check) {
|
|
84
|
+
if (typeof packRootRaw !== 'string' || packRootRaw.trim() === '' || !isAbsolute(packRootRaw)) {
|
|
85
|
+
throw new PackInstallError(400, '`packRoot` must be an absolute path to a directory holding wicked-pack.json');
|
|
86
|
+
}
|
|
87
|
+
const packRoot = resolve(packRootRaw);
|
|
88
|
+
let isDir = false;
|
|
89
|
+
try {
|
|
90
|
+
isDir = statSync(packRoot).isDirectory();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
isDir = false;
|
|
94
|
+
}
|
|
95
|
+
if (!isDir)
|
|
96
|
+
throw new PackInstallError(404, `no directory at ${packRoot}`);
|
|
97
|
+
const manifestPath = join(packRoot, 'wicked-pack.json');
|
|
98
|
+
if (!existsSync(manifestPath))
|
|
99
|
+
throw new PackInstallError(400, `${packRoot} holds no wicked-pack.json`);
|
|
100
|
+
// Garden's gate first (fail closed): a refused manifest never gets as far as a listing.
|
|
101
|
+
const verdict = await check(packRoot);
|
|
102
|
+
if (!verdict.ok) {
|
|
103
|
+
throw new PackInstallError(422, `wicked-garden pack check refused the pack (${verdict.errors.length} error${verdict.errors.length === 1 ? '' : 's'})`, verdict);
|
|
104
|
+
}
|
|
105
|
+
let manifest;
|
|
106
|
+
try {
|
|
107
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
throw new PackInstallError(400, `wicked-pack.json is not JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
111
|
+
}
|
|
112
|
+
const editors = Array.isArray(manifest['editors']) ? manifest['editors'] : [];
|
|
113
|
+
if (manifest['spec'] !== 2 || editors.length === 0) {
|
|
114
|
+
throw new PackInstallError(400, 'the pack declares no editors (spec 2 `editors[]`); there is nothing to install here');
|
|
115
|
+
}
|
|
116
|
+
const blocks = Array.isArray(manifest['blocks']) ? manifest['blocks'] : [];
|
|
117
|
+
const skillsDir = typeof manifest['skills_dir'] === 'string' ? manifest['skills_dir'] : 'skills';
|
|
118
|
+
let skills = [];
|
|
119
|
+
try {
|
|
120
|
+
const dir = join(packRoot, skillsDir);
|
|
121
|
+
if (statSync(dir).isDirectory())
|
|
122
|
+
skills = readdirSync(dir).filter((n) => !n.startsWith('.')).sort();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
skills = [];
|
|
126
|
+
}
|
|
127
|
+
const str = (v) => (typeof v === 'string' ? v : '');
|
|
128
|
+
const strs = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string') : []);
|
|
129
|
+
return {
|
|
130
|
+
code: 'third_party_editors_not_available',
|
|
131
|
+
error: 'third-party editors cannot be installed yet: the install-time conformance run (DES-artifact-editor-plugins EP-C8) ' +
|
|
132
|
+
'has not shipped, and an unverified editor is never installed. The pack passed wicked-garden `pack check`; ' +
|
|
133
|
+
'this is what the install would ask you to approve.',
|
|
134
|
+
check: verdict,
|
|
135
|
+
pack: {
|
|
136
|
+
name: str(manifest['name']),
|
|
137
|
+
vendor: str(manifest['vendor']),
|
|
138
|
+
version: str(manifest['version']),
|
|
139
|
+
editors: editors.map((e) => ({
|
|
140
|
+
id: str(e['id']),
|
|
141
|
+
title: str(e['title']),
|
|
142
|
+
version: str(e['version']),
|
|
143
|
+
kinds: strs(e['kinds']),
|
|
144
|
+
permissions: Array.isArray(e['permissions'])
|
|
145
|
+
? e['permissions'].map((p) => ({ id: str(p['id']), why: str(p['why']) }))
|
|
146
|
+
: [],
|
|
147
|
+
})),
|
|
148
|
+
blocks: blocks.map((b) => ({ id: str(b['id']), label: str(b['label']), produces_kind: str(b['produces_kind']), skills: strs(b['skills']) })),
|
|
149
|
+
skills,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=pack-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-install.js","sourceRoot":"","sources":["../../src/editors/pack-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The artifact-editor registry (DES-artifact-editor-plugins §5.2, §8.6, §9.1; EP-C1).
|
|
3
|
+
*
|
|
4
|
+
* Two kinds of editor exist in the design; one exists today:
|
|
5
|
+
*
|
|
6
|
+
* - **First-party** editors ship INSIDE studio's bundle (§8.6): `<studio dist>/editors/<id>/editor.json`
|
|
7
|
+
* beside the ONE self-contained `index.html` entry. Crew discovers them at boot, pins each entry's
|
|
8
|
+
* sha256 and byte size, and serves the entry through the hashed bundle route (`routes.ts`) with
|
|
9
|
+
* the §8.2 headers. Their ids are `wicked-*` — the reserved first-party namespace every pack is
|
|
10
|
+
* refused (garden `wicked-pack.schema.json`).
|
|
11
|
+
* - **Third-party** editors arrive as garden packs (spec 2 `editors[]`). Their install is REFUSED
|
|
12
|
+
* until the install-time conformance run ships (EP-C8, LATER): `pack-install.ts` still runs
|
|
13
|
+
* garden's fail-closed `pack check` and lists what the operator would be asked to approve, then
|
|
14
|
+
* answers 501. Nothing of a pack is ever written by this registry.
|
|
15
|
+
*
|
|
16
|
+
* Persisted state is ONE file, `<state home>/daemon-editors.json` — the enable/disable flags the
|
|
17
|
+
* operator sets in Settings → Editors (§6.4, human-only). It sits under the `daemon-` prefix both
|
|
18
|
+
* crew's state-home registry and the engine's worker fence already classify (crew#756), so no new
|
|
19
|
+
* top-level entry is introduced; a pack install (EP-C8) will need a registered `editors/` subtree in
|
|
20
|
+
* both repos first.
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import type { EditorView } from '../core/types.js';
|
|
24
|
+
/** The persisted flags file, under the registered `daemon-` prefix. */
|
|
25
|
+
export declare const EDITORS_STATE_FILENAME = "daemon-editors.json";
|
|
26
|
+
/** Where first-party manifests live inside the studio bundle. */
|
|
27
|
+
export declare const STUDIO_EDITORS_DIRNAME = "editors";
|
|
28
|
+
/** The host's bundle cap (§8.5); a pack's `limits.bundleBytes` may only be lower. */
|
|
29
|
+
export declare const HOST_BUNDLE_CAP_BYTES: number;
|
|
30
|
+
export declare const EDITOR_PERMISSIONS: readonly ["artifact.read", "artifact.write", "selection.chip", "composer.draft", "checks.read", "checks.contribute", "sources.read", "media.read", "artifact.export", "ui.fullscreen", "network.media"];
|
|
31
|
+
/** Editor ids: lowercase dash-words, ≤ 64 (garden's shape); first-party ones start with `wicked-`. */
|
|
32
|
+
export declare const EDITOR_ID: RegExp;
|
|
33
|
+
export declare const FIRST_PARTY_ID: RegExp;
|
|
34
|
+
/** Anchored SemVer (garden's shape). */
|
|
35
|
+
export declare const EDITOR_VERSION: RegExp;
|
|
36
|
+
export declare const EditorManifestSchema: z.ZodObject<{
|
|
37
|
+
id: z.ZodString;
|
|
38
|
+
title: z.ZodString;
|
|
39
|
+
version: z.ZodString;
|
|
40
|
+
protocol: z.ZodArray<z.ZodNumber, "many">;
|
|
41
|
+
kinds: z.ZodArray<z.ZodString, "many">;
|
|
42
|
+
entry: z.ZodDefault<z.ZodString>;
|
|
43
|
+
sizes: z.ZodEffects<z.ZodArray<z.ZodEnum<["inline", "pane", "full"]>, "many">, ("inline" | "pane" | "full")[], ("inline" | "pane" | "full")[]>;
|
|
44
|
+
panels: z.ZodOptional<z.ZodArray<z.ZodEnum<["checks"]>, "many">>;
|
|
45
|
+
permissions: z.ZodArray<z.ZodObject<{
|
|
46
|
+
id: z.ZodEnum<["artifact.read", "artifact.write", "selection.chip", "composer.draft", "checks.read", "checks.contribute", "sources.read", "media.read", "artifact.export", "ui.fullscreen", "network.media"]>;
|
|
47
|
+
why: z.ZodString;
|
|
48
|
+
}, "strip", z.ZodTypeAny, {
|
|
49
|
+
id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media";
|
|
50
|
+
why: string;
|
|
51
|
+
}, {
|
|
52
|
+
id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media";
|
|
53
|
+
why: string;
|
|
54
|
+
}>, "many">;
|
|
55
|
+
limits: z.ZodOptional<z.ZodObject<{
|
|
56
|
+
bundleBytes: z.ZodNumber;
|
|
57
|
+
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
bundleBytes: number;
|
|
59
|
+
}, {
|
|
60
|
+
bundleBytes: number;
|
|
61
|
+
}>>;
|
|
62
|
+
}, "strip", z.ZodTypeAny, {
|
|
63
|
+
id: string;
|
|
64
|
+
title: string;
|
|
65
|
+
version: string;
|
|
66
|
+
entry: string;
|
|
67
|
+
protocol: number[];
|
|
68
|
+
kinds: string[];
|
|
69
|
+
sizes: ("inline" | "pane" | "full")[];
|
|
70
|
+
permissions: {
|
|
71
|
+
id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media";
|
|
72
|
+
why: string;
|
|
73
|
+
}[];
|
|
74
|
+
panels?: "checks"[] | undefined;
|
|
75
|
+
limits?: {
|
|
76
|
+
bundleBytes: number;
|
|
77
|
+
} | undefined;
|
|
78
|
+
}, {
|
|
79
|
+
id: string;
|
|
80
|
+
title: string;
|
|
81
|
+
version: string;
|
|
82
|
+
protocol: number[];
|
|
83
|
+
kinds: string[];
|
|
84
|
+
sizes: ("inline" | "pane" | "full")[];
|
|
85
|
+
permissions: {
|
|
86
|
+
id: "artifact.read" | "artifact.write" | "selection.chip" | "composer.draft" | "checks.read" | "checks.contribute" | "sources.read" | "media.read" | "artifact.export" | "ui.fullscreen" | "network.media";
|
|
87
|
+
why: string;
|
|
88
|
+
}[];
|
|
89
|
+
entry?: string | undefined;
|
|
90
|
+
panels?: "checks"[] | undefined;
|
|
91
|
+
limits?: {
|
|
92
|
+
bundleBytes: number;
|
|
93
|
+
} | undefined;
|
|
94
|
+
}>;
|
|
95
|
+
export type EditorManifest = z.infer<typeof EditorManifestSchema>;
|
|
96
|
+
export interface EditorRecord {
|
|
97
|
+
manifest: EditorManifest;
|
|
98
|
+
/** The full 64-hex sha256 of the entry file, pinned when the registry read it. */
|
|
99
|
+
sha256: string;
|
|
100
|
+
bytes: number;
|
|
101
|
+
/** Absolute path of the entry file. Never on the wire. */
|
|
102
|
+
entryPath: string;
|
|
103
|
+
firstParty: boolean;
|
|
104
|
+
source: 'studio-bundle';
|
|
105
|
+
enabled: boolean;
|
|
106
|
+
}
|
|
107
|
+
export declare function sha256Of(buf: Buffer): string;
|
|
108
|
+
/**
|
|
109
|
+
* A path is served from the bundle only if it IS what it looks like: a real directory or a real
|
|
110
|
+
* file, never a link (codex r1 — a link under `editors/` could point outside the bundle and be
|
|
111
|
+
* served as first-party). `null` = fine; else the reason.
|
|
112
|
+
*/
|
|
113
|
+
export declare function notRealEntry(path: string, want: 'dir' | 'file'): string | null;
|
|
114
|
+
/** The entry must be ONE self-contained file: nothing loaded from outside it (§5.2; the CSP would block it anyway). */
|
|
115
|
+
export declare function externalReferences(html: string): string[];
|
|
116
|
+
/**
|
|
117
|
+
* Read the first-party editors a studio bundle ships. A malformed manifest, a missing or oversized
|
|
118
|
+
* entry, an entry with outside references, or a non-`wicked-*` id is skipped with a reason — never
|
|
119
|
+
* served half-right.
|
|
120
|
+
*/
|
|
121
|
+
export declare function discoverStudioEditors(studioRoot: string, log?: (msg: string) => void): Array<Omit<EditorRecord, 'enabled'>>;
|
|
122
|
+
export declare class EditorRegistry {
|
|
123
|
+
private readonly records;
|
|
124
|
+
private readonly stateFile;
|
|
125
|
+
private readonly log;
|
|
126
|
+
constructor(opts?: {
|
|
127
|
+
stateHome?: string;
|
|
128
|
+
studioRoot?: string;
|
|
129
|
+
log?: (msg: string) => void;
|
|
130
|
+
});
|
|
131
|
+
private readState;
|
|
132
|
+
private writeState;
|
|
133
|
+
/** Where the flags live (diagnostics / tests). */
|
|
134
|
+
get stateFilePath(): string;
|
|
135
|
+
list(): EditorRecord[];
|
|
136
|
+
get(id: string): EditorRecord | null;
|
|
137
|
+
/** Enable or disable (§6.4: the operator's click). Returns the record, or `null` for an unknown id. */
|
|
138
|
+
setEnabled(id: string, enabled: boolean): EditorRecord | null;
|
|
139
|
+
/** The wire view: no paths, the hashed entry URL instead. */
|
|
140
|
+
static view(r: EditorRecord): EditorView;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/editors/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGrE,uEAAuE;AACvE,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAC5D,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAChD,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AAErD,eAAO,MAAM,kBAAkB,yMAYqB,CAAC;AAErD,sGAAsG;AACtG,eAAO,MAAM,SAAS,QAAgC,CAAC;AACvD,eAAO,MAAM,cAAc,QAAuC,CAAC;AACnE,wCAAwC;AACxC,eAAO,MAAM,cAAc,QAA8D,CAAC;AAG1F,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9E;AAED,uHAAuH;AACvH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAWzD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC1B,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CA+DtC;AAOD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;gBAEhC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAO;IAW/F,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,UAAU;IAUlB,kDAAkD;IAClD,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,IAAI,YAAY,EAAE;IAItB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAIpC,uGAAuG;IACvG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI;IAS7D,6DAA6D;IAC7D,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,YAAY,GAAG,UAAU;CAmBzC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The artifact-editor registry (DES-artifact-editor-plugins §5.2, §8.6, §9.1; EP-C1).
|
|
3
|
+
*
|
|
4
|
+
* Two kinds of editor exist in the design; one exists today:
|
|
5
|
+
*
|
|
6
|
+
* - **First-party** editors ship INSIDE studio's bundle (§8.6): `<studio dist>/editors/<id>/editor.json`
|
|
7
|
+
* beside the ONE self-contained `index.html` entry. Crew discovers them at boot, pins each entry's
|
|
8
|
+
* sha256 and byte size, and serves the entry through the hashed bundle route (`routes.ts`) with
|
|
9
|
+
* the §8.2 headers. Their ids are `wicked-*` — the reserved first-party namespace every pack is
|
|
10
|
+
* refused (garden `wicked-pack.schema.json`).
|
|
11
|
+
* - **Third-party** editors arrive as garden packs (spec 2 `editors[]`). Their install is REFUSED
|
|
12
|
+
* until the install-time conformance run ships (EP-C8, LATER): `pack-install.ts` still runs
|
|
13
|
+
* garden's fail-closed `pack check` and lists what the operator would be asked to approve, then
|
|
14
|
+
* answers 501. Nothing of a pack is ever written by this registry.
|
|
15
|
+
*
|
|
16
|
+
* Persisted state is ONE file, `<state home>/daemon-editors.json` — the enable/disable flags the
|
|
17
|
+
* operator sets in Settings → Editors (§6.4, human-only). It sits under the `daemon-` prefix both
|
|
18
|
+
* crew's state-home registry and the engine's worker fence already classify (crew#756), so no new
|
|
19
|
+
* top-level entry is introduced; a pack install (EP-C8) will need a registered `editors/` subtree in
|
|
20
|
+
* both repos first.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash } from 'node:crypto';
|
|
23
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
import { crewStateHome } from '../projects/state-home.js';
|
|
27
|
+
/** The persisted flags file, under the registered `daemon-` prefix. */
|
|
28
|
+
export const EDITORS_STATE_FILENAME = 'daemon-editors.json';
|
|
29
|
+
/** Where first-party manifests live inside the studio bundle. */
|
|
30
|
+
export const STUDIO_EDITORS_DIRNAME = 'editors';
|
|
31
|
+
/** The host's bundle cap (§8.5); a pack's `limits.bundleBytes` may only be lower. */
|
|
32
|
+
export const HOST_BUNDLE_CAP_BYTES = 5 * 1024 * 1024;
|
|
33
|
+
export const EDITOR_PERMISSIONS = [
|
|
34
|
+
'artifact.read',
|
|
35
|
+
'artifact.write',
|
|
36
|
+
'selection.chip',
|
|
37
|
+
'composer.draft',
|
|
38
|
+
'checks.read',
|
|
39
|
+
'checks.contribute',
|
|
40
|
+
'sources.read',
|
|
41
|
+
'media.read',
|
|
42
|
+
'artifact.export',
|
|
43
|
+
'ui.fullscreen',
|
|
44
|
+
'network.media',
|
|
45
|
+
];
|
|
46
|
+
/** Editor ids: lowercase dash-words, ≤ 64 (garden's shape); first-party ones start with `wicked-`. */
|
|
47
|
+
export const EDITOR_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
48
|
+
export const FIRST_PARTY_ID = /^wicked-[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
49
|
+
/** Anchored SemVer (garden's shape). */
|
|
50
|
+
export const EDITOR_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
51
|
+
const KIND = /^[a-z][a-z0-9-]{1,40}$/u;
|
|
52
|
+
export const EditorManifestSchema = z.object({
|
|
53
|
+
id: z.string().max(64).regex(EDITOR_ID),
|
|
54
|
+
title: z.string().min(1).regex(/\S/u),
|
|
55
|
+
version: z.string().regex(EDITOR_VERSION),
|
|
56
|
+
protocol: z.array(z.number().int().min(1)).min(1),
|
|
57
|
+
kinds: z.array(z.string().regex(KIND)).min(1),
|
|
58
|
+
entry: z.string().regex(/\.html$/u).default('index.html'),
|
|
59
|
+
sizes: z.array(z.enum(['inline', 'pane', 'full'])).refine((s) => s.includes('inline'), { message: "'inline' is required" }),
|
|
60
|
+
panels: z.array(z.enum(['checks'])).optional(),
|
|
61
|
+
permissions: z.array(z.object({ id: z.enum(EDITOR_PERMISSIONS), why: z.string().min(1) })),
|
|
62
|
+
limits: z.object({ bundleBytes: z.number().int().min(1).max(HOST_BUNDLE_CAP_BYTES) }).optional(),
|
|
63
|
+
});
|
|
64
|
+
export function sha256Of(buf) {
|
|
65
|
+
return createHash('sha256').update(buf).digest('hex');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A path is served from the bundle only if it IS what it looks like: a real directory or a real
|
|
69
|
+
* file, never a link (codex r1 — a link under `editors/` could point outside the bundle and be
|
|
70
|
+
* served as first-party). `null` = fine; else the reason.
|
|
71
|
+
*/
|
|
72
|
+
export function notRealEntry(path, want) {
|
|
73
|
+
let meta;
|
|
74
|
+
try {
|
|
75
|
+
meta = lstatSync(path);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return 'missing';
|
|
79
|
+
}
|
|
80
|
+
if (meta.isSymbolicLink())
|
|
81
|
+
return 'a symlink (not followed)';
|
|
82
|
+
if (want === 'dir' && !meta.isDirectory())
|
|
83
|
+
return 'not a directory';
|
|
84
|
+
if (want === 'file' && !meta.isFile())
|
|
85
|
+
return 'not a file';
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/** The entry must be ONE self-contained file: nothing loaded from outside it (§5.2; the CSP would block it anyway). */
|
|
89
|
+
export function externalReferences(html) {
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const m of html.matchAll(/<(script|link|iframe)\b[^>]*\b(?:src|href)\s*=\s*["']([^"']+)["']/giu)) {
|
|
92
|
+
const tag = m[1].toLowerCase();
|
|
93
|
+
const url = m[2].trim();
|
|
94
|
+
if (tag === 'link' && !/\brel\s*=\s*["']?stylesheet/iu.test(m[0]))
|
|
95
|
+
continue;
|
|
96
|
+
if (/^(?:https?:)?\/\//iu.test(url) || /^[a-z][a-z0-9+.-]*:/iu.test(url) && !/^(?:data|blob|about):/iu.test(url)) {
|
|
97
|
+
out.push(`${tag} ${url}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Read the first-party editors a studio bundle ships. A malformed manifest, a missing or oversized
|
|
104
|
+
* entry, an entry with outside references, or a non-`wicked-*` id is skipped with a reason — never
|
|
105
|
+
* served half-right.
|
|
106
|
+
*/
|
|
107
|
+
export function discoverStudioEditors(studioRoot, log) {
|
|
108
|
+
const dir = join(studioRoot, STUDIO_EDITORS_DIRNAME);
|
|
109
|
+
if (!existsSync(dir))
|
|
110
|
+
return [];
|
|
111
|
+
const out = [];
|
|
112
|
+
for (const name of readdirSync(dir).sort()) {
|
|
113
|
+
const dirProblem = notRealEntry(join(dir, name), 'dir');
|
|
114
|
+
if (dirProblem !== null) {
|
|
115
|
+
if (dirProblem !== 'not a directory')
|
|
116
|
+
log?.(`[editors] ${name}: editor directory is ${dirProblem}; skipped`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const manifestPath = join(dir, name, 'editor.json');
|
|
120
|
+
if (!existsSync(manifestPath))
|
|
121
|
+
continue;
|
|
122
|
+
const manifestProblem = notRealEntry(manifestPath, 'file');
|
|
123
|
+
if (manifestProblem !== null) {
|
|
124
|
+
log?.(`[editors] ${name}: editor.json is ${manifestProblem}; skipped`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
let manifest;
|
|
128
|
+
try {
|
|
129
|
+
const parsed = EditorManifestSchema.safeParse(JSON.parse(readFileSync(manifestPath, 'utf8')));
|
|
130
|
+
if (!parsed.success) {
|
|
131
|
+
log?.(`[editors] ${name}: manifest refused (${parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ')})`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
manifest = parsed.data;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
log?.(`[editors] ${name}: manifest unreadable (${err instanceof Error ? err.message : String(err)})`);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (manifest.id !== name || !FIRST_PARTY_ID.test(manifest.id)) {
|
|
141
|
+
log?.(`[editors] ${name}: a studio-bundle editor must be named wicked-* and live in a directory of its id (got ${manifest.id})`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (manifest.entry.includes('/') || manifest.entry.includes('\\') || manifest.entry.startsWith('.')) {
|
|
145
|
+
log?.(`[editors] ${manifest.id}: entry must be a file beside the manifest (got ${manifest.entry})`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const entryPath = join(dir, name, manifest.entry);
|
|
149
|
+
const entryProblem = notRealEntry(entryPath, 'file');
|
|
150
|
+
if (entryProblem !== null) {
|
|
151
|
+
log?.(`[editors] ${manifest.id}: entry ${manifest.entry} is ${entryProblem}; skipped`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
let buf;
|
|
155
|
+
try {
|
|
156
|
+
buf = readFileSync(entryPath);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
log?.(`[editors] ${manifest.id}: entry unreadable (${err instanceof Error ? err.message : String(err)})`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const cap = manifest.limits?.bundleBytes ?? HOST_BUNDLE_CAP_BYTES;
|
|
163
|
+
if (buf.byteLength > cap) {
|
|
164
|
+
log?.(`[editors] ${manifest.id}: entry is ${buf.byteLength} bytes, over its ${cap}-byte cap`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const outside = externalReferences(buf.toString('utf8'));
|
|
168
|
+
if (outside.length > 0) {
|
|
169
|
+
log?.(`[editors] ${manifest.id}: entry loads from outside itself (${outside.slice(0, 3).join(', ')})`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
out.push({ manifest, sha256: sha256Of(buf), bytes: buf.byteLength, entryPath, firstParty: true, source: 'studio-bundle' });
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
export class EditorRegistry {
|
|
177
|
+
records = new Map();
|
|
178
|
+
stateFile;
|
|
179
|
+
log;
|
|
180
|
+
constructor(opts = {}) {
|
|
181
|
+
this.stateFile = join(opts.stateHome ?? crewStateHome(), EDITORS_STATE_FILENAME);
|
|
182
|
+
this.log = opts.log ?? (() => undefined);
|
|
183
|
+
const state = this.readState();
|
|
184
|
+
if (opts.studioRoot !== undefined) {
|
|
185
|
+
for (const r of discoverStudioEditors(opts.studioRoot, this.log)) {
|
|
186
|
+
this.records.set(r.manifest.id, { ...r, enabled: state.enabled[r.manifest.id] ?? true });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
readState() {
|
|
191
|
+
try {
|
|
192
|
+
const raw = JSON.parse(readFileSync(this.stateFile, 'utf8'));
|
|
193
|
+
const enabled = {};
|
|
194
|
+
for (const [k, v] of Object.entries(raw.enabled ?? {}))
|
|
195
|
+
if (typeof v === 'boolean' && EDITOR_ID.test(k))
|
|
196
|
+
enabled[k] = v;
|
|
197
|
+
return { v: 1, enabled };
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return { v: 1, enabled: {} };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
writeState() {
|
|
204
|
+
const enabled = {};
|
|
205
|
+
for (const r of this.records.values())
|
|
206
|
+
if (!r.enabled)
|
|
207
|
+
enabled[r.manifest.id] = false;
|
|
208
|
+
const state = { v: 1, enabled };
|
|
209
|
+
mkdirSync(join(this.stateFile, '..'), { recursive: true });
|
|
210
|
+
const tmp = `${this.stateFile}.${process.pid}.tmp`;
|
|
211
|
+
writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
|
|
212
|
+
renameSync(tmp, this.stateFile);
|
|
213
|
+
}
|
|
214
|
+
/** Where the flags live (diagnostics / tests). */
|
|
215
|
+
get stateFilePath() {
|
|
216
|
+
return this.stateFile;
|
|
217
|
+
}
|
|
218
|
+
list() {
|
|
219
|
+
return [...this.records.values()].sort((a, b) => Number(b.firstParty) - Number(a.firstParty) || a.manifest.id.localeCompare(b.manifest.id));
|
|
220
|
+
}
|
|
221
|
+
get(id) {
|
|
222
|
+
return this.records.get(id) ?? null;
|
|
223
|
+
}
|
|
224
|
+
/** Enable or disable (§6.4: the operator's click). Returns the record, or `null` for an unknown id. */
|
|
225
|
+
setEnabled(id, enabled) {
|
|
226
|
+
const r = this.records.get(id);
|
|
227
|
+
if (r === undefined)
|
|
228
|
+
return null;
|
|
229
|
+
const next = { ...r, enabled };
|
|
230
|
+
this.records.set(id, next);
|
|
231
|
+
this.writeState();
|
|
232
|
+
return next;
|
|
233
|
+
}
|
|
234
|
+
/** The wire view: no paths, the hashed entry URL instead. */
|
|
235
|
+
static view(r) {
|
|
236
|
+
const m = r.manifest;
|
|
237
|
+
return {
|
|
238
|
+
id: m.id,
|
|
239
|
+
title: m.title,
|
|
240
|
+
version: m.version,
|
|
241
|
+
protocol: [...m.protocol],
|
|
242
|
+
kinds: [...m.kinds],
|
|
243
|
+
sizes: [...m.sizes],
|
|
244
|
+
...(m.panels !== undefined ? { panels: [...m.panels] } : {}),
|
|
245
|
+
permissions: m.permissions.map((p) => ({ id: p.id, why: p.why })),
|
|
246
|
+
sha256: r.sha256,
|
|
247
|
+
bytes: r.bytes,
|
|
248
|
+
first_party: r.firstParty,
|
|
249
|
+
enabled: r.enabled,
|
|
250
|
+
source: r.source,
|
|
251
|
+
entry_url: `/api/v1/editors/${encodeURIComponent(m.id)}/${encodeURIComponent(m.version)}/entry`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/editors/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/api/v1/editors` (DES-artifact-editor-plugins §6, §8.2, §9.1; EP-C1).
|
|
3
|
+
*
|
|
4
|
+
* - `GET /editors` (operator+): the registry, as Settings → Editors renders it.
|
|
5
|
+
* - `POST /editors {packRoot}` (HUMAN-only): garden's fail-closed gate, the approval list, then 501
|
|
6
|
+
* `third_party_editors_not_available` — every pack editor is third-party and EP-C8 has not shipped.
|
|
7
|
+
* - `DELETE /editors/:id` (HUMAN-only): a first-party editor ships with studio and cannot be removed
|
|
8
|
+
* (409; disable it instead); no other editor can exist before EP-C8 (404).
|
|
9
|
+
* - `POST /editors/:id/enabled {enabled}` (HUMAN-only): the operator's switch.
|
|
10
|
+
* - `GET /editors/:id/grants?project=` (operator+): the engine's decided set
|
|
11
|
+
* (`evaluate_editor_grants`: EDITOR-GRANTS ledger + EDITOR-BUILTIN posture, deny dominates), with
|
|
12
|
+
* the token an "allow" would add. The host fetches it before `host.hello` and enforces it.
|
|
13
|
+
* - `GET /editors/:id/:version/entry`: the bundle, with exactly the §8.2 headers. The file path comes
|
|
14
|
+
* from the registry record — never from the URL — and the bytes are re-hashed on every serve: a
|
|
15
|
+
* changed file answers 409 `bundle_hash_mismatch`, never the changed bundle.
|
|
16
|
+
*
|
|
17
|
+
* Human-only means `actor.kind === 'human'` (the DC `remember()` check). Under `auth=off` every
|
|
18
|
+
* same-uid caller is the local human actor — the honest limit §6.4 states; refusing a worker token
|
|
19
|
+
* is a tripwire, not a control.
|
|
20
|
+
*/
|
|
21
|
+
import type { FastifyInstance, FastifyReply } from 'fastify';
|
|
22
|
+
import type { EditorGrantsResponse } from '../core/types.js';
|
|
23
|
+
import { type PackCheckRunner } from './pack-install.js';
|
|
24
|
+
import { EditorRegistry, type EditorRecord } from './registry.js';
|
|
25
|
+
/** The slice of the adapter the grants route needs; every method optional so a stub never throws. */
|
|
26
|
+
export interface EditorGrantsAdapter {
|
|
27
|
+
editorGrantsSupported?(): boolean;
|
|
28
|
+
evaluateEditorGrants?(request: {
|
|
29
|
+
editorId: string;
|
|
30
|
+
version: string;
|
|
31
|
+
sha256: string;
|
|
32
|
+
permissions: string[];
|
|
33
|
+
project?: string;
|
|
34
|
+
firstParty: boolean;
|
|
35
|
+
}): Promise<EditorGrantsResponse>;
|
|
36
|
+
}
|
|
37
|
+
export interface EditorRoutesDeps {
|
|
38
|
+
registry: EditorRegistry;
|
|
39
|
+
adapter: EditorGrantsAdapter;
|
|
40
|
+
packCheck: PackCheckRunner;
|
|
41
|
+
log?: (msg: string) => void;
|
|
42
|
+
}
|
|
43
|
+
export declare function registerEditorRoutes(app: FastifyInstance, deps: EditorRoutesDeps): void;
|
|
44
|
+
/** Re-hash on every serve: a bundle that changed on disk is refused, never served. */
|
|
45
|
+
export declare function serveBundle(record: EditorRecord, reply: FastifyReply, log?: (msg: string) => void): FastifyReply;
|
|
46
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/editors/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAkB,MAAM,SAAS,CAAC;AAI7E,OAAO,KAAK,EAAS,oBAAoB,EAAuB,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAA6B,cAAc,EAAgB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAI3G,qGAAqG;AACrG,MAAM,WAAW,mBAAmB;IAClC,qBAAqB,CAAC,IAAI,OAAO,CAAC;IAClC,oBAAoB,CAAC,CAAC,OAAO,EAAE;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;KACrB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,eAAe,CAAC;IAC3B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CA2HvF;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,CAsBhH"}
|