@markii/host 0.0.1 → 0.17.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/browser-worker.js +639 -0
- package/dist/browser.d.ts +61 -0
- package/dist/browser.js +172 -0
- package/dist/conformance/host/01-plain-open/expected.json +5 -0
- package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
- package/dist/conformance/host/01-plain-open/scenario.json +6 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
- package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
- package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
- package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
- package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
- package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
- package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
- package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
- package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
- package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
- package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
- package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
- package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
- package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
- package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
- package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
- package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
- package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
- package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
- package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
- package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
- package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
- package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
- package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
- package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
- package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
- package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
- package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
- package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
- package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
- package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
- package/dist/conformance/host/14-remove-pack/expected.json +14 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
- package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
- package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
- package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
- package/dist/conformance/host/16-pack-trust/expected.json +37 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
- package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
- package/dist/conformance/host/README.md +221 -0
- package/dist/conformance.d.ts +2 -0
- package/dist/conformance.js +5 -0
- package/dist/diagnostics/render-diagnostics.d.ts +41 -0
- package/dist/diagnostics/render-diagnostics.js +46 -0
- package/dist/export/cascade.d.ts +98 -0
- package/dist/export/cascade.js +159 -0
- package/dist/export/export-zip.d.ts +19 -0
- package/dist/export/export-zip.js +38 -0
- package/dist/export/image-embed.d.ts +126 -0
- package/dist/export/image-embed.js +274 -0
- package/dist/export/note-export.d.ts +332 -0
- package/dist/export/note-export.js +366 -0
- package/dist/export/note-links.d.ts +83 -0
- package/dist/export/note-links.js +241 -0
- package/dist/glue.wasm +0 -0
- package/dist/host/adapter.d.ts +218 -0
- package/dist/host/adapter.js +9 -0
- package/dist/host/cascade-export-behavior.d.ts +120 -0
- package/dist/host/cascade-export-behavior.js +151 -0
- package/dist/host/conformance-runner.d.ts +159 -0
- package/dist/host/conformance-runner.js +632 -0
- package/dist/host/create-host.d.ts +251 -0
- package/dist/host/create-host.js +329 -0
- package/dist/host/editor-behavior.d.ts +153 -0
- package/dist/host/editor-behavior.js +223 -0
- package/dist/host/export/md-plain.d.ts +15 -0
- package/dist/host/export/md-plain.js +125 -0
- package/dist/host/export-behavior.d.ts +111 -0
- package/dist/host/export-behavior.js +257 -0
- package/dist/host/labels.d.ts +23 -0
- package/dist/host/labels.js +32 -0
- package/dist/host/pack-archive.d.ts +30 -0
- package/dist/host/pack-archive.js +62 -0
- package/dist/host/pack-install.d.ts +87 -0
- package/dist/host/pack-install.js +186 -0
- package/dist/host/pack-load.d.ts +71 -0
- package/dist/host/pack-load.js +57 -0
- package/dist/host/pack-remove.d.ts +21 -0
- package/dist/host/pack-remove.js +65 -0
- package/dist/host/pack-trust-behavior.d.ts +79 -0
- package/dist/host/pack-trust-behavior.js +262 -0
- package/dist/host/refresh-interval.d.ts +37 -0
- package/dist/host/refresh-interval.js +59 -0
- package/dist/host/run-behavior.d.ts +88 -0
- package/dist/host/run-behavior.js +189 -0
- package/dist/host/run-diagnostics.d.ts +26 -0
- package/dist/host/run-diagnostics.js +43 -0
- package/dist/host/script-execution.d.ts +72 -0
- package/dist/host/script-execution.js +68 -0
- package/dist/host/terminal-packs.d.ts +39 -0
- package/dist/host/terminal-packs.js +51 -0
- package/dist/html-engine.d.ts +2 -0
- package/dist/html-engine.js +29 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +69 -0
- package/dist/insert/component-catalog.d.ts +23 -0
- package/dist/insert/component-catalog.js +96 -0
- package/dist/node-fs.d.ts +14 -0
- package/dist/node-fs.js +70 -0
- package/dist/node-worker.js +20657 -0
- package/dist/node.d.ts +6 -0
- package/dist/node.js +13 -0
- package/dist/package.json +3 -0
- package/dist/packs/discover.d.ts +119 -0
- package/dist/packs/discover.js +266 -0
- package/dist/packs/pack-diagnostics.d.ts +79 -0
- package/dist/packs/pack-diagnostics.js +103 -0
- package/dist/packs/pack-paths.d.ts +53 -0
- package/dist/packs/pack-paths.js +105 -0
- package/dist/packs/pack-render-registry.d.ts +82 -0
- package/dist/packs/pack-render-registry.js +182 -0
- package/dist/packs/pack-scripts.d.ts +20 -0
- package/dist/packs/pack-scripts.js +96 -0
- package/dist/packs/pack-trust.d.ts +57 -0
- package/dist/packs/pack-trust.js +111 -0
- package/dist/packs/prebuilt.d.ts +49 -0
- package/dist/packs/prebuilt.js +97 -0
- package/dist/path-util.d.ts +24 -0
- package/dist/path-util.js +82 -0
- package/dist/run/browser-isolate.d.ts +45 -0
- package/dist/run/browser-isolate.js +85 -0
- package/dist/run/bundle-document.d.ts +29 -0
- package/dist/run/bundle-document.js +40 -0
- package/dist/run/bundle-run.d.ts +188 -0
- package/dist/run/bundle-run.js +232 -0
- package/dist/run/grant-flow.d.ts +232 -0
- package/dist/run/grant-flow.js +390 -0
- package/dist/run/ip-address.d.ts +41 -0
- package/dist/run/ip-address.js +255 -0
- package/dist/run/isolate.d.ts +59 -0
- package/dist/run/isolate.js +22 -0
- package/dist/run/lua-resolver.d.ts +21 -0
- package/dist/run/lua-resolver.js +63 -0
- package/dist/run/net-bridge-worker.d.ts +26 -0
- package/dist/run/net-bridge-worker.js +66 -0
- package/dist/run/net-bridge.d.ts +56 -0
- package/dist/run/net-bridge.js +59 -0
- package/dist/run/net-pinning.d.ts +82 -0
- package/dist/run/net-pinning.js +137 -0
- package/dist/run/net-provider.d.ts +72 -0
- package/dist/run/net-provider.js +303 -0
- package/dist/run/node-isolate.d.ts +22 -0
- package/dist/run/node-isolate.js +68 -0
- package/dist/run/run-flow.d.ts +186 -0
- package/dist/run/run-flow.js +323 -0
- package/dist/run/run-host.d.ts +107 -0
- package/dist/run/run-host.js +241 -0
- package/dist/run/run-job.d.ts +130 -0
- package/dist/run/run-job.js +279 -0
- package/dist/run/run-progress.d.ts +55 -0
- package/dist/run/run-progress.js +42 -0
- package/dist/run/run-trace.d.ts +44 -0
- package/dist/run/run-trace.js +36 -0
- package/dist/run/script-requirements.d.ts +98 -0
- package/dist/run/script-requirements.js +212 -0
- package/dist/run/snapshot-storage.d.ts +25 -0
- package/dist/run/snapshot-storage.js +31 -0
- package/dist/run/stale-values.d.ts +25 -0
- package/dist/run/stale-values.js +32 -0
- package/dist/run/worker-entry-browser.d.ts +62 -0
- package/dist/run/worker-entry-browser.js +172 -0
- package/dist/run/worker-entry.d.ts +1 -0
- package/dist/run/worker-entry.js +98 -0
- package/dist/values-failure.d.ts +17 -0
- package/dist/values-failure.js +1 -0
- package/dist/values-merge.d.ts +39 -0
- package/dist/values-merge.js +33 -0
- package/package.json +80 -4
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-neutral half of a run: everything between "a job arrived" and
|
|
3
|
+
* "here is its result", with nothing in it that binds to one JavaScript
|
|
4
|
+
* runtime.
|
|
5
|
+
*
|
|
6
|
+
* This exists because the two hosts cannot spawn the same kind of isolate
|
|
7
|
+
* (see `./isolate.ts`). The VS Code extension runs a `node:worker_threads`
|
|
8
|
+
* worker; an Obsidian plugin, living in an Electron renderer, can only run
|
|
9
|
+
* a Web Worker, where `node:http`, `node:dns`, and `Buffer` do not exist.
|
|
10
|
+
* Rather than keep two copies of the run logic in step with each other,
|
|
11
|
+
* the parts that genuinely differ are injected: how a network request is
|
|
12
|
+
* made, and where wasmoon's `glue.wasm` is found.
|
|
13
|
+
*
|
|
14
|
+
* `./worker-entry.ts` supplies the Node ones (a pinned `node:https`
|
|
15
|
+
* request, `glue.wasm` beside the bundle on disk).
|
|
16
|
+
* `./worker-entry-browser.ts` supplies the Web Worker ones (a request
|
|
17
|
+
* bridged to the host over `postMessage`, `glue.wasm` as a blob URL).
|
|
18
|
+
* Everything below runs identically under both, which is the point: the
|
|
19
|
+
* sandbox, the tier gate, the marshaling limits, and the failure
|
|
20
|
+
* classification cannot drift between hosts, because there is only one of
|
|
21
|
+
* each.
|
|
22
|
+
*/
|
|
23
|
+
import { extractScripts, parse } from '@markii/core';
|
|
24
|
+
import { buildDirectiveListing, createValueStore, runDocumentScripts, } from '@markii/runtime';
|
|
25
|
+
import { createLuaExecutor, DEFAULT_MAX_FETCH_BYTES, } from '@markii/lua';
|
|
26
|
+
import { createScriptView } from '@markii/bundle';
|
|
27
|
+
import { cacheFilesFrom } from './bundle-run.js';
|
|
28
|
+
import { createSnapshotStorage } from './snapshot-storage.js';
|
|
29
|
+
import { createPackModuleResolver } from './lua-resolver.js';
|
|
30
|
+
export { isRunProgress } from './run-progress.js';
|
|
31
|
+
/**
|
|
32
|
+
* A policy denial this module's `NetProvider` raises (an ungranted host, a
|
|
33
|
+
* redirect off the allowlist, too many redirects, an over-size response, a
|
|
34
|
+
* credential-bearing redirect target, a pinning refusal from
|
|
35
|
+
* `./net-pinning.ts`). This is NOT the same kind of denial as
|
|
36
|
+
* `@markii/lua`'s own `netGrants` check inside `buildCapabilities` (which
|
|
37
|
+
* records on its own out-of-band `CapabilityDenials` handle): a denial
|
|
38
|
+
* detected HERE happens inside this provider's own `get`/`post`/`patch`, one
|
|
39
|
+
* level below that check. `netProviderDenial` (`@markii/lua`) brands the
|
|
40
|
+
* thrown `Error` with a JS `Symbol`; `@markii/lua` checks that brand on the
|
|
41
|
+
* JS side of the provider call, BEFORE the error crosses into Lua, records
|
|
42
|
+
* the denial on the same non-spoofable handle its own grant check uses, and
|
|
43
|
+
* re-throws a sanitized capability error. So a provider policy refusal comes
|
|
44
|
+
* back as `'capability-denied'` with no help from `runJob`, and no
|
|
45
|
+
* classification signal ever rides in a Lua-visible string.
|
|
46
|
+
*
|
|
47
|
+
* P2-c fix (docs/archive/PENTEST-REPORT-2026-08-23.md §9.3): the previous approach put a
|
|
48
|
+
* per-run random tag INSIDE the thrown message so `runJob` could reclassify
|
|
49
|
+
* the failure by scanning that message. But the message crosses back into
|
|
50
|
+
* Lua, where a script's own `pcall`/`tostring` reads the tag and then forges
|
|
51
|
+
* it into `error(tag .. ": ...")` to relabel an unrelated failure as
|
|
52
|
+
* capability-denied. The brand lives on the JS `Error` object and is consumed
|
|
53
|
+
* before the Lua boundary, so there is nothing for a script to observe or
|
|
54
|
+
* forge, and `runJob` no longer post-processes failure kinds at all.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* `hostname` with IPv6 brackets stripped, for the `hostname` option
|
|
58
|
+
* `http.request`/`https.request` take: Node re-adds the brackets itself
|
|
59
|
+
* (for the `Host` header and TLS SNI) whenever the raw address contains a
|
|
60
|
+
* `:`, so passing an already-bracketed literal through would double them.
|
|
61
|
+
*/
|
|
62
|
+
/** A `CacheProvider` over a plain snapshot object, with the writes it accumulates readable back out. */
|
|
63
|
+
function createSnapshotCacheProvider(snapshot) {
|
|
64
|
+
const store = { ...snapshot };
|
|
65
|
+
return {
|
|
66
|
+
provider: {
|
|
67
|
+
get: (key) => Promise.resolve(store[key]),
|
|
68
|
+
set: (key, entry) => {
|
|
69
|
+
store[key] = entry;
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
snapshot: () => ({ ...store }),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function describeThrown(err) {
|
|
77
|
+
return err instanceof Error ? err.message : String(err);
|
|
78
|
+
}
|
|
79
|
+
/** Runs one job to completion, never throwing — see this module's top doc comment. */
|
|
80
|
+
export async function runJob(job, deps) {
|
|
81
|
+
const cache = createSnapshotCacheProvider(job.cacheSnapshot ?? {});
|
|
82
|
+
const netAllowlist = job.netAllowlist ?? [];
|
|
83
|
+
// The SAME cap governs this worker's own bounded body read
|
|
84
|
+
// (`issueRequest`, B-2) and `@markii/lua`'s own `maxFetchBytes` check —
|
|
85
|
+
// computed once, here, rather than letting each side default
|
|
86
|
+
// independently, so the two can never quietly disagree.
|
|
87
|
+
const maxFetchBytes = job.limits?.maxFetchBytes ?? DEFAULT_MAX_FETCH_BYTES;
|
|
88
|
+
// GitHub issue #10, fail closed: an absent `netPolicy` (an older host, or
|
|
89
|
+
// a caller that hasn't been updated) means restricted addresses are NOT
|
|
90
|
+
// allowed — the same posture `@markii/lua`'s own defaults take, never the
|
|
91
|
+
// opposite.
|
|
92
|
+
const netPolicy = job.netPolicy ?? {
|
|
93
|
+
allowRestrictedAddresses: false,
|
|
94
|
+
};
|
|
95
|
+
const net = deps.createNet(netAllowlist, maxFetchBytes, netPolicy);
|
|
96
|
+
const tree = parse(job.text);
|
|
97
|
+
const scripts = extractScripts(tree);
|
|
98
|
+
// GitHub issue #33: the note's own directives, as capped plain data for
|
|
99
|
+
// the `doc` table a script sees. Built from the tree ALREADY parsed on
|
|
100
|
+
// the line above, so a run still parses the note exactly once, and built
|
|
101
|
+
// HERE rather than in either host so both isolates (the Node worker and
|
|
102
|
+
// the Web Worker) get a listing produced by one piece of code.
|
|
103
|
+
const directives = buildDirectiveListing(tree);
|
|
104
|
+
// Slice 2 of the .mkz Run-path arc (GitHub issue #9): a bundle-backed run
|
|
105
|
+
// gets a snapshot-backed ScriptView (never a live zip/disk handle — see
|
|
106
|
+
// `./snapshot-storage.ts`), scoped to exactly the intersection
|
|
107
|
+
// `createScriptView` (@markii/bundle) computes between the manifest's
|
|
108
|
+
// declared `permissions.bundle` and what the host's grant flow actually
|
|
109
|
+
// granted.
|
|
110
|
+
const bundleStorage = job.bundle
|
|
111
|
+
? createSnapshotStorage(job.bundle.snapshot)
|
|
112
|
+
: undefined;
|
|
113
|
+
const bundleView = bundleStorage && job.bundle
|
|
114
|
+
? createScriptView(bundleStorage, job.bundle.manifest, {
|
|
115
|
+
bundle: job.bundle.grantedBundlePermissions,
|
|
116
|
+
})
|
|
117
|
+
: undefined;
|
|
118
|
+
const executor = createLuaExecutor({
|
|
119
|
+
net,
|
|
120
|
+
// One allowlist governs GET/POST/PATCH alike (B-6, docs/security.md:
|
|
121
|
+
// "the per-host allowlist is the real boundary") — the grant prompt's
|
|
122
|
+
// wording ("can send data to <host>") already promises exactly this,
|
|
123
|
+
// so POST/PATCH must be wired to the same hosts GET is, not silently
|
|
124
|
+
// disabled.
|
|
125
|
+
netGrants: { get: netAllowlist, post: netAllowlist },
|
|
126
|
+
cache: cache.provider,
|
|
127
|
+
bundle: bundleView,
|
|
128
|
+
...(job.packModules
|
|
129
|
+
? { packModuleResolver: createPackModuleResolver(job.packModules) }
|
|
130
|
+
: {}),
|
|
131
|
+
maxFetchBytes,
|
|
132
|
+
limits: {
|
|
133
|
+
...(job.limits?.wallClockMs !== undefined
|
|
134
|
+
? { wallClockMs: job.limits.wallClockMs }
|
|
135
|
+
: {}),
|
|
136
|
+
...(job.limits?.maxInstructions !== undefined
|
|
137
|
+
? { maxInstructions: job.limits.maxInstructions }
|
|
138
|
+
: {}),
|
|
139
|
+
...(job.limits?.maxMemoryBytes !== undefined
|
|
140
|
+
? { maxMemoryBytes: job.limits.maxMemoryBytes }
|
|
141
|
+
: {}),
|
|
142
|
+
},
|
|
143
|
+
wasmUri: deps.wasmUri,
|
|
144
|
+
});
|
|
145
|
+
const store = createValueStore();
|
|
146
|
+
// GitHub issue #35: the ordinal is assigned HERE rather than by either
|
|
147
|
+
// worker entry, so the two hosts cannot number their runs differently.
|
|
148
|
+
let progressIndex = 0;
|
|
149
|
+
const postProgress = deps.postProgress;
|
|
150
|
+
const summary = await runDocumentScripts({
|
|
151
|
+
scripts,
|
|
152
|
+
executor,
|
|
153
|
+
// Announced per script, in document order, as each value is stored —
|
|
154
|
+
// `runDocumentScripts` swallows anything this throws, so a failing
|
|
155
|
+
// channel costs the run nothing.
|
|
156
|
+
...(postProgress
|
|
157
|
+
? {
|
|
158
|
+
onValue: (name, value) => {
|
|
159
|
+
postProgress({
|
|
160
|
+
kind: 'markii:run-progress',
|
|
161
|
+
index: progressIndex++,
|
|
162
|
+
name,
|
|
163
|
+
value,
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
: {}),
|
|
168
|
+
// GitHub issue #11: the trigger the host sent (default `'manual'`) — its
|
|
169
|
+
// mapping to a capability tier (`tierForTrigger`) is the sandbox's own
|
|
170
|
+
// read-only gate for auto/scheduled runs. Never note-influenced.
|
|
171
|
+
trigger: job.trigger ?? 'manual',
|
|
172
|
+
store,
|
|
173
|
+
// Read-only and tier-free: the listing is the note's own content, so
|
|
174
|
+
// it is handed to an auto/scheduled run exactly as to a manual one.
|
|
175
|
+
doc: { directives },
|
|
176
|
+
// `src=scripts/foo.lua` resolution (design point 4): the referenced
|
|
177
|
+
// file's source lives in the snapshot under its own bundle-relative
|
|
178
|
+
// path (`ScriptBlock.src` already carries the full "scripts/..." path
|
|
179
|
+
// — see @markii/core's `scripts.ts`), read through the SAME jailed
|
|
180
|
+
// snapshot storage a script's own `bundle.read` would use. A bare
|
|
181
|
+
// `.mk.md` run (no `job.bundle`) never sets `loadSource` at all, so a
|
|
182
|
+
// `src=` block there is reported as the pre-existing "no loadSource
|
|
183
|
+
// provided" error, unchanged.
|
|
184
|
+
...(bundleStorage
|
|
185
|
+
? {
|
|
186
|
+
loadSource: async (src) => {
|
|
187
|
+
const bytes = await bundleStorage.read(src);
|
|
188
|
+
if (bytes === undefined) {
|
|
189
|
+
throw new Error(`bundle script "${src}" was not found`);
|
|
190
|
+
}
|
|
191
|
+
return new TextDecoder().decode(bytes);
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
: {}),
|
|
195
|
+
});
|
|
196
|
+
// A net-provider policy denial already arrives here classified as
|
|
197
|
+
// 'capability-denied': `@markii/lua` records it on its non-spoofable
|
|
198
|
+
// `CapabilityDenials` handle when the branded `netProviderDenial` crosses
|
|
199
|
+
// its provider-call boundary (see the `NetProvider` doc comment above,
|
|
200
|
+
// P2-c). So `runJob` no longer post-processes any failure kind — it reads
|
|
201
|
+
// `entry.failureKind` verbatim, and the value store already carries the
|
|
202
|
+
// same kind for the corresponding `StoredValue`.
|
|
203
|
+
const failures = summary.results
|
|
204
|
+
.filter((entry) => entry.status === 'error')
|
|
205
|
+
.map((entry) => ({
|
|
206
|
+
name: entry.name,
|
|
207
|
+
message: entry.error ?? 'script failed',
|
|
208
|
+
kind: entry.failureKind ?? 'script-error',
|
|
209
|
+
}));
|
|
210
|
+
// Copied entry-by-entry onto a fresh plain object rather than returned as
|
|
211
|
+
// `store.snapshot()` directly: this preserves the exact N-11 pinned shape
|
|
212
|
+
// (a script literally named `__proto__` assigns through and leaves no own
|
|
213
|
+
// key), which a direct structured-clone of the store's own object would
|
|
214
|
+
// not. The values are otherwise passed through verbatim — failure kinds
|
|
215
|
+
// arrive already correct (see the failures comment above).
|
|
216
|
+
const values = {};
|
|
217
|
+
for (const [name, entry] of Object.entries(store.snapshot())) {
|
|
218
|
+
values[name] = entry;
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
values,
|
|
222
|
+
failures,
|
|
223
|
+
cacheSnapshot: cache.snapshot(),
|
|
224
|
+
...(bundleStorage
|
|
225
|
+
? { cacheOut: cacheFilesFrom(bundleStorage.currentFiles()) }
|
|
226
|
+
: {}),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/** Turns any unexpected internal failure into an ordinary result — see the top doc comment's "never an unhandled rejection" guarantee. */
|
|
230
|
+
export function resultForInternalError(err, fallbackCacheSnapshot) {
|
|
231
|
+
return {
|
|
232
|
+
values: {},
|
|
233
|
+
failures: [
|
|
234
|
+
{
|
|
235
|
+
name: '<document>',
|
|
236
|
+
message: describeThrown(err),
|
|
237
|
+
kind: 'script-error',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
cacheSnapshot: fallbackCacheSnapshot,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const RUN_TRIGGERS = new Set([
|
|
244
|
+
'manual',
|
|
245
|
+
'auto',
|
|
246
|
+
'scheduled',
|
|
247
|
+
]);
|
|
248
|
+
export function isRunJob(value) {
|
|
249
|
+
if (typeof value !== 'object' || value === null)
|
|
250
|
+
return false;
|
|
251
|
+
const job = value;
|
|
252
|
+
// `trigger` is optional, but a PRESENT one must be a known trigger — a
|
|
253
|
+
// malformed job is rejected outright (fail-closed) rather than silently
|
|
254
|
+
// coerced, so a bad `trigger` can never quietly fall through to the
|
|
255
|
+
// full-capability `'manual'` default. Only the trusted host sets this, but
|
|
256
|
+
// validating it keeps the tier gate's input honest regardless.
|
|
257
|
+
if (Object.prototype.hasOwnProperty.call(job, 'trigger') &&
|
|
258
|
+
job.trigger !== undefined &&
|
|
259
|
+
(typeof job.trigger !== 'string' || !RUN_TRIGGERS.has(job.trigger))) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
// Same fail-closed shape check for `netPolicy` (GitHub issue #10): a
|
|
263
|
+
// PRESENT but malformed policy is rejected outright rather than silently
|
|
264
|
+
// falling through to the safe default, keeping the pinning gate's input
|
|
265
|
+
// as honest as the tier gate's above.
|
|
266
|
+
if (Object.prototype.hasOwnProperty.call(job, 'netPolicy') &&
|
|
267
|
+
job.netPolicy !== undefined &&
|
|
268
|
+
(typeof job.netPolicy !== 'object' ||
|
|
269
|
+
job.netPolicy === null ||
|
|
270
|
+
typeof job.netPolicy
|
|
271
|
+
.allowRestrictedAddresses !== 'boolean')) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
return (typeof job.text === 'string' &&
|
|
275
|
+
Array.isArray(job.netAllowlist) &&
|
|
276
|
+
job.netAllowlist.every((h) => typeof h === 'string') &&
|
|
277
|
+
typeof job.cacheSnapshot === 'object' &&
|
|
278
|
+
job.cacheSnapshot !== null);
|
|
279
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-script progress message (GitHub issue #35): its shape and its
|
|
3
|
+
* hostile-shape guard, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* WHY IT IS ITS OWN MODULE. The message is BUILT inside the isolate
|
|
6
|
+
* (`./run-job.ts`, so both worker entries emit an identical one) and READ
|
|
7
|
+
* on the host (`./run-host.ts`, which tells progress from the run's single
|
|
8
|
+
* result message). `./run-job.ts` imports `@markii/lua`, and that import
|
|
9
|
+
* pulls a whole WebAssembly Lua engine in behind it — a host bundle must
|
|
10
|
+
* never carry one, least of all the Obsidian plugin's, which runs in an
|
|
11
|
+
* Electron renderer and delegates every Lua execution to a Web Worker. So
|
|
12
|
+
* the protocol lives apart from the code that speaks it, exactly as
|
|
13
|
+
* `./net-bridge.ts` does for the net bridge.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here imports anything but a type.
|
|
16
|
+
*/
|
|
17
|
+
import type { StoredValue } from '@markii/runtime';
|
|
18
|
+
/**
|
|
19
|
+
* Worker -> host, one per script, sent the moment that script's value was
|
|
20
|
+
* written to the run's store and always BEFORE the single `RunResult`
|
|
21
|
+
* message that settles the run. Scripts run sequentially in one isolate,
|
|
22
|
+
* so the last script's value can be many seconds younger than the first's;
|
|
23
|
+
* without this the preview shows every value as stale until the whole
|
|
24
|
+
* batch is done.
|
|
25
|
+
*
|
|
26
|
+
* `index` is this message's ordinal within the run, counted from 0 in
|
|
27
|
+
* document order. The host uses it to drop a duplicate or out-of-order
|
|
28
|
+
* message rather than trusting arrival order alone — the channel preserves
|
|
29
|
+
* order, but the isolate runs untrusted script content, so nothing coming
|
|
30
|
+
* out of it is taken on trust.
|
|
31
|
+
*
|
|
32
|
+
* Every field is structured-clone-safe, exactly like `RunResult`: `value`
|
|
33
|
+
* is the same already-marshaled `StoredValue` that ends up in
|
|
34
|
+
* `RunResult.values`.
|
|
35
|
+
*/
|
|
36
|
+
export interface RunProgress {
|
|
37
|
+
kind: 'markii:run-progress';
|
|
38
|
+
index: number;
|
|
39
|
+
name: string;
|
|
40
|
+
value: StoredValue;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Hostile-shape guard for a `RunProgress` message, applied by
|
|
44
|
+
* `./run-host.ts` to every message an isolate sends. The isolate runs
|
|
45
|
+
* untrusted script content, so a message claiming to be progress is
|
|
46
|
+
* validated the same way `./run-job.ts`'s `isRunJob` validates a job:
|
|
47
|
+
* every field checked, own properties only, no coercion, and `value`
|
|
48
|
+
* reduced to a plain object carrying one of `@markii/runtime`'s four
|
|
49
|
+
* closed statuses.
|
|
50
|
+
*
|
|
51
|
+
* A message that fails this is NOT progress — the caller then treats it as
|
|
52
|
+
* the run's final result, where `RunResult`'s own consumers already handle
|
|
53
|
+
* a malformed shape.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isRunProgress(value: unknown): value is RunProgress;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hostile-shape guard for a `RunProgress` message, applied by
|
|
3
|
+
* `./run-host.ts` to every message an isolate sends. The isolate runs
|
|
4
|
+
* untrusted script content, so a message claiming to be progress is
|
|
5
|
+
* validated the same way `./run-job.ts`'s `isRunJob` validates a job:
|
|
6
|
+
* every field checked, own properties only, no coercion, and `value`
|
|
7
|
+
* reduced to a plain object carrying one of `@markii/runtime`'s four
|
|
8
|
+
* closed statuses.
|
|
9
|
+
*
|
|
10
|
+
* A message that fails this is NOT progress — the caller then treats it as
|
|
11
|
+
* the run's final result, where `RunResult`'s own consumers already handle
|
|
12
|
+
* a malformed shape.
|
|
13
|
+
*/
|
|
14
|
+
export function isRunProgress(value) {
|
|
15
|
+
if (typeof value !== 'object' || value === null)
|
|
16
|
+
return false;
|
|
17
|
+
const message = value;
|
|
18
|
+
// Own properties only, the same discipline `apps/vscode/src/protocol.ts`
|
|
19
|
+
// applies to every wire message: an object that merely INHERITS `kind`
|
|
20
|
+
// from its prototype is not a progress message.
|
|
21
|
+
const own = (key) => Object.prototype.hasOwnProperty.call(message, key);
|
|
22
|
+
if (!own('kind') || message.kind !== 'markii:run-progress')
|
|
23
|
+
return false;
|
|
24
|
+
if (!own('index') || !own('name') || !own('value'))
|
|
25
|
+
return false;
|
|
26
|
+
if (typeof message.index !== 'number' ||
|
|
27
|
+
!Number.isInteger(message.index) ||
|
|
28
|
+
message.index < 0) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (typeof message.name !== 'string')
|
|
32
|
+
return false;
|
|
33
|
+
const stored = message.value;
|
|
34
|
+
if (typeof stored !== 'object' || stored === null || Array.isArray(stored)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const status = stored.status;
|
|
38
|
+
return (status === 'fresh' ||
|
|
39
|
+
status === 'stale' ||
|
|
40
|
+
status === 'error' ||
|
|
41
|
+
status === 'missing');
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ITEM 3 (AGENTS.md "clean is not silent"): a `'scheduled'`/`'auto'` run
|
|
3
|
+
* that produces identical values is otherwise indistinguishable from one
|
|
4
|
+
* that never happened — no value changed, so nothing in the rendered note
|
|
5
|
+
* moves. This module records the last run's OUTCOME (when it ran, whether
|
|
6
|
+
* it succeeded, and why not if it failed) per document, independent of
|
|
7
|
+
* whatever values it produced, and reads it back for `preview-panel.ts`'s
|
|
8
|
+
* `postUpdate` to surface as a quiet marker — the same "last-known state,
|
|
9
|
+
* persisted, rehydrated on reopen" design `./stale-values.ts` and
|
|
10
|
+
* `./run-flow.ts`'s value persistence already use.
|
|
11
|
+
*
|
|
12
|
+
* `vscode`-free: takes a `GrantMemento`-shaped store (the same minimal
|
|
13
|
+
* `get`/`update` interface `./grant-flow.ts` already defines, which a real
|
|
14
|
+
* `vscode.Memento` and a plain in-memory fake both satisfy), so this stays
|
|
15
|
+
* plain TypeScript vitest can run directly.
|
|
16
|
+
*/
|
|
17
|
+
import type { RunTrigger } from '@markii/runtime';
|
|
18
|
+
import type { GrantMemento, Thenable } from './grant-flow.js';
|
|
19
|
+
/**
|
|
20
|
+
* One run's recorded outcome. `reason` is present only when `ok` is
|
|
21
|
+
* `false`, and is a short, human phrase (never a raw stack or the full
|
|
22
|
+
* caught error) — the same "quiet marker + tooltip, never an error dump"
|
|
23
|
+
* posture every other failure surface in this codebase follows.
|
|
24
|
+
*/
|
|
25
|
+
export interface RunTrace {
|
|
26
|
+
readonly trigger: RunTrigger;
|
|
27
|
+
/** Epoch milliseconds this run completed (or failed) at. */
|
|
28
|
+
readonly ranAt: number;
|
|
29
|
+
readonly ok: boolean;
|
|
30
|
+
readonly reason?: string;
|
|
31
|
+
}
|
|
32
|
+
/** The `workspaceState`/Memento key a document's last-run trace lives under, mirroring `./run-flow.ts`'s `valuesStorageKeyFor`/`cacheStorageKeyFor` naming. */
|
|
33
|
+
export declare function lastRunStorageKeyFor(documentKey: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* A plausible `RunTrace` shape read back from storage — never trusted
|
|
36
|
+
* further than this, matching `./run-flow.ts`'s `isStoredValueRecord`'s own
|
|
37
|
+
* fail-safe posture: a corrupt or foreign persisted value degrades to "no
|
|
38
|
+
* trace", not a thrown error.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isRunTrace(value: unknown): value is RunTrace;
|
|
41
|
+
/** Reads a document's persisted last-run trace, or `undefined` when there is none / it is unusable. Never throws. */
|
|
42
|
+
export declare function readLastRunTrace(memento: GrantMemento, documentKey: string): RunTrace | undefined;
|
|
43
|
+
/** Persists `trace` as `documentKey`'s last-run outcome, superseding whatever was there before. */
|
|
44
|
+
export declare function writeLastRunTrace(memento: GrantMemento, documentKey: string, trace: RunTrace): Thenable<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The `workspaceState`/Memento key a document's last-run trace lives under, mirroring `./run-flow.ts`'s `valuesStorageKeyFor`/`cacheStorageKeyFor` naming. */
|
|
2
|
+
export function lastRunStorageKeyFor(documentKey) {
|
|
3
|
+
return `markii.lastRun:${documentKey}`;
|
|
4
|
+
}
|
|
5
|
+
function isValidTrigger(value) {
|
|
6
|
+
return value === 'manual' || value === 'auto' || value === 'scheduled';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A plausible `RunTrace` shape read back from storage — never trusted
|
|
10
|
+
* further than this, matching `./run-flow.ts`'s `isStoredValueRecord`'s own
|
|
11
|
+
* fail-safe posture: a corrupt or foreign persisted value degrades to "no
|
|
12
|
+
* trace", not a thrown error.
|
|
13
|
+
*/
|
|
14
|
+
export function isRunTrace(value) {
|
|
15
|
+
if (typeof value !== 'object' || value === null)
|
|
16
|
+
return false;
|
|
17
|
+
const v = value;
|
|
18
|
+
if (!isValidTrigger(v.trigger))
|
|
19
|
+
return false;
|
|
20
|
+
if (typeof v.ranAt !== 'number' || !Number.isFinite(v.ranAt))
|
|
21
|
+
return false;
|
|
22
|
+
if (typeof v.ok !== 'boolean')
|
|
23
|
+
return false;
|
|
24
|
+
if (v.reason !== undefined && typeof v.reason !== 'string')
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
/** Reads a document's persisted last-run trace, or `undefined` when there is none / it is unusable. Never throws. */
|
|
29
|
+
export function readLastRunTrace(memento, documentKey) {
|
|
30
|
+
const raw = memento.get(lastRunStorageKeyFor(documentKey));
|
|
31
|
+
return isRunTrace(raw) ? raw : undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Persists `trace` as `documentKey`'s last-run outcome, superseding whatever was there before. */
|
|
34
|
+
export function writeLastRunTrace(memento, documentKey, trace) {
|
|
35
|
+
return memento.update(lastRunStorageKeyFor(documentKey), trace);
|
|
36
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type ScriptBlock } from '@markii/core';
|
|
2
|
+
import type { GrantClosureScript } from '@markii/runtime';
|
|
3
|
+
/** One document's static run requirements. */
|
|
4
|
+
export interface RunRequirements {
|
|
5
|
+
/** Every script block found, in document order — `@markii/core`'s `extractScripts` result, unmodified. */
|
|
6
|
+
scripts: ScriptBlock[];
|
|
7
|
+
/**
|
|
8
|
+
* Hostnames this run's scripts are statically known to reach via
|
|
9
|
+
* `net.*`, deduplicated, in first-seen (document) order. Does NOT
|
|
10
|
+
* include hosts that could only be discovered by actually running the
|
|
11
|
+
* script (see `hasUnknownHosts`).
|
|
12
|
+
*/
|
|
13
|
+
hosts: string[];
|
|
14
|
+
/**
|
|
15
|
+
* `true` when at least one `net.fetch_json`/`net.post`/`net.patch` call
|
|
16
|
+
* site's URL argument could not be resolved to a literal (a
|
|
17
|
+
* concatenation, a variable, any non-literal expression), OR when any
|
|
18
|
+
* script block is a `src=` reference (its actual Lua source lives in a
|
|
19
|
+
* bundle file this function never reads, so its `net.*` usage — if any —
|
|
20
|
+
* is entirely unknown). The caller should read this as "prompt for
|
|
21
|
+
* network access more broadly" rather than trusting `hosts` alone to be
|
|
22
|
+
* the complete set.
|
|
23
|
+
*/
|
|
24
|
+
hasUnknownHosts: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* `scripts` reshaped into `@markii/runtime`'s `GrantClosureScript` — the
|
|
27
|
+
* exact per-script record `computeGrantKey`'s `GrantClosure.scripts`
|
|
28
|
+
* field expects (`{ name, lang, src, code }`). `computeGrantKey` itself
|
|
29
|
+
* sorts records by their own encoded bytes before hashing, so array
|
|
30
|
+
* order here does not affect the resulting key — this is already in
|
|
31
|
+
* document order (from `extractScripts`), which is what makes repeated
|
|
32
|
+
* extraction over the same text byte-identical, satisfying the "stable
|
|
33
|
+
* order" requirement without this module needing to reimplement any of
|
|
34
|
+
* `computeGrantKey`'s own sorting.
|
|
35
|
+
*/
|
|
36
|
+
grantScripts: GrantClosureScript[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Bare, already-lowercased hostnames every script block in `scripts`
|
|
40
|
+
* declares via its own `permissions` fence attribute
|
|
41
|
+
* (`@markii/core`'s `ScriptBlock.permissions`), deduplicated, in
|
|
42
|
+
* first-seen (document, then per-script list) order. Mirrors
|
|
43
|
+
* `./bundle-run.ts`'s `manifestNetHosts` for the bundle-manifest
|
|
44
|
+
* equivalent: this is DECLARED INTENT only, exactly like a manifest's
|
|
45
|
+
* `permissions.net` — `scanRunRequirementHosts` above stays the ONLY
|
|
46
|
+
* source of the hostnames a run is actually prompted for. A caller
|
|
47
|
+
* compares this against the scanned set with
|
|
48
|
+
* `./bundle-run.ts`'s `netDeclarationDiagnostics` (the same
|
|
49
|
+
* declared-vs-scanned diff a bundle manifest already gets), and may show
|
|
50
|
+
* it next to the scanned hosts in a grant prompt as "declares: ...".
|
|
51
|
+
*/
|
|
52
|
+
export declare function scriptDeclaredHosts(scripts: readonly ScriptBlock[]): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Parses `text` and returns its script blocks, in both the raw
|
|
55
|
+
* `@markii/core` `ScriptBlock` shape and the `GrantClosureScript` shape
|
|
56
|
+
* `computeGrantKey` hashes. Split out from `extractRunRequirements` (below)
|
|
57
|
+
* so a caller that needs to resolve `src=` script content BEFORE scanning
|
|
58
|
+
* for hosts — a bundle run, which must read the referenced file out of its
|
|
59
|
+
* snapshot first — can do so without parsing the document twice.
|
|
60
|
+
*/
|
|
61
|
+
export declare function extractScriptBlocks(text: string): {
|
|
62
|
+
scripts: ScriptBlock[];
|
|
63
|
+
grantScripts: GrantClosureScript[];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Resolves a `src=` block's actual Lua source, when the caller has one —
|
|
67
|
+
* for a bundle-backed run, this is the file's content out of the run's
|
|
68
|
+
* snapshot (`./bundle-run.ts`'s `bundleModulesFromSnapshot`). Returning
|
|
69
|
+
* `undefined` for a block (path missing from the snapshot, or a plain
|
|
70
|
+
* `.mk.md` document with no bundle to resolve from at all) leaves that
|
|
71
|
+
* block's hosts unknown, exactly as before this seam existed.
|
|
72
|
+
*/
|
|
73
|
+
export type ResolveScriptSource = (block: ScriptBlock) => string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Scans `scripts` for statically-resolvable `net.*` hosts — an INLINE
|
|
76
|
+
* block's own `code`, or (when `resolveScriptSource` is given and resolves
|
|
77
|
+
* it) a `src=` block's referenced source. A `src=` block with no resolution
|
|
78
|
+
* available folds into `hasUnknownHosts`, same as this module has always
|
|
79
|
+
* done for a bare `.mk.md` document's `src=` references (their source
|
|
80
|
+
* genuinely isn't in `text` at all).
|
|
81
|
+
*/
|
|
82
|
+
export declare function scanRunRequirementHosts(scripts: readonly ScriptBlock[], resolveScriptSource?: ResolveScriptSource): {
|
|
83
|
+
hosts: string[];
|
|
84
|
+
hasUnknownHosts: boolean;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Computes `RunRequirements` for `text` — parses it with `@markii/core`'s
|
|
88
|
+
* `parse`, pulls its script blocks with `extractScripts`, and scans each
|
|
89
|
+
* INLINE block's own source for statically-resolvable `net.*` hosts. Pure:
|
|
90
|
+
* no I/O, no `vscode`, safe to call on every keystroke if a future slice
|
|
91
|
+
* wants a live preview of what a run would ask for.
|
|
92
|
+
*
|
|
93
|
+
* `resolveScriptSource`, when given, lets a `src=` block's hosts be
|
|
94
|
+
* resolved too (a bundle run passes one backed by its snapshot); omitted,
|
|
95
|
+
* every `src=` block folds into `hasUnknownHosts` exactly as before this
|
|
96
|
+
* option existed.
|
|
97
|
+
*/
|
|
98
|
+
export declare function extractRunRequirements(text: string, resolveScriptSource?: ResolveScriptSource): RunRequirements;
|