@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.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/attention-node.d.ts +70 -0
- package/dist/attention-node.d.ts.map +1 -0
- package/dist/attention-node.js +194 -0
- package/dist/attention-node.js.map +1 -0
- package/dist/chronicle-dsl.d.ts +191 -0
- package/dist/chronicle-dsl.d.ts.map +1 -0
- package/dist/chronicle-dsl.js +327 -0
- package/dist/chronicle-dsl.js.map +1 -0
- package/dist/chronicle-terminal.d.ts +305 -0
- package/dist/chronicle-terminal.d.ts.map +1 -0
- package/dist/chronicle-terminal.js +449 -0
- package/dist/chronicle-terminal.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +222 -8
- package/dist/cli.js.map +1 -1
- package/dist/episode.d.ts.map +1 -1
- package/dist/episode.js +11 -3
- package/dist/episode.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/terminal-install.d.ts +84 -0
- package/dist/terminal-install.d.ts.map +1 -0
- package/dist/terminal-install.js +306 -0
- package/dist/terminal-install.js.map +1 -0
- package/package.json +9 -10
|
@@ -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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"}
|