@coralai/nps-cli 0.1.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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +459 -0
  3. package/dist/audit-log.d.ts +26 -0
  4. package/dist/audit-log.d.ts.map +1 -0
  5. package/dist/audit-log.js +120 -0
  6. package/dist/audit-log.js.map +1 -0
  7. package/dist/bindings/bindings-loader.d.ts +19 -0
  8. package/dist/bindings/bindings-loader.d.ts.map +1 -0
  9. package/dist/bindings/bindings-loader.js +84 -0
  10. package/dist/bindings/bindings-loader.js.map +1 -0
  11. package/dist/cli/agent-cmd.d.ts +2 -0
  12. package/dist/cli/agent-cmd.d.ts.map +1 -0
  13. package/dist/cli/agent-cmd.js +125 -0
  14. package/dist/cli/agent-cmd.js.map +1 -0
  15. package/dist/cli/bind-cmd.d.ts +2 -0
  16. package/dist/cli/bind-cmd.d.ts.map +1 -0
  17. package/dist/cli/bind-cmd.js +127 -0
  18. package/dist/cli/bind-cmd.js.map +1 -0
  19. package/dist/cli/daemon-cmd.d.ts +4 -0
  20. package/dist/cli/daemon-cmd.d.ts.map +1 -0
  21. package/dist/cli/daemon-cmd.js +137 -0
  22. package/dist/cli/daemon-cmd.js.map +1 -0
  23. package/dist/cli/index.d.ts +2 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +62 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/config/loader.d.ts +5 -0
  28. package/dist/config/loader.d.ts.map +1 -0
  29. package/dist/config/loader.js +54 -0
  30. package/dist/config/loader.js.map +1 -0
  31. package/dist/config/paths.d.ts +14 -0
  32. package/dist/config/paths.d.ts.map +1 -0
  33. package/dist/config/paths.js +27 -0
  34. package/dist/config/paths.js.map +1 -0
  35. package/dist/config/schema.d.ts +48 -0
  36. package/dist/config/schema.d.ts.map +1 -0
  37. package/dist/config/schema.js +52 -0
  38. package/dist/config/schema.js.map +1 -0
  39. package/dist/daemon/control-client.d.ts +2 -0
  40. package/dist/daemon/control-client.d.ts.map +1 -0
  41. package/dist/daemon/control-client.js +51 -0
  42. package/dist/daemon/control-client.js.map +1 -0
  43. package/dist/daemon/control-socket.d.ts +19 -0
  44. package/dist/daemon/control-socket.d.ts.map +1 -0
  45. package/dist/daemon/control-socket.js +192 -0
  46. package/dist/daemon/control-socket.js.map +1 -0
  47. package/dist/daemon/daemon.d.ts +20 -0
  48. package/dist/daemon/daemon.d.ts.map +1 -0
  49. package/dist/daemon/daemon.js +166 -0
  50. package/dist/daemon/daemon.js.map +1 -0
  51. package/dist/dispatch/dispatch-pipeline.d.ts +39 -0
  52. package/dist/dispatch/dispatch-pipeline.d.ts.map +1 -0
  53. package/dist/dispatch/dispatch-pipeline.js +219 -0
  54. package/dist/dispatch/dispatch-pipeline.js.map +1 -0
  55. package/dist/dispatch/slash-commands.d.ts +35 -0
  56. package/dist/dispatch/slash-commands.d.ts.map +1 -0
  57. package/dist/dispatch/slash-commands.js +67 -0
  58. package/dist/dispatch/slash-commands.js.map +1 -0
  59. package/dist/errors.d.ts +22 -0
  60. package/dist/errors.d.ts.map +1 -0
  61. package/dist/errors.js +17 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/gateway/matrix-gateway.d.ts +24 -0
  64. package/dist/gateway/matrix-gateway.d.ts.map +1 -0
  65. package/dist/gateway/matrix-gateway.js +166 -0
  66. package/dist/gateway/matrix-gateway.js.map +1 -0
  67. package/dist/logger.d.ts +13 -0
  68. package/dist/logger.d.ts.map +1 -0
  69. package/dist/logger.js +19 -0
  70. package/dist/logger.js.map +1 -0
  71. package/dist/main.d.ts +3 -0
  72. package/dist/main.d.ts.map +1 -0
  73. package/dist/main.js +7 -0
  74. package/dist/main.js.map +1 -0
  75. package/dist/profile/profile-registry.d.ts +24 -0
  76. package/dist/profile/profile-registry.d.ts.map +1 -0
  77. package/dist/profile/profile-registry.js +114 -0
  78. package/dist/profile/profile-registry.js.map +1 -0
  79. package/dist/runtime-check.d.ts +18 -0
  80. package/dist/runtime-check.d.ts.map +1 -0
  81. package/dist/runtime-check.js +62 -0
  82. package/dist/runtime-check.js.map +1 -0
  83. package/dist/session/compat-hash.d.ts +21 -0
  84. package/dist/session/compat-hash.d.ts.map +1 -0
  85. package/dist/session/compat-hash.js +115 -0
  86. package/dist/session/compat-hash.js.map +1 -0
  87. package/dist/session/session-store.d.ts +29 -0
  88. package/dist/session/session-store.d.ts.map +1 -0
  89. package/dist/session/session-store.js +123 -0
  90. package/dist/session/session-store.js.map +1 -0
  91. package/dist/version.d.ts +2 -0
  92. package/dist/version.d.ts.map +1 -0
  93. package/dist/version.js +2 -0
  94. package/dist/version.js.map +1 -0
  95. package/package.json +59 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * M5b — MatrixGateway production.
