triflux 3.3.0-dev.3 → 3.3.0-dev.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hub/assign-callbacks.mjs +136 -0
- package/hub/bridge.mjs +283 -97
- package/hub/pipe.mjs +81 -0
- package/hub/server.mjs +56 -53
- package/hub/store.mjs +36 -2
- package/hub/team/cli-team-status.mjs +17 -3
- package/hub/team/native-supervisor.mjs +62 -22
- package/hub/team/native.mjs +266 -200
- package/hub/workers/delegator-mcp.mjs +285 -140
- package/package.json +60 -60
- package/scripts/lib/mcp-filter.mjs +637 -0
- package/scripts/lib/mcp-server-catalog.mjs +118 -0
- package/scripts/mcp-check.mjs +126 -88
- package/scripts/test-tfx-route-no-claude-native.mjs +10 -2
- package/scripts/tfx-route.sh +434 -179
package/hub/bridge.mjs
CHANGED
|
@@ -16,6 +16,9 @@ const HUB_TOKEN_FILE = join(homedir(), '.claude', '.tfx-hub-token');
|
|
|
16
16
|
|
|
17
17
|
// Hub 인증 토큰 읽기 (파일 없으면 null → 하위 호환)
|
|
18
18
|
function readHubToken() {
|
|
19
|
+
if (process.env.TFX_HUB_TOKEN) {
|
|
20
|
+
return String(process.env.TFX_HUB_TOKEN).trim();
|
|
21
|
+
}
|
|
19
22
|
try {
|
|
20
23
|
return readFileSync(HUB_TOKEN_FILE, 'utf8').trim();
|
|
21
24
|
} catch {
|
|
@@ -51,21 +54,45 @@ export function getHubPipePath() {
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
const HUB_OPERATIONS = Object.freeze({
|
|
58
|
+
register: { transport: 'command', action: 'register', httpPath: '/bridge/register' },
|
|
59
|
+
result: { transport: 'command', action: 'result', httpPath: '/bridge/result' },
|
|
60
|
+
control: { transport: 'command', action: 'control', httpPath: '/bridge/control' },
|
|
61
|
+
context: { transport: 'query', action: 'drain', httpPath: '/bridge/context' },
|
|
62
|
+
deregister: { transport: 'command', action: 'deregister', httpPath: '/bridge/deregister' },
|
|
63
|
+
assignAsync: { transport: 'command', action: 'assign', httpPath: '/bridge/assign/async' },
|
|
64
|
+
assignResult: { transport: 'command', action: 'assign_result', httpPath: '/bridge/assign/result' },
|
|
65
|
+
assignStatus: { transport: 'query', action: 'assign_status', httpPath: '/bridge/assign/status' },
|
|
66
|
+
assignRetry: { transport: 'command', action: 'assign_retry', httpPath: '/bridge/assign/retry' },
|
|
67
|
+
teamInfo: { transport: 'query', action: 'team_info', httpPath: '/bridge/team/info' },
|
|
68
|
+
teamTaskList: { transport: 'query', action: 'team_task_list', httpPath: '/bridge/team/task-list' },
|
|
69
|
+
teamTaskUpdate: { transport: 'command', action: 'team_task_update', httpPath: '/bridge/team/task-update' },
|
|
70
|
+
teamSendMessage: { transport: 'command', action: 'team_send_message', httpPath: '/bridge/team/send-message' },
|
|
71
|
+
pipelineState: { transport: 'query', action: 'pipeline_state', httpPath: '/bridge/pipeline/state' },
|
|
72
|
+
pipelineAdvance: { transport: 'command', action: 'pipeline_advance', httpPath: '/bridge/pipeline/advance' },
|
|
73
|
+
pipelineInit: { transport: 'command', action: 'pipeline_init', httpPath: '/bridge/pipeline/init' },
|
|
74
|
+
pipelineList: { transport: 'query', action: 'pipeline_list', httpPath: '/bridge/pipeline/list' },
|
|
75
|
+
hubStatus: { transport: 'query', action: 'status', httpPath: '/status', httpMethod: 'GET' },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export async function requestJson(path, { method = 'POST', body, timeoutMs = 5000 } = {}) {
|
|
55
79
|
const controller = new AbortController();
|
|
56
80
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
57
81
|
|
|
58
82
|
try {
|
|
59
|
-
const headers = {
|
|
83
|
+
const headers = {};
|
|
60
84
|
const token = readHubToken();
|
|
61
85
|
if (token) {
|
|
62
86
|
headers['Authorization'] = `Bearer ${token}`;
|
|
63
87
|
}
|
|
88
|
+
if (body !== undefined) {
|
|
89
|
+
headers['Content-Type'] = 'application/json';
|
|
90
|
+
}
|
|
64
91
|
|
|
65
92
|
const res = await fetch(`${getHubUrl()}${path}`, {
|
|
66
|
-
method
|
|
93
|
+
method,
|
|
67
94
|
headers,
|
|
68
|
-
body: JSON.stringify(body),
|
|
95
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
69
96
|
signal: controller.signal,
|
|
70
97
|
});
|
|
71
98
|
clearTimeout(timer);
|
|
@@ -76,6 +103,10 @@ export async function post(path, body, timeoutMs = 5000) {
|
|
|
76
103
|
}
|
|
77
104
|
}
|
|
78
105
|
|
|
106
|
+
export async function post(path, body, timeoutMs = 5000) {
|
|
107
|
+
return await requestJson(path, { method: 'POST', body, timeoutMs });
|
|
108
|
+
}
|
|
109
|
+
|
|
79
110
|
export async function connectPipe(timeoutMs = 1200) {
|
|
80
111
|
const pipePath = getHubPipePath();
|
|
81
112
|
if (!pipePath) return null;
|
|
@@ -108,12 +139,14 @@ async function pipeRequest(type, action, payload, timeoutMs = 3000) {
|
|
|
108
139
|
return await new Promise((resolve) => {
|
|
109
140
|
const requestId = randomUUID();
|
|
110
141
|
let buffer = '';
|
|
142
|
+
let settled = false;
|
|
111
143
|
const timer = setTimeout(() => {
|
|
112
|
-
|
|
113
|
-
resolve(null);
|
|
144
|
+
finish(null);
|
|
114
145
|
}, timeoutMs);
|
|
115
146
|
|
|
116
147
|
const finish = (result) => {
|
|
148
|
+
if (settled) return;
|
|
149
|
+
settled = true;
|
|
117
150
|
clearTimeout(timer);
|
|
118
151
|
try { socket.end(); } catch {}
|
|
119
152
|
resolve(result);
|
|
@@ -172,6 +205,7 @@ export function parseArgs(argv) {
|
|
|
172
205
|
topics: { type: 'string' },
|
|
173
206
|
capabilities: { type: 'string' },
|
|
174
207
|
file: { type: 'string' },
|
|
208
|
+
payload: { type: 'string' },
|
|
175
209
|
topic: { type: 'string' },
|
|
176
210
|
trace: { type: 'string' },
|
|
177
211
|
correlation: { type: 'string' },
|
|
@@ -180,20 +214,37 @@ export function parseArgs(argv) {
|
|
|
180
214
|
out: { type: 'string' },
|
|
181
215
|
team: { type: 'string' },
|
|
182
216
|
'task-id': { type: 'string' },
|
|
217
|
+
'job-id': { type: 'string' },
|
|
183
218
|
owner: { type: 'string' },
|
|
184
219
|
status: { type: 'string' },
|
|
185
220
|
statuses: { type: 'string' },
|
|
186
221
|
claim: { type: 'boolean' },
|
|
187
222
|
actor: { type: 'string' },
|
|
223
|
+
command: { type: 'string' },
|
|
224
|
+
reason: { type: 'string' },
|
|
188
225
|
from: { type: 'string' },
|
|
189
226
|
to: { type: 'string' },
|
|
190
227
|
text: { type: 'string' },
|
|
228
|
+
task: { type: 'string' },
|
|
229
|
+
'supervisor-agent': { type: 'string' },
|
|
230
|
+
'worker-agent': { type: 'string' },
|
|
231
|
+
priority: { type: 'string' },
|
|
232
|
+
'ttl-ms': { type: 'string' },
|
|
233
|
+
'timeout-ms': { type: 'string' },
|
|
234
|
+
'max-retries': { type: 'string' },
|
|
235
|
+
attempt: { type: 'string' },
|
|
236
|
+
result: { type: 'string' },
|
|
237
|
+
error: { type: 'string' },
|
|
238
|
+
metadata: { type: 'string' },
|
|
239
|
+
'requested-by': { type: 'string' },
|
|
191
240
|
summary: { type: 'string' },
|
|
192
241
|
color: { type: 'string' },
|
|
193
242
|
limit: { type: 'string' },
|
|
194
243
|
'include-internal': { type: 'boolean' },
|
|
195
244
|
subject: { type: 'string' },
|
|
196
245
|
description: { type: 'string' },
|
|
246
|
+
'fix-max': { type: 'string' },
|
|
247
|
+
'ralph-max': { type: 'string' },
|
|
197
248
|
'active-form': { type: 'string' },
|
|
198
249
|
'add-blocks': { type: 'string' },
|
|
199
250
|
'add-blocked-by': { type: 'string' },
|
|
@@ -214,18 +265,35 @@ export function parseJsonSafe(raw, fallback = null) {
|
|
|
214
265
|
}
|
|
215
266
|
}
|
|
216
267
|
|
|
217
|
-
async function
|
|
218
|
-
const viaPipe =
|
|
219
|
-
? await pipeCommand(
|
|
220
|
-
: await pipeQuery(
|
|
221
|
-
if (viaPipe)
|
|
222
|
-
|
|
268
|
+
async function requestHub(operation, body, timeoutMs = 3000, fallback = null) {
|
|
269
|
+
const viaPipe = operation.transport === 'command'
|
|
270
|
+
? await pipeCommand(operation.action, body, timeoutMs)
|
|
271
|
+
: await pipeQuery(operation.action, body, timeoutMs);
|
|
272
|
+
if (viaPipe) {
|
|
273
|
+
return { transport: 'pipe', result: viaPipe };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const viaHttp = operation.httpPath
|
|
277
|
+
? await requestJson(operation.httpPath, {
|
|
278
|
+
method: operation.httpMethod || 'POST',
|
|
279
|
+
body: operation.httpMethod === 'GET' ? undefined : body,
|
|
280
|
+
timeoutMs: Math.max(timeoutMs, 5000),
|
|
281
|
+
})
|
|
282
|
+
: null;
|
|
283
|
+
if (viaHttp) {
|
|
284
|
+
return { transport: 'http', result: viaHttp };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!fallback) return null;
|
|
288
|
+
const viaFallback = await fallback();
|
|
289
|
+
if (!viaFallback) return null;
|
|
290
|
+
return { transport: 'fallback', result: viaFallback };
|
|
223
291
|
}
|
|
224
292
|
|
|
225
293
|
async function cmdRegister(args) {
|
|
226
294
|
const agentId = args.agent;
|
|
227
295
|
const timeoutSec = parseInt(args.timeout || '600', 10);
|
|
228
|
-
const
|
|
296
|
+
const outcome = await requestHub(HUB_OPERATIONS.register, {
|
|
229
297
|
agent_id: agentId,
|
|
230
298
|
cli: args.cli || 'other',
|
|
231
299
|
timeout_sec: timeoutSec,
|
|
@@ -237,6 +305,7 @@ async function cmdRegister(args) {
|
|
|
237
305
|
registered_at: Date.now(),
|
|
238
306
|
},
|
|
239
307
|
});
|
|
308
|
+
const result = outcome?.result;
|
|
240
309
|
|
|
241
310
|
if (result?.ok) {
|
|
242
311
|
console.log(JSON.stringify({
|
|
@@ -256,20 +325,23 @@ async function cmdResult(args) {
|
|
|
256
325
|
output = readFileSync(args.file, 'utf8').slice(0, 49152);
|
|
257
326
|
}
|
|
258
327
|
|
|
259
|
-
const
|
|
328
|
+
const defaultPayload = {
|
|
329
|
+
agent_id: args.agent,
|
|
330
|
+
exit_code: parseInt(args['exit-code'] || '0', 10),
|
|
331
|
+
output_length: output.length,
|
|
332
|
+
output_preview: output.slice(0, 4096),
|
|
333
|
+
output_file: args.file || null,
|
|
334
|
+
completed_at: Date.now(),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const outcome = await requestHub(HUB_OPERATIONS.result, {
|
|
260
338
|
agent_id: args.agent,
|
|
261
339
|
topic: args.topic || 'task.result',
|
|
262
|
-
payload:
|
|
263
|
-
agent_id: args.agent,
|
|
264
|
-
exit_code: parseInt(args['exit-code'] || '0', 10),
|
|
265
|
-
output_length: output.length,
|
|
266
|
-
output_preview: output.slice(0, 4096),
|
|
267
|
-
output_file: args.file || null,
|
|
268
|
-
completed_at: Date.now(),
|
|
269
|
-
},
|
|
340
|
+
payload: args.payload ? parseJsonSafe(args.payload, defaultPayload) : defaultPayload,
|
|
270
341
|
trace_id: args.trace || undefined,
|
|
271
342
|
correlation_id: args.correlation || undefined,
|
|
272
343
|
});
|
|
344
|
+
const result = outcome?.result;
|
|
273
345
|
|
|
274
346
|
if (result?.ok) {
|
|
275
347
|
console.log(JSON.stringify({ ok: true, message_id: result.data?.message_id }));
|
|
@@ -278,13 +350,33 @@ async function cmdResult(args) {
|
|
|
278
350
|
}
|
|
279
351
|
}
|
|
280
352
|
|
|
353
|
+
async function cmdControl(args) {
|
|
354
|
+
const outcome = await requestHub(HUB_OPERATIONS.control, {
|
|
355
|
+
from_agent: args.from || 'lead',
|
|
356
|
+
to_agent: args.to,
|
|
357
|
+
command: args.command,
|
|
358
|
+
reason: args.reason || '',
|
|
359
|
+
payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
|
|
360
|
+
trace_id: args.trace || undefined,
|
|
361
|
+
correlation_id: args.correlation || undefined,
|
|
362
|
+
ttl_ms: args['ttl-ms'] != null ? Number(args['ttl-ms']) : undefined,
|
|
363
|
+
});
|
|
364
|
+
const result = outcome?.result;
|
|
365
|
+
if (result) {
|
|
366
|
+
console.log(JSON.stringify(result));
|
|
367
|
+
} else {
|
|
368
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
281
372
|
async function cmdContext(args) {
|
|
282
|
-
const
|
|
373
|
+
const outcome = await requestHub(HUB_OPERATIONS.context, {
|
|
283
374
|
agent_id: args.agent,
|
|
284
375
|
topics: args.topics ? args.topics.split(',') : undefined,
|
|
285
376
|
max_messages: parseInt(args.max || '10', 10),
|
|
286
377
|
auto_ack: true,
|
|
287
378
|
});
|
|
379
|
+
const result = outcome?.result;
|
|
288
380
|
|
|
289
381
|
if (result?.ok && result.data?.messages?.length) {
|
|
290
382
|
const parts = result.data.messages.map((message, index) => {
|
|
@@ -308,9 +400,10 @@ async function cmdContext(args) {
|
|
|
308
400
|
}
|
|
309
401
|
|
|
310
402
|
async function cmdDeregister(args) {
|
|
311
|
-
const
|
|
403
|
+
const outcome = await requestHub(HUB_OPERATIONS.deregister, {
|
|
312
404
|
agent_id: args.agent,
|
|
313
405
|
});
|
|
406
|
+
const result = outcome?.result;
|
|
314
407
|
|
|
315
408
|
if (result?.ok) {
|
|
316
409
|
console.log(JSON.stringify({ ok: true, agent_id: args.agent, status: 'offline' }));
|
|
@@ -319,20 +412,87 @@ async function cmdDeregister(args) {
|
|
|
319
412
|
}
|
|
320
413
|
}
|
|
321
414
|
|
|
415
|
+
async function cmdAssignAsync(args) {
|
|
416
|
+
const outcome = await requestHub(HUB_OPERATIONS.assignAsync, {
|
|
417
|
+
supervisor_agent: args['supervisor-agent'],
|
|
418
|
+
worker_agent: args['worker-agent'],
|
|
419
|
+
task: args.task,
|
|
420
|
+
topic: args.topic || 'assign.job',
|
|
421
|
+
payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
|
|
422
|
+
priority: args.priority != null ? Number(args.priority) : undefined,
|
|
423
|
+
ttl_ms: args['ttl-ms'] != null ? Number(args['ttl-ms']) : undefined,
|
|
424
|
+
timeout_ms: args['timeout-ms'] != null ? Number(args['timeout-ms']) : undefined,
|
|
425
|
+
max_retries: args['max-retries'] != null ? Number(args['max-retries']) : undefined,
|
|
426
|
+
trace_id: args.trace || undefined,
|
|
427
|
+
correlation_id: args.correlation || undefined,
|
|
428
|
+
});
|
|
429
|
+
const result = outcome?.result;
|
|
430
|
+
if (result) {
|
|
431
|
+
console.log(JSON.stringify(result));
|
|
432
|
+
} else {
|
|
433
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function cmdAssignResult(args) {
|
|
438
|
+
const outcome = await requestHub(HUB_OPERATIONS.assignResult, {
|
|
439
|
+
job_id: args['job-id'],
|
|
440
|
+
worker_agent: args['worker-agent'],
|
|
441
|
+
status: args.status,
|
|
442
|
+
attempt: args.attempt != null ? Number(args.attempt) : undefined,
|
|
443
|
+
result: args.result ? parseJsonSafe(args.result, null) : undefined,
|
|
444
|
+
error: args.error ? parseJsonSafe(args.error, null) : undefined,
|
|
445
|
+
payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
|
|
446
|
+
metadata: args.metadata ? parseJsonSafe(args.metadata, {}) : {},
|
|
447
|
+
});
|
|
448
|
+
const result = outcome?.result;
|
|
449
|
+
if (result) {
|
|
450
|
+
console.log(JSON.stringify(result));
|
|
451
|
+
} else {
|
|
452
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function cmdAssignStatus(args) {
|
|
457
|
+
const outcome = await requestHub(HUB_OPERATIONS.assignStatus, {
|
|
458
|
+
job_id: args['job-id'],
|
|
459
|
+
});
|
|
460
|
+
const result = outcome?.result;
|
|
461
|
+
if (result) {
|
|
462
|
+
console.log(JSON.stringify(result));
|
|
463
|
+
} else {
|
|
464
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function cmdAssignRetry(args) {
|
|
469
|
+
const outcome = await requestHub(HUB_OPERATIONS.assignRetry, {
|
|
470
|
+
job_id: args['job-id'],
|
|
471
|
+
reason: args.reason,
|
|
472
|
+
requested_by: args['requested-by'],
|
|
473
|
+
});
|
|
474
|
+
const result = outcome?.result;
|
|
475
|
+
if (result) {
|
|
476
|
+
console.log(JSON.stringify(result));
|
|
477
|
+
} else {
|
|
478
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
322
482
|
async function cmdTeamInfo(args) {
|
|
323
483
|
const body = {
|
|
324
484
|
team_name: args.team,
|
|
325
485
|
include_members: true,
|
|
326
486
|
include_paths: true,
|
|
327
487
|
};
|
|
328
|
-
const
|
|
488
|
+
const outcome = await requestHub(HUB_OPERATIONS.teamInfo, body, 3000, async () => {
|
|
489
|
+
const { teamInfo } = await import('./team/nativeProxy.mjs');
|
|
490
|
+
return await teamInfo(body);
|
|
491
|
+
});
|
|
492
|
+
const result = outcome?.result;
|
|
329
493
|
if (result) {
|
|
330
494
|
console.log(JSON.stringify(result));
|
|
331
|
-
return;
|
|
332
495
|
}
|
|
333
|
-
// Hub 미실행 fallback — nativeProxy 직접 호출
|
|
334
|
-
const { teamInfo } = await import('./team/nativeProxy.mjs');
|
|
335
|
-
console.log(JSON.stringify(await teamInfo(body)));
|
|
336
496
|
}
|
|
337
497
|
|
|
338
498
|
async function cmdTeamTaskList(args) {
|
|
@@ -343,14 +503,14 @@ async function cmdTeamTaskList(args) {
|
|
|
343
503
|
include_internal: !!args['include-internal'],
|
|
344
504
|
limit: parseInt(args.limit || '200', 10),
|
|
345
505
|
};
|
|
346
|
-
const
|
|
506
|
+
const outcome = await requestHub(HUB_OPERATIONS.teamTaskList, body, 3000, async () => {
|
|
507
|
+
const { teamTaskList } = await import('./team/nativeProxy.mjs');
|
|
508
|
+
return await teamTaskList(body);
|
|
509
|
+
});
|
|
510
|
+
const result = outcome?.result;
|
|
347
511
|
if (result) {
|
|
348
512
|
console.log(JSON.stringify(result));
|
|
349
|
-
return;
|
|
350
513
|
}
|
|
351
|
-
// Hub 미실행 fallback — nativeProxy 직접 호출
|
|
352
|
-
const { teamTaskList } = await import('./team/nativeProxy.mjs');
|
|
353
|
-
console.log(JSON.stringify(await teamTaskList(body)));
|
|
354
514
|
}
|
|
355
515
|
|
|
356
516
|
async function cmdTeamTaskUpdate(args) {
|
|
@@ -369,14 +529,14 @@ async function cmdTeamTaskUpdate(args) {
|
|
|
369
529
|
if_match_mtime_ms: args['if-match-mtime-ms'] != null ? Number(args['if-match-mtime-ms']) : undefined,
|
|
370
530
|
actor: args.actor,
|
|
371
531
|
};
|
|
372
|
-
const
|
|
532
|
+
const outcome = await requestHub(HUB_OPERATIONS.teamTaskUpdate, body, 3000, async () => {
|
|
533
|
+
const { teamTaskUpdate } = await import('./team/nativeProxy.mjs');
|
|
534
|
+
return await teamTaskUpdate(body);
|
|
535
|
+
});
|
|
536
|
+
const result = outcome?.result;
|
|
373
537
|
if (result) {
|
|
374
538
|
console.log(JSON.stringify(result));
|
|
375
|
-
return;
|
|
376
539
|
}
|
|
377
|
-
// Hub 미실행 fallback — nativeProxy 직접 호출
|
|
378
|
-
const { teamTaskUpdate } = await import('./team/nativeProxy.mjs');
|
|
379
|
-
console.log(JSON.stringify(await teamTaskUpdate(body)));
|
|
380
540
|
}
|
|
381
541
|
|
|
382
542
|
async function cmdTeamSendMessage(args) {
|
|
@@ -388,14 +548,14 @@ async function cmdTeamSendMessage(args) {
|
|
|
388
548
|
summary: args.summary,
|
|
389
549
|
color: args.color || 'blue',
|
|
390
550
|
};
|
|
391
|
-
const
|
|
551
|
+
const outcome = await requestHub(HUB_OPERATIONS.teamSendMessage, body, 3000, async () => {
|
|
552
|
+
const { teamSendMessage } = await import('./team/nativeProxy.mjs');
|
|
553
|
+
return await teamSendMessage(body);
|
|
554
|
+
});
|
|
555
|
+
const result = outcome?.result;
|
|
392
556
|
if (result) {
|
|
393
557
|
console.log(JSON.stringify(result));
|
|
394
|
-
return;
|
|
395
558
|
}
|
|
396
|
-
// Hub 미실행 fallback — nativeProxy 직접 호출
|
|
397
|
-
const { teamSendMessage } = await import('./team/nativeProxy.mjs');
|
|
398
|
-
console.log(JSON.stringify(await teamSendMessage(body)));
|
|
399
559
|
}
|
|
400
560
|
|
|
401
561
|
function getHubDbPath() {
|
|
@@ -403,80 +563,98 @@ function getHubDbPath() {
|
|
|
403
563
|
}
|
|
404
564
|
|
|
405
565
|
async function cmdPipelineState(args) {
|
|
406
|
-
|
|
407
|
-
|
|
566
|
+
const outcome = await requestHub(HUB_OPERATIONS.pipelineState, { team_name: args.team }, 3000, async () => {
|
|
567
|
+
try {
|
|
568
|
+
const { default: Database } = await import('better-sqlite3');
|
|
569
|
+
const { ensurePipelineTable, readPipelineState } = await import('./pipeline/state.mjs');
|
|
570
|
+
const dbPath = getHubDbPath();
|
|
571
|
+
if (!existsSync(dbPath)) {
|
|
572
|
+
return { ok: false, error: 'hub_db_not_found' };
|
|
573
|
+
}
|
|
574
|
+
const db = new Database(dbPath, { readonly: true });
|
|
575
|
+
ensurePipelineTable(db);
|
|
576
|
+
const state = readPipelineState(db, args.team);
|
|
577
|
+
db.close();
|
|
578
|
+
return state
|
|
579
|
+
? { ok: true, data: state }
|
|
580
|
+
: { ok: false, error: 'pipeline_not_found' };
|
|
581
|
+
} catch (e) {
|
|
582
|
+
return { ok: false, error: e.message };
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
const result = outcome?.result;
|
|
408
586
|
if (result) {
|
|
409
587
|
console.log(JSON.stringify(result));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
// Hub 미실행 fallback — 직접 SQLite 접근
|
|
413
|
-
try {
|
|
414
|
-
const { default: Database } = await import('better-sqlite3');
|
|
415
|
-
const { ensurePipelineTable, readPipelineState } = await import('./pipeline/state.mjs');
|
|
416
|
-
const dbPath = getHubDbPath();
|
|
417
|
-
if (!existsSync(dbPath)) {
|
|
418
|
-
console.log(JSON.stringify({ ok: false, error: 'hub_db_not_found' }));
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
const db = new Database(dbPath, { readonly: true });
|
|
422
|
-
ensurePipelineTable(db);
|
|
423
|
-
const state = readPipelineState(db, args.team);
|
|
424
|
-
db.close();
|
|
425
|
-
console.log(JSON.stringify(state
|
|
426
|
-
? { ok: true, data: state }
|
|
427
|
-
: { ok: false, error: 'pipeline_not_found' }));
|
|
428
|
-
} catch (e) {
|
|
429
|
-
console.log(JSON.stringify({ ok: false, error: e.message }));
|
|
430
588
|
}
|
|
431
589
|
}
|
|
432
590
|
|
|
433
591
|
async function cmdPipelineAdvance(args) {
|
|
434
|
-
|
|
435
|
-
const result = await post('/bridge/pipeline/advance', {
|
|
592
|
+
const body = {
|
|
436
593
|
team_name: args.team,
|
|
437
594
|
phase: args.status, // --status를 phase로 재활용
|
|
595
|
+
};
|
|
596
|
+
const outcome = await requestHub(HUB_OPERATIONS.pipelineAdvance, body, 3000, async () => {
|
|
597
|
+
try {
|
|
598
|
+
const { default: Database } = await import('better-sqlite3');
|
|
599
|
+
const { createPipeline } = await import('./pipeline/index.mjs');
|
|
600
|
+
const dbPath = getHubDbPath();
|
|
601
|
+
if (!existsSync(dbPath)) {
|
|
602
|
+
return { ok: false, error: 'hub_db_not_found' };
|
|
603
|
+
}
|
|
604
|
+
const db = new Database(dbPath);
|
|
605
|
+
const pipeline = createPipeline(db, args.team);
|
|
606
|
+
const advanceResult = pipeline.advance(args.status);
|
|
607
|
+
db.close();
|
|
608
|
+
return advanceResult;
|
|
609
|
+
} catch (e) {
|
|
610
|
+
return { ok: false, error: e.message };
|
|
611
|
+
}
|
|
438
612
|
});
|
|
613
|
+
const result = outcome?.result;
|
|
439
614
|
if (result) {
|
|
440
615
|
console.log(JSON.stringify(result));
|
|
441
|
-
return;
|
|
442
616
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async function cmdPipelineInit(args) {
|
|
620
|
+
const outcome = await requestHub(HUB_OPERATIONS.pipelineInit, {
|
|
621
|
+
team_name: args.team,
|
|
622
|
+
fix_max: args['fix-max'] != null ? Number(args['fix-max']) : undefined,
|
|
623
|
+
ralph_max: args['ralph-max'] != null ? Number(args['ralph-max']) : undefined,
|
|
624
|
+
});
|
|
625
|
+
const result = outcome?.result;
|
|
626
|
+
if (result) {
|
|
627
|
+
console.log(JSON.stringify(result));
|
|
628
|
+
} else {
|
|
629
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async function cmdPipelineList() {
|
|
634
|
+
const outcome = await requestHub(HUB_OPERATIONS.pipelineList, {});
|
|
635
|
+
const result = outcome?.result;
|
|
636
|
+
if (result) {
|
|
637
|
+
console.log(JSON.stringify(result));
|
|
638
|
+
} else {
|
|
639
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
459
640
|
}
|
|
460
641
|
}
|
|
461
642
|
|
|
462
643
|
async function cmdPing() {
|
|
463
|
-
const
|
|
464
|
-
|
|
644
|
+
const outcome = await requestHub(HUB_OPERATIONS.hubStatus, { scope: 'hub' }, 2000);
|
|
645
|
+
|
|
646
|
+
if (outcome?.transport === 'pipe' && outcome.result?.ok) {
|
|
465
647
|
console.log(JSON.stringify({
|
|
466
648
|
ok: true,
|
|
467
|
-
hub:
|
|
649
|
+
hub: outcome.result.data?.hub?.state || 'healthy',
|
|
468
650
|
pipe_path: getHubPipePath(),
|
|
469
651
|
transport: 'pipe',
|
|
470
652
|
}));
|
|
471
653
|
return;
|
|
472
654
|
}
|
|
473
655
|
|
|
474
|
-
|
|
475
|
-
const
|
|
476
|
-
const timer = setTimeout(() => controller.abort(), 3000);
|
|
477
|
-
const res = await fetch(`${getHubUrl()}/status`, { signal: controller.signal });
|
|
478
|
-
clearTimeout(timer);
|
|
479
|
-
const data = await res.json();
|
|
656
|
+
if (outcome?.transport === 'http' && outcome.result) {
|
|
657
|
+
const data = outcome.result;
|
|
480
658
|
console.log(JSON.stringify({
|
|
481
659
|
ok: true,
|
|
482
660
|
hub: data.hub?.state,
|
|
@@ -484,9 +662,10 @@ async function cmdPing() {
|
|
|
484
662
|
pipe_path: data.pipe?.path || data.pipe_path || null,
|
|
485
663
|
transport: 'http',
|
|
486
664
|
}));
|
|
487
|
-
|
|
488
|
-
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
665
|
+
return;
|
|
489
666
|
}
|
|
667
|
+
|
|
668
|
+
console.log(JSON.stringify({ ok: false, reason: 'hub_unavailable' }));
|
|
490
669
|
}
|
|
491
670
|
|
|
492
671
|
export async function main(argv = process.argv.slice(2)) {
|
|
@@ -496,17 +675,24 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
496
675
|
switch (cmd) {
|
|
497
676
|
case 'register': await cmdRegister(args); break;
|
|
498
677
|
case 'result': await cmdResult(args); break;
|
|
678
|
+
case 'control': await cmdControl(args); break;
|
|
499
679
|
case 'context': await cmdContext(args); break;
|
|
500
680
|
case 'deregister': await cmdDeregister(args); break;
|
|
681
|
+
case 'assign-async': await cmdAssignAsync(args); break;
|
|
682
|
+
case 'assign-result': await cmdAssignResult(args); break;
|
|
683
|
+
case 'assign-status': await cmdAssignStatus(args); break;
|
|
684
|
+
case 'assign-retry': await cmdAssignRetry(args); break;
|
|
501
685
|
case 'team-info': await cmdTeamInfo(args); break;
|
|
502
686
|
case 'team-task-list': await cmdTeamTaskList(args); break;
|
|
503
687
|
case 'team-task-update': await cmdTeamTaskUpdate(args); break;
|
|
504
688
|
case 'team-send-message': await cmdTeamSendMessage(args); break;
|
|
505
689
|
case 'pipeline-state': await cmdPipelineState(args); break;
|
|
506
690
|
case 'pipeline-advance': await cmdPipelineAdvance(args); break;
|
|
691
|
+
case 'pipeline-init': await cmdPipelineInit(args); break;
|
|
692
|
+
case 'pipeline-list': await cmdPipelineList(args); break;
|
|
507
693
|
case 'ping': await cmdPing(args); break;
|
|
508
694
|
default:
|
|
509
|
-
console.error('사용법: bridge.mjs <register|result|context|deregister|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|ping> [--옵션]');
|
|
695
|
+
console.error('사용법: bridge.mjs <register|result|control|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping> [--옵션]');
|
|
510
696
|
process.exit(1);
|
|
511
697
|
}
|
|
512
698
|
}
|