@triflux/remote 10.42.0 → 10.43.0
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/hub/pipe.mjs +3 -0
- package/hub/schema.sql +1 -0
- package/hub/server.mjs +44 -22
- package/hub/store.mjs +22 -17
- package/hub/team/claude-daemon-control.mjs +33 -12
- package/hub/team/cli/commands/start/parse-args.mjs +2 -2
- package/hub/team/headless.mjs +275 -24
- package/hub/team/intervention.mjs +530 -0
- package/hub/team/retry-state-machine.mjs +6 -2
- package/hub/team/swarm-hypervisor.mjs +19 -2
- package/hub/team/swarm-locks.mjs +17 -1
- package/hub/team/uds-orchestrator.mjs +62 -9
- package/hub/tools.mjs +4 -3
- package/hub/workers/codex-app-server-worker.mjs +54 -5
- package/hub/workers/codex-mcp.mjs +1 -1
- package/hub/workers/delegator-mcp.mjs +18 -18
- package/package.json +1 -1
- package/scripts/lib/cli-agy.mjs +1 -0
- package/scripts/lib/cli-claude.mjs +1 -0
- package/scripts/lib/cli-codex.mjs +22 -21
- package/scripts/lib/codex-profile-config.mjs +2 -2
package/hub/pipe.mjs
CHANGED
|
@@ -269,6 +269,9 @@ export function createPipeServer({
|
|
|
269
269
|
payload.heartbeat_ttl_ms || heartbeatTtlMs,
|
|
270
270
|
);
|
|
271
271
|
if (client) touchClient(client);
|
|
272
|
+
if (result?.effective?.updated === false) {
|
|
273
|
+
return { ok: false, error: { code: "AGENT_NOT_FOUND", message: `heartbeat target not registered: ${agentId}` } };
|
|
274
|
+
}
|
|
272
275
|
return { ok: true, data: result };
|
|
273
276
|
}
|
|
274
277
|
|
package/hub/schema.sql
CHANGED
package/hub/server.mjs
CHANGED
|
@@ -32,6 +32,13 @@ import {
|
|
|
32
32
|
import { broker as brokerInstance, reloadBroker } from "@triflux/core/hub/account-broker.mjs";
|
|
33
33
|
import { createAdaptiveEngine } from "@triflux/core/hub/adaptive.mjs";
|
|
34
34
|
import { createAssignCallbackServer } from "@triflux/core/hub/assign-callbacks.mjs";
|
|
35
|
+
import {
|
|
36
|
+
clampDurationMs,
|
|
37
|
+
DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
38
|
+
DEFAULT_ASSIGN_TTL_MS,
|
|
39
|
+
DEFAULT_REGISTER_TIMEOUT_SEC,
|
|
40
|
+
resolveWorkerLeaseTtlMs,
|
|
41
|
+
} from "@triflux/core/hub/lib/timeout-defaults.mjs";
|
|
35
42
|
import { DelegatorService } from "@triflux/core/hub/delegator/index.mjs";
|
|
36
43
|
import { createHitlManager } from "@triflux/core/hub/hitl.mjs";
|
|
37
44
|
import {
|
|
@@ -143,7 +150,7 @@ function buildHubUrl(host, port) {
|
|
|
143
150
|
|
|
144
151
|
function parseHubPort(value) {
|
|
145
152
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
146
|
-
return Number.isFinite(parsed) && parsed
|
|
153
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
function isPidAlive(pid, killFn = process.kill) {
|
|
@@ -963,16 +970,17 @@ export async function startHub({
|
|
|
963
970
|
createDelegatorWorker = createDelegatorMcpWorker,
|
|
964
971
|
} = {}) {
|
|
965
972
|
const resolvedPort = parseHubPort(portOpt);
|
|
966
|
-
const portSpecified = Number.isFinite(resolvedPort)
|
|
973
|
+
const portSpecified = Number.isFinite(resolvedPort);
|
|
974
|
+
const ephemeralPort = resolvedPort === 0;
|
|
967
975
|
const livePeer = detectLivePeer();
|
|
968
976
|
const livePidPort = parseHubPort(livePeer?.port);
|
|
969
|
-
|
|
977
|
+
let port = portSpecified
|
|
970
978
|
? resolvedPort
|
|
971
979
|
: resolveHubPort(process.env, {
|
|
972
980
|
detectPeer: () => livePeer,
|
|
973
981
|
});
|
|
974
982
|
|
|
975
|
-
if (portSpecified && livePeer.alive && livePidPort && port !== livePidPort) {
|
|
983
|
+
if (portSpecified && !ephemeralPort && livePeer.alive && livePidPort && port !== livePidPort) {
|
|
976
984
|
hubLog.warn(
|
|
977
985
|
{
|
|
978
986
|
requestedPort: port,
|
|
@@ -983,11 +991,8 @@ export async function startHub({
|
|
|
983
991
|
);
|
|
984
992
|
}
|
|
985
993
|
|
|
986
|
-
const existingHub = await tryReuseExistingHub({
|
|
987
|
-
port,
|
|
988
|
-
portSpecified,
|
|
989
|
-
host,
|
|
990
|
-
detectPeer: () => livePeer,
|
|
994
|
+
const existingHub = ephemeralPort ? null : await tryReuseExistingHub({
|
|
995
|
+
port, portSpecified, host, detectPeer: () => livePeer,
|
|
991
996
|
});
|
|
992
997
|
if (existingHub) return existingHub;
|
|
993
998
|
|
|
@@ -1026,7 +1031,7 @@ export async function startHub({
|
|
|
1026
1031
|
lockHeld = false;
|
|
1027
1032
|
};
|
|
1028
1033
|
|
|
1029
|
-
const lockedExistingHub = await tryReuseExistingHub({
|
|
1034
|
+
const lockedExistingHub = ephemeralPort ? null : await tryReuseExistingHub({
|
|
1030
1035
|
port,
|
|
1031
1036
|
portSpecified,
|
|
1032
1037
|
host,
|
|
@@ -1038,9 +1043,9 @@ export async function startHub({
|
|
|
1038
1043
|
}
|
|
1039
1044
|
|
|
1040
1045
|
const HUB_TOKEN = process.env.TFX_HUB_TOKEN?.trim() || null;
|
|
1041
|
-
if (HUB_TOKEN) {
|
|
1046
|
+
if (!ephemeralPort && HUB_TOKEN) {
|
|
1042
1047
|
writeOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
1043
|
-
} else {
|
|
1048
|
+
} else if (!ephemeralPort) {
|
|
1044
1049
|
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
1045
1050
|
}
|
|
1046
1051
|
|
|
@@ -1690,7 +1695,7 @@ export async function startHub({
|
|
|
1690
1695
|
const {
|
|
1691
1696
|
agent_id,
|
|
1692
1697
|
cli,
|
|
1693
|
-
timeout_sec =
|
|
1698
|
+
timeout_sec = DEFAULT_REGISTER_TIMEOUT_SEC,
|
|
1694
1699
|
topics = [],
|
|
1695
1700
|
capabilities = [],
|
|
1696
1701
|
metadata = {},
|
|
@@ -1709,7 +1714,10 @@ export async function startHub({
|
|
|
1709
1714
|
metadata?.task ||
|
|
1710
1715
|
cli;
|
|
1711
1716
|
const workerStartedAt = performance.now();
|
|
1712
|
-
const
|
|
1717
|
+
const hasExplicitTimeout = Number.isFinite(Number(body.timeout_sec));
|
|
1718
|
+
const heartbeat_ttl_ms = hasExplicitTimeout
|
|
1719
|
+
? (timeout_sec + 120) * 1000
|
|
1720
|
+
: Math.max((timeout_sec + 120) * 1000, resolveWorkerLeaseTtlMs());
|
|
1713
1721
|
const result = await pipe.executeCommand("register", {
|
|
1714
1722
|
agent_id,
|
|
1715
1723
|
cli,
|
|
@@ -1725,7 +1733,7 @@ export async function startHub({
|
|
|
1725
1733
|
});
|
|
1726
1734
|
recordWorker(agent_id, "spawned", command, 0);
|
|
1727
1735
|
}
|
|
1728
|
-
return writeJson(res, 200, result);
|
|
1736
|
+
return writeJson(res, result.ok ? 200 : 409, result);
|
|
1729
1737
|
}
|
|
1730
1738
|
|
|
1731
1739
|
if (path === "/bridge/result" && req.method === "POST") {
|
|
@@ -1750,6 +1758,16 @@ export async function startHub({
|
|
|
1750
1758
|
return writeJson(res, 200, result);
|
|
1751
1759
|
}
|
|
1752
1760
|
|
|
1761
|
+
if (path === "/bridge/heartbeat" && req.method === "POST") {
|
|
1762
|
+
const { agent_id, ttl_ms } = body;
|
|
1763
|
+
if (!agent_id) return writeJson(res, 400, { ok: false, error: "agent_id 필수" });
|
|
1764
|
+
const result = await pipe.executeCommand("heartbeat", {
|
|
1765
|
+
agent_id,
|
|
1766
|
+
heartbeat_ttl_ms: clampDurationMs(ttl_ms, resolveWorkerLeaseTtlMs()),
|
|
1767
|
+
});
|
|
1768
|
+
return writeJson(res, result.ok ? 200 : 404, result);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1753
1771
|
if (path === "/bridge/control" && req.method === "POST") {
|
|
1754
1772
|
const {
|
|
1755
1773
|
from_agent = "lead",
|
|
@@ -1881,8 +1899,8 @@ export async function startHub({
|
|
|
1881
1899
|
topic = "assign.job",
|
|
1882
1900
|
payload = {},
|
|
1883
1901
|
priority = 5,
|
|
1884
|
-
ttl_ms =
|
|
1885
|
-
timeout_ms =
|
|
1902
|
+
ttl_ms = DEFAULT_ASSIGN_TTL_MS,
|
|
1903
|
+
timeout_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
1886
1904
|
max_retries = 0,
|
|
1887
1905
|
trace_id,
|
|
1888
1906
|
correlation_id,
|
|
@@ -2352,7 +2370,7 @@ export async function startHub({
|
|
|
2352
2370
|
try {
|
|
2353
2371
|
store.close();
|
|
2354
2372
|
} catch {}
|
|
2355
|
-
if (!preserveTokenFile) {
|
|
2373
|
+
if (!ephemeralPort && !preserveTokenFile) {
|
|
2356
2374
|
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
2357
2375
|
}
|
|
2358
2376
|
releaseStartupLock();
|
|
@@ -2426,6 +2444,8 @@ export async function startHub({
|
|
|
2426
2444
|
httpServer.listen(port, host, () => {
|
|
2427
2445
|
httpServer.off("error", onError);
|
|
2428
2446
|
try {
|
|
2447
|
+
const boundAddress = httpServer.address();
|
|
2448
|
+
if (boundAddress && typeof boundAddress === "object") port = boundAddress.port;
|
|
2429
2449
|
let idleTimer = null;
|
|
2430
2450
|
let stopPromise = null;
|
|
2431
2451
|
|
|
@@ -2446,7 +2466,7 @@ export async function startHub({
|
|
|
2446
2466
|
idleTimeoutMs: hubIdleTimeoutMs,
|
|
2447
2467
|
};
|
|
2448
2468
|
|
|
2449
|
-
writeState({
|
|
2469
|
+
if (!ephemeralPort) writeState({
|
|
2450
2470
|
pid: process.pid,
|
|
2451
2471
|
port,
|
|
2452
2472
|
host,
|
|
@@ -2464,7 +2484,7 @@ export async function startHub({
|
|
|
2464
2484
|
session_id: sessionId,
|
|
2465
2485
|
});
|
|
2466
2486
|
releaseStartupLock();
|
|
2467
|
-
void syncHubMcpSettingsIfAvailable({ hubUrl: info.url });
|
|
2487
|
+
if (!ephemeralPort) void syncHubMcpSettingsIfAvailable({ hubUrl: info.url });
|
|
2468
2488
|
|
|
2469
2489
|
hubLog.info(
|
|
2470
2490
|
{
|
|
@@ -2551,8 +2571,10 @@ export async function startHub({
|
|
|
2551
2571
|
synapseRegistry.destroy();
|
|
2552
2572
|
} catch {}
|
|
2553
2573
|
store.close();
|
|
2554
|
-
|
|
2555
|
-
|
|
2574
|
+
if (!ephemeralPort) {
|
|
2575
|
+
cleanupOwnedPidFile();
|
|
2576
|
+
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
2577
|
+
}
|
|
2556
2578
|
httpServer.closeAllConnections();
|
|
2557
2579
|
await new Promise((resolveClose) =>
|
|
2558
2580
|
httpServer.close(resolveClose),
|
package/hub/store.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { mkdirSync, readFileSync } from "node:fs";
|
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { clampDurationMs, DEFAULT_ASSIGN_TIMEOUT_MS, DEFAULT_ASSIGN_TTL_MS } from "@triflux/core/hub/lib/timeout-defaults.mjs";
|
|
8
9
|
import { uuidv7 } from "@triflux/core/hub/lib/uuidv7.mjs";
|
|
9
10
|
import { recalcConfidence } from "@triflux/core/hub/reflexion.mjs";
|
|
10
11
|
|
|
@@ -111,7 +112,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
111
112
|
db.exec(
|
|
112
113
|
"CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT)",
|
|
113
114
|
);
|
|
114
|
-
const SCHEMA_VERSION = "
|
|
115
|
+
const SCHEMA_VERSION = "5";
|
|
115
116
|
const curVer = (() => {
|
|
116
117
|
try {
|
|
117
118
|
return db
|
|
@@ -143,6 +144,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
143
144
|
"type",
|
|
144
145
|
"TEXT NOT NULL DEFAULT 'reflexion'",
|
|
145
146
|
);
|
|
147
|
+
ensureColumn(db, "assign_jobs", "dispatched_at_ms", "INTEGER");
|
|
146
148
|
ensureColumn(
|
|
147
149
|
db,
|
|
148
150
|
"reflexion_entries",
|
|
@@ -233,12 +235,12 @@ export function createStore(dbPath, options = {}) {
|
|
|
233
235
|
job_id, supervisor_agent, worker_agent, topic, task, payload_json,
|
|
234
236
|
status, attempt, retry_count, max_retries, priority, ttl_ms, timeout_ms, deadline_ms,
|
|
235
237
|
trace_id, correlation_id, last_message_id, result_json, error_json,
|
|
236
|
-
created_at_ms, updated_at_ms, started_at_ms, completed_at_ms, last_retry_at_ms
|
|
238
|
+
created_at_ms, updated_at_ms, started_at_ms, dispatched_at_ms, completed_at_ms, last_retry_at_ms
|
|
237
239
|
) VALUES (
|
|
238
240
|
@job_id, @supervisor_agent, @worker_agent, @topic, @task, @payload_json,
|
|
239
241
|
@status, @attempt, @retry_count, @max_retries, @priority, @ttl_ms, @timeout_ms, @deadline_ms,
|
|
240
242
|
@trace_id, @correlation_id, @last_message_id, @result_json, @error_json,
|
|
241
|
-
@created_at_ms, @updated_at_ms, @started_at_ms, @completed_at_ms, @last_retry_at_ms
|
|
243
|
+
@created_at_ms, @updated_at_ms, @started_at_ms, @dispatched_at_ms, @completed_at_ms, @last_retry_at_ms
|
|
242
244
|
)`),
|
|
243
245
|
getAssign: db.prepare("SELECT * FROM assign_jobs WHERE job_id = ?"),
|
|
244
246
|
updateAssign: db.prepare(`
|
|
@@ -263,6 +265,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
263
265
|
error_json=@error_json,
|
|
264
266
|
updated_at_ms=@updated_at_ms,
|
|
265
267
|
started_at_ms=@started_at_ms,
|
|
268
|
+
dispatched_at_ms=@dispatched_at_ms,
|
|
266
269
|
completed_at_ms=@completed_at_ms,
|
|
267
270
|
last_retry_at_ms=@last_retry_at_ms
|
|
268
271
|
WHERE job_id=@job_id`),
|
|
@@ -356,12 +359,6 @@ export function createStore(dbPath, options = {}) {
|
|
|
356
359
|
return Math.max(1, Math.min(Math.trunc(num), 9));
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
function clampDuration(value, fallback = 600000, min = 1000, max = 86400000) {
|
|
360
|
-
const num = Number(value);
|
|
361
|
-
if (!Number.isFinite(num)) return fallback;
|
|
362
|
-
return Math.max(min, Math.min(Math.trunc(num), max));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
362
|
const store = {
|
|
366
363
|
db,
|
|
367
364
|
uuidv7,
|
|
@@ -419,11 +416,12 @@ export function createStore(dbPath, options = {}) {
|
|
|
419
416
|
|
|
420
417
|
refreshLease(agentId, ttlMs = 30000) {
|
|
421
418
|
const now = Date.now();
|
|
422
|
-
S.heartbeat.run(now, now + ttlMs, agentId);
|
|
419
|
+
const write = S.heartbeat.run(now, now + ttlMs, agentId);
|
|
423
420
|
return {
|
|
424
421
|
agent_id: agentId,
|
|
425
422
|
lease_expires_ms: now + ttlMs,
|
|
426
423
|
server_time_ms: now,
|
|
424
|
+
effective: { updated: write.changes > 0, store_type: "sqlite", db_path: dbPath ?? null },
|
|
427
425
|
};
|
|
428
426
|
},
|
|
429
427
|
|
|
@@ -629,9 +627,10 @@ export function createStore(dbPath, options = {}) {
|
|
|
629
627
|
retry_count = 0,
|
|
630
628
|
max_retries = 0,
|
|
631
629
|
priority = 5,
|
|
632
|
-
ttl_ms =
|
|
633
|
-
timeout_ms =
|
|
630
|
+
ttl_ms = DEFAULT_ASSIGN_TTL_MS,
|
|
631
|
+
timeout_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
634
632
|
deadline_ms,
|
|
633
|
+
dispatched_at_ms = null,
|
|
635
634
|
trace_id,
|
|
636
635
|
correlation_id,
|
|
637
636
|
last_message_id = null,
|
|
@@ -639,7 +638,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
639
638
|
error = null,
|
|
640
639
|
}) {
|
|
641
640
|
const now = Date.now();
|
|
642
|
-
const normalizedTimeout =
|
|
641
|
+
const normalizedTimeout = clampDurationMs(timeout_ms, DEFAULT_ASSIGN_TIMEOUT_MS);
|
|
643
642
|
const row = {
|
|
644
643
|
job_id: job_id || uuidv7(),
|
|
645
644
|
supervisor_agent,
|
|
@@ -652,7 +651,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
652
651
|
retry_count: Math.max(0, Number(retry_count) || 0),
|
|
653
652
|
max_retries: Math.max(0, Number(max_retries) || 0),
|
|
654
653
|
priority: clampPriority(priority, 5),
|
|
655
|
-
ttl_ms:
|
|
654
|
+
ttl_ms: clampDurationMs(ttl_ms, normalizedTimeout),
|
|
656
655
|
timeout_ms: normalizedTimeout,
|
|
657
656
|
deadline_ms: Number.isFinite(Number(deadline_ms))
|
|
658
657
|
? Math.trunc(Number(deadline_ms))
|
|
@@ -665,6 +664,10 @@ export function createStore(dbPath, options = {}) {
|
|
|
665
664
|
created_at_ms: now,
|
|
666
665
|
updated_at_ms: now,
|
|
667
666
|
started_at_ms: status === "running" ? now : null,
|
|
667
|
+
dispatched_at_ms:
|
|
668
|
+
dispatched_at_ms == null || !Number.isFinite(Number(dispatched_at_ms))
|
|
669
|
+
? null
|
|
670
|
+
: Math.trunc(Number(dispatched_at_ms)),
|
|
668
671
|
completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
|
|
669
672
|
? now
|
|
670
673
|
: null,
|
|
@@ -689,7 +692,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
689
692
|
const isTerminal = ["succeeded", "failed", "timed_out"].includes(
|
|
690
693
|
nextStatus,
|
|
691
694
|
);
|
|
692
|
-
const nextTimeout =
|
|
695
|
+
const nextTimeout = clampDurationMs(
|
|
693
696
|
patch.timeout_ms ?? current.timeout_ms,
|
|
694
697
|
current.timeout_ms,
|
|
695
698
|
);
|
|
@@ -717,7 +720,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
717
720
|
patch.priority ?? current.priority,
|
|
718
721
|
current.priority || 5,
|
|
719
722
|
),
|
|
720
|
-
ttl_ms:
|
|
723
|
+
ttl_ms: clampDurationMs(
|
|
721
724
|
patch.ttl_ms ?? current.ttl_ms,
|
|
722
725
|
current.ttl_ms || nextTimeout,
|
|
723
726
|
),
|
|
@@ -758,6 +761,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
758
761
|
: nextStatus === "running"
|
|
759
762
|
? current.started_at_ms || now
|
|
760
763
|
: current.started_at_ms,
|
|
764
|
+
dispatched_at_ms: Object.hasOwn(patch, "dispatched_at_ms") ? patch.dispatched_at_ms : current.dispatched_at_ms,
|
|
761
765
|
completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
|
|
762
766
|
? patch.completed_at_ms
|
|
763
767
|
: isTerminal
|
|
@@ -849,7 +853,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
849
853
|
current.attempt + 1,
|
|
850
854
|
Number(patch.attempt ?? current.attempt + 1) || 1,
|
|
851
855
|
);
|
|
852
|
-
const nextTimeout =
|
|
856
|
+
const nextTimeout = clampDurationMs(
|
|
853
857
|
patch.timeout_ms ?? current.timeout_ms,
|
|
854
858
|
current.timeout_ms,
|
|
855
859
|
);
|
|
@@ -861,6 +865,7 @@ export function createStore(dbPath, options = {}) {
|
|
|
861
865
|
deadline_ms: Date.now() + nextTimeout,
|
|
862
866
|
completed_at_ms: null,
|
|
863
867
|
started_at_ms: null,
|
|
868
|
+
dispatched_at_ms: null,
|
|
864
869
|
last_retry_at_ms: Date.now(),
|
|
865
870
|
result: patch.result ?? null,
|
|
866
871
|
error: Object.hasOwn(patch, "error") ? patch.error : current.error,
|
|
@@ -464,25 +464,46 @@ export function sendClaudeControlRequest(
|
|
|
464
464
|
|
|
465
465
|
// claude daemon 은 control.sock 의 mutating op(dispatch 등)에 control-key
|
|
466
466
|
// 인증을 강제한다 (미제시 시 EAUTH "didn't present the daemon control key").
|
|
467
|
-
//
|
|
468
|
-
//
|
|
467
|
+
// OMC runtime config 는 .omc-launch-profile.json 의 sourceConfigDir 에서 key 를
|
|
468
|
+
// 발급한다. control.sock 은 runtime config hash 를 쓰더라도 auth 는 발급 config
|
|
469
|
+
// 에서 읽어야 한다. 일반 daemon 은 <configDir>/daemon/control.key 를 사용한다.
|
|
470
|
+
// 어느 경로에도 파일이 없으면 인증 미강제 daemon 으로 보고 auth 를 생략한다
|
|
471
|
+
// (fail-open — 구버전 호환).
|
|
469
472
|
export async function readDaemonControlKey(
|
|
470
473
|
configDir = resolveClaudeConfigDir(),
|
|
474
|
+
{ diagnostics } = {},
|
|
471
475
|
) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
476
|
+
if (!configDir) return undefined;
|
|
477
|
+
|
|
478
|
+
const runtimeConfigDir = path.resolve(configDir);
|
|
479
|
+
const omcProfile = await readOmcLaunchProfile(runtimeConfigDir);
|
|
480
|
+
const keyConfigDirs = [omcProfile?.sourceConfigDir, runtimeConfigDir].filter(
|
|
481
|
+
(candidate, index, entries) =>
|
|
482
|
+
candidate && entries.indexOf(candidate) === index,
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
for (const keyConfigDir of keyConfigDirs) {
|
|
486
|
+
const keyPath = path.join(keyConfigDir, "daemon", "control.key");
|
|
487
|
+
try {
|
|
488
|
+
const key = await fs.readFile(keyPath, "utf8");
|
|
489
|
+
if (key.trim()) return key.trim();
|
|
490
|
+
} catch (error) {
|
|
491
|
+
if (error?.code === "ENOENT") continue;
|
|
492
|
+
if (Array.isArray(diagnostics)) {
|
|
493
|
+
diagnostics.push({
|
|
494
|
+
code: error?.code || "UNKNOWN",
|
|
495
|
+
path: keyPath,
|
|
496
|
+
message: error?.message || String(error),
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
480
500
|
}
|
|
501
|
+
return undefined;
|
|
481
502
|
}
|
|
482
503
|
|
|
483
|
-
export async function buildDaemonControlAuth(configDir) {
|
|
504
|
+
export async function buildDaemonControlAuth(configDir, opts = {}) {
|
|
484
505
|
if (!configDir) return {};
|
|
485
|
-
const auth = await readDaemonControlKey(configDir);
|
|
506
|
+
const auth = await readDaemonControlKey(configDir, opts);
|
|
486
507
|
return auth ? { auth } : {};
|
|
487
508
|
}
|
|
488
509
|
|
|
@@ -74,7 +74,7 @@ export function parseTeamArgs(args = []) {
|
|
|
74
74
|
const assigns = []; // --assign "codex:프롬프트:역할" 형식
|
|
75
75
|
let autoAttach = true;
|
|
76
76
|
let progressive = true;
|
|
77
|
-
let timeoutSec =
|
|
77
|
+
let timeoutSec = 0;
|
|
78
78
|
let verbose = false;
|
|
79
79
|
let dashboard = true;
|
|
80
80
|
let dashboardLayout = "lite";
|
|
@@ -130,7 +130,7 @@ export function parseTeamArgs(args = []) {
|
|
|
130
130
|
} else if (current === "--no-progressive") {
|
|
131
131
|
progressive = false;
|
|
132
132
|
} else if (current === "--timeout" && args[index + 1]) {
|
|
133
|
-
timeoutSec = Number(args[++index]) ||
|
|
133
|
+
timeoutSec = Number(args[++index]) || 0;
|
|
134
134
|
} else if (current === "--mcp-profile" && args[index + 1]) {
|
|
135
135
|
mcpProfile = args[++index].trim();
|
|
136
136
|
} else if ((current === "--model" || current === "-m") && args[index + 1]) {
|