@akagilnc/pi-workflow-roles 0.1.3771 → 0.1.3783
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/acp-host/production-host.js +330 -181
- package/dist/headless-host/description.js +77 -0
- package/dist/headless-host/mcp-relay.mjs +119 -0
- package/dist/headless-host/production-host.js +26590 -0
- package/dist/host-descriptions.js +44 -3
- package/dist/public-cli/load-production-external-host.js +19 -0
- package/dist/public-cli/load-production-headless-host.js +36 -0
- package/dist/public-cli/main.js +242 -162
- package/dist/public-role-summons.js +40 -5
- package/package.json +1 -1
- package/scripts/build-package.mjs +28 -0
- package/src/acp-host/role-envelope.ts +141 -84
- package/src/acp-host/role-turn-host.ts +12 -0
- package/src/headless-host/description.ts +123 -0
- package/src/headless-host/production-host.ts +75 -0
- package/src/headless-host/role-turn-host.ts +424 -0
- package/src/host-descriptions.ts +53 -6
- package/src/public-cli/cli.ts +2 -2
- package/src/public-cli/load-production-external-host.ts +29 -0
- package/src/public-cli/load-production-headless-host.ts +48 -0
- package/src/public-cli/main.ts +5 -0
- package/src/public-role-summons.ts +62 -9
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One headless CLI host description (#645 / #752).
|
|
3
|
+
* Every host-specific value the generic headless adapter needs — binary, argv
|
|
4
|
+
* shape, session binding — is data here; lifecycle stays one copy so #646 codex
|
|
5
|
+
* is another row, not a fork.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
/** Absolute agent binary for one operator home. */
|
|
9
|
+
export function resolveHeadlessBinary(description, operatorHome) {
|
|
10
|
+
return join(operatorHome, ...description.binaryFromHome);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build one headless CLI argv for a single process turn.
|
|
14
|
+
* Shape: `<promptFlag> <prompt> <fixedArgs…> <system/schema/mcp/model/effort/session…>`.
|
|
15
|
+
*/
|
|
16
|
+
export function headlessTurnArgs(options) {
|
|
17
|
+
const { description } = options;
|
|
18
|
+
const args = [
|
|
19
|
+
description.promptFlag,
|
|
20
|
+
options.prompt,
|
|
21
|
+
...description.fixedArgs,
|
|
22
|
+
description.systemPromptFlag,
|
|
23
|
+
options.systemPromptPath,
|
|
24
|
+
description.jsonSchemaFlag,
|
|
25
|
+
JSON.stringify(options.jsonSchema),
|
|
26
|
+
];
|
|
27
|
+
if (options.mcpConfigPath !== undefined && options.mcpConfigPath !== "") {
|
|
28
|
+
args.push(description.mcpConfigFlag, options.mcpConfigPath);
|
|
29
|
+
}
|
|
30
|
+
if (options.model !== undefined && options.model !== "") {
|
|
31
|
+
args.push(description.modelFlag, options.model);
|
|
32
|
+
}
|
|
33
|
+
if (options.effort !== undefined && options.effort !== "") {
|
|
34
|
+
args.push(description.effortFlag, options.effort);
|
|
35
|
+
}
|
|
36
|
+
if (options.session.kind === "new") {
|
|
37
|
+
args.push(description.sessionIdFlag, options.session.id);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
args.push(description.resumeFlag, options.session.id);
|
|
41
|
+
}
|
|
42
|
+
return args;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Project shared-envelope MCP server rows into Claude `--mcp-config` JSON.
|
|
46
|
+
* Env stays a plain object (Claude CLI shape); ACP rows use `{name,value}[]`.
|
|
47
|
+
*/
|
|
48
|
+
export function headlessMcpConfigDocument(mcpServers) {
|
|
49
|
+
const servers = {};
|
|
50
|
+
for (const row of mcpServers) {
|
|
51
|
+
const name = typeof row.name === "string" ? row.name : undefined;
|
|
52
|
+
const command = typeof row.command === "string" ? row.command : undefined;
|
|
53
|
+
if (name === undefined || name === "" || command === undefined || command === "")
|
|
54
|
+
continue;
|
|
55
|
+
const entry = { command };
|
|
56
|
+
if (Array.isArray(row.args))
|
|
57
|
+
entry.args = row.args;
|
|
58
|
+
if (Array.isArray(row.env)) {
|
|
59
|
+
const env = {};
|
|
60
|
+
for (const item of row.env) {
|
|
61
|
+
if (typeof item !== "object" || item === null)
|
|
62
|
+
continue;
|
|
63
|
+
const record = item;
|
|
64
|
+
if (typeof record.name === "string" && typeof record.value === "string") {
|
|
65
|
+
env[record.name] = record.value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (Object.keys(env).length > 0)
|
|
69
|
+
entry.env = env;
|
|
70
|
+
}
|
|
71
|
+
else if (typeof row.env === "object" && row.env !== null && !Array.isArray(row.env)) {
|
|
72
|
+
entry.env = row.env;
|
|
73
|
+
}
|
|
74
|
+
servers[name] = entry;
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze({ mcpServers: Object.freeze(servers) });
|
|
77
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { connect } from "node:net";
|
|
3
|
+
import { createInterface } from "node:readline";
|
|
4
|
+
|
|
5
|
+
const socketPath = process.env.AK_ACP_MCP_SOCKET;
|
|
6
|
+
const token = process.env.AK_ACP_MCP_TOKEN;
|
|
7
|
+
if (!socketPath || !token) throw new Error("AK ACP MCP relay identity is missing");
|
|
8
|
+
|
|
9
|
+
const upstream = connect(socketPath);
|
|
10
|
+
const waiters = new Map();
|
|
11
|
+
let nextId = 0;
|
|
12
|
+
let terminalError;
|
|
13
|
+
let shutdownRequested = false;
|
|
14
|
+
let exiting = false;
|
|
15
|
+
let inFlight = 0;
|
|
16
|
+
let pendingStdout = 0;
|
|
17
|
+
|
|
18
|
+
function exitStatus() {
|
|
19
|
+
return terminalError === undefined ? 0 : 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function maybeExit() {
|
|
23
|
+
if (!shutdownRequested || exiting) return;
|
|
24
|
+
if (inFlight > 0 || waiters.size > 0 || pendingStdout > 0) return;
|
|
25
|
+
exiting = true;
|
|
26
|
+
stdinLines.close();
|
|
27
|
+
if (!upstream.destroyed) upstream.end();
|
|
28
|
+
process.exit(exitStatus());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function requestShutdown() {
|
|
32
|
+
shutdownRequested = true;
|
|
33
|
+
maybeExit();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function settle(error) {
|
|
37
|
+
if (terminalError !== undefined) return;
|
|
38
|
+
terminalError = error;
|
|
39
|
+
for (const waiter of waiters.values()) waiter.reject(error);
|
|
40
|
+
waiters.clear();
|
|
41
|
+
maybeExit();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
upstream.on("error", (error) => settle(error));
|
|
45
|
+
upstream.on("close", () => {
|
|
46
|
+
// Shutdown may already be requested (stdin EOF) while an RPC is still
|
|
47
|
+
// in flight. Outstanding waiters must still settle, or drain never ends.
|
|
48
|
+
if (!exiting) settle(new Error("AK ACP MCP upstream closed"));
|
|
49
|
+
maybeExit();
|
|
50
|
+
});
|
|
51
|
+
createInterface({ input: upstream }).on("line", (line) => {
|
|
52
|
+
let message;
|
|
53
|
+
try { message = JSON.parse(line); }
|
|
54
|
+
catch (error) { settle(error); upstream.destroy(); return; }
|
|
55
|
+
const waiter = waiters.get(message.id);
|
|
56
|
+
if (waiter === undefined) return;
|
|
57
|
+
waiters.delete(message.id);
|
|
58
|
+
if (message.error !== undefined) {
|
|
59
|
+
const error = new Error(typeof message.error.message === "string" ? message.error.message : "AK ACP MCP relay failure");
|
|
60
|
+
error.code = message.error.code;
|
|
61
|
+
error.cause = message.error;
|
|
62
|
+
waiter.reject(error);
|
|
63
|
+
} else waiter.resolve(message.result);
|
|
64
|
+
maybeExit();
|
|
65
|
+
});
|
|
66
|
+
function request(method, params = {}) {
|
|
67
|
+
if (terminalError !== undefined) return Promise.reject(terminalError);
|
|
68
|
+
const id = ++nextId;
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
waiters.set(id, { resolve, reject });
|
|
71
|
+
upstream.write(`${JSON.stringify({ id, token, method, params })}\n`, (error) => {
|
|
72
|
+
if (error === null || error === undefined) return;
|
|
73
|
+
const waiter = waiters.get(id);
|
|
74
|
+
if (waiter === undefined) return;
|
|
75
|
+
waiters.delete(id);
|
|
76
|
+
waiter.reject(error);
|
|
77
|
+
maybeExit();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function send(message) {
|
|
82
|
+
const payload = `${JSON.stringify(message)}\n`;
|
|
83
|
+
pendingStdout += 1;
|
|
84
|
+
process.stdout.write(payload, (error) => {
|
|
85
|
+
pendingStdout -= 1;
|
|
86
|
+
if (error) settle(error);
|
|
87
|
+
maybeExit();
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function ok(id, result) { if (id !== undefined) send({ jsonrpc: "2.0", id, result }); }
|
|
91
|
+
function fail(id, error) { if (id !== undefined) send({ jsonrpc: "2.0", id, error: { code: -32000, message: error instanceof Error ? error.message : String(error) } }); }
|
|
92
|
+
|
|
93
|
+
const stdinLines = createInterface({ input: process.stdin });
|
|
94
|
+
stdinLines.on("line", async (line) => {
|
|
95
|
+
inFlight += 1;
|
|
96
|
+
try {
|
|
97
|
+
let message;
|
|
98
|
+
try { message = JSON.parse(line); }
|
|
99
|
+
catch (error) { fail(null, error); return; }
|
|
100
|
+
try {
|
|
101
|
+
if (message.method === "initialize") {
|
|
102
|
+
ok(message.id, { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "ak-role-envelope", version: "1" } });
|
|
103
|
+
} else if (message.method === "ping") {
|
|
104
|
+
ok(message.id, {});
|
|
105
|
+
} else if (message.method === "tools/list") {
|
|
106
|
+
ok(message.id, await request("tools/list"));
|
|
107
|
+
} else if (message.method === "tools/call") {
|
|
108
|
+
ok(message.id, await request("tools/call", message.params));
|
|
109
|
+
} else if (message.method !== "notifications/initialized" && message.method !== "initialized") {
|
|
110
|
+
fail(message.id, new Error(`Unsupported MCP method: ${String(message.method)}`));
|
|
111
|
+
}
|
|
112
|
+
} catch (error) { fail(message.id, error); }
|
|
113
|
+
} finally {
|
|
114
|
+
inFlight -= 1;
|
|
115
|
+
maybeExit();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
stdinLines.on("close", requestShutdown);
|
|
119
|
+
process.on("SIGTERM", requestShutdown);
|