triflux 10.3.4 → 10.7.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/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
package/hub/team/notify.mjs
CHANGED
|
@@ -1,52 +1,59 @@
|
|
|
1
1
|
// hub/team/notify.mjs — team notifier (bell / Windows toast / webhook)
|
|
2
2
|
|
|
3
|
-
import { execFile } from
|
|
4
|
-
import os from
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import os from "node:os";
|
|
5
5
|
|
|
6
|
-
export const NOTIFY_EVENT_TYPES = Object.freeze([
|
|
7
|
-
|
|
6
|
+
export const NOTIFY_EVENT_TYPES = Object.freeze([
|
|
7
|
+
"completed",
|
|
8
|
+
"failed",
|
|
9
|
+
"inputWait",
|
|
10
|
+
]);
|
|
11
|
+
export const NOTIFY_CHANNELS = Object.freeze(["bell", "toast", "webhook"]);
|
|
8
12
|
|
|
9
13
|
function freezeRecord(record) {
|
|
10
14
|
return Object.freeze({ ...record });
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
function escapePowerShellSingleQuoted(value) {
|
|
14
|
-
return String(value ??
|
|
18
|
+
return String(value ?? "").replaceAll("'", "''");
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
function normalizeTimestamp(value) {
|
|
18
22
|
if (value instanceof Date) return value.toISOString();
|
|
19
|
-
if (value == null || value ===
|
|
23
|
+
if (value == null || value === "") return new Date().toISOString();
|
|
20
24
|
return String(value);
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function normalizeEvent(event, defaults = {}) {
|
|
24
|
-
if (!event || typeof event !==
|
|
25
|
-
throw new TypeError(
|
|
28
|
+
if (!event || typeof event !== "object") {
|
|
29
|
+
throw new TypeError("notify(event) requires an event object");
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
const type = String(event.type ||
|
|
32
|
+
const type = String(event.type || "").trim();
|
|
29
33
|
if (!NOTIFY_EVENT_TYPES.includes(type)) {
|
|
30
|
-
throw new TypeError(`Unsupported notify event type: ${type ||
|
|
34
|
+
throw new TypeError(`Unsupported notify event type: ${type || "<empty>"}`);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
return freezeRecord({
|
|
34
38
|
type,
|
|
35
|
-
sessionId: event.sessionId == null ?
|
|
36
|
-
host:
|
|
37
|
-
|
|
39
|
+
sessionId: event.sessionId == null ? "" : String(event.sessionId),
|
|
40
|
+
host:
|
|
41
|
+
event.host == null || event.host === ""
|
|
42
|
+
? String(defaults.host || os.hostname())
|
|
43
|
+
: String(event.host),
|
|
44
|
+
summary: event.summary == null ? "" : String(event.summary),
|
|
38
45
|
timestamp: normalizeTimestamp(event.timestamp),
|
|
39
46
|
});
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
function defaultChannelConfig(name, env) {
|
|
43
50
|
switch (name) {
|
|
44
|
-
case
|
|
51
|
+
case "bell":
|
|
45
52
|
return { enabled: true };
|
|
46
|
-
case
|
|
53
|
+
case "toast":
|
|
47
54
|
return { enabled: true };
|
|
48
|
-
case
|
|
49
|
-
const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK ||
|
|
55
|
+
case "webhook": {
|
|
56
|
+
const url = String(env?.TRIFLUX_NOTIFY_WEBHOOK || "");
|
|
50
57
|
return { enabled: Boolean(url), url };
|
|
51
58
|
}
|
|
52
59
|
default:
|
|
@@ -56,33 +63,40 @@ function defaultChannelConfig(name, env) {
|
|
|
56
63
|
|
|
57
64
|
function normalizeChannelConfig(name, value, env) {
|
|
58
65
|
const base = defaultChannelConfig(name, env);
|
|
59
|
-
const patch =
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
const patch =
|
|
67
|
+
typeof value === "boolean"
|
|
68
|
+
? { enabled: value }
|
|
69
|
+
: value && typeof value === "object"
|
|
70
|
+
? value
|
|
71
|
+
: {};
|
|
62
72
|
|
|
63
73
|
const next = {
|
|
64
74
|
...base,
|
|
65
75
|
...patch,
|
|
66
76
|
};
|
|
67
77
|
|
|
68
|
-
if (
|
|
78
|
+
if ("enabled" in next) {
|
|
69
79
|
next.enabled = Boolean(next.enabled);
|
|
70
80
|
}
|
|
71
81
|
|
|
72
|
-
if (name ===
|
|
73
|
-
next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK ||
|
|
82
|
+
if (name === "webhook") {
|
|
83
|
+
next.url = String(next.url || env?.TRIFLUX_NOTIFY_WEBHOOK || "");
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
if (name ===
|
|
86
|
+
if (name === "toast") {
|
|
77
87
|
if (next.command != null) next.command = String(next.command);
|
|
78
|
-
if (next.timeoutMs != null)
|
|
88
|
+
if (next.timeoutMs != null)
|
|
89
|
+
next.timeoutMs = Math.max(
|
|
90
|
+
1,
|
|
91
|
+
Number.parseInt(String(next.timeoutMs), 10) || 5000,
|
|
92
|
+
);
|
|
79
93
|
}
|
|
80
94
|
|
|
81
95
|
return freezeRecord(next);
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
function normalizeChannels(channels, env) {
|
|
85
|
-
const source = channels && typeof channels ===
|
|
99
|
+
const source = channels && typeof channels === "object" ? channels : {};
|
|
86
100
|
const normalized = {};
|
|
87
101
|
for (const name of NOTIFY_CHANNELS) {
|
|
88
102
|
normalized[name] = normalizeChannelConfig(name, source[name], env);
|
|
@@ -97,20 +111,27 @@ function updateChannelConfig(channels, channel, config, env) {
|
|
|
97
111
|
|
|
98
112
|
return Object.freeze({
|
|
99
113
|
...channels,
|
|
100
|
-
[channel]: normalizeChannelConfig(
|
|
114
|
+
[channel]: normalizeChannelConfig(
|
|
115
|
+
channel,
|
|
116
|
+
{
|
|
117
|
+
...channels[channel],
|
|
118
|
+
...(typeof config === "boolean" ? { enabled: config } : config || {}),
|
|
119
|
+
},
|
|
120
|
+
env,
|
|
121
|
+
),
|
|
101
122
|
});
|
|
102
123
|
}
|
|
103
124
|
|
|
104
125
|
function formatEventTitle(event) {
|
|
105
126
|
switch (event.type) {
|
|
106
|
-
case
|
|
107
|
-
return
|
|
108
|
-
case
|
|
109
|
-
return
|
|
110
|
-
case
|
|
111
|
-
return
|
|
127
|
+
case "completed":
|
|
128
|
+
return "Triflux completed";
|
|
129
|
+
case "failed":
|
|
130
|
+
return "Triflux failed";
|
|
131
|
+
case "inputWait":
|
|
132
|
+
return "Triflux waiting for input";
|
|
112
133
|
default:
|
|
113
|
-
return
|
|
134
|
+
return "Triflux notification";
|
|
114
135
|
}
|
|
115
136
|
}
|
|
116
137
|
|
|
@@ -118,12 +139,15 @@ function formatEventBody(event) {
|
|
|
118
139
|
const parts = [];
|
|
119
140
|
if (event.summary) parts.push(event.summary);
|
|
120
141
|
|
|
121
|
-
const meta = [
|
|
142
|
+
const meta = [
|
|
143
|
+
event.sessionId ? `session ${event.sessionId}` : "",
|
|
144
|
+
event.host ? `host ${event.host}` : "",
|
|
145
|
+
]
|
|
122
146
|
.filter(Boolean)
|
|
123
|
-
.join(
|
|
147
|
+
.join(" · ");
|
|
124
148
|
if (meta) parts.push(meta);
|
|
125
149
|
|
|
126
|
-
return parts.join(
|
|
150
|
+
return parts.join("\n") || event.timestamp;
|
|
127
151
|
}
|
|
128
152
|
|
|
129
153
|
function createResult(channel, status, extra = {}) {
|
|
@@ -145,17 +169,18 @@ function execFileAsync(command, args, options, execFileFn) {
|
|
|
145
169
|
}
|
|
146
170
|
|
|
147
171
|
async function sendBell(config, deps) {
|
|
148
|
-
if (!config.enabled)
|
|
172
|
+
if (!config.enabled)
|
|
173
|
+
return createResult("bell", "skipped", { reason: "disabled" });
|
|
149
174
|
const stream = deps.stdout;
|
|
150
|
-
if (!stream || typeof stream.write !==
|
|
151
|
-
return createResult(
|
|
175
|
+
if (!stream || typeof stream.write !== "function") {
|
|
176
|
+
return createResult("bell", "skipped", { reason: "stdout-unavailable" });
|
|
152
177
|
}
|
|
153
178
|
|
|
154
179
|
try {
|
|
155
|
-
stream.write(
|
|
156
|
-
return createResult(
|
|
180
|
+
stream.write("\u0007");
|
|
181
|
+
return createResult("bell", "sent");
|
|
157
182
|
} catch (error) {
|
|
158
|
-
return createResult(
|
|
183
|
+
return createResult("bell", "failed", { error: error.message });
|
|
159
184
|
}
|
|
160
185
|
}
|
|
161
186
|
|
|
@@ -167,32 +192,34 @@ function buildToastScript(title, body) {
|
|
|
167
192
|
`$Title = '${safeTitle}'`,
|
|
168
193
|
`$Body = '${safeBody}'`,
|
|
169
194
|
"if (Get-Command New-BurntToastNotification -ErrorAction SilentlyContinue) {",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
195
|
+
" New-BurntToastNotification -Text @($Title, $Body) | Out-Null",
|
|
196
|
+
" return",
|
|
197
|
+
"}",
|
|
198
|
+
"[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null",
|
|
199
|
+
"[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > $null",
|
|
200
|
+
"$escapedTitle = [System.Security.SecurityElement]::Escape($Title)",
|
|
201
|
+
"$escapedBody = [System.Security.SecurityElement]::Escape($Body)",
|
|
202
|
+
"$xml = New-Object Windows.Data.Xml.Dom.XmlDocument",
|
|
203
|
+
"$xml.LoadXml(\"<toast><visual><binding template='ToastGeneric'><text>$escapedTitle</text><text>$escapedBody</text></binding></visual></toast>\")",
|
|
204
|
+
"$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)",
|
|
180
205
|
"[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Triflux').Show($toast)",
|
|
181
|
-
].join(
|
|
206
|
+
].join("; ");
|
|
182
207
|
}
|
|
183
208
|
|
|
184
209
|
async function sendToast(event, config, deps) {
|
|
185
|
-
if (!config.enabled)
|
|
186
|
-
|
|
187
|
-
|
|
210
|
+
if (!config.enabled)
|
|
211
|
+
return createResult("toast", "skipped", { reason: "disabled" });
|
|
212
|
+
if ((deps.platform || process.platform) !== "win32") {
|
|
213
|
+
return createResult("toast", "skipped", { reason: "unsupported-platform" });
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
const execFileFn = deps.execFile || execFile;
|
|
191
217
|
const candidates = config.command
|
|
192
218
|
? [config.command]
|
|
193
|
-
: Array.isArray(deps.powerShellCandidates) &&
|
|
219
|
+
: Array.isArray(deps.powerShellCandidates) &&
|
|
220
|
+
deps.powerShellCandidates.length > 0
|
|
194
221
|
? deps.powerShellCandidates
|
|
195
|
-
: [
|
|
222
|
+
: ["pwsh", "powershell.exe"];
|
|
196
223
|
|
|
197
224
|
const title = formatEventTitle(event);
|
|
198
225
|
const body = formatEventBody(event);
|
|
@@ -201,40 +228,51 @@ async function sendToast(event, config, deps) {
|
|
|
201
228
|
|
|
202
229
|
for (const command of candidates) {
|
|
203
230
|
try {
|
|
204
|
-
await execFileAsync(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
231
|
+
await execFileAsync(
|
|
232
|
+
command,
|
|
233
|
+
["-NoLogo", "-NoProfile", "-Command", script],
|
|
234
|
+
{
|
|
235
|
+
windowsHide: true,
|
|
236
|
+
timeout: config.timeoutMs || 5000,
|
|
237
|
+
},
|
|
238
|
+
execFileFn,
|
|
239
|
+
);
|
|
240
|
+
return createResult("toast", "sent", { command });
|
|
209
241
|
} catch (error) {
|
|
210
242
|
failures.push(`${command}: ${error.message}`);
|
|
211
243
|
}
|
|
212
244
|
}
|
|
213
245
|
|
|
214
|
-
return createResult(
|
|
246
|
+
return createResult("toast", "failed", {
|
|
247
|
+
error: failures.join(" | ") || "toast-send-failed",
|
|
248
|
+
});
|
|
215
249
|
}
|
|
216
250
|
|
|
217
251
|
async function sendWebhook(event, config, deps) {
|
|
218
|
-
if (!config.enabled)
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
return createResult(
|
|
252
|
+
if (!config.enabled)
|
|
253
|
+
return createResult("webhook", "skipped", { reason: "disabled" });
|
|
254
|
+
if (!config.url)
|
|
255
|
+
return createResult("webhook", "skipped", { reason: "missing-url" });
|
|
256
|
+
if (typeof deps.fetch !== "function") {
|
|
257
|
+
return createResult("webhook", "failed", { error: "fetch-unavailable" });
|
|
222
258
|
}
|
|
223
259
|
|
|
224
260
|
try {
|
|
225
261
|
const response = await deps.fetch(config.url, {
|
|
226
|
-
method:
|
|
227
|
-
headers: {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: { "content-type": "application/json" },
|
|
228
264
|
body: JSON.stringify(event),
|
|
229
265
|
});
|
|
230
266
|
|
|
231
267
|
if (!response?.ok) {
|
|
232
|
-
return createResult(
|
|
268
|
+
return createResult("webhook", "failed", {
|
|
269
|
+
error: `HTTP ${response?.status ?? "unknown"}`,
|
|
270
|
+
});
|
|
233
271
|
}
|
|
234
272
|
|
|
235
|
-
return createResult(
|
|
273
|
+
return createResult("webhook", "sent", { statusCode: response.status });
|
|
236
274
|
} catch (error) {
|
|
237
|
-
return createResult(
|
|
275
|
+
return createResult("webhook", "failed", { error: error.message });
|
|
238
276
|
}
|
|
239
277
|
}
|
|
240
278
|
|
|
@@ -254,7 +292,10 @@ function createNotifierInstance(channels, deps) {
|
|
|
254
292
|
}
|
|
255
293
|
|
|
256
294
|
function setChannel(channel, config) {
|
|
257
|
-
return createNotifierInstance(
|
|
295
|
+
return createNotifierInstance(
|
|
296
|
+
updateChannelConfig(channels, channel, config, deps.env),
|
|
297
|
+
deps,
|
|
298
|
+
);
|
|
258
299
|
}
|
|
259
300
|
|
|
260
301
|
return Object.freeze({ notify, setChannel });
|
|
@@ -286,7 +327,9 @@ export function createNotifier(opts = {}) {
|
|
|
286
327
|
fetch: opts.deps?.fetch || globalThis.fetch?.bind(globalThis),
|
|
287
328
|
platform: opts.platform || process.platform,
|
|
288
329
|
hostname: opts.hostname || os.hostname(),
|
|
289
|
-
powerShellCandidates: Object.freeze(
|
|
330
|
+
powerShellCandidates: Object.freeze(
|
|
331
|
+
opts.deps?.powerShellCandidates || ["pwsh", "powershell.exe"],
|
|
332
|
+
),
|
|
290
333
|
});
|
|
291
334
|
|
|
292
335
|
return createNotifierInstance(normalizeChannels(opts.channels, env), deps);
|
|
@@ -52,9 +52,10 @@ export function decomposeTask(taskDescription, agentCount) {
|
|
|
52
52
|
export function buildLeadPrompt(taskDescription, config) {
|
|
53
53
|
const { agentId, teammateMode = "tmux", workers = [] } = config;
|
|
54
54
|
|
|
55
|
-
const roster =
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const roster =
|
|
56
|
+
workers
|
|
57
|
+
.map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) — ${w.subtask}`)
|
|
58
|
+
.join("\n") || "- (워커 없음)";
|
|
58
59
|
|
|
59
60
|
const workerIds = workers.map((w) => w.agentId).join(", ");
|
|
60
61
|
|
|
@@ -142,7 +143,11 @@ export async function orchestrate(sessionName, assignments, opts = {}) {
|
|
|
142
143
|
agentId: leadAgentId,
|
|
143
144
|
hubUrl,
|
|
144
145
|
teammateMode,
|
|
145
|
-
workers: workers.map((w) => ({
|
|
146
|
+
workers: workers.map((w) => ({
|
|
147
|
+
agentId: w.agentId,
|
|
148
|
+
cli: w.cli,
|
|
149
|
+
subtask: w.subtask,
|
|
150
|
+
})),
|
|
146
151
|
});
|
|
147
152
|
injectPrompt(lead.target, leadPrompt, { useFileRef: true });
|
|
148
153
|
await new Promise((r) => setTimeout(r, 100));
|
package/hub/team/pane.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// hub/team/pane.mjs — pane별 CLI 실행 + stdin 주입
|
|
2
2
|
// 의존성: child_process, fs, os, path (Node.js 내장)만 사용
|
|
3
|
-
import {
|
|
4
|
-
import { join } from "node:path";
|
|
3
|
+
import { mkdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
4
|
import { tmpdir } from "node:os";
|
|
6
|
-
import {
|
|
7
|
-
import { psmuxExec } from "./psmux.mjs";
|
|
8
|
-
|
|
5
|
+
import { join } from "node:path";
|
|
9
6
|
import { buildExecArgs } from "../codex-adapter.mjs";
|
|
7
|
+
import { psmuxExec } from "./psmux.mjs";
|
|
8
|
+
import { detectMultiplexer, tmuxExec } from "./session.mjs";
|
|
10
9
|
|
|
11
10
|
function quoteArg(value) {
|
|
12
11
|
return `"${String(value).replace(/"/g, '\\"')}"`;
|
|
@@ -118,7 +117,11 @@ export function injectPrompt(target, prompt, { useFileRef = false } = {}) {
|
|
|
118
117
|
psmuxExec(["send-keys", "-t", target, "-l", `@${filePath}`]);
|
|
119
118
|
psmuxExec(["send-keys", "-t", target, "Enter"]);
|
|
120
119
|
// TUI가 파일을 읽을 시간을 주고 정리
|
|
121
|
-
setTimeout(() => {
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
try {
|
|
122
|
+
unlinkSync(tmpFile);
|
|
123
|
+
} catch {}
|
|
124
|
+
}, 10000);
|
|
122
125
|
return;
|
|
123
126
|
}
|
|
124
127
|
|
|
@@ -139,7 +142,9 @@ export function injectPrompt(target, prompt, { useFileRef = false } = {}) {
|
|
|
139
142
|
muxExec(`paste-buffer -t ${target}`);
|
|
140
143
|
muxExec(`send-keys -t ${target} Enter`);
|
|
141
144
|
} finally {
|
|
142
|
-
try {
|
|
145
|
+
try {
|
|
146
|
+
unlinkSync(tmpFile);
|
|
147
|
+
} catch {}
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -13,10 +13,7 @@ const TARGET_PROCESS_NAMES = ["node", "python", "python3"];
|
|
|
13
13
|
const SIGTERM_GRACE_MS = 5000;
|
|
14
14
|
|
|
15
15
|
// cmdLine 패턴 기반 화이트리스트 (고아 후보에서 제외)
|
|
16
|
-
const WHITELIST_CMDLINE = [
|
|
17
|
-
/oh-my-claudecode/i,
|
|
18
|
-
/triflux[\\/]hub[\\/]s/i,
|
|
19
|
-
];
|
|
16
|
+
const WHITELIST_CMDLINE = [/oh-my-claudecode/i, /triflux[\\/]hub[\\/]s/i];
|
|
20
17
|
|
|
21
18
|
// 프로세스명 기반 화이트리스트
|
|
22
19
|
const WHITELIST_NAMES = ["claude", "CCXProcess"];
|
|
@@ -44,15 +41,13 @@ async function queryWindowsProcesses(execFileFn) {
|
|
|
44
41
|
const raw = JSON.parse(stdout.trim());
|
|
45
42
|
const items = Array.isArray(raw) ? raw : [raw];
|
|
46
43
|
|
|
47
|
-
return items
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ramMB: Math.round((Number(p.WorkingSetSize) || 0) / 1024 / 1024),
|
|
55
|
-
}));
|
|
44
|
+
return items.filter(Boolean).map((p) => ({
|
|
45
|
+
pid: Number(p.ProcessId),
|
|
46
|
+
name: String(p.Name || "").replace(/\.exe$/i, ""),
|
|
47
|
+
parentPid: Number(p.ParentProcessId) || 0,
|
|
48
|
+
cmdLine: String(p.CommandLine || ""),
|
|
49
|
+
ramMB: Math.round((Number(p.WorkingSetSize) || 0) / 1024 / 1024),
|
|
50
|
+
}));
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
/**
|
|
@@ -225,7 +220,11 @@ export async function findOrphanProcesses(opts = {}) {
|
|
|
225
220
|
const nameLower = p.name.toLowerCase();
|
|
226
221
|
|
|
227
222
|
// 대상 프로세스만
|
|
228
|
-
if (
|
|
223
|
+
if (
|
|
224
|
+
!TARGET_PROCESS_NAMES.some(
|
|
225
|
+
(t) => nameLower === t || nameLower === `${t}.exe`,
|
|
226
|
+
)
|
|
227
|
+
) {
|
|
229
228
|
return false;
|
|
230
229
|
}
|
|
231
230
|
|
|
@@ -299,7 +298,12 @@ export function createProcessCleanup(opts = {}) {
|
|
|
299
298
|
*/
|
|
300
299
|
async function kill() {
|
|
301
300
|
if (dryRun) {
|
|
302
|
-
return lastOrphans.map((p) => ({
|
|
301
|
+
return lastOrphans.map((p) => ({
|
|
302
|
+
pid: p.pid,
|
|
303
|
+
name: p.name,
|
|
304
|
+
killed: false,
|
|
305
|
+
dryRun: true,
|
|
306
|
+
}));
|
|
303
307
|
}
|
|
304
308
|
|
|
305
309
|
const results = await Promise.all(
|
|
@@ -322,7 +326,12 @@ export function createProcessCleanup(opts = {}) {
|
|
|
322
326
|
|
|
323
327
|
return { pid: p.pid, name: p.name, killed: true };
|
|
324
328
|
} catch (err) {
|
|
325
|
-
return {
|
|
329
|
+
return {
|
|
330
|
+
pid: p.pid,
|
|
331
|
+
name: p.name,
|
|
332
|
+
killed: false,
|
|
333
|
+
error: String(err.message || err),
|
|
334
|
+
};
|
|
326
335
|
}
|
|
327
336
|
}),
|
|
328
337
|
);
|