triflux 3.3.0-dev.7 → 4.0.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.
Files changed (91) hide show
  1. package/README.ko.md +108 -199
  2. package/README.md +108 -199
  3. package/bin/triflux.mjs +2415 -1762
  4. package/hooks/keyword-rules.json +361 -354
  5. package/hooks/pipeline-stop.mjs +5 -2
  6. package/hub/assign-callbacks.mjs +136 -136
  7. package/hub/bridge.mjs +734 -708
  8. package/hub/delegator/contracts.mjs +38 -0
  9. package/hub/delegator/index.mjs +14 -0
  10. package/hub/delegator/schema/delegator-tools.schema.json +250 -0
  11. package/hub/delegator/service.mjs +302 -0
  12. package/hub/delegator/tool-definitions.mjs +35 -0
  13. package/hub/hitl.mjs +67 -67
  14. package/hub/paths.mjs +28 -0
  15. package/hub/pipe.mjs +589 -561
  16. package/hub/pipeline/state.mjs +23 -0
  17. package/hub/public/dashboard.html +349 -0
  18. package/hub/public/tray-icon.ico +0 -0
  19. package/hub/public/tray-icon.png +0 -0
  20. package/hub/router.mjs +782 -782
  21. package/hub/schema.sql +40 -40
  22. package/hub/server.mjs +810 -637
  23. package/hub/store.mjs +706 -706
  24. package/hub/team/cli/commands/attach.mjs +37 -0
  25. package/hub/team/cli/commands/control.mjs +43 -0
  26. package/hub/team/cli/commands/debug.mjs +74 -0
  27. package/hub/team/cli/commands/focus.mjs +53 -0
  28. package/hub/team/cli/commands/interrupt.mjs +36 -0
  29. package/hub/team/cli/commands/kill.mjs +37 -0
  30. package/hub/team/cli/commands/list.mjs +24 -0
  31. package/hub/team/cli/commands/send.mjs +37 -0
  32. package/hub/team/cli/commands/start/index.mjs +87 -0
  33. package/hub/team/cli/commands/start/parse-args.mjs +32 -0
  34. package/hub/team/cli/commands/start/start-in-process.mjs +40 -0
  35. package/hub/team/cli/commands/start/start-mux.mjs +73 -0
  36. package/hub/team/cli/commands/start/start-wt.mjs +69 -0
  37. package/hub/team/cli/commands/status.mjs +87 -0
  38. package/hub/team/cli/commands/stop.mjs +31 -0
  39. package/hub/team/cli/commands/task.mjs +30 -0
  40. package/hub/team/cli/commands/tasks.mjs +13 -0
  41. package/hub/team/{cli.mjs → cli/help.mjs} +38 -99
  42. package/hub/team/cli/index.mjs +39 -0
  43. package/hub/team/cli/manifest.mjs +28 -0
  44. package/hub/team/cli/render.mjs +30 -0
  45. package/hub/team/cli/services/attach-fallback.mjs +54 -0
  46. package/hub/team/cli/services/hub-client.mjs +171 -0
  47. package/hub/team/cli/services/member-selector.mjs +30 -0
  48. package/hub/team/cli/services/native-control.mjs +115 -0
  49. package/hub/team/cli/services/runtime-mode.mjs +60 -0
  50. package/hub/team/cli/services/state-store.mjs +34 -0
  51. package/hub/team/cli/services/task-model.mjs +30 -0
  52. package/hub/team/native-supervisor.mjs +69 -63
  53. package/hub/team/native.mjs +367 -266
  54. package/hub/team/nativeProxy.mjs +217 -173
  55. package/hub/team/pane.mjs +149 -149
  56. package/hub/team/psmux.mjs +946 -946
  57. package/hub/team/session.mjs +608 -608
  58. package/hub/team/staleState.mjs +369 -299
  59. package/hub/tools.mjs +107 -107
  60. package/hub/tray.mjs +332 -0
  61. package/hub/workers/claude-worker.mjs +446 -446
  62. package/hub/workers/codex-mcp.mjs +414 -414
  63. package/hub/workers/delegator-mcp.mjs +1045 -1045
  64. package/hub/workers/factory.mjs +21 -21
  65. package/hub/workers/gemini-worker.mjs +349 -349
  66. package/hub/workers/interface.mjs +41 -41
  67. package/package.json +61 -60
  68. package/scripts/__tests__/keyword-detector.test.mjs +234 -234
  69. package/scripts/hub-ensure.mjs +102 -101
  70. package/scripts/keyword-detector.mjs +272 -272
  71. package/scripts/keyword-rules-expander.mjs +521 -521
  72. package/scripts/lib/keyword-rules.mjs +168 -168
  73. package/scripts/lib/mcp-filter.mjs +642 -642
  74. package/scripts/lib/mcp-server-catalog.mjs +118 -118
  75. package/scripts/mcp-check.mjs +126 -126
  76. package/scripts/preflight-cache.mjs +19 -0
  77. package/scripts/run.cjs +62 -62
  78. package/scripts/setup.mjs +68 -31
  79. package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
  80. package/scripts/tfx-route-worker.mjs +161 -161
  81. package/scripts/tfx-route.sh +1360 -1326
  82. package/skills/tfx-auto/SKILL.md +196 -196
  83. package/skills/tfx-auto-codex/SKILL.md +77 -77
  84. package/skills/tfx-multi/SKILL.md +378 -378
  85. package/hub/team/cli-team-common.mjs +0 -348
  86. package/hub/team/cli-team-control.mjs +0 -393
  87. package/hub/team/cli-team-start.mjs +0 -516
  88. package/hub/team/cli-team-status.mjs +0 -283
  89. package/skills/auto-verify/SKILL.md +0 -145
  90. package/skills/manage-skills/SKILL.md +0 -192
  91. package/skills/verify-implementation/SKILL.md +0 -138
