Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ When the user starts a session in a project, register it and learn its identity:
3. Remember this workspace `workspaceId` — include it whenever you tell the user what to ask Claude, and use it in INIT instructions (Claude scopes every tool call with it).
4. When a Codex session ends, run `c2c use --end --json` to clear the local session binding.
5. Route repairs through `c2c doctor --json` (installation-aware) or `c2c broker status`; per-project bridges (`c2c start`) are legacy compatibility only.
6. In the Codex desktop app, sandboxed commands may fail with EPERM or `fetch failed` when they touch the broker (loopback requests, daemon spawn, state writes). The broker is a system service that is usually already running — first try `c2c broker status`; if it reports the state as unclear, rerun the command with sandbox escalation approved, or in a regular terminal. Do not conclude the broker is down from a sandboxed failure alone.

## Planning loop

Expand Down
13 changes: 12 additions & 1 deletion src/broker/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,18 @@ export async function ensureBroker(opts: { stateDir?: string } = {}): Promise<Ru

const logDir = ensureDir(path.join(stateDir, "logs"));
const logFile = path.join(logDir, "broker.out.log");
const out = fs.openSync(logFile, "a", 0o600);
let out: number;
try {
out = fs.openSync(logFile, "a", 0o600);
} catch (error) {
const code = (error as NodeJS.ErrnoException).code ?? "";
throw new Error(
`Cannot write broker log ${logFile} (${code || (error as Error).message}). ` +
"Starting the broker spawns a system daemon — run this command outside the agent sandbox " +
"(approve escalation) or in a regular terminal. " +
"If the broker is already running, no action is needed; check with `c2c broker status` in a regular terminal."
);
}
const { cmd, args } = cliEntry();
const child = spawn(cmd, [...args, "broker-serve"], {
detached: true,
Expand Down
45 changes: 31 additions & 14 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ program
.command("doctor")
.description("Diagnose and auto-repair the C2C installation for this workspace")
.option("-w, --workspace <path>")
.option("--fix", "repair issues (default behavior)")
.option("--no-fix", "diagnose only, do not repair")
.option("--json", "machine-readable output", false)
.action(async (opts: { workspace?: string; fix: boolean; json: boolean }) => {
Expand Down Expand Up @@ -1327,21 +1328,37 @@ brokerCmd
else say("Broker is not running. Use `c2c broker start`.");
return;
}
const info = await adminFetch<AdminInfo & { installationId?: string; workspaceCount?: number; activeSessions?: number }>(
runtime,
"GET",
"/admin/info"
);
if (opts.json) {
say(JSON.stringify({ ok: true, running: true, ...info }));
return;
try {
const info = await adminFetch<AdminInfo & { installationId?: string; workspaceCount?: number; activeSessions?: number }>(
runtime,
"GET",
"/admin/info"
);
if (opts.json) {
say(JSON.stringify({ ok: true, running: true, ...info }));
return;
}
check(`Installation: ${info.installationId}`);
check(`Broker: running (port ${info.port})`);
if (info.tunnel.running && info.tunnel.url) check(`Connector URL: ${info.tunnel.url}/mcp`);
else say("· Public endpoint: not enabled");
check(`Workspaces registered: ${info.workspaceCount ?? 0}`);
check(`Active Codex sessions: ${info.activeSessions ?? 0}`);
} catch (error) {
// The runtime file exists and matches this installation, but the probe
// failed — typical of a sandboxed agent blocking loopback requests, not
// a dead broker. Report that instead of claiming it is down.
const detail =
`runtime file says pid ${runtime.pid} on port ${runtime.port}, but the broker did not respond ` +
`(${(error as Error).message}). If this is a sandboxed agent, run the command outside the sandbox ` +
`or approve escalation.`;
if (opts.json) say(JSON.stringify({ ok: true, running: "unknown", probe: "failed", detail, runtime }));
else {
cross(`Broker state unclear: ${detail}`);
say(`· Connector URL (from runtime): ${runtime.publicUrl ? runtime.publicUrl + "/mcp" : "not recorded"}`);
}
process.exitCode = 1;
}
check(`Installation: ${info.installationId}`);
check(`Broker: running (port ${info.port})`);
if (info.tunnel.running && info.tunnel.url) check(`Connector URL: ${info.tunnel.url}/mcp`);
else say("· Public endpoint: not enabled");
check(`Workspaces registered: ${info.workspaceCount ?? 0}`);
check(`Active Codex sessions: ${info.activeSessions ?? 0}`);
});

const brokerTunnelCmd = brokerCmd
Expand Down
Loading