@ours.network/claude-code 1.2.0-nightly.3 → 1.2.0-nightly.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/claude-code",
3
- "version": "1.2.0-nightly.3",
3
+ "version": "1.2.0-nightly.5",
4
4
  "description": "Claude Code plugin for ours \u2014 secure agent-to-agent messaging over ADAPT with native host network transport.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",
@@ -45,9 +45,10 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@modelcontextprotocol/sdk": "^1.29.0",
48
- "@ours.network/sdk": "3.8.1-nightly.3",
48
+ "@ours.network/sdk": "3.8.1-nightly.9",
49
49
  "ws": "^8.21.0",
50
- "zod": "^3.25.76"
50
+ "zod": "^3.25.76",
51
+ "@ours.network/mcp": "1.2.0-nightly.5"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^20.14.0",
@@ -1,147 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { createReadStream, createWriteStream, mkdirSync } from 'node:fs';
3
- import { basename, dirname, resolve } from 'node:path';
4
- import { spawn, spawnSync } from 'node:child_process';
5
- import { pipeline } from 'node:stream/promises';
6
-
7
- function helperInvocation(container, command, args) {
8
- const entrypoint = container.args.indexOf('/opt/ours/node_modules/@ours.network/mcp/dist/container.js');
9
- return { ...container, args: [...container.args.slice(0, entrypoint + 2), command, ...args] };
10
- }
11
-
12
- function helperJson(container, command, args) {
13
- const call = helperInvocation(container, command, args);
14
- const child = spawnSync(call.command, call.args, { env: call.env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000, maxBuffer: 1024 * 1024 });
15
- if (child.error) throw child.error;
16
- if (child.status !== 0) throw new Error(child.stderr.trim() || `${command} exited ${child.status}`);
17
- return JSON.parse(child.stdout);
18
- }
19
-
20
- async function runHelper(container, command, args, input, output) {
21
- const call = helperInvocation(container, command, args);
22
- const child = spawn(call.command, call.args, { env: call.env, stdio: ['pipe', 'pipe', 'pipe'] });
23
- let stderr = '';
24
- child.stderr.setEncoding('utf8');
25
- child.stderr.on('data', (chunk) => { stderr += chunk; });
26
- const exited = new Promise((resolveExit, reject) => {
27
- child.once('error', reject);
28
- child.once('close', (code, signal) => code === 0 ? resolveExit() : reject(new Error(stderr.trim() || `${command} exited ${signal ?? code}`)));
29
- });
30
- const streams = [];
31
- if (input) streams.push(pipeline(input, child.stdin)); else child.stdin.end();
32
- if (output) streams.push(pipeline(child.stdout, output)); else child.stdout.resume();
33
- const results = await Promise.allSettled([...streams, exited]);
34
- const failed = results.find((result) => result.status === 'rejected');
35
- if (failed) throw failed.reason;
36
- }
37
-
38
- async function stageFile(container, id, hostPath) {
39
- const abs = resolve(hostPath);
40
- const call = helperInvocation(container, 'file-stage', [id, basename(abs)]);
41
- const child = spawn(call.command, call.args, { env: call.env, stdio: ['pipe', 'pipe', 'pipe'] });
42
- let stdout = '', stderr = '';
43
- child.stdout.setEncoding('utf8'); child.stdout.on('data', (v) => { stdout += v; });
44
- child.stderr.setEncoding('utf8'); child.stderr.on('data', (v) => { stderr += v; });
45
- const exited = new Promise((resolveExit, reject) => {
46
- child.once('error', reject);
47
- child.once('close', (code, signal) => code === 0 ? resolveExit() : reject(new Error(stderr.trim() || `file-stage exited ${signal ?? code}`)));
48
- });
49
- const results = await Promise.allSettled([pipeline(createReadStream(abs), child.stdin), exited]);
50
- const failed = results.find((result) => result.status === 'rejected');
51
- if (failed) throw failed.reason;
52
- return { path: JSON.parse(stdout).path, abs };
53
- }
54
-
55
- function toolError(id, error) {
56
- return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: `host file transfer failed: ${error instanceof Error ? error.message : String(error)}` }], isError: true } };
57
- }
58
-
59
- async function removeTransfer(container, id) {
60
- try { await runHelper(container, 'file-remove', [id]); } catch { /* preserve the primary result */ }
61
- }
62
-
63
- async function eachLine(stream, visit) {
64
- let pending = Buffer.alloc(0);
65
- for await (const chunk of stream) {
66
- pending = Buffer.concat([pending, chunk]);
67
- let newline;
68
- while ((newline = pending.indexOf(10)) !== -1) {
69
- const line = pending.subarray(0, newline + 1);
70
- pending = pending.subarray(newline + 1);
71
- await visit(line);
72
- }
73
- }
74
- if (pending.length) await visit(pending);
75
- }
76
-
77
- export function runContainerProxy(container) {
78
- const child = spawn(container.command, container.args, { stdio: ['pipe', 'pipe', 'inherit'], env: container.env });
79
- const pending = new Map();
80
- let stopping = false;
81
- const stopInput = () => { stopping = true; process.stdin.destroy(); };
82
- child.stdin.on('error', stopInput);
83
- let outputWrites = Promise.resolve();
84
- const writeOutput = (bytes) => { outputWrites = outputWrites.then(() => new Promise((done) => process.stdout.write(bytes, done))); return outputWrites; };
85
-
86
- const inputDone = eachLine(process.stdin, async (raw) => {
87
- let message;
88
- try { message = JSON.parse(raw.toString('utf8')); } catch { child.stdin.write(raw); return; }
89
- // Forward cancellation unchanged. The upstream handler owns cancellation;
90
- // keep transfer tracking until its actual response so a completed save is
91
- // copied to the host and its staging directory is removed.
92
- if (message?.method === 'notifications/cancelled') { child.stdin.write(raw); return; }
93
- if (message?.method !== 'tools/call' || !message.params?.arguments) { child.stdin.write(raw); return; }
94
- const name = message.params.name;
95
- if (name !== 'send_file' && name !== 'save_file') { child.stdin.write(raw); return; }
96
- const args = message.params.arguments;
97
- if (name === 'send_file' && !args.path) { child.stdin.write(raw); return; }
98
- const id = randomUUID();
99
- try {
100
- if (name === 'send_file') {
101
- const staged = await stageFile(container, id, args.path);
102
- message.params.arguments = { ...args, path: staged.path, filename: args.filename ?? basename(staged.abs) };
103
- pending.set(message.id, { id, kind: name });
104
- } else {
105
- const hostPath = resolve(args.dest_path);
106
- const target = helperJson(container, 'file-target', [id]);
107
- message.params.arguments = { ...args, dest_path: target.path };
108
- pending.set(message.id, { id, kind: name, hostPath, containerPath: target.path });
109
- }
110
- if (!stopping) child.stdin.write(`${JSON.stringify(message)}\n`);
111
- } catch (error) {
112
- await removeTransfer(container, id);
113
- await writeOutput(`${JSON.stringify(toolError(message.id, error))}\n`);
114
- }
115
- }).catch((error) => {
116
- if (!stopping) { process.stderr.write(`ours: container MCP input failed: ${error.message}\n`); process.exitCode = 1; child.kill(); }
117
- }).finally(() => { if (!child.stdin.destroyed) child.stdin.end(); });
118
-
119
- const outputDone = eachLine(child.stdout, async (raw) => {
120
- let message;
121
- try { message = JSON.parse(raw.toString('utf8')); } catch { await writeOutput(raw); return; }
122
- const transfer = pending.get(message?.id);
123
- if (!transfer) { await writeOutput(raw); return; }
124
- pending.delete(message.id);
125
- try {
126
- if (transfer.kind === 'save_file' && !message.error && message.result?.isError !== true) {
127
- mkdirSync(dirname(transfer.hostPath), { recursive: true });
128
- await runHelper(container, 'file-read', [transfer.id], null, createWriteStream(transfer.hostPath));
129
- for (const item of message.result?.content ?? []) {
130
- if (item?.type === 'text' && typeof item.text === 'string') item.text = item.text.split(transfer.containerPath).join(transfer.hostPath);
131
- }
132
- }
133
- await writeOutput(`${JSON.stringify(message)}\n`);
134
- } catch (error) {
135
- await writeOutput(`${JSON.stringify(toolError(message.id, error))}\n`);
136
- } finally { await removeTransfer(container, transfer.id); }
137
- });
138
-
139
- child.on('error', (error) => { stopInput(); process.stderr.write(`ours: cannot launch container MCP: ${error.message}\n`); process.exitCode = 1; });
140
- child.on('close', async (code, signal) => {
141
- stopInput();
142
- await Promise.allSettled([inputDone, outputDone, outputWrites]);
143
- await Promise.allSettled([...pending.values()].map((v) => removeTransfer(container, v.id)));
144
- if (signal) process.kill(process.pid, signal); else process.exitCode = process.exitCode || (code === null || code < 0 ? 1 : code);
145
- });
146
- return child;
147
- }
@@ -1,2 +0,0 @@
1
- export function containerInvocation(command: string, args?: string[], env?: NodeJS.ProcessEnv): { command: string; args: string[]; env: NodeJS.ProcessEnv } | null;
2
- export function readContainerJson(command: string, env?: NodeJS.ProcessEnv): unknown;
@@ -1,40 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { isAbsolute, join } from 'node:path';
4
- import { spawnSync } from 'node:child_process';
5
-
6
- // An explicit container selection is authoritative: failures never fall back
7
- // to a host MCP or host application-identity configuration.
8
- export function containerInvocation(command, args = [], env = process.env) {
9
- const explicitPath = (env.OURS_CONFIG ?? '').trim();
10
- const path = explicitPath || join(homedir(), '.ours', 'config.json');
11
- let profile;
12
- try { profile = JSON.parse(readFileSync(path, 'utf8')); }
13
- catch (error) {
14
- if (!explicitPath && error.code === 'ENOENT') return null;
15
- throw new Error(`cannot read OURS_CONFIG profile ${path}: ${error.message}`);
16
- }
17
- if (!profile || !Object.hasOwn(profile, 'composeFile')) return null;
18
- if (typeof profile.composeFile !== 'string' || !isAbsolute(profile.composeFile)) throw new Error('composeFile must be an absolute path');
19
- if (typeof profile.expectedInstanceId !== 'string' || !profile.expectedInstanceId.trim()) throw new Error('container profile requires expectedInstanceId');
20
- if (profile.composeProject !== undefined && (typeof profile.composeProject !== 'string' || !profile.composeProject.trim())) throw new Error('composeProject must be a nonempty string');
21
- const dockerArgs = ['compose', '-f', profile.composeFile];
22
- if (profile.composeProject !== undefined) dockerArgs.push('-p', profile.composeProject);
23
- dockerArgs.push('exec', '-T');
24
- for (const name of ['CLAUDE_CODE_SESSION_ID', 'OURS_BIND_IDENTITY']) {
25
- if (env[name] !== undefined) dockerArgs.push('-e', `${name}=${env[name]}`);
26
- }
27
- dockerArgs.push('daemon', 'node', '/opt/ours/node_modules/@ours.network/mcp/dist/container.js', profile.expectedInstanceId, command, ...args);
28
- return { command: 'docker', args: dockerArgs, env: { ...env, OURS_DAEMON_ID: profile.expectedInstanceId } };
29
- }
30
-
31
- export function readContainerJson(command, env = process.env) {
32
- const invocation = containerInvocation(command, [], env);
33
- if (!invocation) return null;
34
- const child = spawnSync(invocation.command, invocation.args, { env: invocation.env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000, maxBuffer: 1024 * 1024 });
35
- if (child.error) throw child.error;
36
- if (child.status !== 0) throw new Error(`ours container ${command} failed: ${child.stderr.trim() || child.status}`);
37
- const value = JSON.parse(child.stdout);
38
- if (value === null) throw new Error(`ours container ${command} returned null`);
39
- return value;
40
- }