@miadi/inquiry-weave 0.6.9 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,327 @@
1
+ /**
2
+ * miadi-chronicle URI parsing, artifact registry, and episode resolution.
3
+ *
4
+ * Contract: rispecs/miadi-chronicle-dsl/SPEC.md (v1.1) and SPEC-TERMINAL.md
5
+ * (v1.2). Grounded in foundations/resource-addressing-and-terminal-hypermedia/.
6
+ *
7
+ * This is the app's three pure modules (`app/chronicle/lib/{dsl,resolveRef,
8
+ * inventoryRef}.ts`) brought into a package that already ships built ESM, so
9
+ * one resolver can answer the browser, the HTTP route, and a terminal. It
10
+ * stays pure — no fs, no fetch, no React. The catalog arrives as an argument.
11
+ *
12
+ * Two changes v1.1 does not have, both from the foundations packet:
13
+ *
14
+ * 1. ROOTLESS FORM. `//` is RFC 3986 §3.2's authority marker, so every
15
+ * conformant parser reads the first path segment as a host:
16
+ * new URL("miadi-chronicle://126") → host "126", pathname ""
17
+ * The most common reference has an EMPTY PATH, stock parsers disagree
18
+ * with this file, and SPEC §8's reserved `//root@…/` growth slot is
19
+ * unreachable because episode data already occupies it. Every non-HTTP
20
+ * scheme that survived — did:, urn:, ark:, swh:, tag:, info: — is
21
+ * rootless. So: `miadi-chronicle:092/126` is canonical, `//` is accepted
22
+ * forever on input and normalized away on output.
23
+ *
24
+ * 2. ARTIFACT REGISTRY. v1.1's "registry" was one const string compared by
25
+ * `===` in three places. It is a map now, with `state-machine` as the
26
+ * second entry.
27
+ */
28
+ export const MIADI_CHRONICLE_SCHEME = "miadi-chronicle:";
29
+ /** Rendering options folded into the URI query under the reserved prefix. */
30
+ export const OPTION_PREFIX = "mc.";
31
+ // Numbered groups: [1]=path [2]=query [3]=fragment.
32
+ //
33
+ // `(?:\/\/)?` is the leniency rule: producers SHOULD emit rootless, parsers
34
+ // MUST accept both. The scheme is case-insensitive per RFC 3986; the path is
35
+ // not.
36
+ //
37
+ // The trailing `\/?` tolerates and normalizes away a trailing slash — RFC 3986
38
+ // reads it as an empty final segment and people type it by reflex; refusing it
39
+ // degraded the reference to raw `{{ }}` braces with nothing explaining why
40
+ // (earned 2026-08-15, episode 103 on the film-media article).
41
+ const URI_RE = /^miadi-chronicle:(?:\/\/)?([A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*)\/?(?:\?([^#]*))?(?:#(.*))?$/i;
42
+ /**
43
+ * Reserved artifact segments — the registry SPEC §7.1 named itself after.
44
+ * Each key is a name no episode folder can carry (folder names are
45
+ * date-prefixed); each value is the artifact kind the resolver dispatches on.
46
+ */
47
+ export const ARTIFACT_SEGMENTS = {
48
+ "services-inventory": "inventory",
49
+ "state-machine": "state-machine",
50
+ // Wheel-backed kinds. These live in the medicine wheel's store rather than
51
+ // in the vessel, and every one of them is keyed by `episode_path` — the full
52
+ // folder name. Measured 2026-08-16: `?episode_number=320` returns 0 records
53
+ // for an episode whose `?episode_path=<folder>` returns 1, because the
54
+ // records carry no `episode_number` field and the filter compares strictly.
55
+ // The number is a nickname; the folder name is the key.
56
+ captures: "captures",
57
+ recordings: "captures", // deprecated alias on the wheel; same records
58
+ inquiry: "inquiry",
59
+ plans: "plans",
60
+ };
61
+ /** `mc.show=` values that name an artifact without using a path segment. */
62
+ const SHOW_TO_KIND = {
63
+ inventory: "inventory",
64
+ "state-machine": "state-machine",
65
+ diagram: "state-machine",
66
+ captures: "captures",
67
+ recordings: "captures",
68
+ inquiry: "inquiry",
69
+ plans: "plans",
70
+ };
71
+ /** Kinds served by the medicine wheel rather than by the vessel on disk. */
72
+ export const WHEEL_ARTIFACT_KINDS = ["captures", "inquiry", "plans"];
73
+ /** The wheel node id for an episode. The observed format, not the expected one:
74
+ * `chronicle:2026-08-05-episode-311-coute-…-factory`. */
75
+ export function wheelNodeId(episodeFolder) {
76
+ return `chronicle:${episodeFolder}`;
77
+ }
78
+ export function isMiadiChronicleUri(href) {
79
+ return typeof href === "string" && /^miadi-chronicle:/i.test(href);
80
+ }
81
+ /** decodeURIComponent that shrugs at malformed escapes ("%zz", trailing "%")
82
+ * instead of throwing — the parse contract is "never throws". */
83
+ function safeDecode(s) {
84
+ try {
85
+ return decodeURIComponent(s);
86
+ }
87
+ catch {
88
+ return s;
89
+ }
90
+ }
91
+ /**
92
+ * The artifact this reference names, by either trigger, or null.
93
+ *
94
+ * Two triggers, deterministic and independent: a reserved segment in the LAST
95
+ * position, or `mc.show=`. The segment wins when both are present and disagree
96
+ * — it is the more explicit of the two.
97
+ *
98
+ * A lone `miadi-chronicle:state-machine` is NOT an artifact reference: with no
99
+ * episode before it there is nothing to resolve the artifact against, so it
100
+ * stays an ordinary (almost certainly unresolved) episode reference rather
101
+ * than becoming a reference to no machine in particular.
102
+ */
103
+ function artifactOf(segments, options) {
104
+ const last = segments[segments.length - 1];
105
+ if (segments.length > 1 && last !== undefined && last in ARTIFACT_SEGMENTS) {
106
+ return ARTIFACT_SEGMENTS[last];
107
+ }
108
+ const show = options.show;
109
+ return (show !== undefined && SHOW_TO_KIND[show]) || null;
110
+ }
111
+ /** Strip a trailing reserved segment; what remains addresses an episode. */
112
+ function episodeSegmentsOf(segments) {
113
+ const last = segments[segments.length - 1];
114
+ if (segments.length > 1 && last !== undefined && last in ARTIFACT_SEGMENTS) {
115
+ return segments.slice(0, -1);
116
+ }
117
+ return segments;
118
+ }
119
+ function buildCanonical(episodePath, query, options, fragment) {
120
+ const params = [];
121
+ for (const [k, v] of Object.entries(query)) {
122
+ params.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`);
123
+ }
124
+ for (const [k, v] of Object.entries(options)) {
125
+ params.push(`${OPTION_PREFIX}${encodeURIComponent(k)}=${encodeURIComponent(v)}`);
126
+ }
127
+ const queryPart = params.length > 0 ? `?${params.join("&")}` : "";
128
+ const fragmentPart = fragment ? `#${fragment}` : "";
129
+ return `${MIADI_CHRONICLE_SCHEME}${episodePath}${queryPart}${fragmentPart}`;
130
+ }
131
+ /** Parse one URI. Returns null on anything malformed — never throws. */
132
+ export function parseMiadiChronicleUri(uri) {
133
+ const m = URI_RE.exec(uri.trim());
134
+ if (!m || !m[1])
135
+ return null;
136
+ const query = {};
137
+ const options = {};
138
+ const rawQuery = m[2];
139
+ if (rawQuery) {
140
+ for (const pair of rawQuery.split("&")) {
141
+ if (!pair)
142
+ continue;
143
+ const eq = pair.indexOf("=");
144
+ const key = safeDecode(eq === -1 ? pair : pair.slice(0, eq));
145
+ const value = eq === -1 ? "" : safeDecode(pair.slice(eq + 1));
146
+ if (key.startsWith(OPTION_PREFIX))
147
+ options[key.slice(OPTION_PREFIX.length)] = value;
148
+ else
149
+ query[key] = value;
150
+ }
151
+ }
152
+ const segments = m[1].split("/");
153
+ const fragment = m[3] || undefined;
154
+ return {
155
+ uri: uri.trim(),
156
+ canonical: buildCanonical(m[1], query, options, fragment),
157
+ episodePath: m[1],
158
+ segments,
159
+ episodeSegments: episodeSegmentsOf(segments),
160
+ artifact: artifactOf(segments, options),
161
+ query,
162
+ options,
163
+ fragment,
164
+ };
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Fragment durability (SPEC-TERMINAL §4.3)
168
+ // ---------------------------------------------------------------------------
169
+ /**
170
+ * Keys a `state-machine` fragment may use.
171
+ *
172
+ * `state` and `event` are name-keyed: SMDF's validation rule V002 forces
173
+ * tree-wide unique state names, and events are keyed by id, so both survive
174
+ * structural motion. Transitions, parameters, actions and event sources are
175
+ * addressed POSITIONALLY (`{state, index}`) — an index shifts the moment a
176
+ * sibling is inserted, and the reference then resolves to the wrong thing
177
+ * without raising anything. Content drift outruns link rot roughly 3:1
178
+ * (Jones et al. 2016, doi:10.1371/journal.pone.0167475) and the silent
179
+ * variant is the damaging one, so a form that cannot be durable is not
180
+ * spellable here.
181
+ *
182
+ * The narrow claim, precisely: a BARE `{state, index}` pair carries no content
183
+ * by which a shifted reference could be re-matched or a mis-resolution
184
+ * detected. Positional addressing can be hardened (Phelps & Wilensky carry
185
+ * node tags alongside the index and resolve by approximate match) — but
186
+ * hardening it here would amount to describing the transition, at which point
187
+ * the index is doing no work. The sanctioned way to address one is therefore
188
+ * by description, from V002-unique names, which inherits their durability:
189
+ * #transition=(from:<State>,on:<Event>,to:<State>)
190
+ * That form EXTENDS this rule rather than breaching it. It is not implemented.
191
+ *
192
+ * Conditional by design: transitions are positional IN SMDF TODAY. OMG XMI
193
+ * gives every model element an `xmi:id`; if SMDF ever mints author-assigned
194
+ * transition ids under the same uniqueness constraint, `#transition=` becomes
195
+ * durable and this rule's own principle would then REQUIRE permitting it.
196
+ */
197
+ export const DURABLE_FRAGMENT_KEYS = ["state", "event"];
198
+ /** Positional keys, refused with a reason rather than silently ignored. */
199
+ export const POSITIONAL_FRAGMENT_KEYS = [
200
+ "transition",
201
+ "parameter",
202
+ "action",
203
+ "source",
204
+ ];
205
+ /**
206
+ * Judge a `key=value` fragment against the durability rule. An unknown key is
207
+ * neither endorsed nor refused — it is passed through, per SPEC §1's rule that
208
+ * unknown parameters are preserved and never fatal.
209
+ */
210
+ export function judgeFragment(fragment) {
211
+ if (!fragment)
212
+ return null;
213
+ const eq = fragment.indexOf("=");
214
+ if (eq === -1)
215
+ return { key: fragment, value: "", durable: true };
216
+ const key = fragment.slice(0, eq);
217
+ const value = fragment.slice(eq + 1);
218
+ if (DURABLE_FRAGMENT_KEYS.includes(key)) {
219
+ return { key, value, durable: true };
220
+ }
221
+ if (POSITIONAL_FRAGMENT_KEYS.includes(key)) {
222
+ return {
223
+ key,
224
+ value,
225
+ durable: false,
226
+ reason: `'${key}' is addressed positionally in SMDF, so this reference would ` +
227
+ `resolve to a different element after any sibling insert — silently. ` +
228
+ `Name a state or an event instead.`,
229
+ };
230
+ }
231
+ return { key, value, durable: true };
232
+ }
233
+ /**
234
+ * Rename is the hole V002 does not cover.
235
+ *
236
+ * V002 gives tree-wide unique state names AT A POINT IN TIME. It says nothing
237
+ * about immutability: under rename, `#state=Foo` fails exactly as a positional
238
+ * reference fails under insertion, and the worse variant — a name reused for a
239
+ * different state — resolves successfully to the wrong element.
240
+ *
241
+ * The policy this package implements is the middle of the three the packet
242
+ * lists: an alias table. A machine MAY carry `former-names`, and a reference
243
+ * to a former name resolves WITH A DEPRECATION SIGNAL rather than failing.
244
+ * That is XPointer's ordered-fallback pattern applied locally, and Memento's
245
+ * idea (reach a prior state) without an archive. Until SMDF carries
246
+ * `former-names`, the honest status is: name-keyed fragments are durable
247
+ * against structural motion and fragile against rename, and this is stated
248
+ * rather than implied.
249
+ */
250
+ export const RENAME_POLICY = "alias-table-pending";
251
+ /** Folder-name tail after date and episode-number prefixes: the true slug. */
252
+ function folderSlug(name) {
253
+ return name.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/^episode-\d+-*/, "");
254
+ }
255
+ /** Deterministic pick over possibly-multiple matches: lexicographically first
256
+ * folder name (= earliest date), remaining matches surfaced as alternates. */
257
+ function pick(matches, matchedBy) {
258
+ if (matches.length === 0)
259
+ return null;
260
+ const sorted = [...matches].sort((a, b) => a.name.localeCompare(b.name));
261
+ return {
262
+ entry: sorted[0],
263
+ matchedBy,
264
+ alternates: sorted.length > 1 ? sorted.slice(1).map((e) => e.name) : undefined,
265
+ };
266
+ }
267
+ function matchSegment(segment, entries) {
268
+ const exact = entries.find((e) => e.name === segment);
269
+ if (exact)
270
+ return { entry: exact, matchedBy: "folder-name" };
271
+ if (/^\d+$/.test(segment)) {
272
+ const num = parseInt(segment, 10);
273
+ const byNumber = pick(entries.filter((e) => e.number === num), "number");
274
+ if (byNumber)
275
+ return byNumber;
276
+ }
277
+ const bySlug = pick(entries.filter((e) => e.slug === segment || folderSlug(e.name) === segment), "slug");
278
+ if (bySlug)
279
+ return bySlug;
280
+ if (/^\d{4}-\d{2}-\d{2}$/.test(segment)) {
281
+ const byDate = pick(entries.filter((e) => e.date === segment || e.name.startsWith(`${segment}-`)), "date");
282
+ if (byDate)
283
+ return byDate;
284
+ }
285
+ return null;
286
+ }
287
+ function toRef(segment, match, fragment) {
288
+ if (!match)
289
+ return { segment, resolved: false };
290
+ const { entry, matchedBy, alternates } = match;
291
+ const fragmentPart = fragment ? `#${fragment}` : "";
292
+ return {
293
+ segment,
294
+ resolved: true,
295
+ matchedBy,
296
+ name: entry.name,
297
+ number: entry.number,
298
+ title: entry.title,
299
+ date: entry.date,
300
+ direction: entry.direction,
301
+ theme: entry.theme,
302
+ route: `/chronicle/${encodeURIComponent(entry.name)}${fragmentPart}`,
303
+ alternates,
304
+ };
305
+ }
306
+ /**
307
+ * Resolve an already-parsed URI against the catalog.
308
+ *
309
+ * Resolution runs over `episodeSegments`, so an artifact reference resolves
310
+ * its episode exactly as a plain reference does — the reserved segment is
311
+ * stripped before matching, never matched against a folder.
312
+ */
313
+ export function resolveChronicleRef(parsed, entries) {
314
+ const segments = parsed.episodeSegments;
315
+ const targetSegment = segments[segments.length - 1];
316
+ const target = toRef(targetSegment, matchSegment(targetSegment, entries), parsed.fragment);
317
+ const waypoints = segments.slice(0, -1).map((s) => toRef(s, matchSegment(s, entries)));
318
+ return { resolved: target.resolved, matchedBy: target.matchedBy, target, waypoints };
319
+ }
320
+ /** One-call convenience: raw URI string → resolution, or null if unparseable. */
321
+ export function resolveChronicleUri(uri, entries) {
322
+ const parsed = parseMiadiChronicleUri(uri);
323
+ if (!parsed)
324
+ return null;
325
+ return { parsed, resolution: resolveChronicleRef(parsed, entries) };
326
+ }
327
+ //# sourceMappingURL=chronicle-dsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle-dsl.js","sourceRoot":"","sources":["../src/chronicle-dsl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * The terminal surface for miadi-chronicle references.
3
+ *
4
+ * Contract: rispecs/miadi-chronicle-dsl/SPEC-TERMINAL.md (v1.2).
5
+ * Grounded in foundations/resource-addressing-and-terminal-hypermedia/.
6
+ *
7
+ * Two jobs the pure module cannot do, because both touch the world:
8
+ *
9
+ * - the OPEN LADDER: turn a resolution into one thing a window manager can
10
+ * act on, preferring what is on this disk over what is on a network.
11
+ * - OSC 8 EMISSION: hand a terminal a link whose label is not its target.
12
+ *
13
+ * Why OSC 8 rather than letting the terminal find URLs in the text: regex
14
+ * linkification is an UNMARKED-ANCHOR mechanism (Grønbæk & Trigg, CACM 37(2),
15
+ * 1994) — the anchor carries no marker, its location must be computed by
16
+ * scanning, and its VALUE IS THE MATCHED TEXT. So under linkification the
17
+ * displayed label and the target are necessarily the same string, and
18
+ * `{{ … | label="Mila gathering" }}` cannot be expressed at all. OSC 8 is a
19
+ * marked anchor: declared, retrievable from the selection, carrying a value
20
+ * independent of what is shown. That is a property of the two anchoring
21
+ * models, not of any terminal's version number.
22
+ */
23
+ import { type ArtifactKind, type ChronicleResolution, type FragmentVerdict, type ParsedChronicleUri, type ResolvableEpisode } from "./chronicle-dsl.js";
24
+ /** VTE and iTerm2 both refuse URIs beyond this. */
25
+ export declare const MAX_OSC8_URI_BYTES = 2083;
26
+ /**
27
+ * Percent-encode anything outside the OSC command-string repertoire.
28
+ *
29
+ * ECMA-48 §5.6 constrains a command string to bit combinations 00/08–00/13 and
30
+ * 02/00–07/14. In practice for a URI that means bytes 32–126: anything else is
31
+ * undefined behaviour and must be escaped before it reaches the wire.
32
+ */
33
+ export declare function encodeForOsc8(uri: string): string;
34
+ export interface Osc8Options {
35
+ /**
36
+ * Hover-grouping hint, NOT a data channel. VTE caps ids near 250 bytes and
37
+ * reserves the right to change that without notice, and the OSC 8 spec
38
+ * instructs multiplexers and pagers to PREFIX incoming ids with a
39
+ * pane-unique string — so anything meaningful packed in here gets truncated
40
+ * and namespaced by layers we do not control.
41
+ *
42
+ * The spec's own rule: simple line-oriented utilities SHOULD NOT assign an
43
+ * id; full-screen applications SHOULD. This defaults to omitting it.
44
+ */
45
+ id?: string;
46
+ }
47
+ /**
48
+ * Wrap a label in an OSC 8 hyperlink.
49
+ *
50
+ * Emitted unconditionally, because there is no alternative. The OSC 8 spec
51
+ * says so in its own words: "Currently there's no way of detecting whether the
52
+ * terminal emulator supports hyperlinks." Any behaviour that branches on
53
+ * support is unimplementable, and backward compatibility is guaranteed one
54
+ * layer down — a terminal that parses OSC per ECMA-48 discards the payload and
55
+ * shows the label.
56
+ *
57
+ * Returns the bare label when the URI exceeds the byte ceiling: a link that
58
+ * would be truncated mid-URI is worse than no link, because it points
59
+ * somewhere real and wrong.
60
+ */
61
+ export declare function osc8(uri: string, label: string, options?: Osc8Options): string;
62
+ /**
63
+ * A chronicle reference as a terminal writes it: the marked anchor AND the
64
+ * address in plain sight.
65
+ *
66
+ * Not a fallback — two requirements met at once. The OSC 8 spec: hyperlinks
67
+ * "should not be the only means of figuring out the target." Engelbart's open
68
+ * hyperdocument requirement: every address stays "understandable and readable
69
+ * by a user." And the angle brackets are RFC 3986 Appendix C's explicit
70
+ * recommendation for delimiting a URI in running text, which is what makes the
71
+ * line survive `script`, CI capture, a pipe to a file, GNU screen, and xterm —
72
+ * none of which will ever render the escape.
73
+ */
74
+ export declare function terminalRef(uri: string, label: string, target: string, options?: Osc8Options): string;
75
+ /**
76
+ * How complete an answer this resolution is.
77
+ *
78
+ * This is a discretised HARVEST report in Fox & Brewer's sense (HotOS-VII,
79
+ * 1999): yield is whether the reference resolved at all, harvest is "the
80
+ * fraction of the data reflected in the response, i.e. the completeness of the
81
+ * answer." `resolved` is the yield; this is the harvest.
82
+ *
83
+ * Its consumer is named, deliberately: `resolveLines()` below prints it on
84
+ * every human-readable run. HTTP shipped exactly this field once — RFC 7234
85
+ * §5.5's `Warning`, including code 112 "Disconnected Operation" — and RFC 9111
86
+ * §8.3 obsoleted it because it "is not widely generated or surfaced to users."
87
+ * A modality nobody renders is dead metadata within two releases.
88
+ */
89
+ export type Modality = "relational" | "standalone" | "none";
90
+ export interface ArtifactTarget {
91
+ kind: ArtifactKind;
92
+ /** Absolute path on this host. */
93
+ path: string;
94
+ /** Other candidates the same reference matched — never hidden (SPEC §1). */
95
+ alternates?: string[];
96
+ }
97
+ /**
98
+ * The default chronicle room. Verified live 2026-08-16: `/chronicle` and
99
+ * `/chronicle/<folder>` both answer 200 on 127.0.0.1:3335, the same server the
100
+ * voice layer already talks to. The 2026-08-07 draft assumed the surface was
101
+ * down because it probed :3000 — the wrong port — and built a ladder around an
102
+ * outage that was not happening.
103
+ */
104
+ export declare const DEFAULT_LOCAL_BASE = "http://127.0.0.1:3335";
105
+ /**
106
+ * The chronicle room has three addresses and one identity.
107
+ *
108
+ * Measured 2026-08-16: `/chronicle/<folder>` answers 200 on all three —
109
+ * `127.0.0.1:3335`, the tailnet front (`$MIADI_URL_BASE_INTERNAL`), and the
110
+ * ngrok front (`$MIADI_URL_BASE`). Same room, three doors, and which door is
111
+ * reachable depends entirely on where the reader is standing.
112
+ *
113
+ * This is ARK's Name Mapping Authority rule arriving as a real requirement:
114
+ * the resolver's hostport is **identity-inert** — "ARKs that differ only in
115
+ * the optional NMA part identify the same object", and the host is
116
+ * "temporary, disposable, and replaceable". So the base never enters
117
+ * `canonical`. `miadi-chronicle:311` is the name; a base is chosen at render
118
+ * time, by audience.
119
+ *
120
+ * Which is also why the earlier draft's single `open` was wrong twice over —
121
+ * it picked a file when it should have picked a room, and it would have
122
+ * picked one room when there are three and the right one depends on who is
123
+ * reading.
124
+ */
125
+ export interface WebBases {
126
+ /** This host only. Fastest, and useless to anyone else. */
127
+ local?: string;
128
+ /** Tailnet — private, reaches other machines on the tailscale network. */
129
+ internal?: string;
130
+ /** Public front. The only one that survives being pasted somewhere. */
131
+ public?: string;
132
+ }
133
+ export type BaseKind = "local" | "internal" | "public";
134
+ /**
135
+ * Read the bases from the environment. Nothing is probed — reachability is a
136
+ * claim about the reader's network, not about this process, and a resolver
137
+ * that silently dropped an unreachable base would hide exactly the fact a
138
+ * reader elsewhere needs.
139
+ */
140
+ export declare function readWebBases(env?: NodeJS.ProcessEnv): WebBases;
141
+ /** One door. A reference has several; which one is right depends on the reader. */
142
+ export interface Destination {
143
+ kind: "room" | "wheel-api" | "wheel-page" | "artifact-file" | "readme" | "vessel";
144
+ /** Where the base came from, for the room kinds. */
145
+ base?: BaseKind;
146
+ /** Short human label — what a terminal prints inside the anchor. */
147
+ label: string;
148
+ url: string;
149
+ }
150
+ export interface Destinations {
151
+ /**
152
+ * Every door, in offer order. A chronicle reference is not one place: the
153
+ * same episode is a room on three fronts and a file on this disk, and which
154
+ * is useful depends on where the reader is standing and what they came for.
155
+ *
156
+ * William's doors principle, in the resolver: *"Depending on the Door you
157
+ * enter Guillaume's world, you don't get the content presented the same
158
+ * way."*
159
+ */
160
+ all: Destination[];
161
+ /** The default pick — `all[0]`, or null when nothing resolved. */
162
+ open: string | null;
163
+ openKind: Destination["kind"] | "none";
164
+ /**
165
+ * The door that survives being pasted somewhere else. A localhost URL in a
166
+ * chronicle, an issue or a voice record is a link that works for exactly one
167
+ * reader on exactly one machine.
168
+ */
169
+ shareable: string | null;
170
+ /**
171
+ * Bytes on this host, for whatever consumes bytes — a script, an agent, an
172
+ * editor, a diff. Always present when the episode resolved, independent of
173
+ * any web surface. This is the local-first guarantee kept where it belongs:
174
+ * local-first is about data ownership and availability, never about
175
+ * presentation.
176
+ */
177
+ path: string | null;
178
+ modality: Modality;
179
+ }
180
+ /**
181
+ * `file://` URLs carry this host's name, always.
182
+ *
183
+ * Browsers ignore the authority in a file URL. OSC 8 explicitly forbids that
184
+ * laxity BECAUSE IT BREAKS UNDER SSH: a terminal is required to refuse opening
185
+ * a local file when the hostname does not match, and to treat empty or
186
+ * "localhost" as local. RFC 8089 formalises `file-auth = "localhost" / host`,
187
+ * so that "a client on another system [can] know that it cannot access the
188
+ * file system."
189
+ *
190
+ * On this stack ssh is the normal case — the chronicle wheel is reached
191
+ * through a tunnel — so `file:///srv/miadi/...` would be a link that opens
192
+ * confidently, on the wrong machine's disk.
193
+ */
194
+ export declare function fileUrl(absolutePath: string, host?: string): string;
195
+ export declare function resolveArtifactTarget(kind: ArtifactKind, episodeDir: string): Promise<ArtifactTarget | null>;
196
+ /**
197
+ * The wheel's own doors, and the one endpoint that can be believed.
198
+ *
199
+ * Measured 2026-08-16: `/graph`, `/nodes` and `/relations` all begin
200
+ * `"use client"`. Fetching `/nodes?node=<a real episode id>` and grepping the
201
+ * HTML for the episode name returns **zero hits** — the shell is byte-identical
202
+ * for any parameter, and focus happens in the browser after it fetches
203
+ * `/api/nodes`. `/graph?direction=nowhere` is a 200 too.
204
+ *
205
+ * Forgewright behaves the same way: `?episode=9999-99-99-episode-000-nope`
206
+ * → 200. Two services, two codebases, one trap.
207
+ *
208
+ * **So an HTTP 200 from any of these pages is not evidence the episode
209
+ * exists.** `GET /api/nodes/<id>` is — it returns the node or a real 404
210
+ * (`{"error":"Node not found: chronicle:nope"}`). A resolver that hands a human
211
+ * a page URL without checking it first is handing them a silently empty wheel.
212
+ */
213
+ export declare const WHEEL_PAGE_DOORS: readonly [{
214
+ readonly path: "/graph";
215
+ readonly label: "wheel·graph";
216
+ }, {
217
+ readonly path: "/nodes";
218
+ readonly label: "wheel·node";
219
+ }, {
220
+ readonly path: "/relations";
221
+ readonly label: "wheel·relations";
222
+ }];
223
+ export declare function wheelUrl(env?: NodeJS.ProcessEnv): string;
224
+ /**
225
+ * Where a wheel-backed artifact actually lives.
226
+ *
227
+ * Every one is keyed by `episode_path` — the full folder name. The number is
228
+ * not a key: `?episode_number=320` returns 0 records for an episode whose
229
+ * `?episode_path=<folder>` returns 1, because the records carry no
230
+ * `episode_number` field and the filter compares strictly.
231
+ */
232
+ export declare function wheelArtifactUrl(kind: ArtifactKind, episodeFolder: string, base: string): string | null;
233
+ export interface WheelVerdict {
234
+ checked: boolean;
235
+ exists: boolean;
236
+ nodeId: string;
237
+ status?: number;
238
+ error?: string;
239
+ }
240
+ /**
241
+ * Ask the wheel whether this episode is actually on it.
242
+ *
243
+ * Opt-in (`--verify`) because it is a network call, and because a resolution
244
+ * that answers from the catalog on disk is already true about the disk. What
245
+ * this adds is the other half: whether the relational layer knows the episode
246
+ * too. Both answers are real, and they can disagree — forgewright's UI sees a
247
+ * 28-episode registry while its own disk routes resolve episodes the registry
248
+ * has never heard of.
249
+ */
250
+ export declare function verifyOnWheel(episodeFolder: string, base: string, timeoutMs?: number): Promise<WheelVerdict>;
251
+ /**
252
+ * Enumerate every door this reference opens.
253
+ *
254
+ * Two questions, never collapsed: *"where do I go to read this?"* is a room,
255
+ * *"where does this live?"* is the vessel. The 2026-08-07 draft answered only
256
+ * the second and called it `open`, which is how a chronicle link came to open
257
+ * emacs on raw JSON.
258
+ */
259
+ export declare function buildDestinations(resolution: ChronicleResolution, parsed: ParsedChronicleUri, chronicleRoot: string, options?: {
260
+ bases?: WebBases;
261
+ prefer?: BaseKind | "local-file";
262
+ wheelUrl?: string;
263
+ }): Promise<{
264
+ destinations: Destinations;
265
+ artifact: ArtifactTarget | null;
266
+ }>;
267
+ export interface ChronicleResolveResult {
268
+ ok: boolean;
269
+ uri: string;
270
+ /** Canonical rootless form — what a caller should persist or cite, and what
271
+ * a future registered `x-scheme-handler/miadi-chronicle` app would receive. */
272
+ canonical: string;
273
+ parsed: {
274
+ episodePath: string;
275
+ segments: string[];
276
+ episodeSegments: string[];
277
+ artifact: ArtifactKind | null;
278
+ query: Record<string, string>;
279
+ options: Record<string, string>;
280
+ fragment?: string;
281
+ };
282
+ resolution: ChronicleResolution;
283
+ artifact: ArtifactTarget | null;
284
+ /** Every door, in offer order. */
285
+ destinations: Destination[];
286
+ /** The default pick — `destinations[0]`. */
287
+ open: string | null;
288
+ openKind: Destinations["openKind"];
289
+ /** The door that survives being pasted elsewhere. */
290
+ shareable: string | null;
291
+ /** Bytes on this host. Independent of `open`. */
292
+ path: string | null;
293
+ modality: Modality;
294
+ /** Present only when the fragment names something positional. */
295
+ fragmentWarning?: FragmentVerdict;
296
+ /** Present only with --verify: what the wheel said about this episode. */
297
+ wheel?: WheelVerdict;
298
+ }
299
+ export declare function resolveForTerminal(uri: string, entries: ResolvableEpisode[], chronicleRoot: string, options?: {
300
+ bases?: WebBases;
301
+ prefer?: BaseKind | "local-file";
302
+ wheelUrl?: string;
303
+ verify?: boolean;
304
+ }): Promise<ChronicleResolveResult | null>;
305
+ //# sourceMappingURL=chronicle-terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronicle-terminal.d.ts","sourceRoot":"","sources":["../src/chronicle-terminal.ts"],"names":[],"mappings":"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"}