@triflux/remote 10.50.2 → 10.52.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/cto/brief.mjs +0 -20
- package/cto/collect.mjs +1 -200
- package/cto/current.schema.json +2 -23
- package/cto/hygiene.mjs +2 -61
- package/cto/lake-root.mjs +0 -56
- package/cto/status.mjs +5 -187
- package/hub/index.mjs +0 -17
- package/hub/team/agent-map.json +0 -1
- package/hub/team/agents-row-attach.py +120 -0
- package/hub/team/agents-row.mjs +200 -0
- package/hub/team/ansi.mjs +1 -35
- package/hub/team/backend.mjs +9 -145
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/cli/commands/attach.mjs +1 -17
- package/hub/team/cli/commands/control.mjs +12 -38
- package/hub/team/cli/commands/debug.mjs +2 -39
- package/hub/team/cli/commands/focus.mjs +5 -31
- package/hub/team/cli/commands/interrupt.mjs +6 -25
- package/hub/team/cli/commands/kill.mjs +1 -30
- package/hub/team/cli/commands/list.mjs +0 -16
- package/hub/team/cli/commands/send.mjs +6 -26
- package/hub/team/cli/commands/start/index.mjs +42 -133
- package/hub/team/cli/commands/start/parse-args.mjs +15 -40
- package/hub/team/cli/commands/start/start-headless.mjs +138 -105
- package/hub/team/cli/commands/start/start-mux.mjs +2 -7
- package/hub/team/cli/commands/status.mjs +2 -57
- package/hub/team/cli/commands/stop.mjs +14 -19
- package/hub/team/cli/help.mjs +4 -5
- package/hub/team/cli/manifest.mjs +5 -1
- package/hub/team/cli/render.mjs +0 -12
- package/hub/team/cli/services/runtime-mode.mjs +27 -98
- package/hub/team/cli/services/state-store.mjs +5 -20
- package/hub/team/handoff.mjs +3 -34
- package/hub/team/headless.mjs +108 -1296
- package/hub/team/orchestrator.mjs +4 -30
- package/hub/team/pane.mjs +6 -28
- package/hub/team/psmux.mjs +3 -34
- package/hub/team/remote-session.mjs +1 -2
- package/hub/team/session-context.mjs +1 -1
- package/hub/team/session.mjs +7 -91
- package/hub/team/tui-core.mjs +1 -1
- package/hub/team/tui-viewer.mjs +11 -58
- package/hub/team/tui.mjs +8 -306
- package/hub/team/uds-orchestrator.mjs +2 -580
- package/hub/team/wt-manager.mjs +2 -2
- package/hub/workers/factory.mjs +6 -84
- package/hub/workers/interface.mjs +1 -7
- package/hub/workers/lib/jsonrpc-core.mjs +11 -0
- package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
- package/package.json +3 -8
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/skill-template.mjs +0 -245
- package/hub/hub-lifecycle.mjs +0 -248
- package/hub/pipe.mjs +0 -825
- package/hub/promote-penalties.mjs +0 -138
- package/hub/public/dashboard.html +0 -416
- package/hub/schema.sql +0 -209
- package/hub/server.mjs +0 -3048
- package/hub/store-adapter.mjs +0 -182
- package/hub/store.mjs +0 -1736
- package/hub/team/build-worker-prompt.mjs +0 -92
- package/hub/team/check-mcp-hub.mjs +0 -49
- package/hub/team/claude-native-bridge.mjs +0 -987
- package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
- package/hub/team/cli/commands/start/start-wt.mjs +0 -86
- package/hub/team/cli/services/hub-client.mjs +0 -264
- package/hub/team/cli/services/native-control.mjs +0 -145
- package/hub/team/codex-review.mjs +0 -520
- package/hub/team/conductor-mesh-bridge.mjs +0 -122
- package/hub/team/conductor-registry.mjs +0 -121
- package/hub/team/conductor.mjs +0 -1529
- package/hub/team/consensus-meta.mjs +0 -176
- package/hub/team/cto-auto-collect.mjs +0 -115
- package/hub/team/cto-pull.mjs +0 -31
- package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
- package/hub/team/dashboard.mjs +0 -322
- package/hub/team/event-log.mjs +0 -84
- package/hub/team/execution-mode.mjs +0 -324
- package/hub/team/extract-completion-payload.mjs +0 -110
- package/hub/team/headless-bridge-session.mjs +0 -17
- package/hub/team/health-probe.mjs +0 -435
- package/hub/team/interactive-native-launcher.mjs +0 -119
- package/hub/team/interactive-native-worker.mjs +0 -80
- package/hub/team/interactive-tui-transport.mjs +0 -479
- package/hub/team/launcher-template.mjs +0 -144
- package/hub/team/lead-control.mjs +0 -191
- package/hub/team/mcp-selector.mjs +0 -145
- package/hub/team/native-supervisor.mjs +0 -460
- package/hub/team/native.mjs +0 -727
- package/hub/team/recovery-store.mjs +0 -149
- package/hub/team/remote-probe.mjs +0 -292
- package/hub/team/remote-watcher.mjs +0 -506
- package/hub/team/runtime-strategy.mjs +0 -200
- package/hub/team/schemas/worker-completion.json +0 -39
- package/hub/team/sentinel-capture.mjs +0 -127
- package/hub/team/session-sync.mjs +0 -316
- package/hub/team/swarm-cli.mjs +0 -390
- package/hub/team/swarm-hypervisor.mjs +0 -2206
- package/hub/team/swarm-intent.mjs +0 -216
- package/hub/team/swarm-locks.mjs +0 -335
- package/hub/team/swarm-planner.mjs +0 -371
- package/hub/team/swarm-preflight.mjs +0 -366
- package/hub/team/swarm-reconciler.mjs +0 -242
- package/hub/team/synapse-cli.mjs +0 -458
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team/synapse-registry.mjs +0 -618
- package/hub/team/tui-remote-adapter.mjs +0 -377
- package/hub/team/tui-synapse.mjs +0 -223
- package/hub/team/worker-completion-validator.mjs +0 -79
- package/hub/team/worker-sandbox.mjs +0 -125
- package/hub/team/worktree-lifecycle.mjs +0 -639
- package/hub/team/wt-templates.json +0 -43
- package/hub/tools.mjs +0 -900
- package/hub/workers/antigravity-route-worker.mjs +0 -179
- package/hub/workers/codex-app-server-worker.mjs +0 -1067
- package/hub/workers/delegator-mcp.mjs +0 -1098
- package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
package/cto/brief.mjs
CHANGED
|
@@ -12,11 +12,6 @@ function oneLine(value, fallback = "n/a") {
|
|
|
12
12
|
return String(text).replace(/\s+/g, " ").trim() || fallback;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function compactDetail(value) {
|
|
16
|
-
const text = oneLine(value);
|
|
17
|
-
return text.length > 140 ? `${text.slice(0, 137)}...` : text;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
15
|
function formatGoal(goal) {
|
|
21
16
|
const id = oneLine(goal?.id, "goal");
|
|
22
17
|
const status = oneLine(goal?.status, "unknown");
|
|
@@ -27,13 +22,6 @@ function formatGoal(goal) {
|
|
|
27
22
|
return `- ${id} ${status}: ${title}`;
|
|
28
23
|
}
|
|
29
24
|
|
|
30
|
-
function formatShard(shard) {
|
|
31
|
-
const id = oneLine(shard?.id || shard?.name || shard?.sessionId, "shard");
|
|
32
|
-
const status = oneLine(shard?.status || shard?.state, "unknown");
|
|
33
|
-
const detail = oneLine(shard?.summary || shard?.objective || shard?.task, "");
|
|
34
|
-
return detail ? `- ${id} ${status}: ${detail}` : `- ${id} ${status}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
25
|
function formatEvent(event) {
|
|
38
26
|
const ts = oneLine(event?.ts, "unknown_ts");
|
|
39
27
|
const name = oneLine(event?.event, "event");
|
|
@@ -57,13 +45,9 @@ function enforceCap(text) {
|
|
|
57
45
|
export function renderBrief(current) {
|
|
58
46
|
const repo = current?.repo || {};
|
|
59
47
|
const summary = current?.summary || {};
|
|
60
|
-
const hub = current?.sources?.tfx_hub || {};
|
|
61
48
|
const activeGoals = Array.isArray(summary.active_goals)
|
|
62
49
|
? summary.active_goals.slice(0, 3)
|
|
63
50
|
: [];
|
|
64
|
-
const swarmShards = Array.isArray(summary.swarm_shards)
|
|
65
|
-
? summary.swarm_shards.slice(0, 2)
|
|
66
|
-
: [];
|
|
67
51
|
const recentEvents = Array.isArray(current?.ledger_tail)
|
|
68
52
|
? current.ledger_tail.slice(-2)
|
|
69
53
|
: [];
|
|
@@ -75,10 +59,6 @@ export function renderBrief(current) {
|
|
|
75
59
|
`summary: ${oneLine(summary.repo_state, "no repo summary")}`,
|
|
76
60
|
"active_goals",
|
|
77
61
|
...(activeGoals.length > 0 ? activeGoals.map(formatGoal) : ["- none"]),
|
|
78
|
-
"swarm_shards",
|
|
79
|
-
...(swarmShards.length > 0 ? swarmShards.map(formatShard) : ["- none"]),
|
|
80
|
-
"hub_status",
|
|
81
|
-
`status: ${oneLine(hub.status, "unknown")} available: ${hub.available === true} detail: ${compactDetail(hub.detail)}`,
|
|
82
62
|
"recent_events",
|
|
83
63
|
...(recentEvents.length > 0 ? recentEvents.map(formatEvent) : ["- none"]),
|
|
84
64
|
"generated_at",
|
package/cto/collect.mjs
CHANGED
|
@@ -6,14 +6,12 @@ import {
|
|
|
6
6
|
readFileSync,
|
|
7
7
|
statSync,
|
|
8
8
|
} from "node:fs";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
9
|
import { join, relative } from "node:path";
|
|
11
10
|
import { fileURLToPath } from "node:url";
|
|
12
11
|
|
|
13
12
|
import { renderBrief } from "./brief.mjs";
|
|
14
13
|
import { appendCtoEvent } from "./events.mjs";
|
|
15
14
|
import {
|
|
16
|
-
firstExisting,
|
|
17
15
|
readJsonLines,
|
|
18
16
|
resolveLakeRootDir,
|
|
19
17
|
writeAtomic,
|
|
@@ -27,30 +25,6 @@ const SOURCE_REGISTRY = [
|
|
|
27
25
|
probe: "git rev-parse, git branch, git status",
|
|
28
26
|
enabled: true,
|
|
29
27
|
},
|
|
30
|
-
{
|
|
31
|
-
id: "tfx_hub",
|
|
32
|
-
kind: "triflux-runtime-artifact",
|
|
33
|
-
probe: ".triflux/hub/status.json or host tfx-hub cache",
|
|
34
|
-
enabled: true,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
id: "tfx_swarm",
|
|
38
|
-
kind: "triflux-runtime-artifact",
|
|
39
|
-
probe: ".triflux/swarm-locks.json or .triflux/swarm/*.json",
|
|
40
|
-
enabled: true,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "tfx_team",
|
|
44
|
-
kind: "triflux-runtime-artifact",
|
|
45
|
-
probe: ".triflux/team-state.json or host tfx-hub team cache",
|
|
46
|
-
enabled: true,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: "tfx_synapse",
|
|
50
|
-
kind: "triflux-runtime-artifact",
|
|
51
|
-
probe: ".triflux/synapse registry or host tfx-hub synapse cache",
|
|
52
|
-
enabled: true,
|
|
53
|
-
},
|
|
54
28
|
{
|
|
55
29
|
id: "ultragoal_omx",
|
|
56
30
|
kind: "durable-goal-artifact",
|
|
@@ -127,14 +101,6 @@ function listDirFiles(dirPath, rootDir, limit = 10) {
|
|
|
127
101
|
}
|
|
128
102
|
}
|
|
129
103
|
|
|
130
|
-
function safeSummary(value) {
|
|
131
|
-
if (value === null || value === undefined) return null;
|
|
132
|
-
if (Array.isArray(value)) return { type: "array", count: value.length };
|
|
133
|
-
if (typeof value === "object")
|
|
134
|
-
return { type: "object", keys: Object.keys(value).slice(0, 20) };
|
|
135
|
-
return String(value).slice(0, 200);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
104
|
function execGit(args, rootDir, execFileSyncFn) {
|
|
139
105
|
return execFileSyncFn("git", args, {
|
|
140
106
|
cwd: rootDir,
|
|
@@ -189,45 +155,6 @@ function collectGit(rootDir, collectedAt, execFileSyncFn) {
|
|
|
189
155
|
}
|
|
190
156
|
}
|
|
191
157
|
|
|
192
|
-
function collectJsonArtifact(
|
|
193
|
-
id,
|
|
194
|
-
candidates,
|
|
195
|
-
rootDir,
|
|
196
|
-
collectedAt,
|
|
197
|
-
summarize = safeSummary,
|
|
198
|
-
) {
|
|
199
|
-
const filePath = firstExisting(candidates);
|
|
200
|
-
if (!filePath) {
|
|
201
|
-
return missingSource(
|
|
202
|
-
"no_shell_readable_artifact",
|
|
203
|
-
"no durable artifact found",
|
|
204
|
-
collectedAt,
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
const parsed = readJson(filePath);
|
|
209
|
-
return sourceState(
|
|
210
|
-
true,
|
|
211
|
-
"ok",
|
|
212
|
-
{
|
|
213
|
-
path: relPath(rootDir, filePath),
|
|
214
|
-
summary: summarize(parsed),
|
|
215
|
-
},
|
|
216
|
-
collectedAt,
|
|
217
|
-
);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
return sourceState(
|
|
220
|
-
true,
|
|
221
|
-
"read_error",
|
|
222
|
-
{
|
|
223
|
-
path: relPath(rootDir, filePath),
|
|
224
|
-
error: error?.message || `${id} artifact read failed`,
|
|
225
|
-
},
|
|
226
|
-
collectedAt,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
158
|
function normalizeGoals(parsed) {
|
|
232
159
|
const rawGoals = Array.isArray(parsed?.goals)
|
|
233
160
|
? parsed.goals
|
|
@@ -306,70 +233,6 @@ function collectUltragoal(dirPath, rootDir, collectedAt) {
|
|
|
306
233
|
}
|
|
307
234
|
}
|
|
308
235
|
|
|
309
|
-
export function collectSwarm(rootDir, collectedAt) {
|
|
310
|
-
const locksPath = join(rootDir, ".triflux", "swarm-locks.json");
|
|
311
|
-
const logsDir = join(rootDir, ".triflux", "swarm-logs");
|
|
312
|
-
if (!existsSync(locksPath) && !existsSync(logsDir)) {
|
|
313
|
-
return missingSource(
|
|
314
|
-
"no_shell_readable_artifact",
|
|
315
|
-
"no swarm locks or logs found",
|
|
316
|
-
collectedAt,
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
try {
|
|
320
|
-
const detail = { locks_path: null, locks: null, shards: [], log_runs: [] };
|
|
321
|
-
if (existsSync(locksPath)) {
|
|
322
|
-
const locks = readJson(locksPath);
|
|
323
|
-
detail.locks_path = relPath(rootDir, locksPath);
|
|
324
|
-
detail.locks = safeSummary(locks);
|
|
325
|
-
if (Array.isArray(locks?.shards)) detail.shards = locks.shards;
|
|
326
|
-
else if (Array.isArray(locks)) detail.shards = locks;
|
|
327
|
-
else if (locks && typeof locks === "object") {
|
|
328
|
-
// 실제 producer(hub/team/swarm-locks.mjs persist)는
|
|
329
|
-
// { "<file>": { workerId, leaseType, sessionMeta } } 형태의 lock map 을 쓴다.
|
|
330
|
-
// workerId 가 곧 shard.name 이다 — swarm-hypervisor 가
|
|
331
|
-
// acquire(shard.name, shard.files) 로 잠그기 때문(swarm-hypervisor.mjs:753).
|
|
332
|
-
// workerId 별로 묶어 active shard row 로 환원한다. (expired lock 정밀 필터는
|
|
333
|
-
// hub snapshot() 의 책임이라, 여기서는 persist 된 보유 lock 전부를 active 로 본다.)
|
|
334
|
-
const byWorker = new Map();
|
|
335
|
-
for (const entry of Object.values(locks)) {
|
|
336
|
-
const workerId =
|
|
337
|
-
entry && typeof entry.workerId === "string" ? entry.workerId : null;
|
|
338
|
-
if (!workerId) continue;
|
|
339
|
-
let row = byWorker.get(workerId);
|
|
340
|
-
if (!row) {
|
|
341
|
-
row = { shard_name: workerId, phase: "active", members: [] };
|
|
342
|
-
byWorker.set(workerId, row);
|
|
343
|
-
}
|
|
344
|
-
const host = entry?.sessionMeta?.host;
|
|
345
|
-
if (typeof host === "string" && host && !row.members.includes(host)) {
|
|
346
|
-
row.members.push(host);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
detail.shards = [...byWorker.values()];
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (existsSync(logsDir)) {
|
|
353
|
-
detail.log_runs = readdirSync(logsDir, { withFileTypes: true })
|
|
354
|
-
.filter((entry) => entry.isDirectory() && entry.name.startsWith("run-"))
|
|
355
|
-
.map((entry) => {
|
|
356
|
-
const path = join(logsDir, entry.name);
|
|
357
|
-
return { id: entry.name, mtime: statSync(path).mtime.toISOString() };
|
|
358
|
-
})
|
|
359
|
-
.sort((a, b) => b.mtime.localeCompare(a.mtime))
|
|
360
|
-
.slice(0, 5);
|
|
361
|
-
}
|
|
362
|
-
return sourceState(true, "ok", detail, collectedAt);
|
|
363
|
-
} catch (error) {
|
|
364
|
-
return sourceState(
|
|
365
|
-
true,
|
|
366
|
-
"read_error",
|
|
367
|
-
error?.message || "swarm read failed",
|
|
368
|
-
collectedAt,
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
236
|
function collectHandoffs(rootDir, collectedAt) {
|
|
374
237
|
const dirPath = join(rootDir, ".omx", "handoffs");
|
|
375
238
|
const files = listDirFiles(dirPath, rootDir, 10);
|
|
@@ -392,7 +255,6 @@ function buildSummary(current) {
|
|
|
392
255
|
const repo = current.repo;
|
|
393
256
|
const omxGoals = current.sources.ultragoal_omx.detail?.active_goals || [];
|
|
394
257
|
const omcGoals = current.sources.ultragoal_omc.detail?.active_goals || [];
|
|
395
|
-
const swarmShards = current.sources.tfx_swarm.detail?.shards || [];
|
|
396
258
|
const availableSources = Object.values(current.sources).filter(
|
|
397
259
|
(source) => source.available,
|
|
398
260
|
).length;
|
|
@@ -400,8 +262,6 @@ function buildSummary(current) {
|
|
|
400
262
|
return {
|
|
401
263
|
repo_state: `branch ${repo.branch || "unknown"} at ${repo.head || "unknown"} is ${repo.dirty ? "dirty" : "clean"}`,
|
|
402
264
|
active_goals: [...omxGoals, ...omcGoals].slice(0, 5),
|
|
403
|
-
swarm_shards: Array.isArray(swarmShards) ? swarmShards.slice(0, 5) : [],
|
|
404
|
-
hub_status: current.sources.tfx_hub.status,
|
|
405
265
|
available_sources: availableSources,
|
|
406
266
|
missing_sources: SOURCE_IDS.length - availableSources,
|
|
407
267
|
};
|
|
@@ -539,68 +399,10 @@ function validateCurrent(current) {
|
|
|
539
399
|
}
|
|
540
400
|
}
|
|
541
401
|
|
|
542
|
-
function collectSources(rootDir, collectedAt, execFileSyncFn
|
|
543
|
-
const home = homedir();
|
|
544
|
-
const hostArtifactCandidates =
|
|
545
|
-
opts.includeHostArtifacts === false
|
|
546
|
-
? {
|
|
547
|
-
hub: [],
|
|
548
|
-
team: [],
|
|
549
|
-
synapse: [],
|
|
550
|
-
}
|
|
551
|
-
: {
|
|
552
|
-
hub: [
|
|
553
|
-
join(home, ".claude", "cache", "tfx-hub", "hub.pid"),
|
|
554
|
-
join(home, ".claude", "cache", "tfx-hub", "hub-state.json"),
|
|
555
|
-
],
|
|
556
|
-
team: [
|
|
557
|
-
join(
|
|
558
|
-
home,
|
|
559
|
-
".claude",
|
|
560
|
-
"cache",
|
|
561
|
-
"tfx-hub",
|
|
562
|
-
`team-state-${process.env.CLAUDE_SESSION_ID || ""}.json`,
|
|
563
|
-
),
|
|
564
|
-
join(home, ".claude", "cache", "tfx-hub", "team-state.json"),
|
|
565
|
-
],
|
|
566
|
-
synapse: [
|
|
567
|
-
join(home, ".claude", "cache", "tfx-hub", "synapse-registry.json"),
|
|
568
|
-
join(home, ".claude", "cache", "tfx-hub", "synapse-sessions.json"),
|
|
569
|
-
],
|
|
570
|
-
};
|
|
402
|
+
function collectSources(rootDir, collectedAt, execFileSyncFn) {
|
|
571
403
|
const git = collectGit(rootDir, collectedAt, execFileSyncFn);
|
|
572
404
|
const sources = {
|
|
573
405
|
git: git.source,
|
|
574
|
-
tfx_hub: collectJsonArtifact(
|
|
575
|
-
"tfx_hub",
|
|
576
|
-
[
|
|
577
|
-
join(rootDir, ".triflux", "hub", "status.json"),
|
|
578
|
-
join(rootDir, ".triflux", "hub-state.json"),
|
|
579
|
-
...hostArtifactCandidates.hub,
|
|
580
|
-
],
|
|
581
|
-
rootDir,
|
|
582
|
-
collectedAt,
|
|
583
|
-
),
|
|
584
|
-
tfx_swarm: collectSwarm(rootDir, collectedAt),
|
|
585
|
-
tfx_team: collectJsonArtifact(
|
|
586
|
-
"tfx_team",
|
|
587
|
-
[
|
|
588
|
-
join(rootDir, ".triflux", "team-state.json"),
|
|
589
|
-
...hostArtifactCandidates.team,
|
|
590
|
-
],
|
|
591
|
-
rootDir,
|
|
592
|
-
collectedAt,
|
|
593
|
-
),
|
|
594
|
-
tfx_synapse: collectJsonArtifact(
|
|
595
|
-
"tfx_synapse",
|
|
596
|
-
[
|
|
597
|
-
join(rootDir, ".triflux", "synapse-registry.json"),
|
|
598
|
-
join(rootDir, ".triflux", "synapse", "registry.json"),
|
|
599
|
-
...hostArtifactCandidates.synapse,
|
|
600
|
-
],
|
|
601
|
-
rootDir,
|
|
602
|
-
collectedAt,
|
|
603
|
-
),
|
|
604
406
|
ultragoal_omx: collectUltragoal(
|
|
605
407
|
join(rootDir, ".omx", "ultragoal"),
|
|
606
408
|
rootDir,
|
|
@@ -641,7 +443,6 @@ export async function runCollect(args = [], opts = {}) {
|
|
|
641
443
|
rootDir,
|
|
642
444
|
generatedAt,
|
|
643
445
|
execFileSyncFn,
|
|
644
|
-
opts,
|
|
645
446
|
);
|
|
646
447
|
const current = {
|
|
647
448
|
schema_version: SCHEMA_VERSION,
|
package/cto/current.schema.json
CHANGED
|
@@ -46,32 +46,11 @@
|
|
|
46
46
|
"type": "object",
|
|
47
47
|
"description": "Durable artifact source states only. A source reports available:false when no shell-readable durable artifact exists.",
|
|
48
48
|
"additionalProperties": false,
|
|
49
|
-
"required": [
|
|
50
|
-
"git",
|
|
51
|
-
"tfx_hub",
|
|
52
|
-
"tfx_swarm",
|
|
53
|
-
"tfx_team",
|
|
54
|
-
"tfx_synapse",
|
|
55
|
-
"ultragoal_omx",
|
|
56
|
-
"ultragoal_omc",
|
|
57
|
-
"handoffs"
|
|
58
|
-
],
|
|
49
|
+
"required": ["git", "ultragoal_omx", "ultragoal_omc", "handoffs"],
|
|
59
50
|
"properties": {
|
|
60
51
|
"git": {
|
|
61
52
|
"$ref": "#/$defs/sourceState"
|
|
62
53
|
},
|
|
63
|
-
"tfx_hub": {
|
|
64
|
-
"$ref": "#/$defs/sourceState"
|
|
65
|
-
},
|
|
66
|
-
"tfx_swarm": {
|
|
67
|
-
"$ref": "#/$defs/sourceState"
|
|
68
|
-
},
|
|
69
|
-
"tfx_team": {
|
|
70
|
-
"$ref": "#/$defs/sourceState"
|
|
71
|
-
},
|
|
72
|
-
"tfx_synapse": {
|
|
73
|
-
"$ref": "#/$defs/sourceState"
|
|
74
|
-
},
|
|
75
54
|
"ultragoal_omx": {
|
|
76
55
|
"$ref": "#/$defs/sourceState"
|
|
77
56
|
},
|
|
@@ -85,7 +64,7 @@
|
|
|
85
64
|
},
|
|
86
65
|
"summary": {
|
|
87
66
|
"type": "object",
|
|
88
|
-
"description": "Human-oriented synthesized authority summary derived from durable artifacts
|
|
67
|
+
"description": "Human-oriented synthesized authority summary derived from durable artifacts. Phase 1 may add source-derived fields here without changing the top-level contract.",
|
|
89
68
|
"additionalProperties": true
|
|
90
69
|
},
|
|
91
70
|
"ledger_tail": {
|
package/cto/hygiene.mjs
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
normalizeLiveSession,
|
|
5
|
-
readJsonLines,
|
|
6
|
-
readSynapseSnapshot,
|
|
7
|
-
resolveLakeRootDir,
|
|
8
|
-
} from "./lake-root.mjs";
|
|
3
|
+
import { readJsonLines, resolveLakeRootDir } from "./lake-root.mjs";
|
|
9
4
|
|
|
10
5
|
const COUNT_KEYS = [
|
|
11
6
|
"active_tasks",
|
|
@@ -28,37 +23,6 @@ function readJson(filePath) {
|
|
|
28
23
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
function normalizeOverlay(value) {
|
|
32
|
-
const sessions = Array.isArray(value)
|
|
33
|
-
? value
|
|
34
|
-
: Array.isArray(value?.sessions)
|
|
35
|
-
? value.sessions
|
|
36
|
-
: Array.isArray(value?.live_sessions)
|
|
37
|
-
? value.live_sessions
|
|
38
|
-
: [];
|
|
39
|
-
return {
|
|
40
|
-
live_sessions: sessions
|
|
41
|
-
.map(normalizeLiveSession)
|
|
42
|
-
.filter((session) => session.sessionId),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function readLiveOverlay(opts = {}) {
|
|
47
|
-
if (opts.overlay) return normalizeOverlay(opts.overlay);
|
|
48
|
-
try {
|
|
49
|
-
const reader = opts.synapseReader || readSynapseSnapshot;
|
|
50
|
-
return normalizeOverlay(
|
|
51
|
-
await reader({
|
|
52
|
-
rootDir: opts.rootDir,
|
|
53
|
-
lakeRoot: opts.lakeRoot,
|
|
54
|
-
synapsePersistPath: opts.synapsePersistPath,
|
|
55
|
-
}),
|
|
56
|
-
);
|
|
57
|
-
} catch {
|
|
58
|
-
return { live_sessions: [] };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
26
|
function eventTime(entry) {
|
|
63
27
|
return typeof entry?.ts === "string" ? entry.ts : "";
|
|
64
28
|
}
|
|
@@ -157,11 +121,7 @@ export function compactHygieneCounts(projection) {
|
|
|
157
121
|
return Object.fromEntries(COUNT_KEYS.map((key) => [key, counts[key] || 0]));
|
|
158
122
|
}
|
|
159
123
|
|
|
160
|
-
export function projectCtoHygiene({
|
|
161
|
-
current = {},
|
|
162
|
-
ledger = null,
|
|
163
|
-
overlay = {},
|
|
164
|
-
} = {}) {
|
|
124
|
+
export function projectCtoHygiene({ current = {}, ledger = null } = {}) {
|
|
165
125
|
const events = Array.isArray(ledger)
|
|
166
126
|
? ledger
|
|
167
127
|
: Array.isArray(current?.ledger_tail)
|
|
@@ -270,23 +230,6 @@ export function projectCtoHygiene({
|
|
|
270
230
|
);
|
|
271
231
|
}
|
|
272
232
|
|
|
273
|
-
for (const session of overlay?.live_sessions || []) {
|
|
274
|
-
const phase = String(session?.phase || session?.status || "").toLowerCase();
|
|
275
|
-
if (phase !== "stale") continue;
|
|
276
|
-
const sessionId = String(
|
|
277
|
-
session?.sessionId || session?.session_id || "",
|
|
278
|
-
).trim();
|
|
279
|
-
if (!sessionId || staleSessions.has(sessionId)) continue;
|
|
280
|
-
rows.push({
|
|
281
|
-
kind: "session",
|
|
282
|
-
id: sessionId,
|
|
283
|
-
status: "stale",
|
|
284
|
-
last_event_at: session?.started_at || null,
|
|
285
|
-
action: "archive_or_resume_session",
|
|
286
|
-
owner: session?.agent_id ? undefined : "unknown",
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
233
|
for (const [key, item] of worktrees) {
|
|
291
234
|
if (item.entry?.event === "worktree_removed") continue;
|
|
292
235
|
const status = statusOf(item.ref, "active");
|
|
@@ -363,11 +306,9 @@ export async function runHygiene(args = [], opts = {}) {
|
|
|
363
306
|
const currentPath = join(lakeRoot, "current.json");
|
|
364
307
|
const current = existsSync(currentPath) ? readJson(currentPath) : {};
|
|
365
308
|
const ledger = readJsonLines(join(lakeRoot, "ledger.jsonl"));
|
|
366
|
-
const overlay = await readLiveOverlay({ ...opts, rootDir, lakeRoot });
|
|
367
309
|
const projection = projectCtoHygiene({
|
|
368
310
|
current,
|
|
369
311
|
ledger: ledger.length ? ledger : null,
|
|
370
|
-
overlay,
|
|
371
312
|
});
|
|
372
313
|
if (opts.json === true || hasFlag(args, "--json"))
|
|
373
314
|
writeJson(stdout, projection);
|
package/cto/lake-root.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
renameSync,
|
|
7
7
|
writeFileSync,
|
|
8
8
|
} from "node:fs";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
9
|
import { basename, dirname, join, parse } from "node:path";
|
|
11
10
|
|
|
12
11
|
const MAX_DEPTH = 64;
|
|
@@ -77,10 +76,6 @@ export function resolveLakeRootDir(cwd, opts = {}) {
|
|
|
77
76
|
return cwd;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
export function firstExisting(paths) {
|
|
81
|
-
return paths.filter(Boolean).find((path) => defaultExistsSync(path)) || null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
79
|
export function toIsoTime(value) {
|
|
85
80
|
if (typeof value === "string" && value.trim()) return value;
|
|
86
81
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -106,57 +101,6 @@ export function pathLabel(value) {
|
|
|
106
101
|
return segments[segments.length - 1] || "";
|
|
107
102
|
}
|
|
108
103
|
|
|
109
|
-
export function normalizeLiveSession(session) {
|
|
110
|
-
return {
|
|
111
|
-
sessionId: String(session?.sessionId || session?.session_id || ""),
|
|
112
|
-
agent_id:
|
|
113
|
-
typeof session?.agent_id === "string"
|
|
114
|
-
? session.agent_id
|
|
115
|
-
: typeof session?.agentId === "string"
|
|
116
|
-
? session.agentId
|
|
117
|
-
: null,
|
|
118
|
-
phase:
|
|
119
|
-
typeof session?.phase === "string"
|
|
120
|
-
? session.phase
|
|
121
|
-
: typeof session?.status === "string"
|
|
122
|
-
? session.status
|
|
123
|
-
: "active",
|
|
124
|
-
started_at: toIsoTime(
|
|
125
|
-
session?.started_at ?? session?.startedAt ?? session?.lastHeartbeat,
|
|
126
|
-
),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export async function readSynapseSnapshot(opts = {}) {
|
|
131
|
-
const rootDir = opts.rootDir || process.cwd();
|
|
132
|
-
const persistPath = firstExisting([
|
|
133
|
-
opts.synapsePersistPath,
|
|
134
|
-
join(rootDir, ".triflux", "synapse-registry.json"),
|
|
135
|
-
join(rootDir, ".triflux", "synapse", "registry.json"),
|
|
136
|
-
join(homedir(), ".claude", "cache", "tfx-hub", "synapse-sessions.json"),
|
|
137
|
-
join(homedir(), ".claude", "cache", "tfx-hub", "synapse-registry.json"),
|
|
138
|
-
]);
|
|
139
|
-
if (!persistPath) return { sessions: [], active_shards: [] };
|
|
140
|
-
const data = JSON.parse(readFileSync(persistPath, "utf8"));
|
|
141
|
-
// 상태 전이는 hub 소유이며 조회는 저장된 live 상태만 읽는다.
|
|
142
|
-
const sessions = Object.entries(data).flatMap(([key, session]) => {
|
|
143
|
-
const sessionId = String(session?.sessionId ?? key).trim();
|
|
144
|
-
if (!sessionId || ["stale", "expired"].includes(session?.status)) return [];
|
|
145
|
-
return [
|
|
146
|
-
{
|
|
147
|
-
...session,
|
|
148
|
-
sessionId,
|
|
149
|
-
status: session?.status === "idle" ? "idle" : "active",
|
|
150
|
-
lastHeartbeat:
|
|
151
|
-
typeof session?.lastHeartbeat === "number"
|
|
152
|
-
? session.lastHeartbeat
|
|
153
|
-
: Date.now(),
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
});
|
|
157
|
-
return { sessions, active_shards: [] };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
104
|
export function readJsonLines(filePath, limit = Infinity) {
|
|
161
105
|
if (!defaultExistsSync(filePath)) return [];
|
|
162
106
|
const lines = readFileSync(filePath, "utf8")
|