spexcode 0.6.6 → 0.6.8

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 (205) hide show
  1. package/README.md +12 -7
  2. package/node_modules/@spexcode/session-application/dist/index.d.ts +17 -0
  3. package/node_modules/@spexcode/session-application/dist/index.js +35 -0
  4. package/node_modules/@spexcode/session-application/dist/migration.d.ts +51 -0
  5. package/node_modules/@spexcode/session-application/dist/migration.js +694 -0
  6. package/node_modules/@spexcode/session-application/dist/production.d.ts +109 -0
  7. package/node_modules/@spexcode/session-application/dist/production.js +438 -0
  8. package/node_modules/@spexcode/session-application/dist/schema.d.ts +2 -0
  9. package/node_modules/@spexcode/session-application/dist/schema.js +39 -0
  10. package/node_modules/@spexcode/session-application/package.json +29 -0
  11. package/node_modules/@spexcode/session-events/dist/errors.d.ts +6 -0
  12. package/node_modules/@spexcode/session-events/dist/errors.js +11 -0
  13. package/node_modules/@spexcode/session-events/dist/index.d.ts +46 -0
  14. package/node_modules/@spexcode/session-events/dist/index.js +245 -0
  15. package/node_modules/@spexcode/session-events/dist/schema.d.ts +3 -0
  16. package/node_modules/@spexcode/session-events/dist/schema.js +44 -0
  17. package/node_modules/@spexcode/session-events/package.json +24 -0
  18. package/node_modules/@spexcode/session-protocol/dist/canonical.d.ts +36 -0
  19. package/node_modules/@spexcode/session-protocol/dist/canonical.js +139 -0
  20. package/node_modules/@spexcode/session-protocol/dist/engine.d.ts +22 -0
  21. package/node_modules/@spexcode/session-protocol/dist/engine.js +478 -0
  22. package/node_modules/@spexcode/session-protocol/dist/errors.d.ts +8 -0
  23. package/node_modules/@spexcode/session-protocol/dist/errors.js +39 -0
  24. package/node_modules/@spexcode/session-protocol/dist/index.d.ts +60 -0
  25. package/node_modules/@spexcode/session-protocol/dist/index.js +4 -0
  26. package/node_modules/@spexcode/session-protocol/dist/schema.d.ts +36 -0
  27. package/node_modules/@spexcode/session-protocol/dist/schema.js +192 -0
  28. package/node_modules/@spexcode/{session-core → session-protocol}/package.json +4 -8
  29. package/node_modules/@spexcode/session-runtime/dist/errors.d.ts +6 -0
  30. package/node_modules/@spexcode/session-runtime/dist/errors.js +11 -0
  31. package/node_modules/@spexcode/session-runtime/dist/index.d.ts +33 -0
  32. package/node_modules/@spexcode/session-runtime/dist/index.js +193 -0
  33. package/node_modules/@spexcode/session-runtime/dist/schema.d.ts +2 -0
  34. package/node_modules/@spexcode/session-runtime/dist/schema.js +37 -0
  35. package/node_modules/@spexcode/session-runtime/package.json +24 -0
  36. package/node_modules/@spexcode/session-selflaunch/bin/spex-session.mjs +4 -0
  37. package/node_modules/@spexcode/session-selflaunch/dist/cli.d.ts +25 -0
  38. package/node_modules/@spexcode/session-selflaunch/dist/cli.js +156 -0
  39. package/node_modules/@spexcode/session-selflaunch/dist/index.d.ts +28 -0
  40. package/node_modules/@spexcode/session-selflaunch/dist/index.js +25 -0
  41. package/node_modules/@spexcode/session-selflaunch/dist/locality.d.ts +20 -0
  42. package/node_modules/@spexcode/session-selflaunch/dist/locality.js +83 -0
  43. package/node_modules/@spexcode/session-selflaunch/dist/path.d.ts +13 -0
  44. package/node_modules/@spexcode/session-selflaunch/dist/path.js +45 -0
  45. package/node_modules/@spexcode/session-selflaunch/package.json +35 -0
  46. package/node_modules/@spexcode/session-topology/dist/errors.d.ts +6 -0
  47. package/node_modules/@spexcode/session-topology/dist/errors.js +11 -0
  48. package/node_modules/@spexcode/session-topology/dist/index.d.ts +24 -0
  49. package/node_modules/@spexcode/session-topology/dist/index.js +173 -0
  50. package/node_modules/@spexcode/session-topology/dist/schema.d.ts +3 -0
  51. package/node_modules/@spexcode/session-topology/dist/schema.js +27 -0
  52. package/node_modules/@spexcode/session-topology/package.json +24 -0
  53. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +56 -19
  54. package/node_modules/@spexcode/spec-cli/dist/cli.js +102 -59
  55. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +1 -3
  56. package/node_modules/@spexcode/spec-cli/dist/client.js +49 -30
  57. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +11 -0
  58. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +46 -9
  59. package/node_modules/@spexcode/spec-cli/dist/delivery-lock.d.ts +2 -0
  60. package/node_modules/@spexcode/spec-cli/dist/delivery-lock.js +58 -0
  61. package/node_modules/@spexcode/spec-cli/dist/doctor.js +53 -11
  62. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +1 -0
  63. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +2 -2
  64. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +2 -1
  65. package/node_modules/@spexcode/spec-cli/dist/gateway.js +6 -3
  66. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +32 -2
  67. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +57 -2
  68. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +2 -0
  69. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +83 -3
  70. package/node_modules/@spexcode/spec-cli/dist/guide.js +20 -7
  71. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +16 -3
  72. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +15 -3
  73. package/node_modules/@spexcode/spec-cli/dist/harness.js +331 -50
  74. package/node_modules/@spexcode/spec-cli/dist/help.js +11 -8
  75. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +8 -0
  76. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +29 -8
  77. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +7 -0
  78. package/node_modules/@spexcode/spec-cli/dist/host.js +93 -0
  79. package/node_modules/@spexcode/spec-cli/dist/index.js +324 -22
  80. package/node_modules/@spexcode/spec-cli/dist/init.js +1 -1
  81. package/node_modules/@spexcode/spec-cli/dist/lint.js +70 -35
  82. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +3 -2
  83. package/node_modules/@spexcode/spec-cli/dist/listen.js +14 -2
  84. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +1 -1
  85. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +2 -2
  86. package/node_modules/@spexcode/spec-cli/dist/materialize.js +176 -35
  87. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +14 -14
  88. package/node_modules/@spexcode/spec-cli/dist/reviews.js +12 -7
  89. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +11 -0
  90. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +79 -1
  91. package/node_modules/@spexcode/spec-cli/dist/session-application.d.ts +23 -0
  92. package/node_modules/@spexcode/spec-cli/dist/session-application.js +189 -0
  93. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +13 -1
  94. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +6 -0
  95. package/node_modules/@spexcode/spec-cli/dist/session-files.js +13 -1
  96. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +39 -22
  97. package/node_modules/@spexcode/{session-core/dist/record-lock.d.ts → spec-cli/dist/session-record-lock.d.ts} +0 -4
  98. package/node_modules/@spexcode/spec-cli/dist/session-record-lock.js +94 -0
  99. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.d.ts +44 -0
  100. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.js +37 -0
  101. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +25 -2
  102. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +68 -11
  103. package/node_modules/@spexcode/spec-cli/dist/session-web.js +4 -4
  104. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +108 -15
  105. package/node_modules/@spexcode/spec-cli/dist/sessions.js +1465 -744
  106. package/node_modules/@spexcode/spec-cli/dist/source-list.d.ts +13 -0
  107. package/node_modules/@spexcode/spec-cli/dist/source-list.js +99 -0
  108. package/node_modules/@spexcode/spec-cli/dist/source-read.d.ts +16 -0
  109. package/node_modules/@spexcode/spec-cli/dist/source-read.js +84 -0
  110. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.d.ts +7 -0
  111. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.js +89 -0
  112. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.d.ts +23 -0
  113. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.js +138 -0
  114. package/node_modules/@spexcode/spec-cli/dist/supervise.js +15 -6
  115. package/node_modules/@spexcode/spec-cli/dist/transcript-reader.d.ts +36 -0
  116. package/node_modules/@spexcode/spec-cli/dist/transcript-reader.js +251 -0
  117. package/node_modules/@spexcode/spec-cli/hooks/dispatch.sh +19 -31
  118. package/node_modules/@spexcode/spec-cli/hooks/harness.sh +6 -6
  119. package/node_modules/@spexcode/spec-cli/package.json +6 -6
  120. package/node_modules/@spexcode/spec-cli/templates/hooks/post-checkout +4 -2
  121. package/node_modules/@spexcode/spec-cli/templates/hooks/post-merge +2 -1
  122. package/node_modules/@spexcode/spec-cli/templates/hooks/pre-commit +5 -3
  123. package/node_modules/@spexcode/spec-cli/templates/hooks/reference-transaction +5 -3
  124. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/spec.md +2 -7
  125. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/idle.sh +4 -10
  126. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/spec.md +1 -1
  127. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/mark-active.sh +22 -24
  128. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/spec.md +10 -2
  129. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/fail.sh +8 -7
  130. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/spec.md +3 -1
  131. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/session-listen.sh +133 -0
  132. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/spec.md +36 -0
  133. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +2 -0
  134. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/spec.md +1 -1
  135. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +17 -20
  136. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/merge/spec.md +33 -0
  137. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/spec.md +2 -6
  138. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/spec.md +7 -0
  139. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +13 -2
  140. package/node_modules/@spexcode/spec-core/dist/anchors.js +311 -15
  141. package/node_modules/@spexcode/spec-core/dist/git.d.ts +4 -0
  142. package/node_modules/@spexcode/spec-core/dist/git.js +9 -9
  143. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +12 -0
  144. package/node_modules/@spexcode/spec-core/dist/layout.js +4 -2
  145. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +1 -0
  146. package/node_modules/@spexcode/spec-core/dist/resilience.js +8 -0
  147. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +1 -0
  148. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +7 -3
  149. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +1 -0
  150. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +4 -0
  151. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +2 -0
  152. package/node_modules/@spexcode/spec-core/dist/specs.js +30 -0
  153. package/node_modules/@spexcode/spec-core/package.json +4 -1
  154. package/node_modules/@spexcode/spec-eval/dist/cli.js +1 -1
  155. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +1 -0
  156. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +6 -1
  157. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +1 -1
  158. package/node_modules/@spexcode/spec-eval/dist/freshness.js +12 -12
  159. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +2 -1
  160. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +2 -1
  161. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +20 -3
  162. package/node_modules/@spexcode/spec-eval/package.json +2 -2
  163. package/node_modules/@spexcode/spec-forge/package.json +2 -2
  164. package/node_modules/@vscode/tree-sitter-wasm/LICENSE +21 -0
  165. package/node_modules/@vscode/tree-sitter-wasm/README.md +36 -0
  166. package/node_modules/@vscode/tree-sitter-wasm/SECURITY.md +41 -0
  167. package/node_modules/@vscode/tree-sitter-wasm/cgmanifest.json +16 -0
  168. package/node_modules/@vscode/tree-sitter-wasm/package.json +42 -0
  169. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-bash.wasm +0 -0
  170. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-c-sharp.wasm +0 -0
  171. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-cpp.wasm +0 -0
  172. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm +0 -0
  173. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-go.wasm +0 -0
  174. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +0 -0
  175. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-java.wasm +0 -0
  176. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-javascript.wasm +0 -0
  177. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-php.wasm +0 -0
  178. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-powershell.wasm +0 -0
  179. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-python.wasm +0 -0
  180. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +0 -0
  181. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ruby.wasm +0 -0
  182. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-rust.wasm +0 -0
  183. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-tsx.wasm +0 -0
  184. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +0 -0
  185. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.js +4075 -0
  186. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.wasm +0 -0
  187. package/node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts +1027 -0
  188. package/package.json +9 -3
  189. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +0 -23
  190. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +0 -195
  191. package/node_modules/@spexcode/session-core/dist/index.d.ts +0 -5
  192. package/node_modules/@spexcode/session-core/dist/index.js +0 -5
  193. package/node_modules/@spexcode/session-core/dist/internal.d.ts +0 -3
  194. package/node_modules/@spexcode/session-core/dist/internal.js +0 -3
  195. package/node_modules/@spexcode/session-core/dist/message.d.ts +0 -22
  196. package/node_modules/@spexcode/session-core/dist/message.js +0 -52
  197. package/node_modules/@spexcode/session-core/dist/record-lock.js +0 -152
  198. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +0 -50
  199. package/node_modules/@spexcode/session-core/dist/runtime-session.js +0 -286
  200. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +0 -14
  201. package/node_modules/@spexcode/session-core/dist/session-cursors.js +0 -82
  202. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +0 -46
  203. package/node_modules/@spexcode/session-core/dist/session-timeline.js +0 -216
  204. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -53
  205. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-sed-v0.fixture +0 -46
