@tarfa/cli 0.1.4 → 0.1.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/{chunk-OC5VX7K3.js → chunk-KTUXNG4Q.js} +59 -10
- package/dist/cli.js +22 -39
- package/dist/serve.js +6 -2
- package/package.json +1 -1
|
@@ -4432,8 +4432,9 @@ var TmuxAdapter = class {
|
|
|
4432
4432
|
return set;
|
|
4433
4433
|
}
|
|
4434
4434
|
async createSession(input) {
|
|
4435
|
+
const env = Object.entries(input.env ?? {}).flatMap(([k, v]) => ["-e", `${k}=${v}`]);
|
|
4435
4436
|
try {
|
|
4436
|
-
await this.tmux(["new-session", "-d", "-s", input.name, "-c", input.cwd, input.command]);
|
|
4437
|
+
await this.tmux(["new-session", "-d", "-s", input.name, "-c", input.cwd, ...env, input.command]);
|
|
4437
4438
|
} catch (err) {
|
|
4438
4439
|
if ((err.stderr ?? "").includes("duplicate session")) throw new DuplicateSessionError(input.name);
|
|
4439
4440
|
throw err;
|
|
@@ -5878,12 +5879,12 @@ function loginShellResolver(shell = process.env.SHELL || "/bin/sh") {
|
|
|
5878
5879
|
}
|
|
5879
5880
|
|
|
5880
5881
|
// src/infrastructure/codex-app-server.ts
|
|
5881
|
-
import {
|
|
5882
|
+
import { realpath as realpath3 } from "node:fs/promises";
|
|
5882
5883
|
import { join as join7 } from "node:path";
|
|
5883
5884
|
import { WebSocket } from "ws";
|
|
5884
5885
|
var CodexAppServerDown = class extends Error {
|
|
5885
|
-
constructor() {
|
|
5886
|
-
super(
|
|
5886
|
+
constructor(socketPath) {
|
|
5887
|
+
super(`the Codex app server is not running (no socket at ${socketPath})`);
|
|
5887
5888
|
}
|
|
5888
5889
|
};
|
|
5889
5890
|
var CodexRpcError = class extends Error {
|
|
@@ -5954,8 +5955,8 @@ var Connection = class {
|
|
|
5954
5955
|
var CodexAppServer = class {
|
|
5955
5956
|
socketPath;
|
|
5956
5957
|
timeoutMs;
|
|
5957
|
-
constructor(options
|
|
5958
|
-
this.socketPath = options.socketPath
|
|
5958
|
+
constructor(options) {
|
|
5959
|
+
this.socketPath = "socketPath" in options ? options.socketPath : join7(options.codexHome, "app-server-control", "app-server-control.sock");
|
|
5959
5960
|
this.timeoutMs = options.timeoutMs ?? 5e3;
|
|
5960
5961
|
}
|
|
5961
5962
|
loadedThreads() {
|
|
@@ -6009,9 +6010,15 @@ var CodexAppServer = class {
|
|
|
6009
6010
|
return thread.id;
|
|
6010
6011
|
});
|
|
6011
6012
|
}
|
|
6012
|
-
|
|
6013
|
+
// Codex 0.167 binds the socket under /tmp/codex-daemon-<uid>/ and publishes this path as a link
|
|
6014
|
+
// to it. Connecting to the link's target keeps the path within the 107 bytes a unix socket
|
|
6015
|
+
// address holds, however deep CODEX_HOME is; a link to nothing means no daemon.
|
|
6016
|
+
async call(run) {
|
|
6017
|
+
const target = await realpath3(this.socketPath).catch((err) => {
|
|
6018
|
+
throw err.code === "ENOENT" ? new CodexAppServerDown(this.socketPath) : err;
|
|
6019
|
+
});
|
|
6013
6020
|
return new Promise((resolve6, reject) => {
|
|
6014
|
-
const ws = new WebSocket(`ws+unix://${
|
|
6021
|
+
const ws = new WebSocket(`ws+unix://${target}:/`, { perMessageDeflate: false });
|
|
6015
6022
|
const c = new Connection(ws);
|
|
6016
6023
|
let settled = false;
|
|
6017
6024
|
const settle = (fn) => {
|
|
@@ -6030,7 +6037,7 @@ var CodexAppServer = class {
|
|
|
6030
6037
|
}, this.timeoutMs);
|
|
6031
6038
|
ws.once("error", (err) => {
|
|
6032
6039
|
const down = err.code === "ENOENT" || err.code === "ECONNREFUSED";
|
|
6033
|
-
settle(() => reject(down ? new CodexAppServerDown() : err));
|
|
6040
|
+
settle(() => reject(down ? new CodexAppServerDown(this.socketPath) : err));
|
|
6034
6041
|
});
|
|
6035
6042
|
ws.once("close", () => settle(() => reject(new Error("the Codex app server closed the connection"))));
|
|
6036
6043
|
ws.once("open", () => {
|
|
@@ -6046,6 +6053,45 @@ var CodexAppServer = class {
|
|
|
6046
6053
|
}
|
|
6047
6054
|
};
|
|
6048
6055
|
|
|
6056
|
+
// src/infrastructure/codex-versions.ts
|
|
6057
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
6058
|
+
function loginShell(shell = process.env.SHELL || "/bin/sh", extraEnv = {}) {
|
|
6059
|
+
return (script) => new Promise((resolve6, reject) => {
|
|
6060
|
+
execFile5(shell, ["-lc", script], { env: { ...childEnv(), ...extraEnv }, timeout: 15e3 }, (err, stdout) => err ? reject(err) : resolve6(stdout));
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
function codexVersions(run = loginShell()) {
|
|
6064
|
+
return async () => {
|
|
6065
|
+
const [cliOut, daemonOut] = await Promise.all([
|
|
6066
|
+
run("codex --version").catch((err) => {
|
|
6067
|
+
throw new Error(`cannot run codex --version: ${err.message}`);
|
|
6068
|
+
}),
|
|
6069
|
+
run("codex app-server daemon version").catch((err) => {
|
|
6070
|
+
throw new Error(`cannot read the Codex app server's version: ${err.message}`);
|
|
6071
|
+
})
|
|
6072
|
+
]);
|
|
6073
|
+
const cli = cliOut.trim().split(/\s+/).pop() ?? "";
|
|
6074
|
+
let appServer;
|
|
6075
|
+
try {
|
|
6076
|
+
appServer = JSON.parse(daemonOut).appServerVersion;
|
|
6077
|
+
} catch {
|
|
6078
|
+
appServer = void 0;
|
|
6079
|
+
}
|
|
6080
|
+
if (typeof appServer !== "string" || appServer === "") throw new Error("the Codex app server is not running");
|
|
6081
|
+
return { cli, appServer };
|
|
6082
|
+
};
|
|
6083
|
+
}
|
|
6084
|
+
|
|
6085
|
+
// src/infrastructure/codex-home.ts
|
|
6086
|
+
import { join as join8 } from "node:path";
|
|
6087
|
+
var MARK = "codex-home=";
|
|
6088
|
+
async function resolveCodexHome(env, home, run = loginShell()) {
|
|
6089
|
+
if (env.CODEX_HOME) return env.CODEX_HOME;
|
|
6090
|
+
const out = await run(`printf '\\n${MARK}%s\\n' "\${CODEX_HOME-}"`).catch(() => "");
|
|
6091
|
+
const value = out.split("\n").reverse().find((line) => line.startsWith(MARK))?.slice(MARK.length);
|
|
6092
|
+
return value || join8(home, ".codex");
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6049
6095
|
export {
|
|
6050
6096
|
packageRoot,
|
|
6051
6097
|
packageVersion,
|
|
@@ -6149,5 +6195,8 @@ export {
|
|
|
6149
6195
|
detectInstalled,
|
|
6150
6196
|
loginShellResolver,
|
|
6151
6197
|
CodexAppServerDown,
|
|
6152
|
-
CodexAppServer
|
|
6198
|
+
CodexAppServer,
|
|
6199
|
+
loginShell,
|
|
6200
|
+
codexVersions,
|
|
6201
|
+
resolveCodexHome
|
|
6153
6202
|
};
|
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
browseDir,
|
|
23
23
|
childEnv,
|
|
24
24
|
cloneOfUnit,
|
|
25
|
+
codexVersions,
|
|
25
26
|
createWithFreshId,
|
|
26
27
|
createWorkspace,
|
|
27
28
|
describeStep,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
loadConfig,
|
|
42
43
|
loadOrCreateAgentKeys,
|
|
43
44
|
locateAgentEnv,
|
|
45
|
+
loginShell,
|
|
44
46
|
loginShellResolver,
|
|
45
47
|
mainCheckout,
|
|
46
48
|
migrateStateDir,
|
|
@@ -53,6 +55,7 @@ import {
|
|
|
53
55
|
planCli,
|
|
54
56
|
planSetup,
|
|
55
57
|
renderService,
|
|
58
|
+
resolveCodexHome,
|
|
56
59
|
resolveStateDir,
|
|
57
60
|
revokeDevice,
|
|
58
61
|
sanitizeDeviceName,
|
|
@@ -63,7 +66,7 @@ import {
|
|
|
63
66
|
unitRunsLauncher,
|
|
64
67
|
withEnrolmentMode,
|
|
65
68
|
withLegacyEnv
|
|
66
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-KTUXNG4Q.js";
|
|
67
70
|
|
|
68
71
|
// src/cli.ts
|
|
69
72
|
import { randomBytes } from "node:crypto";
|
|
@@ -241,10 +244,9 @@ async function readOrNull(path) {
|
|
|
241
244
|
throw err;
|
|
242
245
|
}
|
|
243
246
|
}
|
|
244
|
-
function hooksFiles(home, hooksDir, port) {
|
|
247
|
+
function hooksFiles(home, hooksDir, port, codexHome) {
|
|
245
248
|
const settingsPath = join(home, ".claude", "settings.json");
|
|
246
|
-
const
|
|
247
|
-
const codexHooksPath = join(codexDir, "hooks.json");
|
|
249
|
+
const codexHooksPath = join(codexHome, "hooks.json");
|
|
248
250
|
async function wanted() {
|
|
249
251
|
const files = [];
|
|
250
252
|
for (const script of SCRIPTS) {
|
|
@@ -254,7 +256,7 @@ function hooksFiles(home, hooksDir, port) {
|
|
|
254
256
|
const settings = await readOrNull(settingsPath);
|
|
255
257
|
const merged = installHooks({ settings: settings === null ? {} : JSON.parse(settings) }, { hookCommand: HOOK_COMMAND, guardCommand: GUARD_COMMAND, port });
|
|
256
258
|
files.push({ path: settingsPath, current: settings, text: JSON.stringify(merged, null, 2) });
|
|
257
|
-
if (await fsIsDirectory(
|
|
259
|
+
if (await fsIsDirectory(codexHome)) {
|
|
258
260
|
const codex = await readOrNull(codexHooksPath);
|
|
259
261
|
const codexMerged = installHooks(
|
|
260
262
|
{ settings: codex === null ? {} : JSON.parse(codex) },
|
|
@@ -1078,12 +1080,13 @@ async function spawnCodex(deps, input) {
|
|
|
1078
1080
|
firstMessage: input.brief.replace(/\n+$/, "")
|
|
1079
1081
|
});
|
|
1080
1082
|
if (!/^[0-9A-Za-z-]+$/.test(thread)) throw new Error(`the Codex app server named an unexpected thread id: ${thread}`);
|
|
1083
|
+
const codexHome = await deps.codex.home();
|
|
1081
1084
|
const lockTitle = await titleLock(deps, title);
|
|
1082
1085
|
const command = `codex resume ${thread}`;
|
|
1083
1086
|
const id = await createWithFreshId(
|
|
1084
1087
|
deps.ids,
|
|
1085
1088
|
cwd,
|
|
1086
|
-
(id2) => deps.tmux.createSession({ name: id2, cwd, command, codexThread: thread, ...parent ? { parent: parent.id } : {} })
|
|
1089
|
+
(id2) => deps.tmux.createSession({ name: id2, cwd, command, codexThread: thread, env: { CODEX_HOME: codexHome }, ...parent ? { parent: parent.id } : {} })
|
|
1087
1090
|
);
|
|
1088
1091
|
return finishLaunch(deps, id, title, lockTitle, ` (Codex thread ${thread})`);
|
|
1089
1092
|
}
|
|
@@ -1133,35 +1136,6 @@ function denySettings(denyPath) {
|
|
|
1133
1136
|
};
|
|
1134
1137
|
}
|
|
1135
1138
|
|
|
1136
|
-
// src/infrastructure/codex-versions.ts
|
|
1137
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
1138
|
-
function loginShell(shell = process.env.SHELL || "/bin/sh") {
|
|
1139
|
-
return (script) => new Promise((resolve2, reject) => {
|
|
1140
|
-
execFile6(shell, ["-lc", script], { env: childEnv(), timeout: 15e3 }, (err, stdout) => err ? reject(err) : resolve2(stdout));
|
|
1141
|
-
});
|
|
1142
|
-
}
|
|
1143
|
-
function codexVersions(run4 = loginShell()) {
|
|
1144
|
-
return async () => {
|
|
1145
|
-
const [cliOut, daemonOut] = await Promise.all([
|
|
1146
|
-
run4("codex --version").catch((err) => {
|
|
1147
|
-
throw new Error(`cannot run codex --version: ${err.message}`);
|
|
1148
|
-
}),
|
|
1149
|
-
run4("codex app-server daemon version").catch((err) => {
|
|
1150
|
-
throw new Error(`cannot read the Codex app server's version: ${err.message}`);
|
|
1151
|
-
})
|
|
1152
|
-
]);
|
|
1153
|
-
const cli = cliOut.trim().split(/\s+/).pop() ?? "";
|
|
1154
|
-
let appServer;
|
|
1155
|
-
try {
|
|
1156
|
-
appServer = JSON.parse(daemonOut).appServerVersion;
|
|
1157
|
-
} catch {
|
|
1158
|
-
appServer = void 0;
|
|
1159
|
-
}
|
|
1160
|
-
if (typeof appServer !== "string" || appServer === "") throw new Error("the Codex app server is not running");
|
|
1161
|
-
return { cli, appServer };
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
1139
|
// src/application/workspace-role.ts
|
|
1166
1140
|
var WORKSPACE_ROLES = ["orchestrator", "child", "normal"];
|
|
1167
1141
|
function workspaceRole(w, all) {
|
|
@@ -1266,7 +1240,7 @@ async function run3(argv, rawEnv, home, io = {}) {
|
|
|
1266
1240
|
return JSON.stringify(await browseDir({ lister, hidden }, path, config.projectRoots), null, 2);
|
|
1267
1241
|
}
|
|
1268
1242
|
case "install-hooks": {
|
|
1269
|
-
const hooks = hooksFiles(home, HOOKS_DIR, config.port);
|
|
1243
|
+
const hooks = hooksFiles(home, HOOKS_DIR, config.port, await resolveCodexHome({ ...env, ...await agentEnvValues(env, home) }, home, io.loginShell));
|
|
1270
1244
|
const { codex } = await hooks.install();
|
|
1271
1245
|
if (!codex) return JSON.stringify({ installed: hooks.settingsPath, scriptPath: hooks.scriptPath });
|
|
1272
1246
|
return JSON.stringify({
|
|
@@ -1413,7 +1387,7 @@ async function orchestration(command, argv, env, home, io) {
|
|
|
1413
1387
|
nonce: () => randomBytes(6).toString("hex"),
|
|
1414
1388
|
sleep: io.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
|
|
1415
1389
|
spawnDir: joinPath(config.stateDir, "spawn"),
|
|
1416
|
-
codex: io.codex ?? {
|
|
1390
|
+
codex: io.codex ?? codexSpawn({ ...env, ...values }, home, io)
|
|
1417
1391
|
},
|
|
1418
1392
|
{
|
|
1419
1393
|
projectPath,
|
|
@@ -1633,6 +1607,15 @@ function pwaOrigin(value) {
|
|
|
1633
1607
|
if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error(`--pwa must be an http(s) origin (got ${value})`);
|
|
1634
1608
|
return url.origin;
|
|
1635
1609
|
}
|
|
1610
|
+
function codexSpawn(env, home, io) {
|
|
1611
|
+
let codexHome;
|
|
1612
|
+
const resolve2 = () => codexHome ??= resolveCodexHome(env, home, io.loginShell);
|
|
1613
|
+
return {
|
|
1614
|
+
home: resolve2,
|
|
1615
|
+
versions: async () => codexVersions(loginShell(void 0, { CODEX_HOME: await resolve2() }))(),
|
|
1616
|
+
createThread: async (input) => new CodexAppServer({ codexHome: await resolve2() }).createThread(input)
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1636
1619
|
async function agentEnvValues(env, home) {
|
|
1637
1620
|
try {
|
|
1638
1621
|
return withLegacyEnv(parseEnvFile(await readFile6(await locateAgentEnv(env, home), "utf8")));
|
|
@@ -1790,7 +1773,7 @@ ${SETUP_USAGE.split("\n")[0]}
|
|
|
1790
1773
|
const config = loadConfig({ ...withLegacyEnv(parseEnvFile(envText ?? "")), TARFA_MACHINE_ID: flags.machineId }, home);
|
|
1791
1774
|
const tailscale = fx.tailscale ?? tailscaleCli();
|
|
1792
1775
|
const owner = fx.portOwner ?? portOwner;
|
|
1793
|
-
const hooks = hooksFiles(home, HOOKS_DIR, config.port);
|
|
1776
|
+
const hooks = hooksFiles(home, HOOKS_DIR, config.port, await resolveCodexHome({ ...env, ...withLegacyEnv(parseEnvFile(envText ?? "")) }, home, io.loginShell));
|
|
1794
1777
|
const devices2 = new FileDeviceStore(config.devicesPath);
|
|
1795
1778
|
const hasRoster = await readFile6(config.devicesPath).then(() => true, () => false);
|
|
1796
1779
|
const agentLivePath = cloneRepo === null ? "" : joinPath(cloneRepo, "deploy", "agent-live");
|
|
@@ -1916,7 +1899,7 @@ async function apply2(argv, env, home, io) {
|
|
|
1916
1899
|
if (envText === null) throw new Error(`failed: check: no agent config at ${envPath}: run tarfa setup`);
|
|
1917
1900
|
const config = loadConfig(withLegacyEnv(parseEnvFile(envText)), home);
|
|
1918
1901
|
const service = fx.service ?? serviceInstall({ home, user: userInfo().username, uid: process.getuid?.() ?? 0 });
|
|
1919
|
-
const hooks = hooksFiles(home, HOOKS_DIR, config.port);
|
|
1902
|
+
const hooks = hooksFiles(home, HOOKS_DIR, config.port, await resolveCodexHome({ ...env, ...withLegacyEnv(parseEnvFile(envText)) }, home, io.loginShell));
|
|
1920
1903
|
const launcherPath = joinPath(home, ".tarfa", "bin", "tarfa-agent");
|
|
1921
1904
|
const log = fx.agentLog ?? agentLog({ home });
|
|
1922
1905
|
const result = await runApply(
|
package/dist/serve.js
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
readHandshakeFrame,
|
|
66
66
|
redeemPairing,
|
|
67
67
|
relationOf,
|
|
68
|
+
resolveCodexHome,
|
|
68
69
|
runsClaude,
|
|
69
70
|
sessionOfCodexThread,
|
|
70
71
|
standingOf,
|
|
@@ -73,7 +74,7 @@ import {
|
|
|
73
74
|
systemPathResolver,
|
|
74
75
|
withLegacyEnv,
|
|
75
76
|
writeFileAtomic
|
|
76
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-KTUXNG4Q.js";
|
|
77
78
|
|
|
78
79
|
// src/serve.ts
|
|
79
80
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -4185,6 +4186,9 @@ async function main() {
|
|
|
4185
4186
|
const env = withLegacyEnv(process.env);
|
|
4186
4187
|
const config = loadConfig(env, homedir2());
|
|
4187
4188
|
for (const warning of configWarnings(process.env)) process.stderr.write(`${warning}
|
|
4189
|
+
`);
|
|
4190
|
+
const codexHome = await resolveCodexHome(env, homedir2());
|
|
4191
|
+
process.stdout.write(`tarfa agent: Codex home ${codexHome}
|
|
4188
4192
|
`);
|
|
4189
4193
|
await securePrivateDir(config.stateDir);
|
|
4190
4194
|
await assertPrivateFiles([
|
|
@@ -4241,7 +4245,7 @@ async function main() {
|
|
|
4241
4245
|
delivery: new CompositeDelivery({
|
|
4242
4246
|
claude: new ClaudeInboxDelivery(claudeSessions, async (id) => await tmux.firstPaneId?.(id) ?? null),
|
|
4243
4247
|
pi: new PiInboxDelivery(config.inboxDir),
|
|
4244
|
-
codex: new CodexDelivery(new CodexAppServer())
|
|
4248
|
+
codex: new CodexDelivery(new CodexAppServer({ codexHome }))
|
|
4245
4249
|
}),
|
|
4246
4250
|
log: new FileMessageLog(config.messageLogPath)
|
|
4247
4251
|
},
|