83 lines
2.7 KiB
JavaScript
83 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { resolveSettings } from "@anthropic-ai/claude-agent-sdk";
|
|
import { claudeCliPath, runAcp } from "./acp-agent.js";
|
|
import packageJson from "../package.json" with { type: "json" };
|
|
|
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
console.log(packageJson.version);
|
|
process.exit(0);
|
|
}
|
|
|
|
if (process.argv.includes("--cli")) {
|
|
const { spawn } = await import("node:child_process");
|
|
const args = process.argv.slice(2).filter((arg) => arg !== "--cli");
|
|
const child = spawn(await claudeCliPath(), args, { stdio: "inherit" });
|
|
|
|
const signals =
|
|
process.platform === "win32"
|
|
? (["SIGINT", "SIGTERM"] as const)
|
|
: (["SIGINT", "SIGTERM", "SIGHUP"] as const);
|
|
for (const sig of signals) {
|
|
process.on(sig, () => {
|
|
if (!child.killed) child.kill(sig);
|
|
});
|
|
}
|
|
|
|
child.on("exit", (code, signal) => {
|
|
if (signal && process.platform !== "win32") {
|
|
// Remove our listener so re-raising actually terminates instead of
|
|
// re-entering the no-op handler, which would let us exit with code 0
|
|
// instead of the signal's conventional 128+N.
|
|
process.removeAllListeners(signal);
|
|
process.kill(process.pid, signal);
|
|
} else {
|
|
process.exit(code ?? 1);
|
|
}
|
|
});
|
|
child.on("error", (err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|
|
} else {
|
|
// Apply env vars from the managed-policy tier before any SDK call so the
|
|
// SDK subprocess inherits them. Going through resolveSettings (vs. a raw
|
|
// read of managed-settings.json) also picks up MDM sources on macOS and
|
|
// HKLM/HKCU on Windows.
|
|
const policy = await resolveSettings({ settingSources: [] });
|
|
for (const [key, value] of Object.entries(policy.effective.env ?? {})) {
|
|
process.env[key] = value;
|
|
}
|
|
|
|
// stdout is used to send messages to the client
|
|
// we redirect everything else to stderr to make sure it doesn't interfere with ACP
|
|
console.log = console.error;
|
|
console.info = console.error;
|
|
console.warn = console.error;
|
|
console.debug = console.error;
|
|
|
|
process.on("unhandledRejection", (reason, promise) => {
|
|
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
|
});
|
|
|
|
const { connection, agent } = runAcp();
|
|
|
|
async function shutdown() {
|
|
await agent.dispose().catch((err) => {
|
|
console.error("Error during cleanup:", err);
|
|
});
|
|
process.exit(0);
|
|
}
|
|
|
|
// Exit cleanly when the ACP connection closes (e.g. stdin EOF, transport
|
|
// error). Without this, `process.stdin.resume()` keeps the event loop
|
|
// alive indefinitely, causing orphan process accumulation in oneshot mode.
|
|
connection.closed.then(shutdown);
|
|
|
|
process.on("SIGTERM", shutdown);
|
|
process.on("SIGINT", shutdown);
|
|
|
|
// Keep process alive while connection is open
|
|
process.stdin.resume();
|
|
}
|