aureate 0.0.0-stage → 1.0.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.
Files changed (3) hide show
  1. package/README.md +47 -2
  2. package/bin/aureate.cjs +661 -0
  3. package/package.json +24 -4
package/README.md CHANGED
@@ -1,3 +1,48 @@
1
- # Temporary Holding Version
1
+ # AUREATE
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An offline terminal puzzle game.
4
+
5
+ You inherit an ORISON-4 terminal and one local note from its former owner. Somewhere inside the machine is an unfinished operation named AUREATE.
6
+
7
+ ## Play
8
+
9
+ Run without permanent installation:
10
+
11
+ ```sh
12
+ npx aureate
13
+ ```
14
+
15
+ Or install the command globally:
16
+
17
+ ```sh
18
+ npm install --global aureate
19
+ aureate
20
+ ```
21
+
22
+ Start a new session:
23
+
24
+ ```sh
25
+ aureate --new
26
+ ```
27
+
28
+ Run without loading or writing a save:
29
+
30
+ ```sh
31
+ aureate --new --no-save
32
+ ```
33
+
34
+ Disable animation and terminal control sequences:
35
+
36
+ ```sh
37
+ aureate --no-animation
38
+ ```
39
+
40
+ Use a specific save file:
41
+
42
+ ```sh
43
+ aureate --save ./save.json
44
+ ```
45
+
46
+ Input is case-insensitive. `SAVE` and `QUIT` are process controls. The machine has no help or hint system.
47
+
48
+ AUREATE does not access the network during play and has no runtime package dependencies.
@@ -0,0 +1,661 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/cli/adapter.js
26
+ var import_node_fs = __toESM(require("node:fs"), 1);
27
+ var import_node_os = __toESM(require("node:os"), 1);
28
+ var import_node_path = __toESM(require("node:path"), 1);
29
+ var import_node_readline = __toESM(require("node:readline"), 1);
30
+
31
+ // src/core/game.js
32
+ var ROOMS = {
33
+ desk: ["WELCOME", "BRASS-PLATE"],
34
+ drum: ["ADDRESS-TRACE", "FOLIO-A", "FOLIO-B", "CONTEXT-LEDGER"],
35
+ lens: ["APERTURE", "SERVICE-SLIP", "GLASS-A", "GLASS-B"],
36
+ patch: ["FLOW-TRACE", "CHANNEL-AMBER", "DRY-CHANNEL"],
37
+ bench: ["AUREATE-PLATE", "CROSSPOINT", "AMBER-JACK", "LEGACY-JACK", "SCRIPT-STORE"]
38
+ };
39
+ var TEXT = {
40
+ "desk/welcome": `ELIAS VALE / LAST LOCAL ENTRY
41
+
42
+ To whoever received this machine:
43
+
44
+ I left AUREATE unfinished under /BENCH/AUREATE-PLATE. It opens the note I
45
+ could not leave on this card. No single page carries the route, but ORISON
46
+ still does.
47
+
48
+ Survey what remains before you move anything. The brass battery was new when
49
+ I closed the case.
50
+
51
+ -- E.V.`,
52
+ "desk/brass-plate": `BELLWETHER LOGIC TERMINAL / ORISON-4 / 1978
53
+ LOCAL STORE 4096 W
54
+ LINE INTERFACE: NOT FITTED
55
+
56
+ OWNER RESIDUE 14-9-92
57
+
58
+ DESK> SURVEY
59
+ DESK> TRAVERSE /DRUM
60
+ DRUM> SURVEY`,
61
+ "desk/mark": `DESK MARK
62
+ CALLER != DESK
63
+ ADDRESS /DESK/MARK`,
64
+ "drum/address-trace": `ADDRESS TRACE 6C
65
+
66
+ DRUM> UNSEAL FOLIO-A
67
+ SOURCE DRUM/FOLIO-A
68
+
69
+ DRUM> UNSEAL /DESK/MARK
70
+ SOURCE DESK/MARK
71
+ CALLER DRUM`,
72
+ "drum/folio-a": `SERVICE MARGIN / 12-2-91
73
+
74
+ FINAL OPERATION: AUR---
75
+ REMAINDER: GLASS-A + GLASS-B
76
+ ANCHOR: /LENS`,
77
+ "drum/folio-b": `DOUBLE EXPOSURE
78
+
79
+ //// 0 1 0 //// AMB //// 1 1 0 ////
80
+ VISIBLE != CARRIER
81
+ OPTICAL PLANE: PRESENT`,
82
+ "drum/context-ledger": `LOCAL CONTEXT LEDGER
83
+
84
+ /DESK
85
+ /DRUM
86
+ /LENS
87
+ /PATCH CONTINUITY 0
88
+ /BENCH
89
+
90
+ EXTERNAL CONTEXT: NONE`,
91
+ "lens/aperture": `APERTURE / FIXED OPTICAL CONSOLE
92
+ ANCHOR /LENS
93
+ STATE COLD`,
94
+ "lens/service-slip": `APERTURE SERVICE RESIDUE
95
+
96
+ LENS> INVOKE APERTURE
97
+ APERTURE> FOCUS GLASS-A
98
+ APERTURE> SUPERPOSE GLASS-A :: GLASS-B
99
+ APERTURE> WITHDRAW`,
100
+ "lens/glass-a": `GLASS-A
101
+ VISIBLE CLEAR
102
+ LATENT YES`,
103
+ "lens/glass-b": `GLASS-B
104
+ VISIBLE SMOKED
105
+ LATENT YES`,
106
+ "patch/flow-trace": `LOCAL FLOW TRACE
107
+
108
+ DRAW <SOURCE> | APERTURE{FOCUS} | DELIVER <SINK>
109
+
110
+ CALIBRATION
111
+ SOURCE /PATCH/DRY-CHANNEL
112
+ FILTER APERTURE{FOCUS}
113
+ SINK /PATCH/CHANNEL-AMBER`,
114
+ "patch/channel-amber": `CHANNEL-AMBER
115
+ LOCAL CONTINUITY 1
116
+ EXTERNAL CONTINUITY 0`,
117
+ "patch/dry-channel": `DRY-CHANNEL
118
+ INPUT 0
119
+ LATENT 0
120
+ WIRE NONE`
121
+ };
122
+ var LATENT = {
123
+ "desk/welcome": `OWNER MARK
124
+ ORIGIN /DESK/WELCOME
125
+ VOICE FIRST`,
126
+ "drum/folio-b": `CARRIER AMBER
127
+ SOURCE /DRUM/FOLIO-B
128
+ SINK /BENCH/AMBER-JACK
129
+ PLANE LATENT`,
130
+ "lens/glass-a": "AURE",
131
+ "lens/glass-b": "ATE"
132
+ };
133
+ var TESTAMENT = `TESTAMENT / ELIAS VALE / LOCAL COPY
134
+
135
+ ORISON began at Bellwether Works in 1978. It counted parts, opened test bays,
136
+ and printed faults on a ribbon that stained every hand in the room.
137
+
138
+ When Bellwether closed, ORISON was scrap. I took it home in the passenger seat
139
+ of my van. Its casing was too heavy for one man, even then. Over the years I
140
+ replaced the drum with a small local store, rebuilt the optical reader, and
141
+ wired the dead test channels back through the desk. It has never been on a
142
+ network. Everything you saw was here when you opened the case.
143
+
144
+ AUREATE was the last thing I wrote. Paper goes damp in this house. Magnetic
145
+ store forgets. I trusted neither with the whole account, so I left the account
146
+ inside the route by which it is reached.
147
+
148
+ If these words are on the glass, the battery held and the old relays moved one
149
+ more time. That is enough.
150
+
151
+ Keep the machine if you have room. If not, save the brass plate. My hands wore
152
+ the corner smooth.
153
+
154
+ -- Elias Vale
155
+
156
+ [ AUREATE ]`;
157
+ var ENDING_STATUS = `LOCAL STORE HELD
158
+ OPTICAL COLD
159
+ AUREATE COMPLETE
160
+ ORISON-4 AT REST`;
161
+ function objectKey(target) {
162
+ return `${target.room}/${target.object}`;
163
+ }
164
+ var Game = class {
165
+ constructor(data = {}) {
166
+ const state = data.version === 2 ? data : {};
167
+ this.room = ROOMS[state.room] ? state.room : "desk";
168
+ this.mode = "shell";
169
+ this.flags = new Set(state.flags || []);
170
+ this.scripts = { ...state.scripts || {} };
171
+ this.visited = new Set((state.visited || ["desk"]).filter((room) => ROOMS[room]));
172
+ if (!this.visited.size) this.visited.add("desk");
173
+ this.finished = Boolean(state.finished);
174
+ }
175
+ toJSON() {
176
+ return {
177
+ version: 2,
178
+ room: this.room,
179
+ flags: [...this.flags].sort(),
180
+ scripts: this.scripts,
181
+ visited: [...this.visited].sort(),
182
+ finished: this.finished
183
+ };
184
+ }
185
+ opening() {
186
+ return `ORISON-4 / 1978
187
+ LOCAL STORE OK
188
+ OPTICAL COLD
189
+ LINE --
190
+
191
+ AUTO> UNSEAL /DESK/WELCOME
192
+
193
+ ${TEXT["desk/welcome"]}`;
194
+ }
195
+ get prompt() {
196
+ return this.mode === "aperture" ? "APERTURE> " : `${this.room.toUpperCase()}> `;
197
+ }
198
+ process(input, options = {}) {
199
+ const line = input.trim();
200
+ if (!line) return { text: "", quit: false };
201
+ const upper = line.toUpperCase();
202
+ if (upper === "QUIT") return { text: "LOCAL STORE HELD", quit: true };
203
+ if (upper === "SAVE") return { text: "SAVE REQUESTED", quit: false };
204
+ if (upper === "HELP" || upper === "HINT" || upper === "RECALL") {
205
+ return { text: "NO SUCH LOCAL FILE", quit: false };
206
+ }
207
+ if (this.finished) return { text: TESTAMENT, quit: false };
208
+ if (this.mode === "aperture") return { text: this.aperture(line), quit: false };
209
+ if (line.includes("|") && !upper.startsWith("INSCRIBE ")) {
210
+ return { text: this.pipeline(line, options), quit: false };
211
+ }
212
+ const firstSpace = line.search(/\s/);
213
+ const command = (firstSpace < 0 ? line : line.slice(0, firstSpace)).toUpperCase();
214
+ const argument = firstSpace < 0 ? "" : line.slice(firstSpace).trim();
215
+ let text;
216
+ switch (command) {
217
+ case "SURVEY":
218
+ text = this.survey(argument);
219
+ break;
220
+ case "UNSEAL":
221
+ text = this.unseal(argument);
222
+ break;
223
+ case "TRAVERSE":
224
+ text = this.traverse(argument);
225
+ break;
226
+ case "INVOKE":
227
+ text = this.invoke(argument);
228
+ break;
229
+ case "DRAW": {
230
+ const drawn = this.draw(argument);
231
+ text = drawn.error || `STREAM ${this.pathOf(drawn.stream.source)}
232
+ PLANE VISIBLE
233
+ SINK --`;
234
+ break;
235
+ }
236
+ case "DELIVER":
237
+ text = "DELIVER: INPUT 0";
238
+ break;
239
+ case "INSCRIBE":
240
+ text = this.inscribe(line);
241
+ break;
242
+ case "PERFORM":
243
+ text = this.perform(argument);
244
+ break;
245
+ case "AUREATE":
246
+ text = this.aureate(line);
247
+ break;
248
+ default:
249
+ text = `? ${line.split(/\s+/)[0]}`;
250
+ }
251
+ return { text, quit: false };
252
+ }
253
+ accessibleRooms() {
254
+ const result = ["desk", "drum", "bench"];
255
+ if (this.visited.has("drum") || this.visited.has("lens")) result.push("lens");
256
+ if (this.flags.has("patch_open") || this.visited.has("patch")) result.push("patch");
257
+ return result;
258
+ }
259
+ survey(argument = "") {
260
+ let room = this.room;
261
+ if (argument) {
262
+ room = argument.replace(/^\/+|\/+$/g, "").toLowerCase();
263
+ if (!ROOMS[room] || !this.accessibleRooms().includes(room) && room !== this.room) return `? /${room.toUpperCase()}`;
264
+ }
265
+ const lines = [`/${room.toUpperCase()}`];
266
+ for (const object of ROOMS[room]) {
267
+ let mark = "";
268
+ if (room === "bench") {
269
+ const flag = { CROSSPOINT: "crosspoint", "AMBER-JACK": "amber", "LEGACY-JACK": "legacy" }[object];
270
+ if (flag) mark = this.flags.has(flag) ? " *" : " .";
271
+ }
272
+ lines.push(`${object}${mark}`);
273
+ }
274
+ lines.push("::");
275
+ for (const available of this.accessibleRooms()) lines.push(`/${available.toUpperCase()}`);
276
+ if (room === "drum" && !this.flags.has("patch_open")) lines.push("/PATCH !");
277
+ return lines.join("\n");
278
+ }
279
+ resolve(token) {
280
+ token = token.trim().replaceAll("\\", "/");
281
+ if (!token) return { error: "? ADDRESS" };
282
+ let room;
283
+ let object;
284
+ if (token.startsWith("/")) {
285
+ const parts = token.split("/").filter(Boolean);
286
+ if (parts.length !== 2) return { error: "? ADDRESS" };
287
+ room = parts[0].toLowerCase();
288
+ object = parts[1].toLowerCase().replaceAll("_", "-");
289
+ } else {
290
+ room = this.room;
291
+ object = token.toLowerCase().replaceAll("_", "-");
292
+ }
293
+ const key = `${room}/${object}`;
294
+ const liveBench = room === "bench" && ["aureate-plate", "crosspoint", "amber-jack", "legacy-jack", "script-store"].includes(object);
295
+ if (!(key in TEXT) && !liveBench) return { error: `? ${token}` };
296
+ return { target: { room, object } };
297
+ }
298
+ pathOf(target) {
299
+ return `/${target.room.toUpperCase()}/${target.object.toUpperCase()}`;
300
+ }
301
+ unseal(token) {
302
+ const found = this.resolve(token);
303
+ if (found.error) return found.error;
304
+ const target = found.target;
305
+ const key = objectKey(target);
306
+ if (key === "desk/mark" && this.room === "bench") {
307
+ this.flags.add("crosspoint");
308
+ return `${TEXT[key]}
309
+
310
+ CROSSPOINT *
311
+ IMPRINT
312
+ INVOKE APERTURE
313
+ ANCHOR /LENS`;
314
+ }
315
+ if (target.room === "bench") return this.benchObject(target.object);
316
+ return TEXT[key];
317
+ }
318
+ benchObject(object) {
319
+ if (object === "aureate-plate") {
320
+ if (this.flags.has("legacy")) {
321
+ return `AUREATE-PLATE
322
+
323
+ AUREATE [LEGACY] :: @/DESK/WELCOME >> TESTAMENT`;
324
+ }
325
+ return `AUREATE-PLATE
326
+
327
+ ??????? [??????] :: @????????????? >> TESTAMENT`;
328
+ }
329
+ if (object === "crosspoint") {
330
+ if (this.flags.has("crosspoint")) return "CROSSPOINT *\nCALLER /BENCH\nSOURCE /DESK/MARK";
331
+ return `CROSSPOINT .
332
+ CALLER /BENCH
333
+ SOURCE /DESK/MARK
334
+ TRAVERSE 0`;
335
+ }
336
+ if (object === "amber-jack") {
337
+ if (this.flags.has("amber")) return "AMBER-JACK *\nCARRIER AMBER\nPLANE LATENT";
338
+ return `AMBER-JACK .
339
+ SOURCE /DRUM/FOLIO-B
340
+ PLANE LATENT
341
+ CARRIER AMBER`;
342
+ }
343
+ if (object === "legacy-jack") {
344
+ if (this.flags.has("legacy")) return "LEGACY-JACK *\nNAME LEGACY\nORIGIN /DESK/WELCOME";
345
+ return `LEGACY-JACK .
346
+ NAME LEGACY
347
+ SOURCE /DESK/WELCOME
348
+ PLANE LATENT
349
+ SINK /BENCH/LEGACY-JACK`;
350
+ }
351
+ if (object === "script-store") {
352
+ if (!this.flags.has("amber")) return "SCRIPT-STORE\nSTATE COLD";
353
+ return `SCRIPT-STORE
354
+ STATE HELD
355
+
356
+ INSCRIBE <NAME> := <LINE>
357
+ PERFORM <NAME>`;
358
+ }
359
+ return "?";
360
+ }
361
+ traverse(token) {
362
+ if (!token) return "? CONTEXT";
363
+ const room = token.replace(/^\/+|\/+$/g, "").toLowerCase();
364
+ if (!ROOMS[room]) return `? /${room.toUpperCase()}`;
365
+ this.room = room;
366
+ this.mode = "shell";
367
+ this.visited.add(room);
368
+ return this.survey();
369
+ }
370
+ invoke(token) {
371
+ if (token.trim().toLowerCase() !== "aperture") return `? ${token || "TOOL"}`;
372
+ if (this.room !== "lens") return "APERTURE ANCHOR /LENS";
373
+ this.mode = "aperture";
374
+ return `APERTURE
375
+ ANCHOR /LENS
376
+
377
+ > FOCUS GLASS-A
378
+ > SUPERPOSE GLASS-A :: GLASS-B
379
+ > WITHDRAW`;
380
+ }
381
+ aperture(line) {
382
+ const upper = line.toUpperCase();
383
+ if (upper === "WITHDRAW") {
384
+ this.mode = "shell";
385
+ return "/LENS";
386
+ }
387
+ if (upper.startsWith("FOCUS ")) {
388
+ const found = this.resolve(line.slice(line.search(/\s/) + 1));
389
+ if (found.error) return found.error;
390
+ const key = objectKey(found.target);
391
+ if (!(key in LATENT)) return `${this.pathOf(found.target)}
392
+ LATENT 0`;
393
+ return `${this.pathOf(found.target)}
394
+ LATENT 1
395
+
396
+ ${LATENT[key]}`;
397
+ }
398
+ if (upper.startsWith("SUPERPOSE ")) {
399
+ const parts = line.slice(line.search(/\s/) + 1).split("::");
400
+ if (parts.length !== 2) return "SUPERPOSE ? :: ?";
401
+ const left = this.resolve(parts[0].trim());
402
+ const right = this.resolve(parts[1].trim());
403
+ if (left.error) return left.error;
404
+ if (right.error) return right.error;
405
+ const pair = /* @__PURE__ */ new Set([objectKey(left.target), objectKey(right.target)]);
406
+ if (pair.size === 2 && pair.has("lens/glass-a") && pair.has("lens/glass-b")) {
407
+ this.flags.add("patch_open");
408
+ return `AURE + ATE
409
+ AUREATE
410
+
411
+ /PATCH CONTINUITY 1`;
412
+ }
413
+ return "REGISTRATION 0";
414
+ }
415
+ return `? ${line.split(/\s+/)[0]}`;
416
+ }
417
+ draw(token) {
418
+ const found = this.resolve(token);
419
+ if (found.error) return { error: found.error };
420
+ const target = found.target;
421
+ const text = target.room === "bench" ? this.benchObject(target.object) : TEXT[objectKey(target)];
422
+ return { stream: { source: target, plane: "visible", text } };
423
+ }
424
+ pipeline(line, options = {}) {
425
+ const parts = line.split("|").map((part) => part.trim());
426
+ if (parts.some((part) => !part) || !/^DRAW\s+/i.test(parts[0])) return "FLOW 0";
427
+ const drawn = this.draw(parts[0].replace(/^DRAW\s+/i, ""));
428
+ if (drawn.error) return drawn.error;
429
+ let stream = drawn.stream;
430
+ const result = [`DRAW ${this.pathOf(stream.source)}`];
431
+ let delivered = false;
432
+ for (const part of parts.slice(1)) {
433
+ if (/^APERTURE\{FOCUS\}$/i.test(part)) {
434
+ const key = objectKey(stream.source);
435
+ if (!(key in LATENT)) return "APERTURE{FOCUS} LATENT 0";
436
+ stream = { source: stream.source, plane: "latent", text: LATENT[key] };
437
+ result.push("APERTURE{FOCUS} LATENT 1");
438
+ } else if (/^DELIVER\s+/i.test(part)) {
439
+ if (delivered) return "SINK HELD";
440
+ const found = this.resolve(part.replace(/^DELIVER\s+/i, ""));
441
+ if (found.error) return found.error;
442
+ result.push(this.deliver(stream, found.target, options));
443
+ delivered = true;
444
+ } else {
445
+ return `? ${part}`;
446
+ }
447
+ }
448
+ if (!delivered) result.push("", stream.text);
449
+ return result.join("\n");
450
+ }
451
+ deliver(stream, target, options = {}) {
452
+ const destination = objectKey(target);
453
+ if (destination === "bench/amber-jack") {
454
+ if (objectKey(stream.source) !== "drum/folio-b" || stream.plane !== "latent") return "AMBER-JACK REJECT";
455
+ this.flags.add("amber");
456
+ return `DELIVER /BENCH/AMBER-JACK
457
+ AMBER-JACK *
458
+
459
+ SCRIPT-STORE HELD
460
+ INSCRIBE <NAME> := <LINE>
461
+ PERFORM <NAME>`;
462
+ }
463
+ if (destination === "bench/legacy-jack") {
464
+ if (objectKey(stream.source) !== "desk/welcome" || stream.plane !== "latent") return "LEGACY-JACK REJECT";
465
+ if (!options.scripted || String(options.scriptName).toUpperCase() !== "LEGACY") return "LEGACY-JACK NAME ?";
466
+ this.flags.add("legacy");
467
+ return `DELIVER /BENCH/LEGACY-JACK
468
+ LEGACY-JACK *
469
+ ORIGIN /DESK/WELCOME
470
+ AUREATE-PLATE HELD`;
471
+ }
472
+ return `${this.pathOf(target)} SINK 0`;
473
+ }
474
+ inscribe(line) {
475
+ if (this.room !== "bench") return "SCRIPT-STORE ANCHOR /BENCH";
476
+ const match = line.match(/^INSCRIBE\s+([A-Z0-9_-]+)\s*:=\s*(.+)$/is);
477
+ if (!match) return "INSCRIBE ? := ?";
478
+ const name = match[1].toUpperCase();
479
+ const body = match[2].trim();
480
+ const head = body.split(/\s+/, 1)[0].toUpperCase();
481
+ if (["INSCRIBE", "PERFORM", "AUREATE"].includes(head) || body.length > 300) return "SCRIPT-STORE REJECT";
482
+ this.scripts[name] = body;
483
+ return `${name} := ${body}`;
484
+ }
485
+ perform(token) {
486
+ if (this.room !== "bench") return "SCRIPT-STORE ANCHOR /BENCH";
487
+ const name = token.trim().toUpperCase();
488
+ if (!(name in this.scripts)) return `${name || "?"} : --`;
489
+ const output = this.process(this.scripts[name], { scripted: true, scriptName: name });
490
+ return `${name}
491
+ ${output.text}`;
492
+ }
493
+ aureate(line) {
494
+ const match = line.match(/^AUREATE\s+\[([A-Z0-9_-]+)\]\s*::\s*@([^\s]+)\s*>>\s*TESTAMENT$/i);
495
+ if (!match) return "AUREATE [??????] :: @????????????? >> TESTAMENT";
496
+ if (this.room !== "bench") return "AUREATE CONTEXT ?";
497
+ const script = match[1].toUpperCase();
498
+ const origin = this.resolve(match[2]);
499
+ if (script !== "LEGACY") return "LEGACY --";
500
+ if (origin.error || objectKey(origin.target) !== "desk/welcome") return "ORIGIN ?";
501
+ this.finished = true;
502
+ this.flags.add("aureate");
503
+ return TESTAMENT;
504
+ }
505
+ };
506
+
507
+ // src/cli/adapter.js
508
+ var wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
509
+ async function writeSlow(text, milliseconds = 12) {
510
+ for (const character of text) {
511
+ process.stdout.write(character);
512
+ if (character !== " " && character !== "\n") await wait(milliseconds);
513
+ }
514
+ }
515
+ async function writeLines(text, milliseconds = 24) {
516
+ const lines = text.split("\n");
517
+ for (let index = 0; index < lines.length; index += 1) {
518
+ process.stdout.write(lines[index]);
519
+ if (index < lines.length - 1) process.stdout.write("\n");
520
+ await wait(milliseconds);
521
+ }
522
+ }
523
+ function clearTerminal() {
524
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
525
+ }
526
+ async function boot(game, restored, animated) {
527
+ if (!animated) {
528
+ if (!restored) console.log(game.opening());
529
+ else if (game.finished) console.log("ORISON-4 / 1978\nLOCAL STORE HELD");
530
+ else console.log(`ORISON-4 / 1978
531
+ LOCAL STORE HELD
532
+
533
+ ${game.survey()}`);
534
+ return;
535
+ }
536
+ clearTerminal();
537
+ process.stdout.write("\x1B[?25l");
538
+ await writeSlow("ORISON-4 / 1978", 24);
539
+ process.stdout.write("\n\nLOCAL STORE 0000 W");
540
+ for (const amount of ["1024", "2048", "3072", "4096"]) {
541
+ await wait(110);
542
+ process.stdout.write(`\rLOCAL STORE ${amount} W`);
543
+ }
544
+ await wait(140);
545
+ process.stdout.write(" OK\nOPTICAL COLD\nLINE --\n");
546
+ await wait(260);
547
+ if (restored) {
548
+ process.stdout.write("\nSTORE> HELD");
549
+ if (!game.finished) {
550
+ process.stdout.write("\n\n");
551
+ await writeLines(game.survey(), 16);
552
+ }
553
+ } else {
554
+ const opening = game.opening().split("\n");
555
+ const autoIndex = opening.findIndex((line) => line.startsWith("AUTO>"));
556
+ process.stdout.write("\n");
557
+ await writeSlow(opening[autoIndex], 18);
558
+ process.stdout.write("\n\n");
559
+ await writeLines(opening.slice(autoIndex + 2).join("\n"), 22);
560
+ }
561
+ process.stdout.write("\x1B[?25h");
562
+ }
563
+ async function settleTerminal(animated) {
564
+ if (animated) {
565
+ await wait(420);
566
+ process.stdout.write("\n\n\x1B[?25l");
567
+ await writeLines(ENDING_STATUS, 180);
568
+ process.stdout.write("\n\x1B[?25h");
569
+ } else {
570
+ console.log(`
571
+ ${ENDING_STATUS}`);
572
+ }
573
+ }
574
+ function defaultSavePath() {
575
+ if (process.platform === "win32") {
576
+ const root2 = process.env.APPDATA || import_node_path.default.join(import_node_os.default.homedir(), "AppData", "Roaming");
577
+ return import_node_path.default.join(root2, "AUREATE", "save.json");
578
+ }
579
+ const root = process.env.XDG_DATA_HOME || import_node_path.default.join(import_node_os.default.homedir(), ".local", "share");
580
+ return import_node_path.default.join(root, "aureate", "save.json");
581
+ }
582
+ function loadGame(savePath) {
583
+ try {
584
+ return new Game(JSON.parse(import_node_fs.default.readFileSync(savePath, "utf8")));
585
+ } catch {
586
+ return null;
587
+ }
588
+ }
589
+ function saveGame(game, savePath) {
590
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(savePath), { recursive: true });
591
+ import_node_fs.default.writeFileSync(savePath, `${JSON.stringify(game.toJSON(), null, 2)}
592
+ `, "utf8");
593
+ }
594
+ function parseArgs(args) {
595
+ const options = { fresh: false, noSave: false, noAnimation: false, savePath: defaultSavePath() };
596
+ for (let index = 0; index < args.length; index += 1) {
597
+ if (args[index] === "--new") options.fresh = true;
598
+ else if (args[index] === "--no-save") options.noSave = true;
599
+ else if (args[index] === "--no-animation") options.noAnimation = true;
600
+ else if (args[index] === "--save" && args[index + 1]) options.savePath = import_node_path.default.resolve(args[++index]);
601
+ else if (args[index] === "--help") {
602
+ console.log("AUREATE [--new] [--no-save] [--no-animation] [--save FILE]");
603
+ return null;
604
+ }
605
+ }
606
+ return options;
607
+ }
608
+ async function run(args = process.argv.slice(2)) {
609
+ const options = parseArgs(args);
610
+ if (!options) return;
611
+ let game = null;
612
+ if (!options.fresh && !options.noSave && import_node_fs.default.existsSync(options.savePath)) game = loadGame(options.savePath);
613
+ const restored = Boolean(game);
614
+ if (!game) game = new Game();
615
+ const animated = Boolean(process.stdin.isTTY && process.stdout.isTTY && !options.noAnimation);
616
+ if (animated) process.once("exit", () => process.stdout.write("\x1B[?25h"));
617
+ await boot(game, restored, animated);
618
+ if (game.finished) {
619
+ await settleTerminal(animated);
620
+ return;
621
+ }
622
+ const terminal = import_node_readline.default.createInterface({
623
+ input: process.stdin,
624
+ output: process.stdout,
625
+ terminal: Boolean(process.stdin.isTTY)
626
+ });
627
+ terminal.setPrompt(`
628
+ ${game.prompt}`);
629
+ terminal.prompt();
630
+ for await (const line of terminal) {
631
+ const output = game.process(line);
632
+ if (output.text === "SAVE REQUESTED") {
633
+ if (!options.noSave) saveGame(game, options.savePath);
634
+ console.log(options.noSave ? "LOCAL STORE INHIBITED" : "LOCAL STORE HELD");
635
+ } else {
636
+ if (output.text === TESTAMENT && animated) {
637
+ process.stdout.write("\n");
638
+ await writeLines(output.text, 30);
639
+ process.stdout.write("\n");
640
+ } else if (output.text) {
641
+ console.log(output.text);
642
+ }
643
+ if (!options.noSave) saveGame(game, options.savePath);
644
+ if (game.finished) {
645
+ await settleTerminal(animated);
646
+ break;
647
+ }
648
+ if (output.quit) break;
649
+ }
650
+ terminal.setPrompt(`
651
+ ${game.prompt}`);
652
+ terminal.prompt();
653
+ }
654
+ terminal.close();
655
+ }
656
+
657
+ // src/cli/main.js
658
+ run().catch((error) => {
659
+ console.error(error);
660
+ process.exitCode = 1;
661
+ });
package/package.json CHANGED
@@ -1,6 +1,26 @@
1
1
  {
2
2
  "name": "aureate",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "An offline terminal puzzle game about discovering a missing command.",
5
+ "bin": {
6
+ "aureate": "bin/aureate.cjs"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "README.md"
11
+ ],
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "keywords": [
19
+ "game",
20
+ "terminal",
21
+ "cli",
22
+ "puzzle",
23
+ "interactive-fiction"
24
+ ],
25
+ "license": "UNLICENSED"
26
+ }