@@ -0,0 +1,192 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { fail } from './errors.js';
3
+ export const MIN_SQLITE_VERSION = '3.38.0';
4
+ export const PROTOCOL_COMPONENT = 'session-protocol';
5
+ export const PROTOCOL_MIGRATION_SQL = `
6
+ CREATE TABLE protocol_sessions (
7
+ session_id TEXT NOT NULL PRIMARY KEY,
8
+ created_at_ms INTEGER NOT NULL,
9
+ retired_at_ms INTEGER,
10
+ CHECK (length(session_id) BETWEEN 1 AND 256),
11
+ CHECK (session_id NOT GLOB '*[^0-9A-Za-z_-]*'),
12
+ CHECK (session_id NOT GLOB '-*'),
13
+ CHECK (created_at_ms >= 0),
14
+ CHECK (retired_at_ms IS NULL OR retired_at_ms >= 0)
15
+ ) STRICT;
16
+
17
+ CREATE TABLE protocol_messages (
18
+ enqueue_seq INTEGER PRIMARY KEY AUTOINCREMENT,
19
+ message_id TEXT NOT NULL UNIQUE,
20
+ target_session_id TEXT NOT NULL REFERENCES protocol_sessions(session_id),
21
+ sender_session_id TEXT,
22
+ protocol_version INTEGER NOT NULL,
23
+ kind TEXT NOT NULL,
24
+ body BLOB NOT NULL,
25
+ headers_json TEXT NOT NULL,
26
+ idempotency_key TEXT,
27
+ payload_hash BLOB NOT NULL,
28
+ enqueued_at_ms INTEGER NOT NULL,
29
+ dequeued_at_ms INTEGER,
30
+ CHECK (message_id GLOB '[0-9a-f]*' AND length(message_id) = 32),
31
+ CHECK (protocol_version >= 1),
32
+ CHECK (length(kind) BETWEEN 1 AND 64),
33
+ CHECK (kind NOT GLOB '*[^0-9A-Za-z._-]*'),
34
+ CHECK (length(body) <= 1048576),
35
+ CHECK (json_valid(headers_json) AND json_type(headers_json) = 'object'),
36
+ CHECK (length(CAST(headers_json AS BLOB)) <= 65536),
37
+ CHECK (idempotency_key IS NULL OR length(idempotency_key) BETWEEN 1 AND 256),
38
+ CHECK (length(payload_hash) = 32),
39
+ CHECK (enqueued_at_ms >= 0),
40
+ CHECK (dequeued_at_ms IS NULL OR dequeued_at_ms >= 0),
41
+ CHECK (sender_session_id IS NULL OR length(sender_session_id) BETWEEN 1 AND 256)
42
+ ) STRICT;
43
+
44
+ CREATE UNIQUE INDEX protocol_messages_idempotency
45
+ ON protocol_messages (target_session_id, idempotency_key)
46
+ WHERE idempotency_key IS NOT NULL;
47
+
48
+ CREATE INDEX protocol_messages_pending_fifo
49
+ ON protocol_messages (target_session_id, enqueue_seq)
50
+ WHERE dequeued_at_ms IS NULL;
51
+
52
+ CREATE INDEX protocol_messages_history
53
+ ON protocol_messages (target_session_id, enqueue_seq);
54
+ `;
55
+ export const REGISTRY_SQL = `
56
+ CREATE TABLE IF NOT EXISTS schema_migrations (
57
+ component TEXT NOT NULL,
58
+ version INTEGER NOT NULL,
59
+ checksum TEXT NOT NULL,
60
+ applied_at_ms INTEGER NOT NULL,
61
+ PRIMARY KEY (component, version)
62
+ ) STRICT;
63
+ `;
64
+ const migrationChecksum = (sql) => createHash('sha256').update(Buffer.from(sql, 'utf8')).digest('hex');
65
+ export const PROTOCOL_MIGRATIONS = [
66
+ { version: 1, sql: PROTOCOL_MIGRATION_SQL, checksum: migrationChecksum(PROTOCOL_MIGRATION_SQL) },
67
+ ];
68
+ const targets = new WeakMap();
69
+ export function registerMigrationTarget(protocol, target) {
70
+ targets.set(protocol, target);
71
+ }
72
+ function registryExists(database) {
73
+ return Boolean(database.prepare("SELECT 1 AS present FROM sqlite_master WHERE type='table' AND name='schema_migrations'").get());
74
+ }
75
+ function readApplied(database, component) {
76
+ if (!registryExists(database))
77
+ return null;
78
+ return database.prepare('SELECT version, checksum FROM schema_migrations WHERE component=? ORDER BY version').all(component).map(row => ({
79
+ version: Number(row.version),
80
+ checksum: String(row.checksum),
81
+ }));
82
+ }
83
+ function verifyApplied(component, applied, migrations) {
84
+ const highest = migrations.length;
85
+ if (applied.length > 0 && applied[applied.length - 1].version > highest) {
86
+ fail('PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED', `${component} carries schema generation ${applied[applied.length - 1].version}; this build understands ${highest}`);
87
+ }
88
+ for (let index = 0; index < applied.length; index++) {
89
+ if (applied[index].version !== index + 1) {
90
+ fail('PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED', `${component} migration sequence has a gap at version ${index + 1}`);
91
+ }
92
+ if (applied[index].checksum !== migrations[index].checksum) {
93
+ fail('PROTOCOL_SCHEMA_CHECKSUM_MISMATCH', `${component} migration ${applied[index].version} was rewritten after it was applied`);
94
+ }
95
+ }
96
+ return applied.length;
97
+ }
98
+ function prepareMigrations(migrations) {
99
+ if (!Array.isArray(migrations)) {
100
+ fail('PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED', 'migrations must be an array');
101
+ }
102
+ return migrations.map((migration, index) => {
103
+ if (!migration
104
+ || typeof migration !== 'object'
105
+ || migration.version !== index + 1
106
+ || typeof migration.sql !== 'string') {
107
+ fail('PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED', `migration versions must be contiguous from 1; invalid entry at ${index + 1}`);
108
+ }
109
+ return { ...migration, checksum: migrationChecksum(migration.sql) };
110
+ });
111
+ }
112
+ function validTimestamp(now) {
113
+ const value = now();
114
+ if (!Number.isSafeInteger(value) || value < 0) {
115
+ fail('PROTOCOL_SQLITE_ERROR', 'now() must return a non-negative safe integer');
116
+ }
117
+ return value;
118
+ }
119
+ function applyPending(database, component, migrations, done, now) {
120
+ const insert = database.prepare('INSERT INTO schema_migrations(component,version,checksum,applied_at_ms) VALUES(?,?,?,?)');
121
+ for (const migration of migrations.slice(done)) {
122
+ database.exec(migration.sql);
123
+ insert.run(component, migration.version, migration.checksum, validTimestamp(now));
124
+ }
125
+ }
126
+ function inImmediateTransaction(database, body) {
127
+ database.exec('BEGIN IMMEDIATE');
128
+ try {
129
+ const value = body();
130
+ database.exec('COMMIT');
131
+ return value;
132
+ }
133
+ catch (error) {
134
+ try {
135
+ database.exec('ROLLBACK');
136
+ }
137
+ catch { }
138
+ throw error;
139
+ }
140
+ }
141
+ const protocolObjectsExist = (database) => Boolean(database.prepare("SELECT 1 AS present FROM sqlite_master WHERE type='table' AND name='protocol_sessions'").get());
142
+ export function migrateProtocol(database, readOnly, now) {
143
+ const applied = readApplied(database, PROTOCOL_COMPONENT);
144
+ if (readOnly) {
145
+ if (applied === null || applied.length !== PROTOCOL_MIGRATIONS.length) {
146
+ fail('PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED', 'read-only database is not migrated to this schema generation');
147
+ }
148
+ return verifyApplied(PROTOCOL_COMPONENT, applied, PROTOCOL_MIGRATIONS);
149
+ }
150
+ if (applied !== null && verifyApplied(PROTOCOL_COMPONENT, applied, PROTOCOL_MIGRATIONS) === PROTOCOL_MIGRATIONS.length) {
151
+ return PROTOCOL_MIGRATIONS.length;
152
+ }
153
+ return inImmediateTransaction(database, () => {
154
+ database.exec(REGISTRY_SQL);
155
+ const done = verifyApplied(PROTOCOL_COMPONENT, readApplied(database, PROTOCOL_COMPONENT) ?? [], PROTOCOL_MIGRATIONS);
156
+ if (done === 0 && protocolObjectsExist(database)) {
157
+ fail('PROTOCOL_SCHEMA_REGISTRY_INCONSISTENT', 'protocol tables exist but schema_migrations does not account for them');
158
+ }
159
+ applyPending(database, PROTOCOL_COMPONENT, PROTOCOL_MIGRATIONS, done, now);
160
+ return PROTOCOL_MIGRATIONS.length;
161
+ });
162
+ }
163
+ export function migrateComponent(database, component, migrations, now) {
164
+ const prepared = prepareMigrations(migrations);
165
+ const applied = readApplied(database, component);
166
+ if (applied !== null && verifyApplied(component, applied, prepared) === prepared.length) {
167
+ return prepared.length;
168
+ }
169
+ return inImmediateTransaction(database, () => {
170
+ database.exec(REGISTRY_SQL);
171
+ const done = verifyApplied(component, readApplied(database, component) ?? [], prepared);
172
+ applyPending(database, component, prepared, done, now);
173
+ return prepared.length;
174
+ });
175
+ }
176
+ function invalidComponent(message) {
177
+ fail('PROTOCOL_TRANSACTION_INVALID', message);
178
+ }
179
+ export function applyComponentMigrations(protocol, component, migrations) {
180
+ if (typeof component !== 'string' || !/^[0-9A-Za-z._-]{1,64}$/.test(component)) {
181
+ invalidComponent('component must match [0-9A-Za-z._-]{1,64}');
182
+ }
183
+ if (component === PROTOCOL_COMPONENT) {
184
+ invalidComponent('session-protocol migrations are owned by openProtocol');
185
+ }
186
+ const target = targets.get(protocol);
187
+ if (!target)
188
+ invalidComponent('protocol must be a live handle returned by openProtocol');
189
+ if (target.readOnly)
190
+ fail('PROTOCOL_DATABASE_READONLY', 'component migrations require a writable handle');
191
+ return target.apply(component, migrations);
192
+ }
@@ -1,14 +1,13 @@
1
1
  {
2
- "name": "@spexcode/session-core",
3
- "version": "0.6.6",
2
+ "name": "@spexcode/session-protocol",
3
+ "version": "0.6.8",
4
4
  "type": "module",
5
- "description": "SpexCode's durable file-based session communication protocol.",
5
+ "description": "A durable SQLite session message protocol.",
6
6
  "files": [
7
7
  "dist"
8
8
  ],
9
9
  "exports": {
10
10
  ".": "./dist/index.js",
11
- "./internal": "./dist/internal.js",
12
11
  "./package.json": "./package.json"
13
12
  },
14
13
  "engines": {
@@ -20,10 +19,7 @@
20
19
  "scripts": {
21
20
  "build": "node ../../scripts/build-dist.mjs",
22
21
  "prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
23
- "test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts && node --test scripts/public-boundary.test.mjs"
24
- },
25
- "dependencies": {
26
- "@spexcode/spec-core": "0.6.6"
22
+ "test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
27
23
  },
28
24
  "devDependencies": {
29
25
  "@types/node": "^20.16.0",
@@ -0,0 +1,6 @@
1
+ export type RuntimeBindingErrorCode = 'RUNTIME_BINDING_TRANSACTION_INVALID' | 'RUNTIME_BINDING_NAMESPACE_INVALID' | 'RUNTIME_BINDING_SESSION_ID_INVALID' | 'RUNTIME_BINDING_SESSION_UNKNOWN' | 'RUNTIME_BINDING_SESSION_RETIRED' | 'RUNTIME_BINDING_IDENTITY_INVALID' | 'RUNTIME_BINDING_METADATA_INVALID' | 'RUNTIME_BINDING_GENERATION_REQUIRED' | 'RUNTIME_BINDING_GENERATION_STALE' | 'RUNTIME_BINDING_NOT_FOUND' | 'RUNTIME_BINDING_NOT_BOUND' | 'RUNTIME_BINDING_STORAGE';
2
+ export declare class RuntimeBindingError extends Error {
3
+ readonly code: RuntimeBindingErrorCode;
4
+ constructor(code: RuntimeBindingErrorCode, message: string, cause?: unknown);
5
+ }
6
+ export declare function failRuntimeBinding(code: RuntimeBindingErrorCode, message: string, cause?: unknown): never;
@@ -0,0 +1,11 @@
1
+ export class RuntimeBindingError extends Error {
2
+ code;
3
+ constructor(code, message, cause) {
4
+ super(message, { cause });
5
+ this.name = 'RuntimeBindingError';
6
+ this.code = code;
7
+ }
8
+ }
9
+ export function failRuntimeBinding(code, message, cause) {
10
+ throw new RuntimeBindingError(code, message, cause);
11
+ }
@@ -0,0 +1,33 @@
1
+ import type { ProtocolTransaction, SessionProtocol } from '@spexcode/session-protocol';
2
+ import { RuntimeBindingError } from './errors.js';
3
+ export interface RuntimeIdentity {
4
+ namespace: string;
5
+ runtimeKind: string;
6
+ nativeSessionId: string;
7
+ nativeStartToken: string;
8
+ metadata?: Record<string, unknown>;
9
+ }
10
+ export interface RuntimeBinding {
11
+ namespace: string;
12
+ protocolSessionId: string;
13
+ runtimeKind: string;
14
+ nativeSessionId: string;
15
+ nativeStartToken: string;
16
+ bindingGeneration: number;
17
+ status: 'bound' | 'unbound';
18
+ boundAtMs: number;
19
+ unboundAtMs: number | null;
20
+ metadata: Record<string, unknown>;
21
+ }
22
+ export interface BindingOptions {
23
+ expectedGeneration?: number;
24
+ now?: number;
25
+ }
26
+ export interface SessionRuntimeBindings {
27
+ bind(tx: ProtocolTransaction, protocolSessionId: string, identity: RuntimeIdentity, options?: BindingOptions): RuntimeBinding;
28
+ unbind(tx: ProtocolTransaction, namespace: string, protocolSessionId: string, options?: BindingOptions): RuntimeBinding;
29
+ resolve(namespace: string, protocolSessionId: string, tx?: ProtocolTransaction): RuntimeBinding | null;
30
+ }
31
+ export declare function openRuntimeBindings(protocol: SessionProtocol): SessionRuntimeBindings;
32
+ export { RuntimeBindingError };
33
+ export type { RuntimeBindingErrorCode } from './errors.js';
@@ -0,0 +1,193 @@
1
+ import { applyComponentMigrations } from '@spexcode/session-protocol';
2
+ import { failRuntimeBinding, RuntimeBindingError } from './errors.js';
3
+ import { RUNTIME_BINDINGS_MIGRATIONS } from './schema.js';
4
+ const NAMESPACE = /^[0-9A-Za-z._:/-]{1,128}$/;
5
+ const SESSION_ID = /^(?!-)[0-9A-Za-z_-]{1,256}$/;
6
+ const RUNTIME_KIND = /^[0-9A-Za-z._:-]{1,64}$/;
7
+ const MAX_METADATA_BYTES = 8192;
8
+ const SELECT_COLUMNS = `namespace, protocol_session_id, runtime_kind, native_session_id,
9
+ native_start_token, binding_generation, status, bound_at_ms, unbound_at_ms, metadata_json`;
10
+ export function openRuntimeBindings(protocol) {
11
+ applyComponentMigrations(protocol, 'session-runtime-bindings', RUNTIME_BINDINGS_MIGRATIONS);
12
+ const requireTransaction = (tx) => {
13
+ if (!tx || typeof tx.exec !== 'function' || typeof tx.query !== 'function') {
14
+ failRuntimeBinding('RUNTIME_BINDING_TRANSACTION_INVALID', 'a live protocol transaction context is required');
15
+ }
16
+ return tx;
17
+ };
18
+ const requireNamespace = (namespace) => {
19
+ if (typeof namespace !== 'string' || !NAMESPACE.test(namespace)) {
20
+ failRuntimeBinding('RUNTIME_BINDING_NAMESPACE_INVALID', 'namespace has an invalid grammar');
21
+ }
22
+ };
23
+ const requireSessionId = (sessionId) => {
24
+ if (typeof sessionId !== 'string' || !SESSION_ID.test(sessionId)) {
25
+ failRuntimeBinding('RUNTIME_BINDING_SESSION_ID_INVALID', 'protocol session id has an invalid grammar');
26
+ }
27
+ };
28
+ const requireIdentity = (identity) => {
29
+ if (!identity || typeof identity !== 'object') {
30
+ failRuntimeBinding('RUNTIME_BINDING_IDENTITY_INVALID', 'runtime identity must be an object');
31
+ }
32
+ requireNamespace(identity.namespace);
33
+ if (typeof identity.runtimeKind !== 'string' || !RUNTIME_KIND.test(identity.runtimeKind)) {
34
+ failRuntimeBinding('RUNTIME_BINDING_IDENTITY_INVALID', 'runtime kind has an invalid grammar');
35
+ }
36
+ if (typeof identity.nativeSessionId !== 'string' || identity.nativeSessionId.length < 1 || identity.nativeSessionId.length > 512) {
37
+ failRuntimeBinding('RUNTIME_BINDING_IDENTITY_INVALID', 'native session id must be nonempty and bounded');
38
+ }
39
+ if (typeof identity.nativeStartToken !== 'string' || identity.nativeStartToken.length < 1 || identity.nativeStartToken.length > 512) {
40
+ failRuntimeBinding('RUNTIME_BINDING_IDENTITY_INVALID', 'native start token must be nonempty and bounded');
41
+ }
42
+ const metadata = identity.metadata ?? {};
43
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
44
+ failRuntimeBinding('RUNTIME_BINDING_METADATA_INVALID', 'metadata must be a JSON object');
45
+ }
46
+ let encoded;
47
+ try {
48
+ encoded = JSON.stringify(metadata);
49
+ }
50
+ catch (error) {
51
+ failRuntimeBinding('RUNTIME_BINDING_METADATA_INVALID', 'metadata must be JSON serializable', error);
52
+ }
53
+ if (encoded === undefined || Buffer.byteLength(encoded, 'utf8') > MAX_METADATA_BYTES) {
54
+ failRuntimeBinding('RUNTIME_BINDING_METADATA_INVALID', 'metadata exceeds the 8192-byte limit');
55
+ }
56
+ try {
57
+ const roundTrip = JSON.parse(encoded);
58
+ if (!roundTrip || typeof roundTrip !== 'object' || Array.isArray(roundTrip)) {
59
+ failRuntimeBinding('RUNTIME_BINDING_METADATA_INVALID', 'metadata must round-trip as an object');
60
+ }
61
+ }
62
+ catch (error) {
63
+ failRuntimeBinding('RUNTIME_BINDING_METADATA_INVALID', 'metadata must be valid JSON', error);
64
+ }
65
+ return encoded;
66
+ };
67
+ const requireNow = (now) => {
68
+ const value = now ?? Date.now();
69
+ if (!Number.isSafeInteger(value) || value < 0) {
70
+ failRuntimeBinding('RUNTIME_BINDING_IDENTITY_INVALID', 'timestamp must be a non-negative safe integer');
71
+ }
72
+ return value;
73
+ };
74
+ const addressState = (tx, sessionId) => {
75
+ const rows = tx.query('SELECT retired_at_ms FROM protocol_sessions WHERE session_id=?', sessionId);
76
+ if (rows.length === 0)
77
+ failRuntimeBinding('RUNTIME_BINDING_SESSION_UNKNOWN', `unknown protocol session: ${sessionId}`);
78
+ return rows[0].retired_at_ms === null ? 'active' : 'retired';
79
+ };
80
+ const rowToBinding = (row) => {
81
+ let metadata;
82
+ try {
83
+ metadata = JSON.parse(row.metadata_json);
84
+ }
85
+ catch (error) {
86
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'stored metadata is not valid JSON', error);
87
+ }
88
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
89
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'stored metadata is not a JSON object');
90
+ }
91
+ return {
92
+ namespace: String(row.namespace),
93
+ protocolSessionId: String(row.protocol_session_id),
94
+ runtimeKind: String(row.runtime_kind),
95
+ nativeSessionId: String(row.native_session_id),
96
+ nativeStartToken: String(row.native_start_token),
97
+ bindingGeneration: Number(row.binding_generation),
98
+ status: row.status,
99
+ boundAtMs: Number(row.bound_at_ms),
100
+ unboundAtMs: row.unbound_at_ms === null ? null : Number(row.unbound_at_ms),
101
+ metadata: metadata,
102
+ };
103
+ };
104
+ const read = (tx, namespace, sessionId) => {
105
+ const rows = tx.query(`SELECT ${SELECT_COLUMNS} FROM session_runtime_bindings WHERE namespace=? AND protocol_session_id=?`, namespace, sessionId);
106
+ return rows.length === 0 ? null : rowToBinding(rows[0]);
107
+ };
108
+ const checkGeneration = (existing, expectedGeneration) => {
109
+ if (!existing) {
110
+ if (expectedGeneration !== undefined) {
111
+ failRuntimeBinding('RUNTIME_BINDING_GENERATION_STALE', 'expected generation has no binding to match');
112
+ }
113
+ return;
114
+ }
115
+ if (expectedGeneration === undefined) {
116
+ failRuntimeBinding('RUNTIME_BINDING_GENERATION_REQUIRED', 'expected generation is required for an existing binding');
117
+ }
118
+ if (!Number.isSafeInteger(expectedGeneration) || expectedGeneration !== existing.bindingGeneration) {
119
+ failRuntimeBinding('RUNTIME_BINDING_GENERATION_STALE', 'binding generation is stale');
120
+ }
121
+ };
122
+ const bind = (txInput, protocolSessionId, identity, options = {}) => {
123
+ const tx = requireTransaction(txInput);
124
+ requireSessionId(protocolSessionId);
125
+ const metadataJson = requireIdentity(identity);
126
+ if (addressState(tx, protocolSessionId) === 'retired') {
127
+ failRuntimeBinding('RUNTIME_BINDING_SESSION_RETIRED', `protocol session is retired: ${protocolSessionId}`);
128
+ }
129
+ const existing = read(tx, identity.namespace, protocolSessionId);
130
+ checkGeneration(existing, options.expectedGeneration);
131
+ const generation = existing ? existing.bindingGeneration + 1 : 1;
132
+ const now = requireNow(options.now);
133
+ try {
134
+ if (existing) {
135
+ tx.exec(`UPDATE session_runtime_bindings SET runtime_kind=?, native_session_id=?, native_start_token=?,
136
+ binding_generation=?, status='bound', bound_at_ms=?, unbound_at_ms=NULL, metadata_json=?
137
+ WHERE namespace=? AND protocol_session_id=?`, identity.runtimeKind, identity.nativeSessionId, identity.nativeStartToken, generation, now, metadataJson, identity.namespace, protocolSessionId);
138
+ }
139
+ else {
140
+ tx.exec(`INSERT INTO session_runtime_bindings
141
+ (namespace, protocol_session_id, runtime_kind, native_session_id, native_start_token,
142
+ binding_generation, status, bound_at_ms, unbound_at_ms, metadata_json)
143
+ VALUES (?,?,?,?,?,?, 'bound', ?, NULL, ?)`, identity.namespace, protocolSessionId, identity.runtimeKind, identity.nativeSessionId, identity.nativeStartToken, generation, now, metadataJson);
144
+ }
145
+ }
146
+ catch (error) {
147
+ if (error instanceof RuntimeBindingError)
148
+ throw error;
149
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'failed to persist runtime binding', error);
150
+ }
151
+ const result = read(tx, identity.namespace, protocolSessionId);
152
+ if (!result)
153
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'binding disappeared after bind');
154
+ return result;
155
+ };
156
+ const unbind = (txInput, namespace, protocolSessionId, options = {}) => {
157
+ const tx = requireTransaction(txInput);
158
+ requireNamespace(namespace);
159
+ requireSessionId(protocolSessionId);
160
+ if (addressState(tx, protocolSessionId) === 'retired') {
161
+ failRuntimeBinding('RUNTIME_BINDING_SESSION_RETIRED', `protocol session is retired: ${protocolSessionId}`);
162
+ }
163
+ const existing = read(tx, namespace, protocolSessionId);
164
+ if (!existing)
165
+ failRuntimeBinding('RUNTIME_BINDING_NOT_FOUND', 'runtime binding does not exist');
166
+ checkGeneration(existing, options.expectedGeneration);
167
+ if (existing.status !== 'bound')
168
+ failRuntimeBinding('RUNTIME_BINDING_NOT_BOUND', 'runtime binding is already unbound');
169
+ const now = requireNow(options.now);
170
+ try {
171
+ tx.exec(`UPDATE session_runtime_bindings SET status='unbound', binding_generation=?, unbound_at_ms=?
172
+ WHERE namespace=? AND protocol_session_id=? AND binding_generation=?`, existing.bindingGeneration + 1, now, namespace, protocolSessionId, existing.bindingGeneration);
173
+ }
174
+ catch (error) {
175
+ if (error instanceof RuntimeBindingError)
176
+ throw error;
177
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'failed to persist runtime unbinding', error);
178
+ }
179
+ const result = read(tx, namespace, protocolSessionId);
180
+ if (!result)
181
+ failRuntimeBinding('RUNTIME_BINDING_STORAGE', 'binding disappeared after unbind');
182
+ return result;
183
+ };
184
+ const resolve = (namespace, protocolSessionId, txInput) => {
185
+ requireNamespace(namespace);
186
+ requireSessionId(protocolSessionId);
187
+ if (txInput)
188
+ return read(requireTransaction(txInput), namespace, protocolSessionId);
189
+ return protocol.withTransaction(tx => read(tx, namespace, protocolSessionId));
190
+ };
191
+ return { bind, unbind, resolve };
192
+ }
193
+ export { RuntimeBindingError };
@@ -0,0 +1,2 @@
1
+ import type { ComponentMigration } from '@spexcode/session-protocol';
2
+ export declare const RUNTIME_BINDINGS_MIGRATIONS: readonly ComponentMigration[];
@@ -0,0 +1,37 @@
1
+ export const RUNTIME_BINDINGS_MIGRATIONS = [
2
+ {
3
+ version: 1,
4
+ sql: `
5
+ CREATE TABLE session_runtime_bindings (
6
+ namespace TEXT NOT NULL,
7
+ protocol_session_id TEXT NOT NULL REFERENCES protocol_sessions(session_id),
8
+ runtime_kind TEXT NOT NULL,
9
+ native_session_id TEXT NOT NULL,
10
+ native_start_token TEXT NOT NULL,
11
+ binding_generation INTEGER NOT NULL,
12
+ status TEXT NOT NULL,
13
+ bound_at_ms INTEGER NOT NULL,
14
+ unbound_at_ms INTEGER,
15
+ metadata_json TEXT NOT NULL,
16
+ PRIMARY KEY (namespace, protocol_session_id),
17
+ CHECK (length(namespace) BETWEEN 1 AND 128),
18
+ CHECK (namespace NOT GLOB '*[^0-9A-Za-z._:/-]*'),
19
+ CHECK (length(runtime_kind) BETWEEN 1 AND 64),
20
+ CHECK (runtime_kind NOT GLOB '*[^0-9A-Za-z._:-]*'),
21
+ CHECK (length(native_session_id) BETWEEN 1 AND 512),
22
+ CHECK (length(native_start_token) BETWEEN 1 AND 512),
23
+ CHECK (binding_generation >= 1),
24
+ CHECK (status IN ('bound', 'unbound')),
25
+ CHECK (bound_at_ms >= 0),
26
+ CHECK (unbound_at_ms IS NULL OR unbound_at_ms >= bound_at_ms),
27
+ CHECK (json_valid(metadata_json) AND json_type(metadata_json) = 'object'),
28
+ CHECK (length(metadata_json) <= 8192),
29
+ CHECK ((status = 'bound' AND unbound_at_ms IS NULL)
30
+ OR (status = 'unbound' AND unbound_at_ms IS NOT NULL))
31
+ ) STRICT;
32
+
33
+ CREATE INDEX session_runtime_bindings_native
34
+ ON session_runtime_bindings (runtime_kind, native_session_id, status);
35
+ `,
36
+ },
37
+ ];
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@spexcode/session-runtime",
3
+ "version": "0.6.8",
4
+ "publishConfig": { "access": "public" },
5
+ "type": "module",
6
+ "description": "Adopter-owned bindings between protocol addresses and native runtime identities.",
7
+ "files": ["dist"],
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "engines": { "node": ">=22" },
13
+ "scripts": {
14
+ "build": "node ../../scripts/build-dist.mjs",
15
+ "prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
16
+ "test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
17
+ },
18
+ "dependencies": { "@spexcode/session-protocol": "0.6.8" },
19
+ "devDependencies": {
20
+ "@types/node": "^20.16.0",
21
+ "tsx": "^4.19.2",
22
+ "typescript": "^5.6.3"
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../dist/cli.js'
3
+
4
+ process.exitCode = await runCli()
@@ -0,0 +1,25 @@
1
+ import type { SessionProtocol } from '@spexcode/session-protocol';
2
+ import type { SelfLaunchEnvironment } from './path.js';
3
+ type CommandName = 'initialize' | 'enqueue' | 'dequeue' | 'pending';
4
+ interface ParsedCommand {
5
+ readonly command: CommandName;
6
+ readonly values: ReadonlyMap<string, string>;
7
+ readonly headers: readonly [string, string][];
8
+ readonly assumeLocal: boolean;
9
+ }
10
+ export declare function parseCommand(argv: readonly string[]): ParsedCommand;
11
+ interface CliDependencies {
12
+ readonly open: (databasePath: string) => SessionProtocol;
13
+ readonly requireLocal: (databasePath: string, options: {
14
+ assumeLocal?: boolean;
15
+ }) => string;
16
+ }
17
+ export interface CliRunOptions {
18
+ readonly argv?: readonly string[];
19
+ readonly env?: SelfLaunchEnvironment;
20
+ readonly stdout?: (text: string) => void;
21
+ readonly stderr?: (text: string) => void;
22
+ readonly dependencies?: CliDependencies;
23
+ }
24
+ export declare function runCli(options?: CliRunOptions): Promise<number>;
25
+ export {};