@oatelauser/crosschat 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 (59) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +25 -0
  3. package/README.md +244 -0
  4. package/dist/claude/deliver.js +33 -0
  5. package/dist/claude/deliver.js.map +1 -0
  6. package/dist/claude/frame.js +16 -0
  7. package/dist/claude/frame.js.map +1 -0
  8. package/dist/claude/key-file.js +45 -0
  9. package/dist/claude/key-file.js.map +1 -0
  10. package/dist/claude/registry.js +79 -0
  11. package/dist/claude/registry.js.map +1 -0
  12. package/dist/claude/resolve-exe.js +31 -0
  13. package/dist/claude/resolve-exe.js.map +1 -0
  14. package/dist/cli.js +270 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/codex/client.js +130 -0
  17. package/dist/codex/client.js.map +1 -0
  18. package/dist/codex/deliver.js +162 -0
  19. package/dist/codex/deliver.js.map +1 -0
  20. package/dist/codex/discovery.js +45 -0
  21. package/dist/codex/discovery.js.map +1 -0
  22. package/dist/codex/queue.js +71 -0
  23. package/dist/codex/queue.js.map +1 -0
  24. package/dist/codex/rollout-meta.js +118 -0
  25. package/dist/codex/rollout-meta.js.map +1 -0
  26. package/dist/codex/rpc.js +153 -0
  27. package/dist/codex/rpc.js.map +1 -0
  28. package/dist/codex/transport.js +357 -0
  29. package/dist/codex/transport.js.map +1 -0
  30. package/dist/commands/claude-wrapper.js +28 -0
  31. package/dist/commands/claude-wrapper.js.map +1 -0
  32. package/dist/commands/doctor.js +169 -0
  33. package/dist/commands/doctor.js.map +1 -0
  34. package/dist/commands/install-skills.js +26 -0
  35. package/dist/commands/install-skills.js.map +1 -0
  36. package/dist/commands/send.js +122 -0
  37. package/dist/commands/send.js.map +1 -0
  38. package/dist/commands/status.js +77 -0
  39. package/dist/commands/status.js.map +1 -0
  40. package/dist/envelope.js +34 -0
  41. package/dist/envelope.js.map +1 -0
  42. package/dist/errors.js +15 -0
  43. package/dist/errors.js.map +1 -0
  44. package/dist/identity.js +42 -0
  45. package/dist/identity.js.map +1 -0
  46. package/dist/outbox.js +172 -0
  47. package/dist/outbox.js.map +1 -0
  48. package/dist/platform/pipe-transport.js +73 -0
  49. package/dist/platform/pipe-transport.js.map +1 -0
  50. package/dist/platform/process-inspector.js +15 -0
  51. package/dist/platform/process-inspector.js.map +1 -0
  52. package/dist/rate-limit.js +49 -0
  53. package/dist/rate-limit.js.map +1 -0
  54. package/dist/ref.js +64 -0
  55. package/dist/ref.js.map +1 -0
  56. package/dist/resolve.js +30 -0
  57. package/dist/resolve.js.map +1 -0
  58. package/package.json +47 -0
  59. package/skills/crosschat/SKILL.md +65 -0
