@shipers-dev/multi 0.6.3 → 0.6.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/dist/index.js +49 -29
- package/package.json +1 -1
- package/src/acpx-runner.ts +23 -6
- package/src/index.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -5381,6 +5381,16 @@ function extractText(content) {
|
|
|
5381
5381
|
}
|
|
5382
5382
|
|
|
5383
5383
|
// src/acpx-runner.ts
|
|
5384
|
+
import { appendFileSync } from "fs";
|
|
5385
|
+
import { join } from "path";
|
|
5386
|
+
var HOME = process.env.HOME || process.env.USERPROFILE || ".";
|
|
5387
|
+
var LOG_PATH = join(HOME, ".multi", "logs", "agent.log");
|
|
5388
|
+
function dlog(msg) {
|
|
5389
|
+
try {
|
|
5390
|
+
appendFileSync(LOG_PATH, `[${new Date().toISOString()}] ${msg}
|
|
5391
|
+
`);
|
|
5392
|
+
} catch {}
|
|
5393
|
+
}
|
|
5384
5394
|
async function runAcpx(opts) {
|
|
5385
5395
|
const args = ["acpx", "--format", "json", "--json-strict", "--approve-all", "--ttl", "0"];
|
|
5386
5396
|
if (opts.cwd)
|
|
@@ -5388,15 +5398,22 @@ async function runAcpx(opts) {
|
|
|
5388
5398
|
args.push(opts.agentType);
|
|
5389
5399
|
if (opts.sessionName) {
|
|
5390
5400
|
const ensureArgs = ["acpx", "--ttl", "0", ...opts.cwd ? ["--cwd", opts.cwd] : [], opts.agentType, "sessions", "ensure", "--name", opts.sessionName];
|
|
5401
|
+
dlog(`[acpx] ensure: ${ensureArgs.join(" ")}`);
|
|
5391
5402
|
try {
|
|
5392
5403
|
const ensure = Bun.spawn(ensureArgs, { stdout: "pipe", stderr: "pipe", stdin: "ignore" });
|
|
5393
|
-
await ensure.
|
|
5394
|
-
|
|
5404
|
+
const ensureOut = await new Response(ensure.stdout).text();
|
|
5405
|
+
const ensureErr = await new Response(ensure.stderr).text();
|
|
5406
|
+
const ensureCode = await ensure.exited;
|
|
5407
|
+
dlog(`[acpx] ensure exit=${ensureCode} stdout=${ensureOut.slice(0, 300)} stderr=${ensureErr.slice(0, 300)}`);
|
|
5408
|
+
} catch (e) {
|
|
5409
|
+
dlog(`[acpx] ensure spawn error: ${String(e)}`);
|
|
5410
|
+
}
|
|
5395
5411
|
args.push("prompt", "-s", opts.sessionName);
|
|
5396
5412
|
} else {
|
|
5397
5413
|
args.push("prompt");
|
|
5398
5414
|
}
|
|
5399
5415
|
args.push(opts.prompt);
|
|
5416
|
+
dlog(`[acpx] prompt: ${args.slice(0, 10).join(" ")} ... (prompt len=${opts.prompt.length})`);
|
|
5400
5417
|
const proc = Bun.spawn(args, { stdout: "pipe", stderr: "pipe", stdin: "ignore" });
|
|
5401
5418
|
let stopReason = "end_turn";
|
|
5402
5419
|
(async () => {
|
|
@@ -5415,7 +5432,7 @@ async function runAcpx(opts) {
|
|
|
5415
5432
|
const ln = sb.slice(0, nl).trim();
|
|
5416
5433
|
sb = sb.slice(nl + 1);
|
|
5417
5434
|
if (ln)
|
|
5418
|
-
|
|
5435
|
+
dlog(`[acpx stderr] ${ln.slice(0, 500)}`);
|
|
5419
5436
|
}
|
|
5420
5437
|
}
|
|
5421
5438
|
} catch {}
|
|
@@ -5423,6 +5440,7 @@ async function runAcpx(opts) {
|
|
|
5423
5440
|
const reader = proc.stdout.getReader();
|
|
5424
5441
|
const dec = new TextDecoder;
|
|
5425
5442
|
let buf = "";
|
|
5443
|
+
let lineCount = 0;
|
|
5426
5444
|
while (true) {
|
|
5427
5445
|
const { value, done } = await reader.read();
|
|
5428
5446
|
if (done)
|
|
@@ -5435,6 +5453,8 @@ async function runAcpx(opts) {
|
|
|
5435
5453
|
buf = buf.slice(nl + 1);
|
|
5436
5454
|
if (!line)
|
|
5437
5455
|
continue;
|
|
5456
|
+
lineCount++;
|
|
5457
|
+
dlog(`[acpx stdout] ${line.slice(0, 500)}`);
|
|
5438
5458
|
const events = parseAcpLine(line, (r) => {
|
|
5439
5459
|
stopReason = r;
|
|
5440
5460
|
});
|
|
@@ -5442,6 +5462,7 @@ async function runAcpx(opts) {
|
|
|
5442
5462
|
await opts.onEvent(ev);
|
|
5443
5463
|
}
|
|
5444
5464
|
}
|
|
5465
|
+
dlog(`[acpx] stdout lines total=${lineCount}`);
|
|
5445
5466
|
if (buf.trim()) {
|
|
5446
5467
|
const events = parseAcpLine(buf.trim(), (r) => {
|
|
5447
5468
|
stopReason = r;
|
|
@@ -5463,7 +5484,7 @@ function parseAcpLine(line, setStop) {
|
|
|
5463
5484
|
return [];
|
|
5464
5485
|
}
|
|
5465
5486
|
const out = [];
|
|
5466
|
-
if (msg.method === "session/update" && msg.params?.sessionUpdate) {
|
|
5487
|
+
if (msg.method === "session/update" && msg.params?.update?.sessionUpdate) {
|
|
5467
5488
|
return handleSessionUpdate(msg.params);
|
|
5468
5489
|
}
|
|
5469
5490
|
if (msg.id && msg.result && typeof msg.result === "object" && "stopReason" in msg.result) {
|
|
@@ -5481,7 +5502,6 @@ function handleSessionUpdate(params) {
|
|
|
5481
5502
|
const u = params.update || {};
|
|
5482
5503
|
const kind = u.sessionUpdate;
|
|
5483
5504
|
const out = [];
|
|
5484
|
-
out.push({ event_type: "progress", payload: { message: `[acpx upd] ${kind} keys=${Object.keys(u).join(",")}` } });
|
|
5485
5505
|
switch (kind) {
|
|
5486
5506
|
case "agent_message_chunk":
|
|
5487
5507
|
case "agent_thought_chunk": {
|
|
@@ -5543,17 +5563,17 @@ function extractText2(content) {
|
|
|
5543
5563
|
|
|
5544
5564
|
// src/index.ts
|
|
5545
5565
|
import { parseArgs } from "util";
|
|
5546
|
-
import { mkdirSync as mkdirSync2, existsSync as existsSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, appendFileSync, unlinkSync, readdirSync, statSync } from "fs";
|
|
5547
|
-
import { join, dirname as dirname2 } from "path";
|
|
5548
|
-
var
|
|
5549
|
-
var MULTI_DIR =
|
|
5550
|
-
var CONFIG_PATH =
|
|
5551
|
-
var PID_PATH =
|
|
5552
|
-
var
|
|
5553
|
-
var SKILLS_DIR =
|
|
5554
|
-
var STOP_PATH =
|
|
5555
|
-
var TASKS_DB_PATH =
|
|
5556
|
-
var VERSION = "0.6.
|
|
5566
|
+
import { mkdirSync as mkdirSync2, existsSync as existsSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, appendFileSync as appendFileSync2, unlinkSync, readdirSync, statSync } from "fs";
|
|
5567
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
5568
|
+
var HOME2 = process.env.HOME || process.env.USERPROFILE || ".";
|
|
5569
|
+
var MULTI_DIR = join2(HOME2, ".multi");
|
|
5570
|
+
var CONFIG_PATH = join2(MULTI_DIR, "config.json");
|
|
5571
|
+
var PID_PATH = join2(MULTI_DIR, "agent.pid");
|
|
5572
|
+
var LOG_PATH2 = join2(MULTI_DIR, "logs", "agent.log");
|
|
5573
|
+
var SKILLS_DIR = join2(MULTI_DIR, "skills");
|
|
5574
|
+
var STOP_PATH = join2(MULTI_DIR, "stop.flag");
|
|
5575
|
+
var TASKS_DB_PATH = join2(MULTI_DIR, "tasks.db");
|
|
5576
|
+
var VERSION = "0.6.5";
|
|
5557
5577
|
var COMMANDS = {
|
|
5558
5578
|
setup: "Register this device with a workspace",
|
|
5559
5579
|
connect: "Connect device to realtime hub and execute assigned tasks",
|
|
@@ -5563,7 +5583,7 @@ var COMMANDS = {
|
|
|
5563
5583
|
logs: "View execution logs"
|
|
5564
5584
|
};
|
|
5565
5585
|
function ensureDirs() {
|
|
5566
|
-
for (const d of [MULTI_DIR,
|
|
5586
|
+
for (const d of [MULTI_DIR, join2(MULTI_DIR, "logs"), SKILLS_DIR]) {
|
|
5567
5587
|
if (!existsSync2(d))
|
|
5568
5588
|
mkdirSync2(d, { recursive: true });
|
|
5569
5589
|
}
|
|
@@ -5572,7 +5592,7 @@ function log(msg) {
|
|
|
5572
5592
|
ensureDirs();
|
|
5573
5593
|
const line = `[${new Date().toISOString()}] ${msg}
|
|
5574
5594
|
`;
|
|
5575
|
-
|
|
5595
|
+
appendFileSync2(LOG_PATH2, line);
|
|
5576
5596
|
process.stdout.write(line);
|
|
5577
5597
|
}
|
|
5578
5598
|
async function main() {
|
|
@@ -5738,7 +5758,7 @@ async function syncSkills(apiUrl, workspaceId) {
|
|
|
5738
5758
|
return;
|
|
5739
5759
|
ensureDirs();
|
|
5740
5760
|
for (const skill of res.data) {
|
|
5741
|
-
writeFileSync2(
|
|
5761
|
+
writeFileSync2(join2(SKILLS_DIR, `${skill.name}.json`), JSON.stringify(skill, null, 2));
|
|
5742
5762
|
}
|
|
5743
5763
|
if (res.data.length)
|
|
5744
5764
|
console.log(` Synced ${res.data.length} skill(s) \u2192 ${SKILLS_DIR}`);
|
|
@@ -5906,7 +5926,7 @@ async function cmdConnectDetached(apiUrl) {
|
|
|
5906
5926
|
}
|
|
5907
5927
|
}
|
|
5908
5928
|
ensureDirs();
|
|
5909
|
-
const logFd = Bun.file(
|
|
5929
|
+
const logFd = Bun.file(LOG_PATH2);
|
|
5910
5930
|
const args = Bun.argv.slice(1).filter((a) => a !== "-d" && a !== "--detach");
|
|
5911
5931
|
const proc = Bun.spawn([process.execPath, ...args, "--api", apiUrl], {
|
|
5912
5932
|
stdio: ["ignore", "ignore", "ignore"],
|
|
@@ -5965,13 +5985,13 @@ async function handleRunTask(apiUrl, deviceId, task, detected) {
|
|
|
5965
5985
|
await postStream(apiUrl, issueId, "progress", { message: `Device ${deviceId} picked up ${isFollowup ? "follow-up" : "task"}` });
|
|
5966
5986
|
let attachmentRefs = [];
|
|
5967
5987
|
if (task.from_comment_id) {
|
|
5968
|
-
const baseDir = workingDir ||
|
|
5969
|
-
const inDir =
|
|
5988
|
+
const baseDir = workingDir || join2(MULTI_DIR, "tmp", issueId);
|
|
5989
|
+
const inDir = join2(baseDir, ".multi-in", task.from_comment_id);
|
|
5970
5990
|
attachmentRefs = await downloadCommentAttachments(apiUrl, task.from_comment_id, inDir);
|
|
5971
5991
|
if (attachmentRefs.length)
|
|
5972
5992
|
log(` fetched ${attachmentRefs.length} attachment(s) \u2192 ${inDir}`);
|
|
5973
5993
|
}
|
|
5974
|
-
const outDir =
|
|
5994
|
+
const outDir = join2(workingDir || join2(MULTI_DIR, "tmp", issueId), ".multi-out");
|
|
5975
5995
|
let liveCommentId;
|
|
5976
5996
|
let liveBody = "";
|
|
5977
5997
|
let hadError = false;
|
|
@@ -6400,14 +6420,14 @@ async function resolveAcpAdapter(agentType, detectedPath) {
|
|
|
6400
6420
|
}
|
|
6401
6421
|
const adapterName = "claude-code-acp";
|
|
6402
6422
|
const candidates = [
|
|
6403
|
-
|
|
6423
|
+
join2(HOME2, ".bun", "install", "global", "node_modules", ".bin", adapterName)
|
|
6404
6424
|
];
|
|
6405
6425
|
try {
|
|
6406
6426
|
const here = new URL(import.meta.url).pathname;
|
|
6407
6427
|
let dir = here;
|
|
6408
6428
|
for (let i = 0;i < 8; i++) {
|
|
6409
6429
|
dir = dirname2(dir);
|
|
6410
|
-
const bin =
|
|
6430
|
+
const bin = join2(dir, "node_modules", ".bin", adapterName);
|
|
6411
6431
|
if (existsSync2(bin))
|
|
6412
6432
|
return [bin];
|
|
6413
6433
|
}
|
|
@@ -6435,7 +6455,7 @@ async function downloadCommentAttachments(apiUrl, commentId, destDir) {
|
|
|
6435
6455
|
continue;
|
|
6436
6456
|
const buf = new Uint8Array(await res.arrayBuffer());
|
|
6437
6457
|
const safe = it.filename.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
6438
|
-
const p =
|
|
6458
|
+
const p = join2(destDir, safe);
|
|
6439
6459
|
writeFileSync2(p, buf);
|
|
6440
6460
|
out.push({ filename: it.filename, path: p });
|
|
6441
6461
|
}
|
|
@@ -6456,7 +6476,7 @@ async function uploadOutputDir(apiUrl, commentId, dir) {
|
|
|
6456
6476
|
if (depth > 3)
|
|
6457
6477
|
return;
|
|
6458
6478
|
for (const name of readdirSync(d)) {
|
|
6459
|
-
const p =
|
|
6479
|
+
const p = join2(d, name);
|
|
6460
6480
|
try {
|
|
6461
6481
|
const st = statSync(p);
|
|
6462
6482
|
if (st.isDirectory())
|
|
@@ -6732,11 +6752,11 @@ async function cmdStop() {
|
|
|
6732
6752
|
} catch {}
|
|
6733
6753
|
}
|
|
6734
6754
|
async function cmdLogs() {
|
|
6735
|
-
if (!existsSync2(
|
|
6755
|
+
if (!existsSync2(LOG_PATH2)) {
|
|
6736
6756
|
console.log("No logs yet.");
|
|
6737
6757
|
return;
|
|
6738
6758
|
}
|
|
6739
|
-
const content = readFileSync2(
|
|
6759
|
+
const content = readFileSync2(LOG_PATH2, "utf8");
|
|
6740
6760
|
console.log(content.split(`
|
|
6741
6761
|
`).slice(-100).join(`
|
|
6742
6762
|
`));
|
package/package.json
CHANGED
package/src/acpx-runner.ts
CHANGED
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
// Runs with --approve-all for now (no permission UI forwarding).
|
|
7
7
|
|
|
8
8
|
import type { AcpEvent } from './acp-runner';
|
|
9
|
+
import { appendFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
|
|
12
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || '.';
|
|
13
|
+
const LOG_PATH = join(HOME, '.multi', 'logs', 'agent.log');
|
|
14
|
+
function dlog(msg: string) {
|
|
15
|
+
try { appendFileSync(LOG_PATH, `[${new Date().toISOString()}] ${msg}\n`); } catch {}
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
export interface AcpxRunOpts {
|
|
11
19
|
agentType: 'pi' | 'codex' | 'openclaw' | 'claude' | string;
|
|
@@ -23,16 +31,23 @@ export async function runAcpx(opts: AcpxRunOpts): Promise<{ stopReason: string }
|
|
|
23
31
|
// We run it as a separate invocation, then prompt.
|
|
24
32
|
if (opts.sessionName) {
|
|
25
33
|
const ensureArgs = ['acpx', '--ttl', '0', ...(opts.cwd ? ['--cwd', opts.cwd] : []), opts.agentType, 'sessions', 'ensure', '--name', opts.sessionName];
|
|
34
|
+
dlog(`[acpx] ensure: ${ensureArgs.join(' ')}`);
|
|
26
35
|
try {
|
|
27
36
|
const ensure = Bun.spawn(ensureArgs, { stdout: 'pipe', stderr: 'pipe', stdin: 'ignore' });
|
|
28
|
-
await ensure.
|
|
29
|
-
|
|
37
|
+
const ensureOut = await new Response(ensure.stdout as any).text();
|
|
38
|
+
const ensureErr = await new Response(ensure.stderr as any).text();
|
|
39
|
+
const ensureCode = await ensure.exited;
|
|
40
|
+
dlog(`[acpx] ensure exit=${ensureCode} stdout=${ensureOut.slice(0, 300)} stderr=${ensureErr.slice(0, 300)}`);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
dlog(`[acpx] ensure spawn error: ${String(e)}`);
|
|
43
|
+
}
|
|
30
44
|
args.push('prompt', '-s', opts.sessionName);
|
|
31
45
|
} else {
|
|
32
46
|
args.push('prompt');
|
|
33
47
|
}
|
|
34
48
|
args.push(opts.prompt);
|
|
35
49
|
|
|
50
|
+
dlog(`[acpx] prompt: ${args.slice(0, 10).join(' ')} ... (prompt len=${opts.prompt.length})`);
|
|
36
51
|
const proc = Bun.spawn(args, { stdout: 'pipe', stderr: 'pipe', stdin: 'ignore' });
|
|
37
52
|
let stopReason = 'end_turn';
|
|
38
53
|
|
|
@@ -50,7 +65,7 @@ export async function runAcpx(opts: AcpxRunOpts): Promise<{ stopReason: string }
|
|
|
50
65
|
while ((nl = sb.indexOf('\n')) !== -1) {
|
|
51
66
|
const ln = sb.slice(0, nl).trim();
|
|
52
67
|
sb = sb.slice(nl + 1);
|
|
53
|
-
if (ln)
|
|
68
|
+
if (ln) dlog(`[acpx stderr] ${ln.slice(0, 500)}`);
|
|
54
69
|
}
|
|
55
70
|
}
|
|
56
71
|
} catch {}
|
|
@@ -60,6 +75,7 @@ export async function runAcpx(opts: AcpxRunOpts): Promise<{ stopReason: string }
|
|
|
60
75
|
const dec = new TextDecoder();
|
|
61
76
|
let buf = '';
|
|
62
77
|
|
|
78
|
+
let lineCount = 0;
|
|
63
79
|
while (true) {
|
|
64
80
|
const { value, done } = await reader.read();
|
|
65
81
|
if (done) break;
|
|
@@ -69,10 +85,13 @@ export async function runAcpx(opts: AcpxRunOpts): Promise<{ stopReason: string }
|
|
|
69
85
|
const line = buf.slice(0, nl).trim();
|
|
70
86
|
buf = buf.slice(nl + 1);
|
|
71
87
|
if (!line) continue;
|
|
88
|
+
lineCount++;
|
|
89
|
+
dlog(`[acpx stdout] ${line.slice(0, 500)}`);
|
|
72
90
|
const events = parseAcpLine(line, (r) => { stopReason = r; });
|
|
73
91
|
for (const ev of events) await opts.onEvent(ev);
|
|
74
92
|
}
|
|
75
93
|
}
|
|
94
|
+
dlog(`[acpx] stdout lines total=${lineCount}`);
|
|
76
95
|
if (buf.trim()) {
|
|
77
96
|
const events = parseAcpLine(buf.trim(), (r) => { stopReason = r; });
|
|
78
97
|
for (const ev of events) await opts.onEvent(ev);
|
|
@@ -91,7 +110,7 @@ function parseAcpLine(line: string, setStop: (r: string) => void): AcpEvent[] {
|
|
|
91
110
|
const out: AcpEvent[] = [];
|
|
92
111
|
|
|
93
112
|
// session/update notification
|
|
94
|
-
if (msg.method === 'session/update' && msg.params?.sessionUpdate) {
|
|
113
|
+
if (msg.method === 'session/update' && msg.params?.update?.sessionUpdate) {
|
|
95
114
|
return handleSessionUpdate(msg.params);
|
|
96
115
|
}
|
|
97
116
|
// prompt result
|
|
@@ -112,8 +131,6 @@ function handleSessionUpdate(params: any): AcpEvent[] {
|
|
|
112
131
|
const u = params.update || {};
|
|
113
132
|
const kind = u.sessionUpdate;
|
|
114
133
|
const out: AcpEvent[] = [];
|
|
115
|
-
// Fallthrough debug: emit the update kind + keys so we can see what acpx is sending
|
|
116
|
-
out.push({ event_type: 'progress', payload: { message: `[acpx upd] ${kind} keys=${Object.keys(u).join(',')}` } });
|
|
117
134
|
switch (kind) {
|
|
118
135
|
case 'agent_message_chunk':
|
|
119
136
|
case 'agent_thought_chunk': {
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ const LOG_PATH = join(MULTI_DIR, 'logs', 'agent.log');
|
|
|
17
17
|
const SKILLS_DIR = join(MULTI_DIR, 'skills');
|
|
18
18
|
const STOP_PATH = join(MULTI_DIR, 'stop.flag');
|
|
19
19
|
const TASKS_DB_PATH = join(MULTI_DIR, 'tasks.db');
|
|
20
|
-
const VERSION = '0.6.
|
|
20
|
+
const VERSION = '0.6.5';
|
|
21
21
|
|
|
22
22
|
const COMMANDS = {
|
|
23
23
|
setup: 'Register this device with a workspace',
|