@triflux/remote 10.51.0 → 10.53.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 -8
- package/cto/collect.mjs +1 -127
- package/cto/current.schema.json +2 -19
- package/cto/hygiene.mjs +2 -61
- package/cto/lake-root.mjs +0 -56
- package/cto/status.mjs +5 -139
- package/hub/index.mjs +0 -9
- package/hub/team/agents-row-attach.py +120 -0
- package/hub/team/agents-row.mjs +200 -0
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/claude-transcript.mjs +19 -0
- package/hub/team/cli/commands/debug.mjs +1 -2
- package/hub/team/cli/commands/start/index.mjs +2 -1
- package/hub/team/cli/commands/start/start-headless.mjs +2 -0
- package/hub/team/cli/commands/start/start-mux.mjs +2 -4
- package/hub/team/cli/services/state-store.mjs +5 -20
- package/hub/team/headless.mjs +132 -551
- package/hub/team/psmux.mjs +3 -33
- package/hub/team/results-index.mjs +92 -0
- package/hub/team/session-context.mjs +1 -1
- package/hub/team/tui-viewer.mjs +9 -56
- package/hub/team/tui.mjs +0 -37
- package/hub/team/uds-orchestrator.mjs +20 -599
- package/hub/workers/factory.mjs +6 -51
- 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 -6
- package/package.json +3 -8
- package/scripts/lib/agent-route-policy.mjs +0 -67
- package/scripts/lib/cli-claude.mjs +0 -12
- package/scripts/lib/env-probe.mjs +0 -70
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/mcp-guard-engine.mjs +42 -101
- 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/cli/services/hub-client.mjs +0 -178
- package/hub/team/conductor-registry.mjs +0 -121
- package/hub/team/cto-auto-collect.mjs +0 -115
- package/hub/team/lead-control.mjs +0 -191
- package/hub/team/session-sync.mjs +0 -316
- package/hub/team/swarm-intent.mjs +0 -18
- 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-synapse.mjs +0 -223
- package/hub/team/worker-sandbox.mjs +0 -125
- package/hub/tools.mjs +0 -900
- 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 -26
- package/scripts/lib/context.mjs +0 -51
- 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");
|
|
@@ -50,7 +45,6 @@ function enforceCap(text) {
|
|
|
50
45
|
export function renderBrief(current) {
|
|
51
46
|
const repo = current?.repo || {};
|
|
52
47
|
const summary = current?.summary || {};
|
|
53
|
-
const hub = current?.sources?.tfx_hub || {};
|
|
54
48
|
const activeGoals = Array.isArray(summary.active_goals)
|
|
55
49
|
? summary.active_goals.slice(0, 3)
|
|
56
50
|
: [];
|
|
@@ -65,8 +59,6 @@ export function renderBrief(current) {
|
|
|
65
59
|
`summary: ${oneLine(summary.repo_state, "no repo summary")}`,
|
|
66
60
|
"active_goals",
|
|
67
61
|
...(activeGoals.length > 0 ? activeGoals.map(formatGoal) : ["- none"]),
|
|
68
|
-
"hub_status",
|
|
69
|
-
`status: ${oneLine(hub.status, "unknown")} available: ${hub.available === true} detail: ${compactDetail(hub.detail)}`,
|
|
70
62
|
"recent_events",
|
|
71
63
|
...(recentEvents.length > 0 ? recentEvents.map(formatEvent) : ["- none"]),
|
|
72
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,24 +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_team",
|
|
38
|
-
kind: "triflux-runtime-artifact",
|
|
39
|
-
probe: ".triflux/team-state.json or host tfx-hub team cache",
|
|
40
|
-
enabled: true,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "tfx_synapse",
|
|
44
|
-
kind: "triflux-runtime-artifact",
|
|
45
|
-
probe: ".triflux/synapse registry or host tfx-hub synapse cache",
|
|
46
|
-
enabled: true,
|
|
47
|
-
},
|
|
48
28
|
{
|
|
49
29
|
id: "ultragoal_omx",
|
|
50
30
|
kind: "durable-goal-artifact",
|
|
@@ -121,14 +101,6 @@ function listDirFiles(dirPath, rootDir, limit = 10) {
|
|
|
121
101
|
}
|
|
122
102
|
}
|
|
123
103
|
|
|
124
|
-
function safeSummary(value) {
|
|
125
|
-
if (value === null || value === undefined) return null;
|
|
126
|
-
if (Array.isArray(value)) return { type: "array", count: value.length };
|
|
127
|
-
if (typeof value === "object")
|
|
128
|
-
return { type: "object", keys: Object.keys(value).slice(0, 20) };
|
|
129
|
-
return String(value).slice(0, 200);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
104
|
function execGit(args, rootDir, execFileSyncFn) {
|
|
133
105
|
return execFileSyncFn("git", args, {
|
|
134
106
|
cwd: rootDir,
|
|
@@ -183,45 +155,6 @@ function collectGit(rootDir, collectedAt, execFileSyncFn) {
|
|
|
183
155
|
}
|
|
184
156
|
}
|
|
185
157
|
|
|
186
|
-
function collectJsonArtifact(
|
|
187
|
-
id,
|
|
188
|
-
candidates,
|
|
189
|
-
rootDir,
|
|
190
|
-
collectedAt,
|
|
191
|
-
summarize = safeSummary,
|
|
192
|
-
) {
|
|
193
|
-
const filePath = firstExisting(candidates);
|
|
194
|
-
if (!filePath) {
|
|
195
|
-
return missingSource(
|
|
196
|
-
"no_shell_readable_artifact",
|
|
197
|
-
"no durable artifact found",
|
|
198
|
-
collectedAt,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
const parsed = readJson(filePath);
|
|
203
|
-
return sourceState(
|
|
204
|
-
true,
|
|
205
|
-
"ok",
|
|
206
|
-
{
|
|
207
|
-
path: relPath(rootDir, filePath),
|
|
208
|
-
summary: summarize(parsed),
|
|
209
|
-
},
|
|
210
|
-
collectedAt,
|
|
211
|
-
);
|
|
212
|
-
} catch (error) {
|
|
213
|
-
return sourceState(
|
|
214
|
-
true,
|
|
215
|
-
"read_error",
|
|
216
|
-
{
|
|
217
|
-
path: relPath(rootDir, filePath),
|
|
218
|
-
error: error?.message || `${id} artifact read failed`,
|
|
219
|
-
},
|
|
220
|
-
collectedAt,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
158
|
function normalizeGoals(parsed) {
|
|
226
159
|
const rawGoals = Array.isArray(parsed?.goals)
|
|
227
160
|
? parsed.goals
|
|
@@ -329,7 +262,6 @@ function buildSummary(current) {
|
|
|
329
262
|
return {
|
|
330
263
|
repo_state: `branch ${repo.branch || "unknown"} at ${repo.head || "unknown"} is ${repo.dirty ? "dirty" : "clean"}`,
|
|
331
264
|
active_goals: [...omxGoals, ...omcGoals].slice(0, 5),
|
|
332
|
-
hub_status: current.sources.tfx_hub.status,
|
|
333
265
|
available_sources: availableSources,
|
|
334
266
|
missing_sources: SOURCE_IDS.length - availableSources,
|
|
335
267
|
};
|
|
@@ -467,67 +399,10 @@ function validateCurrent(current) {
|
|
|
467
399
|
}
|
|
468
400
|
}
|
|
469
401
|
|
|
470
|
-
function collectSources(rootDir, collectedAt, execFileSyncFn
|
|
471
|
-
const home = homedir();
|
|
472
|
-
const hostArtifactCandidates =
|
|
473
|
-
opts.includeHostArtifacts === false
|
|
474
|
-
? {
|
|
475
|
-
hub: [],
|
|
476
|
-
team: [],
|
|
477
|
-
synapse: [],
|
|
478
|
-
}
|
|
479
|
-
: {
|
|
480
|
-
hub: [
|
|
481
|
-
join(home, ".claude", "cache", "tfx-hub", "hub.pid"),
|
|
482
|
-
join(home, ".claude", "cache", "tfx-hub", "hub-state.json"),
|
|
483
|
-
],
|
|
484
|
-
team: [
|
|
485
|
-
join(
|
|
486
|
-
home,
|
|
487
|
-
".claude",
|
|
488
|
-
"cache",
|
|
489
|
-
"tfx-hub",
|
|
490
|
-
`team-state-${process.env.CLAUDE_SESSION_ID || ""}.json`,
|
|
491
|
-
),
|
|
492
|
-
join(home, ".claude", "cache", "tfx-hub", "team-state.json"),
|
|
493
|
-
],
|
|
494
|
-
synapse: [
|
|
495
|
-
join(home, ".claude", "cache", "tfx-hub", "synapse-registry.json"),
|
|
496
|
-
join(home, ".claude", "cache", "tfx-hub", "synapse-sessions.json"),
|
|
497
|
-
],
|
|
498
|
-
};
|
|
402
|
+
function collectSources(rootDir, collectedAt, execFileSyncFn) {
|
|
499
403
|
const git = collectGit(rootDir, collectedAt, execFileSyncFn);
|
|
500
404
|
const sources = {
|
|
501
405
|
git: git.source,
|
|
502
|
-
tfx_hub: collectJsonArtifact(
|
|
503
|
-
"tfx_hub",
|
|
504
|
-
[
|
|
505
|
-
join(rootDir, ".triflux", "hub", "status.json"),
|
|
506
|
-
join(rootDir, ".triflux", "hub-state.json"),
|
|
507
|
-
...hostArtifactCandidates.hub,
|
|
508
|
-
],
|
|
509
|
-
rootDir,
|
|
510
|
-
collectedAt,
|
|
511
|
-
),
|
|
512
|
-
tfx_team: collectJsonArtifact(
|
|
513
|
-
"tfx_team",
|
|
514
|
-
[
|
|
515
|
-
join(rootDir, ".triflux", "team-state.json"),
|
|
516
|
-
...hostArtifactCandidates.team,
|
|
517
|
-
],
|
|
518
|
-
rootDir,
|
|
519
|
-
collectedAt,
|
|
520
|
-
),
|
|
521
|
-
tfx_synapse: collectJsonArtifact(
|
|
522
|
-
"tfx_synapse",
|
|
523
|
-
[
|
|
524
|
-
join(rootDir, ".triflux", "synapse-registry.json"),
|
|
525
|
-
join(rootDir, ".triflux", "synapse", "registry.json"),
|
|
526
|
-
...hostArtifactCandidates.synapse,
|
|
527
|
-
],
|
|
528
|
-
rootDir,
|
|
529
|
-
collectedAt,
|
|
530
|
-
),
|
|
531
406
|
ultragoal_omx: collectUltragoal(
|
|
532
407
|
join(rootDir, ".omx", "ultragoal"),
|
|
533
408
|
rootDir,
|
|
@@ -568,7 +443,6 @@ export async function runCollect(args = [], opts = {}) {
|
|
|
568
443
|
rootDir,
|
|
569
444
|
generatedAt,
|
|
570
445
|
execFileSyncFn,
|
|
571
|
-
opts,
|
|
572
446
|
);
|
|
573
447
|
const current = {
|
|
574
448
|
schema_version: SCHEMA_VERSION,
|
package/cto/current.schema.json
CHANGED
|
@@ -46,28 +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_team",
|
|
53
|
-
"tfx_synapse",
|
|
54
|
-
"ultragoal_omx",
|
|
55
|
-
"ultragoal_omc",
|
|
56
|
-
"handoffs"
|
|
57
|
-
],
|
|
49
|
+
"required": ["git", "ultragoal_omx", "ultragoal_omc", "handoffs"],
|
|
58
50
|
"properties": {
|
|
59
51
|
"git": {
|
|
60
52
|
"$ref": "#/$defs/sourceState"
|
|
61
53
|
},
|
|
62
|
-
"tfx_hub": {
|
|
63
|
-
"$ref": "#/$defs/sourceState"
|
|
64
|
-
},
|
|
65
|
-
"tfx_team": {
|
|
66
|
-
"$ref": "#/$defs/sourceState"
|
|
67
|
-
},
|
|
68
|
-
"tfx_synapse": {
|
|
69
|
-
"$ref": "#/$defs/sourceState"
|
|
70
|
-
},
|
|
71
54
|
"ultragoal_omx": {
|
|
72
55
|
"$ref": "#/$defs/sourceState"
|
|
73
56
|
},
|
|
@@ -81,7 +64,7 @@
|
|
|
81
64
|
},
|
|
82
65
|
"summary": {
|
|
83
66
|
"type": "object",
|
|
84
|
-
"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.",
|
|
85
68
|
"additionalProperties": true
|
|
86
69
|
},
|
|
87
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")
|
package/cto/status.mjs
CHANGED
|
@@ -2,16 +2,9 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { compactHygieneCounts, projectCtoHygiene } from "./hygiene.mjs";
|
|
5
|
-
import {
|
|
6
|
-
normalizeLiveSession,
|
|
7
|
-
pathLabel,
|
|
8
|
-
readSynapseSnapshot,
|
|
9
|
-
resolveLakeRootDir,
|
|
10
|
-
shortHash,
|
|
11
|
-
} from "./lake-root.mjs";
|
|
5
|
+
import { resolveLakeRootDir } from "./lake-root.mjs";
|
|
12
6
|
|
|
13
7
|
const SCHEMA_VERSION = "cto-lake.v1";
|
|
14
|
-
const SYNAPSE_TIMEOUT_MS = 1500;
|
|
15
8
|
|
|
16
9
|
function hasFlag(args, flag) {
|
|
17
10
|
return Array.isArray(args) && args.includes(flag);
|
|
@@ -25,126 +18,7 @@ function readJson(filePath) {
|
|
|
25
18
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
26
19
|
}
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
if (typeof cwd !== "string" || !cwd) return "";
|
|
30
|
-
if (cwd.includes("\\") || /^[A-Za-z]:/u.test(cwd)) return cwd;
|
|
31
|
-
|
|
32
|
-
const normalized = cwd.replace(/\/+$/u, "");
|
|
33
|
-
const gitWorktreeMarker = "/.worktrees/";
|
|
34
|
-
const gitWorktreeIndex = normalized.indexOf(gitWorktreeMarker);
|
|
35
|
-
if (gitWorktreeIndex > 0) {
|
|
36
|
-
const suffix = normalized.slice(
|
|
37
|
-
gitWorktreeIndex + gitWorktreeMarker.length,
|
|
38
|
-
);
|
|
39
|
-
if (/^[^/]+(?:\/|$)/u.test(suffix)) {
|
|
40
|
-
return normalized.slice(0, gitWorktreeIndex);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const claudeMarker = "/.claude/worktrees/";
|
|
45
|
-
const claudeIndex = normalized.indexOf(claudeMarker);
|
|
46
|
-
if (claudeIndex > 0) {
|
|
47
|
-
const suffix = normalized.slice(claudeIndex + claudeMarker.length);
|
|
48
|
-
if (/^[^/]+(?:\/|$)/u.test(suffix)) {
|
|
49
|
-
return normalized.slice(0, claudeIndex);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const codexMarker = "/.codex-swarm/";
|
|
54
|
-
const codexIndex = normalized.indexOf(codexMarker);
|
|
55
|
-
if (codexIndex > 0) {
|
|
56
|
-
const suffix = normalized.slice(codexIndex + codexMarker.length);
|
|
57
|
-
if (/^wt-[^/]+(?:\/|$)/u.test(suffix)) {
|
|
58
|
-
return normalized.slice(0, codexIndex);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return cwd;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function redactedCwdFields(cwd) {
|
|
66
|
-
if (typeof cwd !== "string" || !cwd) return {};
|
|
67
|
-
const repoRoot = deriveRepoRootFromCwd(cwd);
|
|
68
|
-
return {
|
|
69
|
-
cwdLabel: pathLabel(cwd),
|
|
70
|
-
cwdHash: shortHash(cwd),
|
|
71
|
-
repoRootLabel: pathLabel(repoRoot),
|
|
72
|
-
repoRootHash: shortHash(repoRoot),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function normalizeSynapseOverlay(value) {
|
|
77
|
-
const sessions = Array.isArray(value)
|
|
78
|
-
? value
|
|
79
|
-
: Array.isArray(value?.sessions)
|
|
80
|
-
? value.sessions
|
|
81
|
-
: Array.isArray(value?.live_sessions)
|
|
82
|
-
? value.live_sessions
|
|
83
|
-
: [];
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
live_sessions: sessions
|
|
87
|
-
.map((session) => ({
|
|
88
|
-
...normalizeLiveSession(session),
|
|
89
|
-
host: typeof session?.host === "string" ? session.host : "local",
|
|
90
|
-
...redactedCwdFields(session?.cwd),
|
|
91
|
-
}))
|
|
92
|
-
.filter((session) => session.sessionId),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function withTimeout(promise, timeoutMs = SYNAPSE_TIMEOUT_MS) {
|
|
97
|
-
let timer = null;
|
|
98
|
-
const timeout = new Promise((_, reject) => {
|
|
99
|
-
timer = setTimeout(
|
|
100
|
-
() => reject(new Error("synapse read timeout")),
|
|
101
|
-
timeoutMs,
|
|
102
|
-
);
|
|
103
|
-
if (typeof timer.unref === "function") timer.unref();
|
|
104
|
-
});
|
|
105
|
-
return Promise.race([Promise.resolve(promise), timeout]).finally(() => {
|
|
106
|
-
if (timer) clearTimeout(timer);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function readSynapseOverlay(opts) {
|
|
111
|
-
try {
|
|
112
|
-
const reader = opts.synapseReader || readSynapseSnapshot;
|
|
113
|
-
const raw = await withTimeout(
|
|
114
|
-
reader({
|
|
115
|
-
rootDir: opts.rootDir,
|
|
116
|
-
lakeRoot: opts.lakeRoot,
|
|
117
|
-
synapsePersistPath: opts.synapsePersistPath,
|
|
118
|
-
}),
|
|
119
|
-
opts.synapseTimeoutMs || SYNAPSE_TIMEOUT_MS,
|
|
120
|
-
);
|
|
121
|
-
return normalizeSynapseOverlay(raw);
|
|
122
|
-
} catch {
|
|
123
|
-
return { live_sessions: [] };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function deriveLiveSessionGroups(liveSessions) {
|
|
128
|
-
const groups = new Map();
|
|
129
|
-
for (const session of liveSessions || []) {
|
|
130
|
-
if (!session?.repoRootHash) continue;
|
|
131
|
-
if (!groups.has(session.repoRootHash)) {
|
|
132
|
-
groups.set(session.repoRootHash, {
|
|
133
|
-
repoRootLabel: session.repoRootLabel || "",
|
|
134
|
-
repoRootHash: session.repoRootHash,
|
|
135
|
-
session_count: 0,
|
|
136
|
-
sessions: [],
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
const group = groups.get(session.repoRootHash);
|
|
140
|
-
group.session_count += 1;
|
|
141
|
-
group.sessions.push(session.sessionId);
|
|
142
|
-
}
|
|
143
|
-
return [...groups.values()];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function projectStatus(current, overlay) {
|
|
147
|
-
const liveSessions = overlay.live_sessions;
|
|
21
|
+
function projectStatus(current) {
|
|
148
22
|
return {
|
|
149
23
|
schema_version: current?.schema_version || SCHEMA_VERSION,
|
|
150
24
|
generated_at: current?.generated_at || null,
|
|
@@ -152,10 +26,7 @@ function projectStatus(current, overlay) {
|
|
|
152
26
|
sources: current?.sources || {},
|
|
153
27
|
summary: current?.summary || {},
|
|
154
28
|
ledger_tail: Array.isArray(current?.ledger_tail) ? current.ledger_tail : [],
|
|
155
|
-
|
|
156
|
-
live_session_groups: deriveLiveSessionGroups(liveSessions),
|
|
157
|
-
active_shards: [],
|
|
158
|
-
hygiene: compactHygieneCounts(projectCtoHygiene({ current, overlay })),
|
|
29
|
+
hygiene: compactHygieneCounts(projectCtoHygiene({ current })),
|
|
159
30
|
};
|
|
160
31
|
}
|
|
161
32
|
|
|
@@ -214,7 +85,7 @@ function renderHumanStatus(status, now) {
|
|
|
214
85
|
`generated_at: ${status.generated_at || "unknown"} (${formatSnapshotAge(status.generated_at, now)})`,
|
|
215
86
|
`repo: ${formatRepo(status.repo)}`,
|
|
216
87
|
`sources: ${available}/${total} available`,
|
|
217
|
-
`active: ${
|
|
88
|
+
`active: ${activeGoals} goals`,
|
|
218
89
|
];
|
|
219
90
|
if (recent.length) {
|
|
220
91
|
lines.push("ledger:");
|
|
@@ -244,12 +115,7 @@ export async function runStatus(args = [], opts = {}) {
|
|
|
244
115
|
}
|
|
245
116
|
|
|
246
117
|
const current = readJson(currentPath);
|
|
247
|
-
const
|
|
248
|
-
...opts,
|
|
249
|
-
rootDir,
|
|
250
|
-
lakeRoot,
|
|
251
|
-
});
|
|
252
|
-
const status = projectStatus(current, overlay);
|
|
118
|
+
const status = projectStatus(current);
|
|
253
119
|
|
|
254
120
|
if (jsonOut) writeJson(stdout, status);
|
|
255
121
|
else stdout.write(renderHumanStatus(status, opts.now ?? Date.now()));
|
package/hub/index.mjs
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
// @triflux/remote — barrel export (auto-generated by pack.mjs)
|
|
2
|
-
// Server
|
|
3
|
-
export { startHub, getHubInfo, getOrCreateServer } from './server.mjs';
|
|
4
|
-
|
|
5
|
-
// Store
|
|
6
|
-
export { createStoreAdapter } from './store-adapter.mjs';
|
|
7
|
-
|
|
8
|
-
// Pipe
|
|
9
|
-
export { createPipeServer, getPipePath } from './pipe.mjs';
|
|
10
|
-
|
|
11
2
|
// Team
|
|
12
3
|
export { orchestrate, decomposeTask, buildLeadPrompt, buildPrompt } from './team/orchestrator.mjs';
|
|
13
4
|
export { createPsmuxSession, startCapture } from './team/psmux.mjs';
|