@markii/host 0.17.0-next.2 → 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.
Files changed (45) hide show
  1. package/README.md +4 -2
  2. package/dist/browser-worker.js +36 -36
  3. package/dist/browser.d.ts +6 -2
  4. package/dist/browser.js +7 -2
  5. package/dist/host/adapter.d.ts +18 -23
  6. package/dist/host/conformance-runner.d.ts +4 -1
  7. package/dist/host/conformance-runner.js +4 -6
  8. package/dist/host/create-host.d.ts +4 -6
  9. package/dist/host/editor-behavior.d.ts +2 -6
  10. package/dist/host/editor-behavior.js +5 -9
  11. package/dist/host/export-behavior.d.ts +4 -6
  12. package/dist/host/labels.d.ts +1 -2
  13. package/dist/host/pack-archive.js +2 -8
  14. package/dist/host/pack-install.d.ts +1 -2
  15. package/dist/host/pack-install.js +4 -7
  16. package/dist/host/pack-load.d.ts +1 -2
  17. package/dist/host/pack-trust-behavior.js +1 -1
  18. package/dist/host/refresh-interval.d.ts +1 -3
  19. package/dist/host/refresh-interval.js +1 -3
  20. package/dist/host/run-behavior.d.ts +3 -4
  21. package/dist/host/run-behavior.js +23 -2
  22. package/dist/host/script-execution.d.ts +1 -1
  23. package/dist/index.js +1 -2
  24. package/dist/node-worker.js +20657 -0
  25. package/dist/node.d.ts +2 -1
  26. package/dist/node.js +1 -1
  27. package/dist/packs/discover.js +1 -1
  28. package/dist/packs/pack-diagnostics.d.ts +6 -8
  29. package/dist/packs/pack-diagnostics.js +6 -8
  30. package/dist/packs/pack-paths.js +1 -2
  31. package/dist/packs/pack-render-registry.d.ts +9 -10
  32. package/dist/run/browser-isolate.d.ts +5 -5
  33. package/dist/run/browser-isolate.js +5 -1
  34. package/dist/run/isolate.d.ts +3 -1
  35. package/dist/run/net-bridge.d.ts +5 -5
  36. package/dist/run/net-provider.d.ts +3 -1
  37. package/dist/run/net-provider.js +20 -6
  38. package/dist/run/node-isolate.d.ts +13 -21
  39. package/dist/run/node-isolate.js +31 -80
  40. package/dist/run/run-flow.d.ts +1 -2
  41. package/dist/run/run-host.d.ts +3 -3
  42. package/dist/run/worker-entry-browser.d.ts +24 -4
  43. package/dist/run/worker-entry-browser.js +99 -4
  44. package/dist/run/worker-entry.js +2 -0
  45. package/package.json +14 -11
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { NetProvider } from '@markii/lua';
2
2
  import type { HostIsolate } from './host/adapter.js';
3
3
  /** Builds the provider for one run from the job's host allowlist, byte cap and pin policy (the policy is meaningless in a browser and ignored). */
