@triflux/remote 10.41.1 → 10.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/hub/store.mjs CHANGED
@@ -5,6 +5,7 @@ import { mkdirSync, readFileSync } from "node:fs";
5
5
  import { createRequire } from "node:module";
6
6
  import { dirname, join } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
+ import { clampDurationMs, DEFAULT_ASSIGN_TIMEOUT_MS, DEFAULT_ASSIGN_TTL_MS } from "@triflux/core/hub/lib/timeout-defaults.mjs";
8
9
  import { uuidv7 } from "@triflux/core/hub/lib/uuidv7.mjs";
9
10
  import { recalcConfidence } from "@triflux/core/hub/reflexion.mjs";
10
11
 
@@ -111,7 +112,7 @@ export function createStore(dbPath, options = {}) {
111
112
  db.exec(
112
113
  "CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT)",
113
114
  );
114
- const SCHEMA_VERSION = "4";
115
+ const SCHEMA_VERSION = "5";
115
116
  const curVer = (() => {
116
117
  try {
117
118
  return db
@@ -143,6 +144,7 @@ export function createStore(dbPath, options = {}) {
143
144
  "type",
144
145
  "TEXT NOT NULL DEFAULT 'reflexion'",
145
146
  );
147
+ ensureColumn(db, "assign_jobs", "dispatched_at_ms", "INTEGER");
146
148
  ensureColumn(
147
149
  db,
148
150
  "reflexion_entries",
@@ -233,12 +235,12 @@ export function createStore(dbPath, options = {}) {
233
235
  job_id, supervisor_agent, worker_agent, topic, task, payload_json,
234
236
  status, attempt, retry_count, max_retries, priority, ttl_ms, timeout_ms, deadline_ms,
235
237
  trace_id, correlation_id, last_message_id, result_json, error_json,
236
- created_at_ms, updated_at_ms, started_at_ms, completed_at_ms, last_retry_at_ms
238
+ created_at_ms, updated_at_ms, started_at_ms, dispatched_at_ms, completed_at_ms, last_retry_at_ms
237
239
  ) VALUES (
238
240
  @job_id, @supervisor_agent, @worker_agent, @topic, @task, @payload_json,
239
241
  @status, @attempt, @retry_count, @max_retries, @priority, @ttl_ms, @timeout_ms, @deadline_ms,
240
242
  @trace_id, @correlation_id, @last_message_id, @result_json, @error_json,
241
- @created_at_ms, @updated_at_ms, @started_at_ms, @completed_at_ms, @last_retry_at_ms
243
+ @created_at_ms, @updated_at_ms, @started_at_ms, @dispatched_at_ms, @completed_at_ms, @last_retry_at_ms
242
244
  )`),
243
245
  getAssign: db.prepare("SELECT * FROM assign_jobs WHERE job_id = ?"),
244
246
  updateAssign: db.prepare(`
@@ -263,6 +265,7 @@ export function createStore(dbPath, options = {}) {
263
265
  error_json=@error_json,
264
266
  updated_at_ms=@updated_at_ms,
265
267
  started_at_ms=@started_at_ms,
268
+ dispatched_at_ms=@dispatched_at_ms,
266
269
  completed_at_ms=@completed_at_ms,
267
270
  last_retry_at_ms=@last_retry_at_ms
268
271
  WHERE job_id=@job_id`),
@@ -356,12 +359,6 @@ export function createStore(dbPath, options = {}) {
356
359
  return Math.max(1, Math.min(Math.trunc(num), 9));
357
360
  }
358
361
 
359
- function clampDuration(value, fallback = 600000, min = 1000, max = 86400000) {
360
- const num = Number(value);
361
- if (!Number.isFinite(num)) return fallback;
362
- return Math.max(min, Math.min(Math.trunc(num), max));
363
- }
364
-
365
362
  const store = {
366
363
  db,
367
364
  uuidv7,
@@ -381,7 +378,8 @@ export function createStore(dbPath, options = {}) {
381
378
  }) {
382
379
  const now = Date.now();
383
380
  const leaseExpires = now + heartbeat_ttl_ms;
384
- S.upsertAgent.run({
381
+ const leaseId = uuidv7();
382
+ const write = S.upsertAgent.run({
385
383
  agent_id,
386
384
  cli,
387
385
  pid: pid ?? null,
@@ -392,11 +390,23 @@ export function createStore(dbPath, options = {}) {
392
390
  status: "online",
393
391
  metadata_json: JSON.stringify(metadata),
394
392
  });
393
+ const persisted = parseAgentRow(S.getAgent.get(agent_id));
395
394
  return {
396
395
  agent_id,
397
- lease_id: uuidv7(),
396
+ lease_id: leaseId,
398
397
  lease_expires_ms: leaseExpires,
399
398
  server_time_ms: now,
399
+ effective: {
400
+ updated: write.changes > 0,
401
+ online:
402
+ persisted?.status === "online" &&
403
+ Number(persisted.lease_expires_ms) === leaseExpires,
404
+ status: persisted?.status ?? null,
405
+ last_seen_ms: persisted?.last_seen_ms ?? null,
406
+ lease_expires_ms: persisted?.lease_expires_ms ?? null,
407
+ store_type: "sqlite",
408
+ db_path: dbPath ?? null,
409
+ },
400
410
  };
401
411
  },
402
412
 
@@ -406,11 +416,12 @@ export function createStore(dbPath, options = {}) {
406
416
 
407
417
  refreshLease(agentId, ttlMs = 30000) {
408
418
  const now = Date.now();
409
- S.heartbeat.run(now, now + ttlMs, agentId);
419
+ const write = S.heartbeat.run(now, now + ttlMs, agentId);
410
420
  return {
411
421
  agent_id: agentId,
412
422
  lease_expires_ms: now + ttlMs,
413
423
  server_time_ms: now,
424
+ effective: { updated: write.changes > 0, store_type: "sqlite", db_path: dbPath ?? null },
414
425
  };
415
426
  },
416
427
 
@@ -616,9 +627,10 @@ export function createStore(dbPath, options = {}) {
616
627
  retry_count = 0,
617
628
  max_retries = 0,
618
629
  priority = 5,
619
- ttl_ms = 600000,
620
- timeout_ms = 600000,
630
+ ttl_ms = DEFAULT_ASSIGN_TTL_MS,
631
+ timeout_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
621
632
  deadline_ms,
633
+ dispatched_at_ms = null,
622
634
  trace_id,
623
635
  correlation_id,
624
636
  last_message_id = null,
@@ -626,7 +638,7 @@ export function createStore(dbPath, options = {}) {
626
638
  error = null,
627
639
  }) {
628
640
  const now = Date.now();
629
- const normalizedTimeout = clampDuration(timeout_ms, 600000);
641
+ const normalizedTimeout = clampDurationMs(timeout_ms, DEFAULT_ASSIGN_TIMEOUT_MS);
630
642
  const row = {
631
643
  job_id: job_id || uuidv7(),
632
644
  supervisor_agent,
@@ -639,7 +651,7 @@ export function createStore(dbPath, options = {}) {
639
651
  retry_count: Math.max(0, Number(retry_count) || 0),
640
652
  max_retries: Math.max(0, Number(max_retries) || 0),
641
653
  priority: clampPriority(priority, 5),
642
- ttl_ms: clampDuration(ttl_ms, normalizedTimeout),
654
+ ttl_ms: clampDurationMs(ttl_ms, normalizedTimeout),
643
655
  timeout_ms: normalizedTimeout,
644
656
  deadline_ms: Number.isFinite(Number(deadline_ms))
645
657
  ? Math.trunc(Number(deadline_ms))
@@ -652,6 +664,10 @@ export function createStore(dbPath, options = {}) {
652
664
  created_at_ms: now,
653
665
  updated_at_ms: now,
654
666
  started_at_ms: status === "running" ? now : null,
667
+ dispatched_at_ms:
668
+ dispatched_at_ms == null || !Number.isFinite(Number(dispatched_at_ms))
669
+ ? null
670
+ : Math.trunc(Number(dispatched_at_ms)),
655
671
  completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
656
672
  ? now
657
673
  : null,
@@ -676,7 +692,7 @@ export function createStore(dbPath, options = {}) {
676
692
  const isTerminal = ["succeeded", "failed", "timed_out"].includes(
677
693
  nextStatus,
678
694
  );
679
- const nextTimeout = clampDuration(
695
+ const nextTimeout = clampDurationMs(
680
696
  patch.timeout_ms ?? current.timeout_ms,
681
697
  current.timeout_ms,
682
698
  );
@@ -704,7 +720,7 @@ export function createStore(dbPath, options = {}) {
704
720
  patch.priority ?? current.priority,
705
721
  current.priority || 5,
706
722
  ),
707
- ttl_ms: clampDuration(
723
+ ttl_ms: clampDurationMs(
708
724
  patch.ttl_ms ?? current.ttl_ms,
709
725
  current.ttl_ms || nextTimeout,
710
726
  ),
@@ -745,6 +761,7 @@ export function createStore(dbPath, options = {}) {
745
761
  : nextStatus === "running"
746
762
  ? current.started_at_ms || now
747
763
  : current.started_at_ms,
764
+ dispatched_at_ms: Object.hasOwn(patch, "dispatched_at_ms") ? patch.dispatched_at_ms : current.dispatched_at_ms,
748
765
  completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
749
766
  ? patch.completed_at_ms
750
767
  : isTerminal
@@ -836,7 +853,7 @@ export function createStore(dbPath, options = {}) {
836
853
  current.attempt + 1,
837
854
  Number(patch.attempt ?? current.attempt + 1) || 1,
838
855
  );
839
- const nextTimeout = clampDuration(
856
+ const nextTimeout = clampDurationMs(
840
857
  patch.timeout_ms ?? current.timeout_ms,
841
858
  current.timeout_ms,
842
859
  );
@@ -848,6 +865,7 @@ export function createStore(dbPath, options = {}) {
848
865
  deadline_ms: Date.now() + nextTimeout,
849
866
  completed_at_ms: null,
850
867
  started_at_ms: null,
868
+ dispatched_at_ms: null,
851
869
  last_retry_at_ms: Date.now(),
852
870
  result: patch.result ?? null,
853
871
  error: Object.hasOwn(patch, "error") ? patch.error : current.error,
@@ -464,25 +464,46 @@ export function sendClaudeControlRequest(
464
464
 
465
465
  // claude daemon 은 control.sock 의 mutating op(dispatch 등)에 control-key
466
466
  // 인증을 강제한다 (미제시 시 EAUTH "didn't present the daemon control key").
467
- // key 는 <configDir>/daemon/control.key (configDir 스코프별). 파일이 없으면
468
- // 인증 미강제 daemon 으로 보고 auth 필드를 생략한다 (fail-open — 구버전 호환).
467
+ // OMC runtime config 는 .omc-launch-profile.json 의 sourceConfigDir 에서 key 를
468
+ // 발급한다. control.sock 은 runtime config hash 를 쓰더라도 auth 는 발급 config
469
+ // 에서 읽어야 한다. 일반 daemon 은 <configDir>/daemon/control.key 를 사용한다.
470
+ // 어느 경로에도 파일이 없으면 인증 미강제 daemon 으로 보고 auth 를 생략한다
471
+ // (fail-open — 구버전 호환).
469
472
  export async function readDaemonControlKey(
470
473
  configDir = resolveClaudeConfigDir(),
474
+ { diagnostics } = {},
471
475
  ) {
472
- try {
473
- const key = await fs.readFile(
474
- path.join(configDir, "daemon", "control.key"),
475
- "utf8",
476
- );
477
- return key.trim() || undefined;
478
- } catch {
479
- return undefined;
476
+ if (!configDir) return undefined;
477
+
478
+ const runtimeConfigDir = path.resolve(configDir);
479
+ const omcProfile = await readOmcLaunchProfile(runtimeConfigDir);
480
+ const keyConfigDirs = [omcProfile?.sourceConfigDir, runtimeConfigDir].filter(
481
+ (candidate, index, entries) =>
482
+ candidate && entries.indexOf(candidate) === index,
483
+ );
484
+
485
+ for (const keyConfigDir of keyConfigDirs) {
486
+ const keyPath = path.join(keyConfigDir, "daemon", "control.key");
487
+ try {
488
+ const key = await fs.readFile(keyPath, "utf8");
489
+ if (key.trim()) return key.trim();
490
+ } catch (error) {
491
+ if (error?.code === "ENOENT") continue;
492
+ if (Array.isArray(diagnostics)) {
493
+ diagnostics.push({
494
+ code: error?.code || "UNKNOWN",
495
+ path: keyPath,
496
+ message: error?.message || String(error),
497
+ });
498
+ }
499
+ }
480
500
  }
501
+ return undefined;
481
502
  }
482
503
 
483
- export async function buildDaemonControlAuth(configDir) {
504
+ export async function buildDaemonControlAuth(configDir, opts = {}) {
484
505
  if (!configDir) return {};
485
- const auth = await readDaemonControlKey(configDir);
506
+ const auth = await readDaemonControlKey(configDir, opts);
486
507
  return auth ? { auth } : {};
487
508
  }
488
509
 
@@ -74,7 +74,7 @@ export function parseTeamArgs(args = []) {
74
74
  const assigns = []; // --assign "codex:프롬프트:역할" 형식
75
75
  let autoAttach = true;
76
76
  let progressive = true;
77
- let timeoutSec = 300;
77
+ let timeoutSec = 0;
78
78
  let verbose = false;
79
79
  let dashboard = true;
80
80
  let dashboardLayout = "lite";
@@ -130,7 +130,7 @@ export function parseTeamArgs(args = []) {
130
130
  } else if (current === "--no-progressive") {
131
131
  progressive = false;
132
132
  } else if (current === "--timeout" && args[index + 1]) {
133
- timeoutSec = Number(args[++index]) || 300;
133
+ timeoutSec = Number(args[++index]) || 0;
134
134
  } else if (current === "--mcp-profile" && args[index + 1]) {
135
135
  mcpProfile = args[++index].trim();
136
136
  } else if ((current === "--model" || current === "-m") && args[index + 1]) {