@@ -6,9 +6,12 @@
6
6
 
7
7
  import { existsSync } from 'node:fs';
8
8
  import { join } from 'node:path';
9
- import { homedir } from 'node:os';
9
+ import { fileURLToPath } from 'node:url';
10
10
 
11
- const HUB_DB_PATH = join(homedir(), '.claude', 'cache', 'tfx-hub', 'state.db');
11
+ import { getPipelineStateDbPath } from '../hub/pipeline/state.mjs';
12
+
13
+ const PROJECT_ROOT = fileURLToPath(new URL('..', import.meta.url));
14
+ const HUB_DB_PATH = getPipelineStateDbPath(PROJECT_ROOT);
12
15
  const TERMINAL = new Set(['complete', 'failed']);
13
16
 
14
17
  async function getPipelineStopPrompt() {
@@ -1,136 +1,136 @@
1
- // hub/assign-callbacks.mjs — assign job 상태 변경용 Named Pipe/Unix socket 브로드캐스터
2
-
3
- import net from 'node:net';
4
- import { existsSync, unlinkSync } from 'node:fs';
5
- import { join } from 'node:path';
6
-
7
- export function getAssignCallbackPipePath(sessionId = process.pid) {
8
- if (process.platform === 'win32') {
9
- return `\\\\.\\pipe\\triflux-assign-callback-${sessionId}`;
10
- }
11
- return join('/tmp', `triflux-assign-callback-${sessionId}.sock`);
12
- }
13
-
14
- function buildAssignCallbackEvent(event = {}, row = null) {
15
- const source = row || event || {};
16
- const updatedAtMs = Number(source.updated_at_ms);
17
- const createdAtMs = Number(source.created_at_ms);
18
- const timestampMs = Number.isFinite(updatedAtMs)
19
- ? updatedAtMs
20
- : (Number.isFinite(createdAtMs) ? createdAtMs : Date.now());
21
-
22
- return {
23
- event: 'assign_job_status',
24
- job_id: source.job_id || event.job_id || null,
25
- supervisor_agent: source.supervisor_agent || null,
26
- worker_agent: source.worker_agent || null,
27
- topic: source.topic || null,
28
- task: source.task || null,
29
- status: source.status || event.status || null,
30
- attempt: Number.isFinite(Number(source.attempt)) ? Number(source.attempt) : null,
31
- retry_count: Number.isFinite(Number(source.retry_count)) ? Number(source.retry_count) : null,
32
- max_retries: Number.isFinite(Number(source.max_retries)) ? Number(source.max_retries) : null,
33
- priority: Number.isFinite(Number(source.priority)) ? Number(source.priority) : null,
34
- ttl_ms: Number.isFinite(Number(source.ttl_ms)) ? Number(source.ttl_ms) : null,
35
- timeout_ms: Number.isFinite(Number(source.timeout_ms)) ? Number(source.timeout_ms) : null,
36
- deadline_ms: Number.isFinite(Number(source.deadline_ms)) ? Number(source.deadline_ms) : null,
37
- trace_id: source.trace_id || null,
38
- correlation_id: source.correlation_id || null,
39
- last_message_id: source.last_message_id || null,
40
- result: Object.prototype.hasOwnProperty.call(source, 'result')
41
- ? source.result
42
- : (Object.prototype.hasOwnProperty.call(event, 'result') ? event.result : null),
43
- error: Object.prototype.hasOwnProperty.call(source, 'error')
44
- ? source.error
45
- : (Object.prototype.hasOwnProperty.call(event, 'error') ? event.error : null),
46
- created_at_ms: Number.isFinite(createdAtMs) ? createdAtMs : null,
47
- updated_at_ms: Number.isFinite(updatedAtMs) ? updatedAtMs : null,
48
- started_at_ms: Number.isFinite(Number(source.started_at_ms)) ? Number(source.started_at_ms) : null,
49
- completed_at_ms: Number.isFinite(Number(source.completed_at_ms)) ? Number(source.completed_at_ms) : null,
50
- last_retry_at_ms: Number.isFinite(Number(source.last_retry_at_ms)) ? Number(source.last_retry_at_ms) : null,
51
- timestamp: new Date(timestampMs).toISOString(),
52
- };
53
- }
54
-
55
- export function createAssignCallbackServer({ store = null, sessionId = process.pid } = {}) {
56
- const pipePath = getAssignCallbackPipePath(sessionId);
57
- const clients = new Set();
58
- let server = null;
59
- let detachStoreListener = null;
60
-
61
- function removeSocket(socket) {
62
- if (!socket) return;
63
- clients.delete(socket);
64
- try { socket.destroy(); } catch {}
65
- }
66
-
67
- function broadcast(event) {
68
- const frame = `${JSON.stringify(event)}\n`;
69
- for (const socket of Array.from(clients)) {
70
- if (!socket.writable || socket.destroyed) {
71
- removeSocket(socket);
72
- continue;
73
- }
74
- try {
75
- socket.write(frame);
76
- } catch {
77
- removeSocket(socket);
78
- }
79
- }
80
- }
81
-
82
- return {
83
- path: pipePath,
84
- getStatus() {
85
- return {
86
- path: pipePath,
87
- clients: clients.size,
88
- };
89
- },
90
- async start() {
91
- if (server) return { path: pipePath };
92
- if (process.platform !== 'win32' && existsSync(pipePath)) {
93
- try { unlinkSync(pipePath); } catch {}
94
- }
95
-
96
- server = net.createServer((socket) => {
97
- clients.add(socket);
98
- socket.setEncoding('utf8');
99
- socket.on('error', () => removeSocket(socket));
100
- socket.on('close', () => removeSocket(socket));
101
- });
102
-
103
- await new Promise((resolve, reject) => {
104
- server.once('error', reject);
105
- server.listen(pipePath, () => {
106
- server?.off('error', reject);
107
- resolve();
108
- });
109
- });
110
-
111
- if (store?.onAssignStatusChange && !detachStoreListener) {
112
- detachStoreListener = store.onAssignStatusChange((event, row) => {
113
- broadcast(buildAssignCallbackEvent(event, row));
114
- });
115
- }
116
-
117
- return { path: pipePath };
118
- },
119
- async stop() {
120
- if (detachStoreListener) {
121
- try { detachStoreListener(); } catch {}
122
- detachStoreListener = null;
123
- }
124
- if (!server) return;
125
- for (const socket of Array.from(clients)) {
126
- removeSocket(socket);
127
- }
128
- await new Promise((resolve) => server.close(resolve));
129
- server = null;
130
- if (process.platform !== 'win32' && existsSync(pipePath)) {
131
- try { unlinkSync(pipePath); } catch {}
132
- }
133
- },
134
- broadcast,
135
- };
136
- }
1
+ // hub/assign-callbacks.mjs — assign job 상태 변경용 Named Pipe/Unix socket 브로드캐스터
2
+
3
+ import net from 'node:net';
4
+ import { existsSync, unlinkSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+
7
+ export function getAssignCallbackPipePath(sessionId = process.pid) {
8
+ if (process.platform === 'win32') {
9
+ return `\\\\.\\pipe\\triflux-assign-callback-${sessionId}`;
10
+ }
11
+ return join('/tmp', `triflux-assign-callback-${sessionId}.sock`);
12
+ }
13
+
14
+ function buildAssignCallbackEvent(event = {}, row = null) {
15
+ const source = row || event || {};
16
+ const updatedAtMs = Number(source.updated_at_ms);
17
+ const createdAtMs = Number(source.created_at_ms);
18
+ const timestampMs = Number.isFinite(updatedAtMs)
19
+ ? updatedAtMs
20
+ : (Number.isFinite(createdAtMs) ? createdAtMs : Date.now());
21
+
22
+ return {
23
+ event: 'assign_job_status',
24
+ job_id: source.job_id || event.job_id || null,
25
+ supervisor_agent: source.supervisor_agent || null,
26
+ worker_agent: source.worker_agent || null,
27
+ topic: source.topic || null,
28
+ task: source.task || null,
29
+ status: source.status || event.status || null,
30
+ attempt: Number.isFinite(Number(source.attempt)) ? Number(source.attempt) : null,
31
+ retry_count: Number.isFinite(Number(source.retry_count)) ? Number(source.retry_count) : null,
32
+ max_retries: Number.isFinite(Number(source.max_retries)) ? Number(source.max_retries) : null,
33
+ priority: Number.isFinite(Number(source.priority)) ? Number(source.priority) : null,
34
+ ttl_ms: Number.isFinite(Number(source.ttl_ms)) ? Number(source.ttl_ms) : null,
35
+ timeout_ms: Number.isFinite(Number(source.timeout_ms)) ? Number(source.timeout_ms) : null,
36
+ deadline_ms: Number.isFinite(Number(source.deadline_ms)) ? Number(source.deadline_ms) : null,
37
+ trace_id: source.trace_id || null,
38
+ correlation_id: source.correlation_id || null,
39
+ last_message_id: source.last_message_id || null,
40
+ result: Object.prototype.hasOwnProperty.call(source, 'result')
41
+ ? source.result
42
+ : (Object.prototype.hasOwnProperty.call(event, 'result') ? event.result : null),
43
+ error: Object.prototype.hasOwnProperty.call(source, 'error')
44
+ ? source.error
45
+ : (Object.prototype.hasOwnProperty.call(event, 'error') ? event.error : null),
46
+ created_at_ms: Number.isFinite(createdAtMs) ? createdAtMs : null,
47
+ updated_at_ms: Number.isFinite(updatedAtMs) ? updatedAtMs : null,
48
+ started_at_ms: Number.isFinite(Number(source.started_at_ms)) ? Number(source.started_at_ms) : null,
49
+ completed_at_ms: Number.isFinite(Number(source.completed_at_ms)) ? Number(source.completed_at_ms) : null,
50
+ last_retry_at_ms: Number.isFinite(Number(source.last_retry_at_ms)) ? Number(source.last_retry_at_ms) : null,
51
+ timestamp: new Date(timestampMs).toISOString(),
52
+ };
53
+ }
54
+
55
+ export function createAssignCallbackServer({ store = null, sessionId = process.pid } = {}) {
56
+ const pipePath = getAssignCallbackPipePath(sessionId);
57
+ const clients = new Set();
58
+ let server = null;
59
+ let detachStoreListener = null;
60
+
61
+ function removeSocket(socket) {
62
+ if (!socket) return;
63
+ clients.delete(socket);
64
+ try { socket.destroy(); } catch {}
65
+ }
66
+
67
+ function broadcast(event) {
68
+ const frame = `${JSON.stringify(event)}\n`;
69
+ for (const socket of Array.from(clients)) {
70
+ if (!socket.writable || socket.destroyed) {
71
+ removeSocket(socket);
72
+ continue;
73
+ }
74
+ try {
75
+ socket.write(frame);
76
+ } catch {
77
+ removeSocket(socket);
78
+ }
79
+ }
80
+ }
81
+
82
+ return {
83
+ path: pipePath,
84
+ getStatus() {
85
+ return {
86
+ path: pipePath,
87
+ clients: clients.size,
88
+ };
89
+ },
90
+ async start() {
91
+ if (server) return { path: pipePath };
92
+ if (process.platform !== 'win32' && existsSync(pipePath)) {
93
+ try { unlinkSync(pipePath); } catch {}
94
+ }
95
+
96
+ server = net.createServer((socket) => {
97
+ clients.add(socket);
98
+ socket.setEncoding('utf8');
99
+ socket.on('error', () => removeSocket(socket));
100
+ socket.on('close', () => removeSocket(socket));
101
+ });
102
+
103
+ await new Promise((resolve, reject) => {
104
+ server.once('error', reject);
105
+ server.listen(pipePath, () => {
106
+ server?.off('error', reject);
107
+ resolve();
108
+ });
109
+ });
110
+
111
+ if (store?.onAssignStatusChange && !detachStoreListener) {
112
+ detachStoreListener = store.onAssignStatusChange((event, row) => {
113
+ broadcast(buildAssignCallbackEvent(event, row));
114
+ });
115
+ }
116
+
117
+ return { path: pipePath };
118
+ },
119
+ async stop() {
120
+ if (detachStoreListener) {
121
+ try { detachStoreListener(); } catch {}
122
+ detachStoreListener = null;
123
+ }
124
+ if (!server) return;
125
+ for (const socket of Array.from(clients)) {
126
+ removeSocket(socket);
127
+ }
128
+ await new Promise((resolve) => server.close(resolve));
129
+ server = null;
130
+ if (process.platform !== 'win32' && existsSync(pipePath)) {
131
+ try { unlinkSync(pipePath); } catch {}
132
+ }
133
+ },
134
+ broadcast,
135
+ };
136
+ }