@@ -0,0 +1,71 @@
1
+ import { spawn } from 'node:child_process';
2
+ import process from 'node:process';
3
+ import { MultichatError } from '../errors.js';
4
+ import { resolveCodexExecutable } from './transport.js';
5
+ const DEFAULT_QUEUE_TIMEOUT_MS = 30_000;
6
+ const STDERR_EXCERPT_CHARS = 300;
7
+ const STDERR_CAPTURE_LIMIT = 4_096;
8
+ /**
9
+ * The message travels as a single argv element (`--message <content>`), so
10
+ * there is no shell quoting involved. Windows caps the command line at ~32K
11
+ * characters; multichat's 16KiB body limit (enforced in commands/send.ts)
12
+ * plus the envelope overhead keeps argv safely below that ceiling.
13
+ */
14
+ export async function queueToCodexThread(threadId, content, options = {}) {
15
+ const timeoutMs = options.timeoutMs ?? DEFAULT_QUEUE_TIMEOUT_MS;
16
+ const command = options.executable ?? resolveCodexExecutable();
17
+ const doSpawn = options.spawnProcess ??
18
+ ((cmd, args) => spawn(cmd, [...args], {
19
+ stdio: ['ignore', 'pipe', 'pipe'],
20
+ shell: false,
21
+ windowsHide: true,
22
+ env: process.env,
23
+ }));
24
+ return new Promise((resolve, reject) => {
25
+ let child;
26
+ try {
27
+ child = doSpawn(command, ['queue', '--thread', threadId, '--message', content]);
28
+ }
29
+ catch (err) {
30
+ reject(queueFailed(threadId, command, 'process could not be spawned', err));
31
+ return;
32
+ }
33
+ let stderrText = '';
34
+ let settled = false;
35
+ const finish = (settle) => {
36
+ if (settled)
37
+ return;
38
+ settled = true;
39
+ clearTimeout(timer);
40
+ settle();
41
+ };
42
+ const timer = setTimeout(() => {
43
+ finish(() => {
44
+ try {
45
+ child.kill();
46
+ }
47
+ catch {
48
+ // already dead
49
+ }
50
+ reject(queueFailed(threadId, command, `timed out after ${timeoutMs}ms`));
51
+ });
52
+ }, timeoutMs);
53
+ child.stderr?.on('data', (chunk) => {
54
+ if (stderrText.length < STDERR_CAPTURE_LIMIT)
55
+ stderrText += chunk.toString('utf8');
56
+ });
57
+ child.on('error', (err) => finish(() => reject(queueFailed(threadId, command, 'process error', err, stderrText))));
58
+ // 'close' (not 'exit'): fires after the stdio pipes drained, so stderr is in.
59
+ child.on('close', (code) => finish(() => {
60
+ if (code === 0)
61
+ resolve({ status: 'queued' });
62
+ else
63
+ reject(queueFailed(threadId, command, `exited with code ${code}`, undefined, stderrText));
64
+ }));
65
+ });
66
+ }
67
+ function queueFailed(threadId, command, reason, cause, stderrText = '') {
68
+ const excerpt = stderrText === '' ? '' : ` stderr: ${stderrText.replace(/\s+/g, ' ').trim().slice(0, STDERR_EXCERPT_CHARS)}`;
69
+ return new MultichatError('CODEX_QUEUE_FAILED', `codex queue for thread ${threadId} failed (${command}): ${reason}.${excerpt}`, { cause, stderrText: stderrText === '' ? undefined : stderrText });
70
+ }
71
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/codex/queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { closeSync, openSync, readdirSync, readSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import process from 'node:process';
5
+ /** Codex home directory (rollout sessions root lives under `<home>/sessions`). */
6
+ export function codexHomeDir() {
7
+ return process.env.CODEX_HOME ?? join(homedir(), '.codex');
8
+ }
9
+ /**
10
+ * Read only the first line of a file. Chunked because a session_meta first
11
+ * line embeds base instructions and can far exceed one read buffer.
12
+ */
13
+ function readFirstLine(path) {
14
+ let fd;
15
+ try {
16
+ fd = openSync(path, 'r');
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ try {
22
+ const chunks = [];
23
+ const buf = Buffer.alloc(65536);
24
+ for (;;) {
25
+ const n = readSync(fd, buf, 0, buf.length, null);
26
+ if (n <= 0)
27
+ break;
28
+ const newline = buf.subarray(0, n).indexOf(0x0a);
29
+ if (newline >= 0) {
30
+ chunks.push(Buffer.from(buf.subarray(0, newline)));
31
+ return Buffer.concat(chunks).toString('utf8');
32
+ }
33
+ // Copy: subarray views the reused read buffer and would be overwritten.
34
+ chunks.push(Buffer.from(buf.subarray(0, n)));
35
+ }
36
+ return Buffer.concat(chunks).toString('utf8');
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ finally {
42
+ closeSync(fd);
43
+ }
44
+ }
45
+ function nonEmptyString(value) {
46
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
47
+ }
48
+ /** Extract meta fields with tolerance for missing/malformed payload pieces. */
49
+ function parseMeta(line) {
50
+ if (!line)
51
+ return undefined;
52
+ try {
53
+ const payload = JSON.parse(line)?.payload;
54
+ if (typeof payload !== 'object' || payload === null)
55
+ return undefined;
56
+ const record = payload;
57
+ const meta = {
58
+ cwd: nonEmptyString(record.cwd),
59
+ originator: nonEmptyString(record.originator),
60
+ createdAt: nonEmptyString(record.timestamp),
61
+ };
62
+ return meta.cwd !== undefined || meta.originator !== undefined || meta.createdAt !== undefined
63
+ ? meta
64
+ : undefined;
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ /** One recursive walk of the sessions tree; maps each wanted id to its rollout file. */
71
+ function findRolloutFiles(dir, wanted, found = new Map()) {
72
+ let entries;
73
+ try {
74
+ entries = readdirSync(dir, { withFileTypes: true });
75
+ }
76
+ catch {
77
+ return found;
78
+ }
79
+ for (const entry of entries) {
80
+ if (found.size === wanted.size)
81
+ break;
82
+ const path = join(dir, entry.name);
83
+ if (entry.isDirectory()) {
84
+ findRolloutFiles(path, wanted, found);
85
+ }
86
+ else if (entry.isFile() && entry.name.startsWith('rollout-') && entry.name.endsWith('.jsonl')) {
87
+ const stem = entry.name.slice(0, -'.jsonl'.length);
88
+ for (const id of wanted) {
89
+ if (stem.endsWith(`-${id}`)) {
90
+ found.set(id, path);
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ return found;
97
+ }
98
+ /**
99
+ * Look up rollout metadata for several thread ids with a single directory
100
+ * walk (a per-status call must stay well under a couple of seconds).
101
+ */
102
+ export function lookupRolloutMetas(codexHome, threadIds) {
103
+ const metas = new Map();
104
+ const wanted = new Set(threadIds.filter((id) => id.length > 0));
105
+ if (wanted.size === 0)
106
+ return metas;
107
+ for (const [id, path] of findRolloutFiles(join(codexHome, 'sessions'), wanted)) {
108
+ const meta = parseMeta(readFirstLine(path));
109
+ if (meta !== undefined)
110
+ metas.set(id, meta);
111
+ }
112
+ return metas;
113
+ }
114
+ /** Single-thread convenience wrapper around {@link lookupRolloutMetas}. */
115
+ export function lookupRolloutMeta(codexHome, threadId) {
116
+ return lookupRolloutMetas(codexHome, [threadId]).get(threadId);
117
+ }
118
+ //# sourceMappingURL=rollout-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollout-meta.js","sourceRoot":"","sources":["../../src/codex/rollout-meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ import { MultichatError } from '../errors.js';
2
+ /**
3
+ * JSON-RPC client for the codex app-server wire protocol. Wire facts verified
4
+ * on this machine (docs/research/codex-windows-feasibility.md §1.5): response and
5
+ * notification frames carry NO `jsonrpc` field (`{id,result}` / `{method,params}`),
6
+ * so parsing must not require it.
7
+ */
8
+ /** Whitelist of client methods; anything else is a programming error. */
9
+ const WHITELISTED_METHODS = new Set([
10
+ 'initialize',
11
+ 'thread/list',
12
+ 'thread/resume',
13
+ 'turn/start',
14
+ 'turn/steer',
15
+ 'thread/unsubscribe',
16
+ // Used only by the isolated live self-test (thread/start creates a fresh
17
+ // thread owned by crosschat; thread/delete removes it). deliver() and
18
+ // discovery() never call these on user threads.
19
+ 'thread/start',
20
+ 'thread/delete',
21
+ ]);
22
+ /** JSON-RPC error response (the server explicitly rejected the request). */
23
+ export class CodexRpcRejectedError extends MultichatError {
24
+ rpcCode;
25
+ constructor(rpcCode, message) {
26
+ super('CODEX_RPC_REJECTED', message);
27
+ this.name = 'CodexRpcRejectedError';
28
+ this.rpcCode = rpcCode;
29
+ }
30
+ }
31
+ function isRecord(value) {
32
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
33
+ }
34
+ export class CodexRpcClient {
35
+ channel;
36
+ requestTimeoutMs;
37
+ nextId = 1;
38
+ pending = new Map();
39
+ /** Timed-out request ids: late matching responses are dropped, not faults. */
40
+ abandoned = new Set();
41
+ notificationListeners = new Set();
42
+ closed = false;
43
+ /** Every notification received, in order (turn/started, item/completed, ...). */
44
+ notifications = [];
45
+ /**
46
+ * Server-initiated requests (approval family etc.), recorded as evidence.
47
+ * crosschat never answers approvals, so no response is ever sent back.
48
+ */
49
+ serverRequests = [];
50
+ constructor(channel, requestTimeoutMs = 30_000) {
51
+ this.channel = channel;
52
+ this.requestTimeoutMs = requestTimeoutMs;
53
+ channel.onMessage((payload) => this.handlePayload(payload));
54
+ channel.onClose(() => this.failAll(new MultichatError('CODEX_TRANSPORT_CLOSED', 'codex channel closed.')));
55
+ }
56
+ request(method, params) {
57
+ if (!WHITELISTED_METHODS.has(method)) {
58
+ throw new MultichatError('CODEX_PROTOCOL_ERROR', `codex rpc method not allowed: ${method}`);
59
+ }
60
+ if (this.closed) {
61
+ throw new MultichatError('CODEX_TRANSPORT_CLOSED', 'codex channel closed.');
62
+ }
63
+ const id = this.nextId;
64
+ this.nextId += 1;
65
+ return new Promise((resolve, reject) => {
66
+ const timer = setTimeout(() => {
67
+ this.pending.delete(id);
68
+ this.abandoned.add(id);
69
+ reject(new MultichatError('CODEX_REQUEST_TIMEOUT', `codex request timed out: ${method}`));
70
+ }, this.requestTimeoutMs);
71
+ this.pending.set(id, { resolve, reject, timer });
72
+ this.channel.send(JSON.stringify({ id, method, params }));
73
+ });
74
+ }
75
+ notify(method, params) {
76
+ this.channel.send(JSON.stringify({ method, params }));
77
+ }
78
+ onNotification(listener) {
79
+ this.notificationListeners.add(listener);
80
+ return () => this.notificationListeners.delete(listener);
81
+ }
82
+ dispose() {
83
+ this.closed = true;
84
+ this.failAll(new MultichatError('CODEX_TRANSPORT_CLOSED', 'codex rpc client disposed.'));
85
+ }
86
+ handlePayload(payload) {
87
+ let parsed;
88
+ try {
89
+ parsed = JSON.parse(payload);
90
+ }
91
+ catch {
92
+ this.protocolFailure();
93
+ return;
94
+ }
95
+ if (!isRecord(parsed)) {
96
+ this.protocolFailure();
97
+ return;
98
+ }
99
+ if (typeof parsed.method === 'string') {
100
+ const frame = { method: parsed.method, params: parsed.params };
101
+ if (parsed.id !== undefined) {
102
+ this.serverRequests.push(frame);
103
+ }
104
+ else {
105
+ this.notifications.push(frame);
106
+ for (const listener of [...this.notificationListeners])
107
+ listener(frame);
108
+ }
109
+ return;
110
+ }
111
+ if (typeof parsed.id !== 'number' || !Number.isSafeInteger(parsed.id)) {
112
+ this.protocolFailure();
113
+ return;
114
+ }
115
+ const pending = this.pending.get(parsed.id);
116
+ if (pending === undefined) {
117
+ // Responses to requests we already timed out arrive late; drop them.
118
+ if (!this.abandoned.has(parsed.id))
119
+ this.protocolFailure();
120
+ return;
121
+ }
122
+ const hasResult = Object.hasOwn(parsed, 'result');
123
+ const hasError = Object.hasOwn(parsed, 'error');
124
+ if (hasResult === hasError) {
125
+ this.protocolFailure();
126
+ return;
127
+ }
128
+ this.pending.delete(parsed.id);
129
+ clearTimeout(pending.timer);
130
+ if (hasError) {
131
+ const err = parsed.error;
132
+ const rpcCode = isRecord(err) && typeof err.code === 'number' ? err.code : 0;
133
+ const message = isRecord(err) && typeof err.message === 'string' ? err.message : 'codex rpc error';
134
+ pending.reject(new CodexRpcRejectedError(rpcCode, message));
135
+ return;
136
+ }
137
+ pending.resolve(parsed.result);
138
+ }
139
+ protocolFailure() {
140
+ if (this.closed)
141
+ return;
142
+ this.closed = true;
143
+ this.failAll(new MultichatError('CODEX_PROTOCOL_ERROR', 'malformed codex rpc frame.'));
144
+ }
145
+ failAll(error) {
146
+ for (const pending of this.pending.values()) {
147
+ clearTimeout(pending.timer);
148
+ pending.reject(error);
149
+ }
150
+ this.pending.clear();
151
+ }
152
+ }
153
+ //# sourceMappingURL=rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/codex/rpc.ts"],"names":[],"mappings":"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"}