3
+ *
4
+ * Direct matrix-js-sdk@41 client (lib decision from M5a). DM-only:
5
+ * - encrypted rooms refused with reply
6
+ * - multi-participant rooms refused
7
+ * - final-only reply (no streaming) per CEO plan rev 5 Q2 resolution
8
+ * - typing indicator during agent run
9
+ * - basic reconnect on transient errors
10
+ *
11
+ * Token revoke is detected via 401 — gateway logs but does NOT crash daemon
12
+ * (so admin can fix config and `nps daemon stop && start`).
13
+ */
14
+ import { createClient, RoomEvent } from 'matrix-js-sdk';
15
+ import { getLogger } from '../logger.js';
16
+ export class MatrixGateway {
17
+ config;
18
+ pipeline;
19
+ logger;
20
+ client;
21
+ dispatching = false;
22
+ stopRequested = false;
23
+ constructor(config, pipeline, logger) {
24
+ this.config = config;
25
+ this.pipeline = pipeline;
26
+ this.logger = logger ?? getLogger('matrix-gateway');
27
+ }
28
+ async start() {
29
+ this.client = createClient({
30
+ baseUrl: this.config.homeserver,
31
+ accessToken: this.config.accessToken,
32
+ userId: this.config.userId,
33
+ });
34
+ await new Promise((resolve, reject) => {
35
+ const onSync = (state) => {
36
+ if (state === 'PREPARED' || state === 'SYNCING') {
37
+ this.client?.removeListener('sync', onSync);
38
+ resolve();
39
+ }
40
+ else if (state === 'ERROR') {
41
+ this.client?.removeListener('sync', onSync);
42
+ reject(new Error('sync entered ERROR state — check Matrix token / homeserver'));
43
+ }
44
+ };
45
+ this.client?.on('sync', onSync);
46
+ void this.client?.startClient({ initialSyncLimit: 5 }).catch(reject);
47
+ setTimeout(() => reject(new Error('sync timeout after 30s')), 30_000);
48
+ });
49
+ this.client?.on(RoomEvent.Timeline, (event, room) => this.onTimeline(event, room));
50
+ this.logger.info({ homeserver: this.config.homeserver, userId: this.config.userId }, 'matrix gateway connected');
51
+ }
52
+ async stop() {
53
+ this.stopRequested = true;
54
+ if (this.client) {
55
+ try {
56
+ this.client.stopClient();
57
+ }
58
+ catch { /* ignore */ }
59
+ this.client = undefined;
60
+ }
61
+ this.logger.info('matrix gateway stopped');
62
+ }
63
+ onTimeline(event, room) {
64
+ if (this.stopRequested || !this.client || !room)
65
+ return;
66
+ if (event.getType() !== 'm.room.message')
67
+ return;
68
+ // Ignore historic events from initial sync
69
+ if (event.getTs() < Date.now() - 60_000)
70
+ return;
71
+ // Ignore our own messages
72
+ if (event.getSender() === this.config.userId)
73
+ return;
74
+ // Ignore non-text content
75
+ const content = event.getContent();
76
+ if (content.msgtype !== 'm.text' || typeof content.body !== 'string')
77
+ return;
78
+ void this.handleMessage(event, room);
79
+ }
80
+ async handleMessage(event, room) {
81
+ if (this.dispatching) {
82
+ // v0.1 single-flight: queue or reject? Plan says single-user, so reject with reply.
83
+ this.logger.info({ eventId: event.getId(), room: room.roomId }, 'busy with prior turn — replying busy');
84
+ try {
85
+ await this.client?.sendTextMessage(room.roomId, '⏳ previous turn still in progress, please wait');
86
+ }
87
+ catch { /* ignore */ }
88
+ return;
89
+ }
90
+ // DM-only enforcement
91
+ const memberCount = room.getJoinedMembers().length;
92
+ if (memberCount !== 2) {
93
+ this.logger.info({ room: room.roomId, members: memberCount }, 'refusing non-DM room');
94
+ try {
95
+ await this.client?.sendTextMessage(room.roomId, '⛔ nps v0.1 supports direct messages only. Multi-participant rooms are not yet supported.');
96
+ }
97
+ catch { /* ignore */ }
98
+ return;
99
+ }
100
+ // Encryption refusal
101
+ const isEncrypted = !!room.currentState.getStateEvents('m.room.encryption', '');
102
+ if (isEncrypted) {
103
+ this.logger.info({ room: room.roomId }, 'refusing encrypted room');
104
+ try {
105
+ await this.client?.sendTextMessage(room.roomId, '🔒 nps v0.1 ships with E2EE disabled. Please use an unencrypted DM.');
106
+ }
107
+ catch { /* ignore */ }
108
+ return;
109
+ }
110
+ this.dispatching = true;
111
+ try {
112
+ const sender = event.getSender();
113
+ const eventId = event.getId();
114
+ const text = event.getContent()['body'];
115
+ if (!sender || !eventId) {
116
+ this.logger.warn({ eventId, sender }, 'incoming event missing required fields');
117
+ return;
118
+ }
119
+ const incoming = {
120
+ channelId: room.roomId,
121
+ eventId,
122
+ text,
123
+ sender: `matrix:${sender}`,
124
+ transport: 'matrix',
125
+ };
126
+ const reply = this.makeReplyTarget(room);
127
+ await this.pipeline.dispatch(incoming, reply);
128
+ }
129
+ catch (e) {
130
+ this.logger.error({ err: e instanceof Error ? e.message : e }, 'dispatch failed');
131
+ }
132
+ finally {
133
+ this.dispatching = false;
134
+ }
135
+ }
136
+ makeReplyTarget(room) {
137
+ const client = this.client;
138
+ if (!client) {
139
+ return { send: async () => { } };
140
+ }
141
+ const typingMs = this.config.typingTimeoutMs ?? 20_000;
142
+ return {
143
+ startTyping: async () => {
144
+ try {
145
+ await client.sendTyping(room.roomId, true, typingMs);
146
+ }
147
+ catch { /* ignore */ }
148
+ },
149
+ stopTyping: async () => {
150
+ try {
151
+ await client.sendTyping(room.roomId, false, 0);
152
+ }
153
+ catch { /* ignore */ }
154
+ },
155
+ send: async (text) => {
156
+ try {
157
+ await client.sendTextMessage(room.roomId, text);
158
+ }
159
+ catch (e) {
160
+ this.logger.error({ err: e instanceof Error ? e.message : e, room: room.roomId }, 'sendTextMessage failed');
161
+ }
162
+ },
163
+ };
164
+ }
165
+ }
166
+ //# sourceMappingURL=matrix-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matrix-gateway.js","sourceRoot":"","sources":["../../src/gateway/matrix-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * M13 — structured logger.
3
+ *
4
+ * pino → stdout. Under systemd, journald captures stdout; `journalctl --user -u nps.service`
5
+ * shows JSON lines. Outside systemd, plain JSON to stdout.
6
+ *
7
+ * Module-level child loggers via `getLogger(name)` for namespaced output.
8
+ */
9
+ import { type Logger } from 'pino';
10
+ declare const rootLogger: Logger<never, boolean>;
11
+ export declare function getLogger(name: string): Logger;
12
+ export { rootLogger };
13
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAEzC,QAAA,MAAM,UAAU,wBAId,CAAC;AAEH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * M13 — structured logger.
3
+ *
4
+ * pino → stdout. Under systemd, journald captures stdout; `journalctl --user -u nps.service`
5
+ * shows JSON lines. Outside systemd, plain JSON to stdout.
6
+ *
7
+ * Module-level child loggers via `getLogger(name)` for namespaced output.
8
+ */
9
+ import { pino } from 'pino';
10
+ const rootLogger = pino({
11
+ level: process.env['NPS_LOG_LEVEL'] ?? 'info',
12
+ base: { app: 'nps-cli' },
13
+ timestamp: pino.stdTimeFunctions.isoTime,
14
+ });
15
+ export function getLogger(name) {
16
+ return rootLogger.child({ mod: name });
17
+ }
18
+ export { rootLogger };
19
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAe,MAAM,MAAM,CAAC;AAEzC,MAAM,UAAU,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,MAAM;IAC7C,IAAI,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE;IACxB,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO;CACzC,CAAC,CAAC;AAEH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
package/dist/main.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * nps — entrypoint. Routes to src/cli/index.ts.
4
+ */
5
+ import { runCli } from './cli/index.js';
6
+ void runCli(process.argv).then(code => process.exit(code));
7
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,KAAK,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { Logger } from 'pino';
3
+ import type { ProfileConfig } from '../config/schema.js';
4
+ export interface ProfileEntry {
5
+ name: string;
6
+ dir: string;
7
+ config: ProfileConfig;
8
+ }
9
+ export declare class ProfileRegistry extends EventEmitter {
10
+ private readonly agentsDir;
11
+ private logger;
12
+ private profiles;
13
+ private watcher;
14
+ constructor(agentsDir: string, logger?: Logger);
15
+ /** Initial scan + start watcher. */
16
+ start(): Promise<void>;
17
+ stop(): Promise<void>;
18
+ /** Scan once, replacing the entire in-memory map. */
19
+ private scan;
20
+ private onFsEvent;
21
+ list(): ProfileEntry[];
22
+ get(name: string): ProfileEntry | undefined;
23
+ }
24
+ //# sourceMappingURL=profile-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-registry.d.ts","sourceRoot":"","sources":["../../src/profile/profile-registry.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAM7C,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,OAAO,CAAwB;gBAGpB,SAAS,EAAE,MAAM,EAClC,MAAM,CAAC,EAAE,MAAM;IAMjB,oCAAoC;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,qDAAqD;IACrD,OAAO,CAAC,IAAI;IA4BZ,OAAO,CAAC,SAAS;IAwBjB,IAAI,IAAI,YAAY,EAAE;IAItB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAG5C"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * M2 — ProfileRegistry.
3
+ *
4
+ * Scans ~/.nps/agents/ for valid profile directories (containing nps.yaml)
5
+ * and watches via chokidar for add/remove/edit. Exposes:
6
+ * - list(): all current profile names
7
+ * - get(name): the loaded ProfileConfig or undefined
8
+ * - on('change' | 'added' | 'removed', cb): subscribe to changes
9
+ */
10
+ import { existsSync, readdirSync, statSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { EventEmitter } from 'node:events';
13
+ import chokidar from 'chokidar';
14
+ import { getLogger } from '../logger.js';
15
+ import { loadProfileConfig } from '../config/loader.js';
16
+ export class ProfileRegistry extends EventEmitter {
17
+ agentsDir;
18
+ logger;
19
+ profiles = new Map();
20
+ watcher;
21
+ constructor(agentsDir, logger) {
22
+ super();
23
+ this.agentsDir = agentsDir;
24
+ this.logger = logger ?? getLogger('profile-registry');
25
+ }
26
+ /** Initial scan + start watcher. */
27
+ async start() {
28
+ this.scan();
29
+ if (!existsSync(this.agentsDir)) {
30
+ this.logger.warn({ agentsDir: this.agentsDir }, 'agents directory does not exist yet — watching parent for creation');
31
+ }
32
+ this.watcher = chokidar.watch(this.agentsDir, {
33
+ ignoreInitial: true,
34
+ // Watch the nps.yaml file inside each profile dir to detect edits
35
+ depth: 2,
36
+ persistent: true,
37
+ });
38
+ this.watcher.on('add', path => this.onFsEvent('add', path));
39
+ this.watcher.on('change', path => this.onFsEvent('change', path));
40
+ this.watcher.on('unlink', path => this.onFsEvent('unlink', path));
41
+ this.watcher.on('addDir', path => this.onFsEvent('addDir', path));
42
+ this.watcher.on('unlinkDir', path => this.onFsEvent('unlinkDir', path));
43
+ this.logger.info({ agentsDir: this.agentsDir, count: this.profiles.size }, 'profile registry started');
44
+ }
45
+ async stop() {
46
+ if (this.watcher) {
47
+ await this.watcher.close();
48
+ this.watcher = undefined;
49
+ }
50
+ }
51
+ /** Scan once, replacing the entire in-memory map. */
52
+ scan() {
53
+ const found = new Map();
54
+ if (!existsSync(this.agentsDir)) {
55
+ this.profiles = found;
56
+ return;
57
+ }
58
+ let entries = [];
59
+ try {
60
+ entries = readdirSync(this.agentsDir);
61
+ }
62
+ catch (e) {
63
+ this.logger.error({ err: e instanceof Error ? e.message : e }, 'failed to read agents dir');
64
+ return;
65
+ }
66
+ for (const name of entries) {
67
+ const dir = join(this.agentsDir, name);
68
+ try {
69
+ if (!statSync(dir).isDirectory())
70
+ continue;
71
+ const yamlPath = join(dir, 'nps.yaml');
72
+ if (!existsSync(yamlPath))
73
+ continue;
74
+ const config = loadProfileConfig(yamlPath);
75
+ found.set(name, { name, dir, config });
76
+ }
77
+ catch (e) {
78
+ this.logger.warn({ profile: name, err: e instanceof Error ? e.message : e }, 'skip invalid profile');
79
+ }
80
+ }
81
+ this.profiles = found;
82
+ }
83
+ onFsEvent(event, path) {
84
+ // Simple strategy: re-scan on any nps.yaml-relevant event. Cheap with <100 profiles.
85
+ if (!path.includes(this.agentsDir))
86
+ return;
87
+ const before = new Set(this.profiles.keys());
88
+ this.scan();
89
+ const after = new Set(this.profiles.keys());
90
+ for (const name of after) {
91
+ if (!before.has(name)) {
92
+ this.logger.info({ profile: name, event }, 'profile added');
93
+ this.emit('added', name, this.profiles.get(name));
94
+ }
95
+ else if (event === 'change') {
96
+ // re-emit change for edits
97
+ this.emit('change', name, this.profiles.get(name));
98
+ }
99
+ }
100
+ for (const name of before) {
101
+ if (!after.has(name)) {
102
+ this.logger.info({ profile: name, event }, 'profile removed');
103
+ this.emit('removed', name);
104
+ }
105
+ }
106
+ }
107
+ list() {
108
+ return Array.from(this.profiles.values());
109
+ }
110
+ get(name) {
111
+ return this.profiles.get(name);
112
+ }
113
+ }
114
+ //# sourceMappingURL=profile-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-registry.js","sourceRoot":"","sources":["../../src/profile/profile-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Logger } from 'pino';
2
+ export interface RuntimeCheckResult {
3
+ ok: boolean;
4
+ claude: {
5
+ ok: boolean;
6
+ path?: string;
7
+ version?: string;
8
+ };
9
+ shim: {
10
+ ok: boolean;
11
+ path?: string;
12
+ version?: string;
13
+ };
14
+ }
15
+ export declare function checkRuntime(): RuntimeCheckResult;
16
+ /** Run the boot check and throw an NpsError with actionable guidance if anything is missing. */
17
+ export declare function assertRuntime(logger?: Logger): RuntimeCheckResult;
18
+ //# sourceMappingURL=runtime-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-check.d.ts","sourceRoot":"","sources":["../src/runtime-check.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,IAAI,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxD;AAqBD,wBAAgB,YAAY,IAAI,kBAAkB,CAgBjD;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAkBjE"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * M10 — boot-time runtime check.
3
+ *
4
+ * Before nps daemon starts, verify:
5
+ * - `claude` binary on PATH
6
+ * - `claude-agent-acp` shim on PATH
7
+ *
8
+ * On miss, print actionable install instructions and exit 1.
9
+ */
10
+ import { execFileSync } from 'node:child_process';
11
+ import { NpsError } from './errors.js';
12
+ function which(bin) {
13
+ try {
14
+ const out = execFileSync('which', [bin], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
15
+ const path = out.trim();
16
+ return path.length > 0 ? path : undefined;
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ }
22
+ function tryVersion(path, flag = '--version') {
23
+ try {
24
+ const out = execFileSync(path, [flag], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 });
25
+ return out.trim().split('\n')[0];
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ }
31
+ export function checkRuntime() {
32
+ const claudePath = which('claude');
33
+ const shimPath = which('claude-agent-acp');
34
+ return {
35
+ ok: !!claudePath && !!shimPath,
36
+ claude: {
37
+ ok: !!claudePath,
38
+ ...(claudePath ? { path: claudePath } : {}),
39
+ ...(claudePath ? { version: tryVersion(claudePath) ?? 'unknown' } : {}),
40
+ },
41
+ shim: {
42
+ ok: !!shimPath,
43
+ ...(shimPath ? { path: shimPath } : {}),
44
+ ...(shimPath ? { version: tryVersion(shimPath) ?? 'unknown' } : {}),
45
+ },
46
+ };
47
+ }
48
+ /** Run the boot check and throw an NpsError with actionable guidance if anything is missing. */
49
+ export function assertRuntime(logger) {
50
+ const r = checkRuntime();
51
+ if (!r.claude.ok) {
52
+ logger?.error('claude binary missing on PATH');
53
+ throw new NpsError('CLAUDE_BIN_MISSING', `claude binary not found on PATH. Install Claude Code from https://claude.ai/download or run: npm i -g @anthropic-ai/claude-code`);
54
+ }
55
+ if (!r.shim.ok) {
56
+ logger?.error('claude-agent-acp shim missing on PATH');
57
+ throw new NpsError('ACP_SHIM_MISSING', `claude-agent-acp shim not found on PATH. Install with: npm i -g @agentclientprotocol/claude-agent-acp`);
58
+ }
59
+ logger?.info({ claude: r.claude, shim: r.shim }, 'runtime check passed');
60
+ return r;
61
+ }
62
+ //# sourceMappingURL=runtime-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-check.js","sourceRoot":"","sources":["../src/runtime-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface CompatHashInputs {
2
+ /** Profile directory absolute path (contains nps.yaml + CLAUDE.md + .claude/skills/) */
3
+ profileDir: string;
4
+ /** Pre-fetched claude binary version (cached at daemon boot) */
5
+ claudeVersion: string;
6
+ /** Pre-fetched claude-agent-acp shim version */
7
+ shimVersion: string;
8
+ /** @coralai/claude-code-agent npm package version (cached at boot) */
9
+ agentPkgVersion: string;
10
+ }
11
+ /**
12
+ * Compute compatHash for a profile state. Throws COMPAT_HASH_FAILED on any
13
+ * unrecoverable read error (per plan rev 5 — fail-loud, never silently fresh-start).
14
+ */
15
+ export declare function computeCompatHash(input: CompatHashInputs): string;
16
+ /** Cache claude / claude-agent-acp version once at daemon boot to keep hash stable. */
17
+ export declare function cacheRuntimeVersions(): {
18
+ claudeVersion: string;
19
+ shimVersion: string;
20
+ };
21
+ //# sourceMappingURL=compat-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat-hash.d.ts","sourceRoot":"","sources":["../../src/session/compat-hash.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CA2CjE;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,IAAI;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAKrF"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * compatHash — per CEO plan rev 5 spec.
3
+ *
4
+ * sha256 of canonical-ordered bytes:
5
+ * 1. profile.yaml (sorted-keys yaml dump, LF EOL)
6
+ * 2. CLAUDE.md (utf-8, LF EOL)
7
+ * 3. skills/<name>/SKILL.md, sorted by filename
8
+ * 4. @coralai/claude-code-agent npm version
9
+ * 5. claude --version
10
+ * 6. claude-agent-acp --version
11
+ *
12
+ * Each input is bracketed by `\n--SEP--\n` separator.
13
+ * Missing file → literal "<MISSING>" + path marker.
14
+ * Workspace contents NOT in hash (intentionally — task layer, not identity).
15
+ */
16
+ import { createHash } from 'node:crypto';
17
+ import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
18
+ import { execFileSync } from 'node:child_process';
19
+ import { join } from 'node:path';
20
+ import { stringify as yamlStringify, parse as yamlParse } from 'yaml';
21
+ import { NpsError } from '../errors.js';
22
+ const SEP = '\n--SEP--\n';
23
+ /**
24
+ * Compute compatHash for a profile state. Throws COMPAT_HASH_FAILED on any
25
+ * unrecoverable read error (per plan rev 5 — fail-loud, never silently fresh-start).
26
+ */
27
+ export function computeCompatHash(input) {
28
+ const buf = [];
29
+ // 1. profile.yaml — canonicalize via yaml parse → yaml dump (sorted keys via {sortMapEntries})
30
+ const profileYamlPath = join(input.profileDir, 'nps.yaml');
31
+ buf.push(canonicalYaml(profileYamlPath));
32
+ buf.push(SEP);
33
+ // 2. CLAUDE.md
34
+ buf.push(readCanonical(join(input.profileDir, 'CLAUDE.md')));
35
+ buf.push(SEP);
36
+ // 3. skills/<x>/SKILL.md — sorted filenames
37
+ const skillsDir = join(input.profileDir, '.claude', 'skills');
38
+ if (existsSync(skillsDir)) {
39
+ let names = [];
40
+ try {
41
+ names = readdirSync(skillsDir).sort();
42
+ }
43
+ catch (e) {
44
+ throw new NpsError('COMPAT_HASH_FAILED', `failed to list skills dir ${skillsDir}: ${e instanceof Error ? e.message : e}`);
45
+ }
46
+ for (const name of names) {
47
+ const skillFile = join(skillsDir, name, 'SKILL.md');
48
+ buf.push(`SKILL:${name}\n`);
49
+ buf.push(readCanonical(skillFile));
50
+ buf.push(SEP);
51
+ }
52
+ }
53
+ else {
54
+ buf.push('SKILLS:<MISSING>\n');
55
+ buf.push(SEP);
56
+ }
57
+ // 4. claude-code-agent npm version
58
+ buf.push(`CCA:${input.agentPkgVersion}`);
59
+ buf.push(SEP);
60
+ // 5. claude --version
61
+ buf.push(`CLAUDE:${input.claudeVersion}`);
62
+ buf.push(SEP);
63
+ // 6. claude-agent-acp --version
64
+ buf.push(`SHIM:${input.shimVersion}`);
65
+ return createHash('sha256').update(buf.join(''), 'utf8').digest('hex');
66
+ }
67
+ /** Cache claude / claude-agent-acp version once at daemon boot to keep hash stable. */
68
+ export function cacheRuntimeVersions() {
69
+ return {
70
+ claudeVersion: cliVersion('claude'),
71
+ shimVersion: cliVersion('claude-agent-acp'),
72
+ };
73
+ }
74
+ function cliVersion(bin) {
75
+ try {
76
+ const out = execFileSync(bin, ['--version'], {
77
+ encoding: 'utf8',
78
+ stdio: ['ignore', 'pipe', 'ignore'],
79
+ timeout: 5000,
80
+ });
81
+ return out.trim().split('\n')[0] ?? '<unknown>';
82
+ }
83
+ catch (e) {
84
+ throw new NpsError('COMPAT_HASH_FAILED', `failed to fetch ${bin} --version: ${e instanceof Error ? e.message : e}`);
85
+ }
86
+ }
87
+ /** Read file with LF normalization. On missing file, return "<MISSING>" marker. */
88
+ function readCanonical(filePath) {
89
+ if (!existsSync(filePath)) {
90
+ return `<MISSING>:${filePath}`;
91
+ }
92
+ try {
93
+ // Resolve symlinks to target content per plan spec.
94
+ const real = realpathSync(filePath);
95
+ const content = readFileSync(real, 'utf8');
96
+ return content.replace(/\r\n?/g, '\n');
97
+ }
98
+ catch (e) {
99
+ throw new NpsError('COMPAT_HASH_FAILED', `failed to read ${filePath}: ${e instanceof Error ? e.message : e}`);
100
+ }
101
+ }
102
+ /** Re-emit yaml with sorted keys + LF. Used for profile.yaml canonicalization. */
103
+ function canonicalYaml(filePath) {
104
+ if (!existsSync(filePath))
105
+ return `<MISSING>:${filePath}`;
106
+ try {
107
+ const raw = readFileSync(filePath, 'utf8');
108
+ const parsed = yamlParse(raw);
109
+ return yamlStringify(parsed, { sortMapEntries: true, lineWidth: 0 }).replace(/\r\n?/g, '\n');
110
+ }
111
+ catch (e) {
112
+ throw new NpsError('COMPAT_HASH_FAILED', `failed to canonicalize yaml ${filePath}: ${e instanceof Error ? e.message : e}`);
113
+ }
114
+ }
115
+ //# sourceMappingURL=compat-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat-hash.js","sourceRoot":"","sources":["../../src/session/compat-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,GAAG,GAAG,aAAa,CAAC;AAa1B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAuB;IACvD,MAAM,GAAG,GAAa,EAAE,CAAC;IAEzB,+FAA+F;IAC/F,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC3D,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC;IACzC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEd,eAAe;IACf,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7D,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEd,4CAA4C;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9D,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,IAAI,CAAC;YAAC,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAAC,CAAC;QAC9C,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,6BAA6B,SAAS,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YACpD,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;YAC5B,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IAED,mCAAmC;IACnC,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;IACzC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEd,sBAAsB;IACtB,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1C,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEd,gCAAgC;IAChC,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;IAEtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,oBAAoB;IAClC,OAAO;QACL,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC;QACnC,WAAW,EAAE,UAAU,CAAC,kBAAkB,CAAC;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,EAAE;YAC3C,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;IAClD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,mBAAmB,GAAG,eAAe,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtH,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,aAAa,QAAQ,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,CAAC;QACH,oDAAoD;QACpD,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,kBAAkB,QAAQ,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,aAAa,QAAQ,EAAE,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,aAAa,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/F,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC,oBAAoB,EAAE,+BAA+B,QAAQ,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7H,CAAC;AACH,CAAC"}