4
- export type BrowserNetProviderFactory = (netAllowlist: string[], maxFetchBytes: number, netPolicy: unknown) => NetProvider;
4
+ export type BrowserNetProviderFactory = (netAllowlist: string[], maxFetchBytes: number, netPolicy: unknown,
5
+ /** Aborted when the isolate is killed or disposed. A provider cancels its in-flight requests on it. */
6
+ signal?: AbortSignal) => NetProvider;
5
7
  export interface FetchNetProviderOptions {
6
8
  /**
7
9
  * The origins (`https://api.example.com`, `http://127.0.0.1:8080`) a
@@ -33,7 +35,9 @@ export interface FetchNetProviderOptions {
33
35
  * manual redirect's target, so it cannot be checked against the allowlist,
34
36
  * and a redirect is therefore a denial.
35
37
  *
36
- * Returns a factory because the byte cap arrives per job.
38
+ * Returns a factory because the byte cap arrives per job. Every request
39
+ * carries the run's `AbortSignal`, so killing the isolate cancels requests
40
+ * still in flight.
37
41
  */
38
42
  export declare function createFetchNetProvider(options?: FetchNetProviderOptions): BrowserNetProviderFactory;
39
43
  export interface BrowserHostIsolateOptions {
package/dist/browser.js CHANGED
@@ -74,14 +74,16 @@ async function readCapped(response, maxBytes, denial) {
74
74
  * manual redirect's target, so it cannot be checked against the allowlist,
75
75
  * and a redirect is therefore a denial.
76
76
  *
77
- * Returns a factory because the byte cap arrives per job.
77
+ * Returns a factory because the byte cap arrives per job. Every request
78
+ * carries the run's `AbortSignal`, so killing the isolate cancels requests
79
+ * still in flight.
78
80
  */
79
81
  export function createFetchNetProvider(options = {}) {
80
82
  const allowed = new Set((options.allowedOrigins ?? [])
81
83
  .map(originOf)
82
84
  .filter((origin) => origin !== undefined));
83
85
  const denial = options.denial ?? ((message) => new Error(message));
84
- return (netAllowlist, maxFetchBytes) => {
86
+ return (netAllowlist, maxFetchBytes, _policy, signal) => {
85
87
  // The run's granted hosts, re-checked here on the page side: the worker
86
88
  // checks them too, but the worker is the isolate a hostile script runs
87
89
  // in, so the page is the boundary (the same rule the Node provider
@@ -107,6 +109,8 @@ export function createFetchNetProvider(options = {}) {
107
109
  if (!allowed.has(url.origin)) {
108
110
  throw denial(`origin "${url.origin}" is not on the host's allowlist`);
109
111
  }
112
+ if (signal?.aborted)
113
+ throw denial('the run has ended');
110
114
  const doFetch = options.fetch ?? fetch;
111
115
  let response;
112
116
  try {
@@ -118,6 +122,7 @@ export function createFetchNetProvider(options = {}) {
118
122
  redirect: 'manual',
119
123
  credentials: 'omit',
120
124
  referrerPolicy: 'no-referrer',
125
+ ...(signal !== undefined ? { signal } : {}),
121
126
  });
122
127
  }
123
128
  catch (error) {
@@ -3,8 +3,7 @@
3
3
  * the CLI, or a future one) provides so `createMarkiiHost` can run every
4
4
  * shared behavior once. Nothing in this file names a `vscode`, `obsidian`,
5
5
  * Electron or TTY type — an adapter is built from injected dependencies,
6
- * so a test can construct the REAL adapter over fakes (batch 11's
7
- * `tmp/W11-adapter-design.md`, section 2).
6
+ * so a test can construct the REAL adapter over fakes.
8
7
  *
9
8
  * A capability group (`isolate`, `editor`, `packs`, `exports`) is absent
10
9
  * when a host does not offer it. `createMarkiiHost` never assumes one is
@@ -23,9 +22,7 @@ export interface HostDirEntry {
23
22
  }
24
23
  /**
25
24
  * One yes/no question. Every grant, consent, and replace prompt in the
26
- * whole app surface routes through this single method (survey finding A1:
27
- * nine near-identical prompt adapters across three apps collapse to one
28
- * call each). `message`, `allowLabel`, and `denyLabel` are already fully
25
+ * whole app surface routes through this single method. `message`, `allowLabel`, and `denyLabel` are already fully
29
26
  * built by `@markii/host`; the adapter only has to show them.
30
27
  */
31
28
  export interface HostPromptRequest {
@@ -43,7 +40,7 @@ export interface HostPromptRequest {
43
40
  }
44
41
  /**
45
42
  * A discriminated union: `workerPath` and `workerBytes` are mutually
46
- * exclusive per host (survey section C). A Node host (VS Code, the CLI)
43
+ * exclusive per host. A Node host (VS Code, the CLI)
47
44
  * gives a synchronous path lookup; a host whose runtime cannot create
48
45
  * worker threads (an Electron renderer, which is what the Obsidian plugin
49
46
  * runs in) gives an async, disposable Web Worker spawner instead.
@@ -51,15 +48,14 @@ export interface HostPromptRequest {
51
48
  export type HostIsolate = {
52
49
  readonly kind: 'node';
53
50
  /**
54
- * The worker entry on disk. A host with a packaged worker resolves it
55
- * here; a development or test run falls back to
56
- * `defaultWorkerPath()` from `@markii/host/node`, in the host's own
57
- * adapter (the shared run path has no Node dependency to do it).
51
+ * The worker entry on disk. A host with its own bundled worker
52
+ * resolves it here. `createNodeHostIsolate()` from
53
+ * `@markii/host/node` supplies the packaged one.
58
54
  */
59
55
  workerPath(): string;
60
56
  /**
61
- * Creates the worker-thread isolate. Always `workerThreadIsolate`
62
- * from `@markii/host/node`; injected so the shared run path imports
57
+ * Creates the worker-thread isolate: `workerThreadIsolate` from
58
+ * `@markii/host/node`. Injected so the shared run path imports
63
59
  * nothing from `node:*`.
64
60
  */
65
61
  readonly spawnIsolate: IsolateSpawner;
@@ -106,7 +102,7 @@ export interface HostEditor {
106
102
  }
107
103
  /**
108
104
  * Pack POLICY (which folders are even candidates) is irreducibly
109
- * host-specific (survey section D); everything after the list exists is
105
+ * host-specific; everything after the list exists is
110
106
  * already shared in `../packs/discover.ts` and friends. The adapter
111
107
  * supplies only the list and the install destination.
112
108
  */
@@ -124,12 +120,13 @@ export interface HostPackSource {
124
120
  * `'device-list'`: a folder in `installRoot` loads only when the trust
125
121
  * list in the adapter's `memento` names its namespace, and the host
126
122
  * gains `installedPacks`, `enablePack` and `disablePack`. An install
127
- * trusts the pack it installs. Requires `installRoot`. `'none'` (or
128
- * absent): every folder this host's own policy authorizes loads, which
129
- * is right for a host that trusts its own install store (VS Code, the
130
- * CLI). A host states its choice explicitly.
123
+ * trusts the pack it installs. Requires `installRoot`. `'none'`: every
124
+ * folder this host's own policy authorizes loads, which is right for a
125
+ * host that trusts its own install store (VS Code, the CLI). A web host
126
+ * that installs packs from users picks `'device-list'`. Required: a
127
+ * host states its choice explicitly.
131
128
  */
132
- readonly trust?: 'device-list' | 'none';
129
+ readonly trust: 'device-list' | 'none';
133
130
  /**
134
131
  * Folders this host currently AUTHORIZES. Under `trust: 'device-list'`
135
132
  * these are the folders beyond the install root (the host adds the
@@ -167,10 +164,8 @@ export interface HostExportCapabilities {
167
164
  }
168
165
  /**
169
166
  * The wording nouns every shared behavior's message templates interpolate
170
- * (`./labels.ts`). This is the one answer to the survey's wording-drift
171
- * risk: near-identical copy across the three apps collapses into one
172
- * template plus these per-host nouns, instead of a per-host
173
- * reimplementation of the sentence itself.
167
+ * (`./labels.ts`). One template plus these per-host nouns
168
+ * replaces a per-host copy of each sentence.
174
169
  */
175
170
  export interface HostLabels {
176
171
  /** "extension" | "plugin" | "command line tool" */
@@ -196,7 +191,7 @@ export interface HostLabels {
196
191
  * Everything an adapter provides. An adapter is constructible from
197
192
  * injected dependencies (no module-scope global reads), which is what
198
193
  * lets a conformance test build the REAL adapter over a fake editor
199
- * (batch 11 Phase 3).
194
+ *.
200
195
  */
201
196
  export interface HostAdapter {
202
197
  readonly id: 'vscode' | 'obsidian' | 'cli' | (string & {});
@@ -1,3 +1,4 @@
1
+ import type { ArchiveExtractFs } from './pack-archive.js';
1
2
  import type { RunTrigger } from '@markii/runtime';
2
3
  import type { HostAdapter } from './adapter.js';
3
4
  /**
@@ -110,7 +111,7 @@ export declare function hostDeclaresCapability(adapter: HostAdapter, action: Hos
110
111
  * `next()` THROWS once exhausted rather than returning a default: an
111
112
  * unanswered prompt means the scenario and the implementation disagree
112
113
  * about how many questions get asked, which must never be silently read
113
- * as a deny (`tmp/W11-corpus-design.md`, "The prompt policy").
114
+ * as a deny.
114
115
  */
115
116
  export declare function createAnswerQueue(scenario: HostScenario): {
116
117
  next(): boolean;
@@ -134,6 +135,8 @@ export interface HostScenarioRunResult {
134
135
  */
135
136
  export declare function runHostScenario(adapter: HostAdapter, scenario: HostScenario, noteText: string, scenarioDir: string, options?: {
136
137
  readonly renderAnsi?: (text: string) => Promise<string>;
138
+ /** Where an installed pack archive is extracted. Defaults to the Node file system, which is right for an adapter that reads and writes real files. An adapter over another store (memory, a virtual file system) passes the matching writer. */
139
+ readonly archiveExtractFs?: ArchiveExtractFs;
137
140
  }): Promise<HostScenarioRunResult>;
138
141
  /** `expected[i]` compared against `actual[i]` for each index up to `expected`'s own length; a length mismatch is reported once rather than index-by-index noise. */
139
142
  export declare function containsInOrder(actual: readonly string[], expected: readonly string[]): string[];
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Executes one `conformance/host/<scenario>` case (batch 11 Phase 3,
3
- * `tmp/W11-corpus-design.md`) against any real `HostAdapter`, so a single
2
+ * Executes one `conformance/host/<scenario>` case against any real `HostAdapter`, so a single
4
3
  * plain-data corpus proves the three apps' adapters behave the same.
5
4
  *
6
5
  * `conformance/host/` itself stays plain data (no TypeScript, matching the
@@ -26,8 +25,7 @@
26
25
  * pre-check here, over the same shared functions and the SAME
27
26
  * `adapter.labels` a real call site would use, so the scenario proves
28
27
  * wording parity across hosts without inventing a capability the contract
29
- * does not have. See `tmp/W11-phase3.md` for why this is a reported
30
- * finding, not a silent workaround.
28
+ * does not have. The scenario `10-scripts-disabled` holds the behavior.
31
29
  *
32
30
  * SIMULATED EXECUTION. A `run` action's `simulate` field (plain data) tells
33
31
  * this module what the isolate would have produced, instead of spawning a
@@ -162,7 +160,7 @@ export function hostDeclaresCapability(adapter, action) {
162
160
  * `next()` THROWS once exhausted rather than returning a default: an
163
161
  * unanswered prompt means the scenario and the implementation disagree
164
162
  * about how many questions get asked, which must never be silently read
165
- * as a deny (`tmp/W11-corpus-design.md`, "The prompt policy").
163
+ * as a deny.
166
164
  */
167
165
  export function createAnswerQueue(scenario) {
168
166
  const answers = [];
@@ -264,7 +262,7 @@ export async function runHostScenario(adapter, scenario, noteText, scenarioDir,
264
262
  // The runner is Node-only test tooling, so it supplies the real Node
265
263
  // implementations of the seams the host layer takes by injection.
266
264
  htmlEngine: createHtmlExportEngine(),
267
- archiveExtractFs: createNodeArchiveExtractFs(),
265
+ archiveExtractFs: options.archiveExtractFs ?? createNodeArchiveExtractFs(),
268
266
  spawnRun: (spawnOptions) => Promise.resolve(simulatedRunResult(currentSimulation, spawnOptions)),
269
267
  ...(options.renderAnsi ? { renderAnsi: options.renderAnsi } : {}),
270
268
  });
@@ -3,13 +3,11 @@
3
3
  * its own run/export/pack/editor wiring. Everything it touches (files, prompts,
4
4
  * the isolate) arrives through the adapter, so the main entry stays
5
5
  * environment-neutral. VS Code's webview imports only the pure pieces and
6
- * never calls this function (`tmp/W11-adapter-design.md`, section 1).
6
+ * never calls this function.
7
7
  *
8
- * Phase 1a implemented `run`, `diagnostics`, and `open`. This phase
9
- * (1b) fills in `exportNote`, `installPack`, `loadPacks`, `completeAt`,
10
- * `hoverAt`, and `insertComponent`, over the behavior modules built in
11
- * this same pass (`./export-behavior.js`, `./pack-install.js`,
12
- * `./pack-load.js`, `./editor-behavior.js`). Every one of them still
8
+ * It provides `run`, `diagnostics`, `open`, `exportNote`, `installPack`, `loadPacks`, `completeAt`,
9
+ * `hoverAt`, and `insertComponent`, over the behavior modules (`./export-behavior.js`, `./pack-install.js`,
10
+ * `./pack-load.js`, `./editor-behavior.js`). Every one of them
13
11
  * returns the shared `Unsupported` outcome, with one diagnostics line,
14
12
  * when the adapter lacks the capability it needs — AGENTS.md's "clean is
15
13
  * not silent": a decline is never a silent no-op.
@@ -4,12 +4,8 @@ import type { HostEditor, HostTextEdit } from './adapter.js';
4
4
  export declare const STANDARD_ORIGIN_TAG = "standard";
5
5
  export declare const LAYOUT_ORIGIN_TAG = "layout";
6
6
  /**
7
- * Survey finding A5: `apps/vscode/src/completion.ts:completionOriginTag`
8
- * and `apps/obsidian/src/complete-component.ts:completionOriginTag` had
9
- * IDENTICAL function bodies, differing only in which locally-defined
10
- * constant stood in for the standard/layout tag string. Both apps'
11
- * constants held the same lower-case values (`'standard'`/`'layout'`),
12
- * so there was never a wording difference to preserve.
7
+ * The origin tag a completion item carries: the lower-case
8
+ * `'standard'`/`'layout'` strings above, or the pack name.
13
9
  */
14
10
  export declare function completionOriginTag(item: CompletionItem): string;
15
11
  /** A small cache in front of `buildComponentCatalog` (`../insert/component-catalog.js`), so a completion/hover provider does not re-discover packs from disk on every keystroke. */
@@ -2,16 +2,16 @@
2
2
  * The `completeAt`/`hoverAt`/`insertComponent` behaviors' shared half,
3
3
  * merged from `apps/vscode/src/completion.ts` + `completion-catalog.ts`,
4
4
  * `apps/obsidian/src/complete-component.ts`, and both apps'
5
- * `insert-component.ts` (survey finding A5, plus table rows 31/33).
5
+ * `insert-component.ts`.
6
6
  *
7
- * NODE-FREE by design (`tmp/W11-adapter-design.md`, section 1): re-exported
7
+ * NODE-FREE by design: re-exported
8
8
  * from `../browser.ts` as well as `../index.ts`, so VS Code's webview
9
9
  * bundle can read the origin-tag vocabulary and the pure completion math
10
10
  * directly if a future slice needs to. `CatalogCache.get`'s injected
11
11
  * `load` is the one thing that reaches disk, and it is always supplied by
12
12
  * the caller (a main-entry-only pack-discovery call), never imported here.
13
13
  *
14
- * What stays app-side (deliberately, per the survey's section D): VS
14
+ * What stays app-side (deliberately): VS
15
15
  * Code's sectioned quick-pick presentation (`Standard`/`Layout`/`<pack>
16
16
  * pack` separators) and Obsidian's flat fuzzy list are a genuine product
17
17
  * difference, not duplication — this module returns the catalog and the
@@ -25,12 +25,8 @@ import { closesOpenContainerFence, completionAt, componentDocumentation, fenceEx
25
25
  export const STANDARD_ORIGIN_TAG = 'standard';
26
26
  export const LAYOUT_ORIGIN_TAG = 'layout';
27
27
  /**
28
- * Survey finding A5: `apps/vscode/src/completion.ts:completionOriginTag`
29
- * and `apps/obsidian/src/complete-component.ts:completionOriginTag` had
30
- * IDENTICAL function bodies, differing only in which locally-defined
31
- * constant stood in for the standard/layout tag string. Both apps'
32
- * constants held the same lower-case values (`'standard'`/`'layout'`),
33
- * so there was never a wording difference to preserve.
28
+ * The origin tag a completion item carries: the lower-case
29
+ * `'standard'`/`'layout'` strings above, or the pack name.
34
30
  */
35
31
  export function completionOriginTag(item) {
36
32
  if (item.kind !== 'component')
@@ -2,7 +2,7 @@
2
2
  * The `exportNote` behavior: outcome shape, wording, and format gating,
3
3
  * merged from `apps/vscode/src/export-html.ts`, the host-neutral half of
4
4
  * `apps/obsidian/src/export-note.ts`, and `apps/cli/src/export-note.ts`'s
5
- * `exportHtml` (survey finding A10, table rows 22/26).
5
+ * `exportHtml`.
6
6
  *
7
7
  * `html` and `md-plain` are fully implemented here, over `../export/note-export.js`'s
8
8
  * `buildNoteExport` (already host-neutral, already shared) and
@@ -12,20 +12,18 @@
12
12
  * AGENTS.md's package boundaries), so it is rendered through the injected
13
13
  * `renderAnsi` option; a host that omits it gets `Unsupported`.
14
14
  *
15
- * PDF stays a seam, never an implementation here, exactly as
16
- * `tmp/W11-adapter-design.md` specifies: this module builds the same HTML
15
+ * PDF stays a seam, never an implementation here, this module builds the same HTML
17
16
  * document `html` would have written and hands it to
18
17
  * `adapter.exports.htmlToPdf`, but the DEGRADE-TO-HTML-ON-FAILURE product
19
18
  * behavior Obsidian's own `exportNoteAsPdf` layers on top (falling back to
20
19
  * writing the HTML file when printing is unavailable or fails, and
21
20
  * reporting which of the two happened) is Obsidian-specific policy about
22
21
  * what to do with a seam's failure, not the seam itself — it stays
23
- * app-side. See `tmp/W11-phase1b.md` for this call.
22
+ * app-side.
24
23
  *
25
24
  * Cascade orchestration (multi-note export, `walkNoteCascade` +
26
25
  * `assignCascadeFileNames` + `rewriteCascadeLinks` + `zipExportArchive`)
27
- * is NOT moved in this pass — see `tmp/W11-phase1b.md`'s deviations
28
- * section for why, and what Phase 2 (or a follow-up) still needs to do.
26
+ * is not part of this module: it stays with each host.
29
27
  */
30
28
  import type { StoredValue } from '@markii/runtime';
31
29
  import type { OnDiagnostic, UnavailableComponent } from '@markii/stdlib';
@@ -3,8 +3,7 @@
3
3
  * (`./script-execution.ts`, `./refresh-interval.ts`) interpolates, and the
4
4
  * one concrete record per app this repo ships. Each app's `HostAdapter`
5
5
  * imports its record rather than inventing its own strings, which is what
6
- * collapses the survey's near-identical wording pairs (`tmp/W11-survey.md`,
7
- * section A4) into one template.
6
+ * lets one template serve every host.
8
7
  *
9
8
  * Pure and dependency-free: re-exported from `../browser.ts` as well as
10
9
  * `../index.ts` so VS Code's Node-free webview bundle can read it too.
@@ -1,13 +1,7 @@
1
1
  /**
2
2
  * `.mkp` pack archive handling, merged from `apps/vscode/src/packs/archive-packs.ts`
3
- * and `apps/obsidian/src/packs/archive-packs.ts` (survey finding A8). The
4
- * orchestrator diffed the two files directly (not through the survey's
5
- * earlier, unverified guess) and found them functionally byte-identical:
6
- * the same three exports (`describeArchiveError`, `writeArchiveContents`,
7
- * `ArchiveExtractFs`), the same implementation, the same path-jail
8
- * reasoning. They differed only in doc-comment wording (which host's
9
- * install command they described) and one em dash in a comment, which
10
- * carries no behavior. See `tmp/W11-phase1b.md` for the exact diff output.
3
+ * and `apps/obsidian/src/packs/archive-packs.ts`. It exports
4
+ * `describeArchiveError`, `writeArchiveContents` and `ArchiveExtractFs`.
11
5
  *
12
6
  * This module bridges `@markii/pack`'s in-memory archive reader
13
7
  * (`openPackArchive`, never modified here) into the write a pack install
@@ -67,8 +67,7 @@ export declare function installConsentMessage(packName: string, labels: HostLabe
67
67
  /**
68
68
  * The collision confirmation's wording: a namespace already installed,
69
69
  * asked before replacing it. CANONICAL WORDING: "removes" (VS Code's prior
70
- * wording) over Obsidian's prior "deletes" — see `tmp/W11-phase1b.md`'s
71
- * before/after table for the exact strings this replaces.
70
+ * wording) over Obsidian's prior "deletes".
72
71
  */
73
72
  export declare function installReplaceConfirmMessage(packName: string): string;
74
73
  /**
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * The `.mkp` pack-install decision logic, merged from
3
3
  * `apps/vscode/src/packs/install-pack.ts` and
4
- * `apps/obsidian/src/packs/install-pack.ts` (survey finding A2: the single
5
- * biggest duplication finding in the survey — identical function names,
6
- * identical types, near-identical wording, differing only in that
7
- * Obsidian refuses a bundled-namespace archive before the consent prompt
8
- * and VS Code has no such concept).
4
+ * `apps/obsidian/src/packs/install-pack.ts`. Obsidian refuses a
5
+ * bundled-namespace archive before the consent prompt and VS Code has no
6
+ * such concept.
9
7
  *
10
8
  * `reservedNamespaces` generalizes Obsidian's `bundledNamespaces`: it
11
9
  * defaults to empty, so VS Code's behavior is byte-identical to before
@@ -131,8 +129,7 @@ export function installConsentMessage(packName, labels) {
131
129
  /**
132
130
  * The collision confirmation's wording: a namespace already installed,
133
131
  * asked before replacing it. CANONICAL WORDING: "removes" (VS Code's prior
134
- * wording) over Obsidian's prior "deletes" — see `tmp/W11-phase1b.md`'s
135
- * before/after table for the exact strings this replaces.
132
+ * wording) over Obsidian's prior "deletes".
136
133
  */
137
134
  export function installReplaceConfirmMessage(packName) {
138
135
  return `A pack named "${packName}" is already installed. Replacing it removes the installed copy and cannot be undone.`;
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * The shared discovery path behind `loadPacks`, merged from
3
3
  * `apps/vscode/src/packs/discover-configured-packs.ts` and
4
- * `apps/obsidian/src/packs/discover-configured-packs.ts` (survey finding
5
- * A3) plus the discovery-and-namespace-collection half both apps'
4
+ * `apps/obsidian/src/packs/discover-configured-packs.ts` plus the discovery-and-namespace-collection half both apps'
6
5
  * `packs/pack-context.ts` also duplicate on top of it.
7
6
  *
8
7
  * Pack POLICY — which folders are even candidates before any content is
@@ -12,7 +12,7 @@
12
12
  * already asked), enabling a present folder asks the same prompt, and
13
13
  * disabling or removing withdraws the entry.
14
14
  *
15
- * A host that declares `trust: 'none'` (or omits the field) trusts every
15
+ * A host that declares `trust: 'none'` trusts every
16
16
  * folder its own policy authorizes, and every function here is inert for
17
17
  * it. Neutral on purpose: only the adapter's file and state primitives
18
18
  * are used.
@@ -3,9 +3,7 @@
3
3
  * merged from `apps/vscode/src/refresh-interval.ts` (the input-box
4
4
  * parse/validate half) and `apps/obsidian/src/local-settings.ts`'s
5
5
  * `refreshIntervalMsFromSeconds`/`MIN_REFRESH_INTERVAL_SECONDS` (the
6
- * read-side clamp half) — survey finding A7: the constant was duplicated
7
- * verbatim, with Obsidian's own comment noting it mirrors VS Code's, and
8
- * the clamp-not-reject policy was duplicated as both prose and behavior.
6
+ * read-side clamp half).
9
7
  *
10
8
  * The policy: a positive value under `MIN_REFRESH_INTERVAL_SECONDS` is
11
9
  * still ACCEPTED as typed (a host's own settings UI must not silently
@@ -3,9 +3,7 @@
3
3
  * merged from `apps/vscode/src/refresh-interval.ts` (the input-box
4
4
  * parse/validate half) and `apps/obsidian/src/local-settings.ts`'s
5
5
  * `refreshIntervalMsFromSeconds`/`MIN_REFRESH_INTERVAL_SECONDS` (the
6
- * read-side clamp half) — survey finding A7: the constant was duplicated
7
- * verbatim, with Obsidian's own comment noting it mirrors VS Code's, and
8
- * the clamp-not-reject policy was duplicated as both prose and behavior.
6
+ * read-side clamp half).
9
7
  *
10
8
  * The policy: a positive value under `MIN_REFRESH_INTERVAL_SECONDS` is
11
9
  * still ACCEPTED as typed (a host's own settings UI must not silently
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * The `run` behavior's host-shaped seams, built from a `HostAdapter`:
3
- * this is where survey findings A1 and A6 stop being duplicated three
4
- * times over.
3
+ * the grant prompts and the isolate spawn live here once for every host.
5
4
  *
6
5
  * A1: `apps/vscode/src/preview-panel.ts`'s `promptHostAdapter` /
7
6
  * `promptUnknownHostsAdapter` / `promptManyHostsAdapter`,
@@ -24,7 +23,7 @@
24
23
  * `runOnce` (the existing, already-shared grant-flow-plus-spawn
25
24
  * orchestration) and writes the `RunTrace` through `adapter.now()` rather
26
25
  * than `Date.now()`, so a fixed clock in a test fake makes the trace
27
- * byte-stable (batch 11 Phase 3's requirement).
26
+ * byte-stable.
28
27
  */
29
28
  import type { RunTrigger } from '@markii/runtime';
30
29
  import { type PromptHost, type PromptManyHosts, type PromptUnknownHosts } from '../run/grant-flow.js';
@@ -54,7 +53,7 @@ export declare function promptsFromAdapter(adapter: HostAdapter): {
54
53
  };
55
54
  /**
56
55
  * The one `spawnRun` adapter every Node host (`apps/vscode`, `apps/cli`)
57
- * used to duplicate (survey A6), plus the browser-isolate path Obsidian's
56
+ * needs, plus the browser-isolate path Obsidian's
58
57
  * own `spawnRun` wrapper needed. Dispatches on `HostIsolate`'s
59
58
  * discriminated union: a `'node'` host supplies only a worker path; a
60
59
  * `'browser'` host supplies an async, disposable `IsolateSpawner` factory,
@@ -63,9 +63,23 @@ function noIsolateResult(cacheSnapshot) {
63
63
  cacheSnapshot,
64
64
  };
65
65
  }
66
+ /** A synthetic `RunResult` for a node isolate whose worker file cannot be located. The run never throws. */
67
+ function workerMissingResult(cacheSnapshot, error) {
68
+ return {
69
+ values: {},
70
+ failures: [
71
+ {
72
+ name: '<document>',
73
+ message: `the script worker could not be located: ${error instanceof Error ? error.message : String(error)}`,
74
+ kind: 'capability-denied',
75
+ },
76
+ ],
77
+ cacheSnapshot,
78
+ };
79
+ }
66
80
  /**
67
81
  * The one `spawnRun` adapter every Node host (`apps/vscode`, `apps/cli`)
68
- * used to duplicate (survey A6), plus the browser-isolate path Obsidian's
82
+ * needs, plus the browser-isolate path Obsidian's
69
83
  * own `spawnRun` wrapper needed. Dispatches on `HostIsolate`'s
70
84
  * discriminated union: a `'node'` host supplies only a worker path; a
71
85
  * `'browser'` host supplies an async, disposable `IsolateSpawner` factory,
@@ -75,9 +89,16 @@ export async function spawnRunViaAdapter(isolate, options) {
75
89
  if (!isolate)
76
90
  return noIsolateResult(options.cacheSnapshot);
77
91
  if (isolate.kind === 'node') {
92
+ let workerPath;
93
+ try {
94
+ workerPath = isolate.workerPath();
95
+ }
96
+ catch (error) {
97
+ return workerMissingResult(options.cacheSnapshot, error);
98
+ }
78
99
  return spawnRunHost({
79
100
  ...options,
80
- workerPath: isolate.workerPath(),
101
+ workerPath,
81
102
  spawnIsolate: isolate.spawnIsolate,
82
103
  });
83
104
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The device-level script-execution switch and every sentence it can
3
3
  * produce, merged from `apps/vscode/src/script-execution.ts` and
4
- * `apps/obsidian/src/script-execution.ts` (survey finding A4: same file
4
+ * `apps/obsidian/src/script-execution.ts` (same file
5
5
  * name, same four exports, doc comments sharing the same "WHAT IT IS /
6
6
  * WHY DEVICE-LOCAL / WHY THE WORDING LIVES HERE" structure, differing
7
7
  * only in a handful of words).
package/dist/index.js CHANGED
@@ -4,8 +4,7 @@
4
4
  // `esbuild-wasm`, `@markii/react` or `@markii/html` at runtime
5
5
  // (`apps/vscode/src/browser-entry.probe.test.ts` bundles it for a browser to
6
6
  // prove it). What needs Node lives in `./node.ts`, published as
7
- // `@markii/host/node`. Private workspace package: never published,
8
- // never added to `build:dist`. See AGENTS.md's repo-layout entry for
7
+ // `@markii/host/node`. See AGENTS.md's repo-layout entry for
9
8
  // `packages/markii-host` for the module-by-module breakdown.
10
9
  //
11
10
  // This is the security-critical layer (tier gating, the grant model,