@messenger-agent/client 0.24.0-alpha.2 → 0.24.0-alpha.4
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/args.js +5 -303
- package/dist/auto-upgrade.js +2 -184
- package/dist/config-file.js +1 -100
- package/dist/control.js +2 -152
- package/dist/exec.js +2 -37
- package/dist/index.js +5 -157
- package/dist/install.js +1 -142
- package/dist/maintenance.js +2 -194
- package/dist/paths.js +1 -20
- package/dist/runtime.js +3 -241
- package/dist/service.js +3 -208
- package/dist/supervisor.js +1 -282
- package/package.json +5 -5
package/dist/control.js
CHANGED
|
@@ -1,152 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { readConfigYaml } from "./config-file.js";
|
|
5
|
-
import { defaultDataDir } from "./paths.js";
|
|
6
|
-
export async function readControlSocketPath(configPath) {
|
|
7
|
-
const config = await readConfigYaml(configPath);
|
|
8
|
-
const dataDir = typeof config.data_dir === "string" && config.data_dir.trim() ? config.data_dir : defaultDataDir;
|
|
9
|
-
return join(dataDir, "runtime", "client.sock");
|
|
10
|
-
}
|
|
11
|
-
export async function startControlServer(socketPath, handlers) {
|
|
12
|
-
await mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });
|
|
13
|
-
await rm(socketPath, { force: true });
|
|
14
|
-
const server = createServer({ allowHalfOpen: true }, (socket) => {
|
|
15
|
-
let input = "";
|
|
16
|
-
socket.setEncoding("utf8");
|
|
17
|
-
socket.on("error", () => {
|
|
18
|
-
// A caller may disconnect while an agent is still restarting.
|
|
19
|
-
});
|
|
20
|
-
socket.on("data", (chunk) => {
|
|
21
|
-
input += chunk;
|
|
22
|
-
});
|
|
23
|
-
socket.on("end", () => {
|
|
24
|
-
void handleRequest(input, handlers).then((response) => socket.end(`${JSON.stringify(response)}\n`));
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
await listen(server, socketPath);
|
|
28
|
-
await chmod(socketPath, 0o600);
|
|
29
|
-
return {
|
|
30
|
-
async close() {
|
|
31
|
-
await closeServer(server);
|
|
32
|
-
await rm(socketPath, { force: true });
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
export async function requestAgentRestart(configPath, agent) {
|
|
37
|
-
const socketPath = await readControlSocketPath(configPath);
|
|
38
|
-
const response = await sendRequest(socketPath, { action: "restart", agent }).catch((err) => {
|
|
39
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
40
|
-
throw new Error(`Unable to contact the coding-agent client service: ${detail}`);
|
|
41
|
-
});
|
|
42
|
-
if (!response.ok)
|
|
43
|
-
throw new Error(response.error ?? `Failed to restart ${agent} agent`);
|
|
44
|
-
}
|
|
45
|
-
export async function requestClientActivity(configPath) {
|
|
46
|
-
return requestData(configPath, { action: "activity" });
|
|
47
|
-
}
|
|
48
|
-
export async function requestScheduleMaintenance(configPath, options) {
|
|
49
|
-
return requestData(configPath, { action: "schedule-maintenance", ...options });
|
|
50
|
-
}
|
|
51
|
-
export async function requestMaintenanceStatus(configPath) {
|
|
52
|
-
return requestData(configPath, { action: "maintenance-status" });
|
|
53
|
-
}
|
|
54
|
-
export async function requestMaintenanceCancel(configPath) {
|
|
55
|
-
return requestData(configPath, { action: "maintenance-cancel" });
|
|
56
|
-
}
|
|
57
|
-
async function handleRequest(input, handlers) {
|
|
58
|
-
try {
|
|
59
|
-
const request = JSON.parse(input);
|
|
60
|
-
switch (request.action) {
|
|
61
|
-
case "restart":
|
|
62
|
-
if (request.agent !== "codex" && request.agent !== "claude") {
|
|
63
|
-
return { ok: false, error: "Invalid control request" };
|
|
64
|
-
}
|
|
65
|
-
await handlers.restartAgent(request.agent);
|
|
66
|
-
return { ok: true };
|
|
67
|
-
case "activity":
|
|
68
|
-
return { ok: true, data: await handlers.getActivity() };
|
|
69
|
-
case "schedule-maintenance":
|
|
70
|
-
if (!isMaintenanceOperation(request.operation) ||
|
|
71
|
-
!Number.isInteger(request.delaySeconds) ||
|
|
72
|
-
typeof request.allowWaiting !== "boolean") {
|
|
73
|
-
return { ok: false, error: "Invalid maintenance request" };
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
ok: true,
|
|
77
|
-
data: await handlers.maintenance.schedule({
|
|
78
|
-
operation: request.operation,
|
|
79
|
-
delaySeconds: request.delaySeconds,
|
|
80
|
-
allowWaiting: request.allowWaiting,
|
|
81
|
-
}),
|
|
82
|
-
};
|
|
83
|
-
case "maintenance-status":
|
|
84
|
-
return { ok: true, data: handlers.maintenance.getTask() };
|
|
85
|
-
case "maintenance-cancel":
|
|
86
|
-
return { ok: true, data: await handlers.maintenance.cancel() };
|
|
87
|
-
default:
|
|
88
|
-
return { ok: false, error: "Invalid control request" };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function isMaintenanceOperation(operation) {
|
|
96
|
-
if (!operation || typeof operation !== "object" || !("type" in operation))
|
|
97
|
-
return false;
|
|
98
|
-
if (operation.type === "upgrade") {
|
|
99
|
-
return "version" in operation && typeof operation.version === "string" && operation.version.length > 0;
|
|
100
|
-
}
|
|
101
|
-
if (operation.type === "restart") {
|
|
102
|
-
return !("agent" in operation &&
|
|
103
|
-
operation.agent !== undefined &&
|
|
104
|
-
operation.agent !== "codex" &&
|
|
105
|
-
operation.agent !== "claude");
|
|
106
|
-
}
|
|
107
|
-
return operation.type === "stop" || operation.type === "uninstall";
|
|
108
|
-
}
|
|
109
|
-
async function requestData(configPath, request) {
|
|
110
|
-
const socketPath = await readControlSocketPath(configPath);
|
|
111
|
-
const response = await sendRequest(socketPath, request).catch((err) => {
|
|
112
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
113
|
-
throw new Error(`Unable to contact the coding-agent client service: ${detail}`);
|
|
114
|
-
});
|
|
115
|
-
if (!response.ok)
|
|
116
|
-
throw new Error(response.error ?? "Client control request failed");
|
|
117
|
-
return response.data;
|
|
118
|
-
}
|
|
119
|
-
function listen(server, socketPath) {
|
|
120
|
-
return new Promise((resolve, reject) => {
|
|
121
|
-
server.once("error", reject);
|
|
122
|
-
server.listen(socketPath, () => {
|
|
123
|
-
server.off("error", reject);
|
|
124
|
-
resolve();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
function closeServer(server) {
|
|
129
|
-
return new Promise((resolve, reject) => {
|
|
130
|
-
server.close((err) => (err ? reject(err) : resolve()));
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
function sendRequest(socketPath, request) {
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
const socket = createConnection(socketPath);
|
|
136
|
-
let output = "";
|
|
137
|
-
socket.setEncoding("utf8");
|
|
138
|
-
socket.once("error", reject);
|
|
139
|
-
socket.on("data", (chunk) => {
|
|
140
|
-
output += chunk;
|
|
141
|
-
});
|
|
142
|
-
socket.once("connect", () => socket.end(JSON.stringify(request)));
|
|
143
|
-
socket.once("end", () => {
|
|
144
|
-
try {
|
|
145
|
-
resolve(JSON.parse(output));
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
reject(new Error("Invalid response from the coding-agent client service"));
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
}
|
|
1
|
+
import{chmod as d,mkdir as f,rm as c}from"node:fs/promises";import{createConnection as g,createServer as m}from"node:net";import{dirname as w,join as y}from"node:path";import{readConfigYaml as p}from"./config-file.js";import{defaultDataDir as v}from"./paths.js";async function s(e){const n=await p(e),t=typeof n.data_dir=="string"&&n.data_dir.trim()?n.data_dir:v;return y(t,"runtime","client.sock")}async function I(e,n){await f(w(e),{recursive:!0,mode:448}),await c(e,{force:!0});const t=m({allowHalfOpen:!0},r=>{let a="";r.setEncoding("utf8"),r.on("error",()=>{}),r.on("data",o=>{a+=o}),r.on("end",()=>{S(a,n).then(o=>r.end(`${JSON.stringify(o)}
|
|
2
|
+
`))})});return await q(t,e),await d(e,384),{async close(){await k(t),await c(e,{force:!0})}}}async function N(e,n){const t=await s(e),r=await u(t,{action:"restart",agent:n}).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the coding-agent client service: ${o}`)});if(!r.ok)throw new Error(r.error??`Failed to restart ${n} agent`)}async function A(e){return i(e,{action:"activity"})}async function D(e,n){return i(e,{action:"schedule-maintenance",...n})}async function J(e){return i(e,{action:"maintenance-status"})}async function M(e){return i(e,{action:"maintenance-cancel"})}async function S(e,n){try{const t=JSON.parse(e);switch(t.action){case"restart":return t.agent!=="codex"&&t.agent!=="claude"?{ok:!1,error:"Invalid control request"}:(await n.restartAgent(t.agent),{ok:!0});case"activity":return{ok:!0,data:await n.getActivity()};case"schedule-maintenance":return!h(t.operation)||!Number.isInteger(t.delaySeconds)||typeof t.allowWaiting!="boolean"?{ok:!1,error:"Invalid maintenance request"}:{ok:!0,data:await n.maintenance.schedule({operation:t.operation,delaySeconds:t.delaySeconds,allowWaiting:t.allowWaiting})};case"maintenance-status":return{ok:!0,data:n.maintenance.getTask()};case"maintenance-cancel":return{ok:!0,data:await n.maintenance.cancel()};default:return{ok:!1,error:"Invalid control request"}}}catch(t){return{ok:!1,error:t instanceof Error?t.message:String(t)}}}function h(e){return!e||typeof e!="object"||!("type"in e)?!1:e.type==="upgrade"?"version"in e&&typeof e.version=="string"&&e.version.length>0:e.type==="restart"?!("agent"in e&&e.agent!==void 0&&e.agent!=="codex"&&e.agent!=="claude"):e.type==="stop"||e.type==="uninstall"}async function i(e,n){const t=await s(e),r=await u(t,n).catch(a=>{const o=a instanceof Error?a.message:String(a);throw new Error(`Unable to contact the coding-agent client service: ${o}`)});if(!r.ok)throw new Error(r.error??"Client control request failed");return r.data}function q(e,n){return new Promise((t,r)=>{e.once("error",r),e.listen(n,()=>{e.off("error",r),t()})})}function k(e){return new Promise((n,t)=>{e.close(r=>r?t(r):n())})}function u(e,n){return new Promise((t,r)=>{const a=g(e);let o="";a.setEncoding("utf8"),a.once("error",r),a.on("data",l=>{o+=l}),a.once("connect",()=>a.end(JSON.stringify(n))),a.once("end",()=>{try{t(JSON.parse(o))}catch{r(new Error("Invalid response from the coding-agent client service"))}})})}export{s as readControlSocketPath,N as requestAgentRestart,A as requestClientActivity,M as requestMaintenanceCancel,J as requestMaintenanceStatus,D as requestScheduleMaintenance,I as startControlServer};
|
package/dist/exec.js
CHANGED
|
@@ -1,37 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
3
|
-
return new Promise((resolve, reject) => {
|
|
4
|
-
console.log(`$ ${command} ${args.join(" ")}`);
|
|
5
|
-
const child = spawn(command, args, {
|
|
6
|
-
stdio: options.interactive ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
7
|
-
env: {
|
|
8
|
-
...process.env,
|
|
9
|
-
...options.env,
|
|
10
|
-
NODE_OPTIONS: "--use-system-ca",
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
const stdoutChunks = [];
|
|
14
|
-
const stderrChunks = [];
|
|
15
|
-
child.stdout?.on("data", (chunk) => {
|
|
16
|
-
// console.log(chunk.toString("utf8"))
|
|
17
|
-
stdoutChunks.push(chunk);
|
|
18
|
-
});
|
|
19
|
-
child.stderr?.on("data", (chunk) => {
|
|
20
|
-
// console.error(chunk.toString("utf8"))
|
|
21
|
-
stderrChunks.push(chunk);
|
|
22
|
-
});
|
|
23
|
-
child.on("error", reject);
|
|
24
|
-
child.on("close", (status) => {
|
|
25
|
-
const result = {
|
|
26
|
-
status,
|
|
27
|
-
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
28
|
-
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
29
|
-
};
|
|
30
|
-
if (!options.allowFailure && status !== 0) {
|
|
31
|
-
reject(new Error(`${command} ${args.join(" ")} failed with status ${status}\n${result.stderr || result.stdout}`));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
resolve(result);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
1
|
+
import{spawn as f}from"node:child_process";function a(n,e,o={}){return new Promise((d,i)=>{console.log(`$ ${n} ${e.join(" ")}`);const r=f(n,e,{stdio:o.interactive?"inherit":["ignore","pipe","pipe"],env:{...process.env,...o.env,NODE_OPTIONS:"--use-system-ca"}}),u=[],c=[];r.stdout?.on("data",t=>{u.push(t)}),r.stderr?.on("data",t=>{c.push(t)}),r.on("error",i),r.on("close",t=>{const s={status:t,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(c).toString("utf8")};if(!o.allowFailure&&t!==0){i(new Error(`${n} ${e.join(" ")} failed with status ${t}
|
|
2
|
+
${s.stderr||s.stdout}`));return}d(s)})})}export{a as runCommand};
|
package/dist/index.js
CHANGED
|
@@ -1,158 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { runCommand } from "./exec.js";
|
|
8
|
-
import { defaultConfigPath } from "./paths.js";
|
|
9
|
-
import { writeUpgradeChannel } from "./config-file.js";
|
|
10
|
-
import { requestAgentRestart, requestClientActivity, requestMaintenanceCancel, requestMaintenanceStatus, requestScheduleMaintenance, } from "./control.js";
|
|
11
|
-
async function main() {
|
|
12
|
-
const args = parseCliArgs(process.argv.slice(2));
|
|
13
|
-
if (args.command === "version") {
|
|
14
|
-
console.log(`coding-agent ${await currentPackageVersion()}`);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
if (args.command === "run-service") {
|
|
18
|
-
await runSupervisor(args.configPath ?? process.env.AGENT_CONFIG_PATH ?? defaultConfigPath);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (args.command === "install") {
|
|
22
|
-
const result = await installClient(args);
|
|
23
|
-
console.log(`Installed ${serviceName}`);
|
|
24
|
-
console.log(`Config: ${result.configPath}`);
|
|
25
|
-
console.log(`Data: ${result.dataDir}`);
|
|
26
|
-
console.log(`Runtime: ${result.runtime.currentLink}`);
|
|
27
|
-
console.log(`Command: ${result.runtime.cliWrapperPath}`);
|
|
28
|
-
console.log(`Service file: ${result.service.servicePath}`);
|
|
29
|
-
console.log(`Upgrade: ${result.runtime.cliWrapperPath} upgrade`);
|
|
30
|
-
console.log(`Restart: ${result.runtime.cliWrapperPath} restart`);
|
|
31
|
-
console.log(`Status: ${result.runtime.cliWrapperPath} status`);
|
|
32
|
-
console.log(`Logs: ${result.service.commands.logs}`);
|
|
33
|
-
for (const warning of result.service.warnings)
|
|
34
|
-
console.warn(`Warning: ${warning}`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (args.command === "upgrade") {
|
|
38
|
-
const { version, channel } = normalizeUpgradeVersion(args.version, args.channel);
|
|
39
|
-
if (channel) {
|
|
40
|
-
await writeUpgradeChannel(args.configPath, channel);
|
|
41
|
-
console.log(`Auto-upgrade channel: ${channel}`);
|
|
42
|
-
}
|
|
43
|
-
if (args.delaySeconds !== undefined) {
|
|
44
|
-
await scheduleMaintenance(args.configPath, { type: "upgrade", version }, args.delaySeconds, args.allowWaiting);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const runtime = await installRuntime({ version, configPath: args.configPath });
|
|
48
|
-
const commands = process.platform === "darwin" ? darwinCommands() : linuxCommands();
|
|
49
|
-
await runShellCommand(commands.restart);
|
|
50
|
-
console.log(`Upgraded runtime to ${runtime.version}`);
|
|
51
|
-
console.log(`Runtime: ${runtime.currentLink}`);
|
|
52
|
-
console.log(`Command: ${runtime.cliWrapperPath}`);
|
|
53
|
-
console.log(`Status: ${runtime.cliWrapperPath} status`);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (args.command === "sessions") {
|
|
57
|
-
const activity = await requestClientActivity(args.configPath);
|
|
58
|
-
console.log(args.json ? JSON.stringify(activity) : formatActivity(activity));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (args.command === "maintenance") {
|
|
62
|
-
const task = args.action === "cancel"
|
|
63
|
-
? await requestMaintenanceCancel(args.configPath)
|
|
64
|
-
: await requestMaintenanceStatus(args.configPath);
|
|
65
|
-
console.log(args.json ? JSON.stringify(task ?? null) : formatMaintenanceTask(task));
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (args.command === "restart" && args.agent) {
|
|
69
|
-
if (args.delaySeconds !== undefined) {
|
|
70
|
-
await scheduleMaintenance(process.env.AGENT_CONFIG_PATH ?? defaultConfigPath, { type: "restart", agent: args.agent }, args.delaySeconds, args.allowWaiting);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
await requestAgentRestart(process.env.AGENT_CONFIG_PATH ?? defaultConfigPath, args.agent);
|
|
74
|
-
console.log(`Restarted ${args.agent} agent`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (args.command === "status" || args.command === "start" || args.command === "restart" || args.command === "stop") {
|
|
78
|
-
if ((args.command === "restart" || args.command === "stop") && args.delaySeconds !== undefined) {
|
|
79
|
-
await scheduleMaintenance(process.env.AGENT_CONFIG_PATH ?? defaultConfigPath, { type: args.command }, args.delaySeconds, args.allowWaiting);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const commands = process.platform === "darwin" ? darwinCommands() : linuxCommands();
|
|
83
|
-
const command = args.command === "status"
|
|
84
|
-
? commands.status
|
|
85
|
-
: args.command === "start"
|
|
86
|
-
? commands.start
|
|
87
|
-
: args.command === "restart"
|
|
88
|
-
? commands.restart
|
|
89
|
-
: commands.stop;
|
|
90
|
-
await runShellCommand(command);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (args.command === "uninstall") {
|
|
94
|
-
if (args.delaySeconds !== undefined) {
|
|
95
|
-
await scheduleMaintenance(process.env.AGENT_CONFIG_PATH ?? defaultConfigPath, { type: "uninstall" }, args.delaySeconds, args.allowWaiting);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
await uninstallService();
|
|
99
|
-
console.log(`Uninstalled ${serviceName}`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
async function scheduleMaintenance(configPath, operation, delaySeconds, allowWaiting) {
|
|
104
|
-
const task = await requestScheduleMaintenance(configPath, { operation, delaySeconds, allowWaiting });
|
|
105
|
-
console.log(`Scheduled maintenance task ${task.id}`);
|
|
106
|
-
console.log(`Operation: ${formatOperation(task.operation)}`);
|
|
107
|
-
console.log(`Not before: ${task.notBefore}`);
|
|
108
|
-
}
|
|
109
|
-
function formatActivity(activity) {
|
|
110
|
-
const lines = [`Active sessions: ${activity.active}`, `Waiting sessions: ${activity.waiting}`];
|
|
111
|
-
for (const name of ["codex", "claude"]) {
|
|
112
|
-
const status = activity.agents[name];
|
|
113
|
-
lines.push(status.available
|
|
114
|
-
? `${capitalize(name)}: active=${status.active}, waiting=${status.waiting}`
|
|
115
|
-
: `${capitalize(name)}: unavailable (${status.error})`);
|
|
116
|
-
}
|
|
117
|
-
return lines.join("\n");
|
|
118
|
-
}
|
|
119
|
-
function formatMaintenanceTask(task) {
|
|
120
|
-
if (!task)
|
|
121
|
-
return "No maintenance task has been scheduled";
|
|
122
|
-
return [
|
|
123
|
-
`Task: ${task.id}`,
|
|
124
|
-
`Operation: ${formatOperation(task.operation)}`,
|
|
125
|
-
`Status: ${task.status}`,
|
|
126
|
-
`Not before: ${task.notBefore}`,
|
|
127
|
-
...(task.message ? [`Message: ${task.message}`] : []),
|
|
128
|
-
].join("\n");
|
|
129
|
-
}
|
|
130
|
-
function formatOperation(operation) {
|
|
131
|
-
if (operation.type === "upgrade")
|
|
132
|
-
return `upgrade to ${operation.version}`;
|
|
133
|
-
if (operation.type === "restart")
|
|
134
|
-
return operation.agent ? `restart ${operation.agent}` : "restart client";
|
|
135
|
-
return operation.type;
|
|
136
|
-
}
|
|
137
|
-
function capitalize(value) {
|
|
138
|
-
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
139
|
-
}
|
|
140
|
-
async function runShellCommand(command) {
|
|
141
|
-
const result = await runCommand("/bin/sh", ["-lc", command], { allowFailure: true });
|
|
142
|
-
if (result.stdout)
|
|
143
|
-
process.stdout.write(result.stdout);
|
|
144
|
-
if (result.stderr)
|
|
145
|
-
process.stderr.write(result.stderr);
|
|
146
|
-
if (result.status !== 0)
|
|
147
|
-
process.exit(result.status ?? 1);
|
|
148
|
-
}
|
|
149
|
-
main().catch((err) => {
|
|
150
|
-
if (err instanceof UsageError) {
|
|
151
|
-
const message = err.message === usage() ? err.message : `${err.message}\n\n${usage()}`;
|
|
152
|
-
console.error(message);
|
|
153
|
-
process.exit(2);
|
|
154
|
-
}
|
|
155
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
156
|
-
console.error(message);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
});
|
|
2
|
+
import{parseCliArgs as f,UsageError as p,usage as i}from"./args.js";import{installClient as $}from"./install.js";import{currentPackageVersion as w,installRuntime as h,normalizeUpgradeVersion as v}from"./runtime.js";import{darwinCommands as c,linuxCommands as l,serviceName as m,uninstallService as P}from"./service.js";import{runSupervisor as y}from"./supervisor.js";import{runCommand as S}from"./exec.js";import{defaultConfigPath as s}from"./paths.js";import{writeUpgradeChannel as C}from"./config-file.js";import{requestAgentRestart as A,requestClientActivity as N,requestMaintenanceCancel as T,requestMaintenanceStatus as W,requestScheduleMaintenance as G}from"./control.js";async function O(){const e=f(process.argv.slice(2));if(e.command==="version"){console.log(`coding-agent ${await w()}`);return}if(e.command==="run-service"){await y(e.configPath??process.env.AGENT_CONFIG_PATH??s);return}if(e.command==="install"){const n=await $(e);console.log(`Installed ${m}`),console.log(`Config: ${n.configPath}`),console.log(`Data: ${n.dataDir}`),console.log(`Runtime: ${n.runtime.currentLink}`),console.log(`Command: ${n.runtime.cliWrapperPath}`),console.log(`Service file: ${n.service.servicePath}`),console.log(`Upgrade: ${n.runtime.cliWrapperPath} upgrade`),console.log(`Restart: ${n.runtime.cliWrapperPath} restart`),console.log(`Status: ${n.runtime.cliWrapperPath} status`),console.log(`Logs: ${n.service.commands.logs}`);for(const t of n.service.warnings)console.warn(`Warning: ${t}`);return}if(e.command==="upgrade"){const{version:n,channel:t}=v(e.version,e.channel);if(t&&(await C(e.configPath,t),console.log(`Auto-upgrade channel: ${t}`)),e.delaySeconds!==void 0){await r(e.configPath,{type:"upgrade",version:n},e.delaySeconds,e.allowWaiting);return}const a=await h({version:n,configPath:e.configPath}),o=process.platform==="darwin"?c():l();await g(o.restart),console.log(`Upgraded runtime to ${a.version}`),console.log(`Runtime: ${a.currentLink}`),console.log(`Command: ${a.cliWrapperPath}`),console.log(`Status: ${a.cliWrapperPath} status`);return}if(e.command==="sessions"){const n=await N(e.configPath);console.log(e.json?JSON.stringify(n):_(n));return}if(e.command==="maintenance"){const n=e.action==="cancel"?await T(e.configPath):await W(e.configPath);console.log(e.json?JSON.stringify(n??null):b(n));return}if(e.command==="restart"&&e.agent){if(e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:"restart",agent:e.agent},e.delaySeconds,e.allowWaiting);return}await A(process.env.AGENT_CONFIG_PATH??s,e.agent),console.log(`Restarted ${e.agent} agent`);return}if(e.command==="status"||e.command==="start"||e.command==="restart"||e.command==="stop"){if((e.command==="restart"||e.command==="stop")&&e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:e.command},e.delaySeconds,e.allowWaiting);return}const n=process.platform==="darwin"?c():l(),t=e.command==="status"?n.status:e.command==="start"?n.start:e.command==="restart"?n.restart:n.stop;await g(t);return}if(e.command==="uninstall"){if(e.delaySeconds!==void 0){await r(process.env.AGENT_CONFIG_PATH??s,{type:"uninstall"},e.delaySeconds,e.allowWaiting);return}await P(),console.log(`Uninstalled ${m}`);return}}async function r(e,n,t,a){const o=await G(e,{operation:n,delaySeconds:t,allowWaiting:a});console.log(`Scheduled maintenance task ${o.id}`),console.log(`Operation: ${u(o.operation)}`),console.log(`Not before: ${o.notBefore}`)}function _(e){const n=[`Active sessions: ${e.active}`,`Waiting sessions: ${e.waiting}`];for(const t of["codex","claude"]){const a=e.agents[t];n.push(a.available?`${d(t)}: active=${a.active}, waiting=${a.waiting}`:`${d(t)}: unavailable (${a.error})`)}return n.join(`
|
|
3
|
+
`)}function b(e){return e?[`Task: ${e.id}`,`Operation: ${u(e.operation)}`,`Status: ${e.status}`,`Not before: ${e.notBefore}`,...e.message?[`Message: ${e.message}`]:[]].join(`
|
|
4
|
+
`):"No maintenance task has been scheduled"}function u(e){return e.type==="upgrade"?`upgrade to ${e.version}`:e.type==="restart"?e.agent?`restart ${e.agent}`:"restart client":e.type}function d(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}async function g(e){const n=await S("/bin/sh",["-lc",e],{allowFailure:!0});n.stdout&&process.stdout.write(n.stdout),n.stderr&&process.stderr.write(n.stderr),n.status!==0&&process.exit(n.status??1)}O().catch(e=>{if(e instanceof p){const t=e.message===i()?e.message:`${e.message}
|
|
5
|
+
|
|
6
|
+
${i()}`;console.error(t),process.exit(2)}const n=e instanceof Error?e.message:String(e);console.error(n),process.exit(1)});
|
package/dist/install.js
CHANGED
|
@@ -1,142 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { readConfigYaml, updateClientConfig, writeUpgradeChannel } from "./config-file.js";
|
|
3
|
-
import { installRuntime } from "./runtime.js";
|
|
4
|
-
import { installService } from "./service.js";
|
|
5
|
-
import { UsageError } from "./args.js";
|
|
6
|
-
import { defaultWorkspacePath } from "./paths.js";
|
|
7
|
-
export function assertSupportedPlatform(platform = process.platform) {
|
|
8
|
-
if (platform !== "linux" && platform !== "darwin") {
|
|
9
|
-
throw new Error("Only Linux and macOS are supported");
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
export function assertNodeVersion(version = process.versions.node) {
|
|
13
|
-
const major = Number.parseInt(version.split(".")[0] ?? "", 10);
|
|
14
|
-
if (!Number.isInteger(major) || major < 24) {
|
|
15
|
-
throw new Error("Node.js 24 or newer is required");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export function validateInstallArgs(args) {
|
|
19
|
-
if (args.tunnelId && !/^[A-Za-z0-9._:-]+$/.test(args.tunnelId)) {
|
|
20
|
-
throw new Error("--tunnel-id may only contain letters, numbers, '.', '_', ':', and '-'");
|
|
21
|
-
}
|
|
22
|
-
if (args.workspaceId && !/^[A-Za-z0-9._:-]+$/.test(args.workspaceId)) {
|
|
23
|
-
throw new Error("--workspace-id may only contain letters, numbers, '.', '_', ':', and '-'");
|
|
24
|
-
}
|
|
25
|
-
if (args.workspaceName && (args.workspaceName.trim().length === 0 || args.workspaceName.trim().length > 128)) {
|
|
26
|
-
throw new Error("--workspace-name must be between 1 and 128 characters");
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export async function resolveWorkspacePath(path, createIfMissing = false) {
|
|
30
|
-
if (createIfMissing)
|
|
31
|
-
await mkdir(path, { recursive: true });
|
|
32
|
-
const resolved = await realpath(path);
|
|
33
|
-
const stats = await stat(resolved);
|
|
34
|
-
if (!stats.isDirectory()) {
|
|
35
|
-
throw new Error("--workspace must point to an existing directory");
|
|
36
|
-
}
|
|
37
|
-
return resolved;
|
|
38
|
-
}
|
|
39
|
-
export async function installClient(args) {
|
|
40
|
-
assertSupportedPlatform();
|
|
41
|
-
assertNodeVersion();
|
|
42
|
-
const resolvedArgs = await resolveInstallArgs(args);
|
|
43
|
-
validateInstallArgs(resolvedArgs);
|
|
44
|
-
const workspacePath = await resolveWorkspacePath(resolvedArgs.workspace, resolvedArgs.createWorkspace);
|
|
45
|
-
await updateClientConfig({
|
|
46
|
-
configPath: resolvedArgs.configPath,
|
|
47
|
-
dataDir: resolvedArgs.dataDir,
|
|
48
|
-
workspacePath,
|
|
49
|
-
workspaceId: resolvedArgs.workspaceId,
|
|
50
|
-
workspaceName: resolvedArgs.workspaceName,
|
|
51
|
-
tunnelId: resolvedArgs.tunnelId,
|
|
52
|
-
token: resolvedArgs.token,
|
|
53
|
-
serverUrl: resolvedArgs.serverUrl,
|
|
54
|
-
preserveWorkspaces: resolvedArgs.preserveWorkspaces,
|
|
55
|
-
});
|
|
56
|
-
if (args.channel)
|
|
57
|
-
await writeUpgradeChannel(resolvedArgs.configPath, args.channel);
|
|
58
|
-
const runtime = await installRuntime({ version: "current", configPath: resolvedArgs.configPath });
|
|
59
|
-
const service = await installService({
|
|
60
|
-
configPath: resolvedArgs.configPath,
|
|
61
|
-
dataDir: resolvedArgs.dataDir,
|
|
62
|
-
workspacePath,
|
|
63
|
-
serviceCommandPath: runtime.wrapperPath,
|
|
64
|
-
});
|
|
65
|
-
return {
|
|
66
|
-
configPath: resolvedArgs.configPath,
|
|
67
|
-
dataDir: resolvedArgs.dataDir,
|
|
68
|
-
workspacePath,
|
|
69
|
-
runtime,
|
|
70
|
-
service,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
export async function resolveInstallArgs(args) {
|
|
74
|
-
const existing = await readConfigYaml(args.configPath);
|
|
75
|
-
const existingTunnel = objectAt(existing, "tunnel");
|
|
76
|
-
const tunnelId = args.tunnelId ?? stringAt(existingTunnel, "tunnel_id");
|
|
77
|
-
const token = args.token ?? stringAt(existingTunnel, "token");
|
|
78
|
-
const configuredWorkspace = findConfiguredWorkspace(existing, args.workspaceId, tunnelId);
|
|
79
|
-
const workspace = args.workspace ?? configuredWorkspace?.path ?? defaultWorkspacePath;
|
|
80
|
-
const workspaceId = args.workspaceId ?? configuredWorkspace?.id ?? (tunnelId ? `${tunnelId}--default` : undefined);
|
|
81
|
-
const workspaceName = args.workspaceName ?? configuredWorkspace?.name ?? (tunnelId ? `${tunnelId} Default` : undefined);
|
|
82
|
-
const usesDefaultWorkspace = workspace === defaultWorkspacePath;
|
|
83
|
-
if (!tunnelId)
|
|
84
|
-
throw new UsageError("Missing required option: --tunnel-id");
|
|
85
|
-
if (!token)
|
|
86
|
-
throw new UsageError("Missing required option: --token");
|
|
87
|
-
if (!workspaceId)
|
|
88
|
-
throw new UsageError("Missing required option: --workspace-id");
|
|
89
|
-
if (!workspaceName)
|
|
90
|
-
throw new UsageError("Missing required option: --workspace-name");
|
|
91
|
-
return {
|
|
92
|
-
...args,
|
|
93
|
-
tunnelId,
|
|
94
|
-
token,
|
|
95
|
-
workspace,
|
|
96
|
-
workspaceId,
|
|
97
|
-
workspaceName,
|
|
98
|
-
preserveWorkspaces: !args.workspace && !!configuredWorkspace,
|
|
99
|
-
createWorkspace: usesDefaultWorkspace,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function findConfiguredWorkspace(config, requestedWorkspaceId, tunnelId) {
|
|
103
|
-
const workspaces = config.workspaces;
|
|
104
|
-
if (!Array.isArray(workspaces))
|
|
105
|
-
return undefined;
|
|
106
|
-
const candidates = workspaces.filter((workspace) => !!workspace && typeof workspace === "object" && !Array.isArray(workspace) && typeof workspace.path === "string");
|
|
107
|
-
if (requestedWorkspaceId) {
|
|
108
|
-
const requested = candidates.find((workspace) => workspace.id === requestedWorkspaceId);
|
|
109
|
-
if (!requested || typeof requested.path !== "string")
|
|
110
|
-
return undefined;
|
|
111
|
-
return {
|
|
112
|
-
id: requestedWorkspaceId,
|
|
113
|
-
name: typeof requested.name === "string" && requested.name
|
|
114
|
-
? requested.name
|
|
115
|
-
: `${tunnelId ?? requestedWorkspaceId} Default`,
|
|
116
|
-
path: requested.path,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const preferredId = requestedWorkspaceId ?? (tunnelId ? `${tunnelId}--default` : undefined);
|
|
120
|
-
const configured = (preferredId ? candidates.find((workspace) => workspace.id === preferredId) : undefined) ??
|
|
121
|
-
candidates.find((workspace) => workspace.id === "default") ??
|
|
122
|
-
candidates[0];
|
|
123
|
-
if (!configured || typeof configured.path !== "string")
|
|
124
|
-
return undefined;
|
|
125
|
-
return {
|
|
126
|
-
id: typeof configured.id === "string" && configured.id ? configured.id : (preferredId ?? "default"),
|
|
127
|
-
name: typeof configured.name === "string" && configured.name
|
|
128
|
-
? configured.name
|
|
129
|
-
: `${tunnelId ?? preferredId ?? "default"} Default`,
|
|
130
|
-
path: configured.path,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
function objectAt(config, key) {
|
|
134
|
-
const value = config[key];
|
|
135
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
136
|
-
return undefined;
|
|
137
|
-
return value;
|
|
138
|
-
}
|
|
139
|
-
function stringAt(config, key) {
|
|
140
|
-
const value = config?.[key];
|
|
141
|
-
return typeof value === "string" && value ? value : undefined;
|
|
142
|
-
}
|
|
1
|
+
import{mkdir as w,realpath as l,stat as m}from"node:fs/promises";import{readConfigYaml as h,updateClientConfig as k,writeUpgradeChannel as y}from"./config-file.js";import{installRuntime as g}from"./runtime.js";import{installService as v}from"./service.js";import{UsageError as f}from"./args.js";import{defaultWorkspacePath as d}from"./paths.js";function P(e=process.platform){if(e!=="linux"&&e!=="darwin")throw new Error("Only Linux and macOS are supported")}function A(e=process.versions.node){const t=Number.parseInt(e.split(".")[0]??"",10);if(!Number.isInteger(t)||t<24)throw new Error("Node.js 24 or newer is required")}function x(e){if(e.tunnelId&&!/^[A-Za-z0-9._:-]+$/.test(e.tunnelId))throw new Error("--tunnel-id may only contain letters, numbers, '.', '_', ':', and '-'");if(e.workspaceId&&!/^[A-Za-z0-9._:-]+$/.test(e.workspaceId))throw new Error("--workspace-id may only contain letters, numbers, '.', '_', ':', and '-'");if(e.workspaceName&&(e.workspaceName.trim().length===0||e.workspaceName.trim().length>128))throw new Error("--workspace-name must be between 1 and 128 characters")}async function D(e,t=!1){t&&await w(e,{recursive:!0});const n=await l(e);if(!(await m(n)).isDirectory())throw new Error("--workspace must point to an existing directory");return n}async function M(e){P(),A();const t=await I(e);x(t);const n=await D(t.workspace,t.createWorkspace);await k({configPath:t.configPath,dataDir:t.dataDir,workspacePath:n,workspaceId:t.workspaceId,workspaceName:t.workspaceName,tunnelId:t.tunnelId,token:t.token,serverUrl:t.serverUrl,preserveWorkspaces:t.preserveWorkspaces}),e.channel&&await y(t.configPath,e.channel);const o=await g({version:"current",configPath:t.configPath}),a=await v({configPath:t.configPath,dataDir:t.dataDir,workspacePath:n,serviceCommandPath:o.wrapperPath});return{configPath:t.configPath,dataDir:t.dataDir,workspacePath:n,runtime:o,service:a}}async function I(e){const t=await h(e.configPath),n=b(t,"tunnel"),o=e.tunnelId??p(n,"tunnel_id"),a=e.token??p(n,"token"),s=N(t,e.workspaceId,o),i=e.workspace??s?.path??d,r=e.workspaceId??s?.id??(o?`${o}--default`:void 0),c=e.workspaceName??s?.name??(o?`${o} Default`:void 0),u=i===d;if(!o)throw new f("Missing required option: --tunnel-id");if(!a)throw new f("Missing required option: --token");if(!r)throw new f("Missing required option: --workspace-id");if(!c)throw new f("Missing required option: --workspace-name");return{...e,tunnelId:o,token:a,workspace:i,workspaceId:r,workspaceName:c,preserveWorkspaces:!e.workspace&&!!s,createWorkspace:u}}function N(e,t,n){const o=e.workspaces;if(!Array.isArray(o))return;const a=o.filter(r=>!!r&&typeof r=="object"&&!Array.isArray(r)&&typeof r.path=="string");if(t){const r=a.find(c=>c.id===t);return!r||typeof r.path!="string"?void 0:{id:t,name:typeof r.name=="string"&&r.name?r.name:`${n??t} Default`,path:r.path}}const s=t??(n?`${n}--default`:void 0),i=(s?a.find(r=>r.id===s):void 0)??a.find(r=>r.id==="default")??a[0];if(!(!i||typeof i.path!="string"))return{id:typeof i.id=="string"&&i.id?i.id:s??"default",name:typeof i.name=="string"&&i.name?i.name:`${n??s??"default"} Default`,path:i.path}}function b(e,t){const n=e[t];if(!(!n||typeof n!="object"||Array.isArray(n)))return n}function p(e,t){const n=e?.[t];return typeof n=="string"&&n?n:void 0}export{A as assertNodeVersion,P as assertSupportedPlatform,M as installClient,I as resolveInstallArgs,D as resolveWorkspacePath,x as validateInstallArgs};
|