triflux 10.3.2 → 10.3.4
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/.claude-plugin/plugin.json +22 -22
- package/LICENSE +21 -21
- package/README.ko.md +16 -0
- package/README.md +8 -0
- package/hooks/hook-registry.json +256 -256
- package/hub/adaptive-inject.mjs +1 -1
- package/hub/assign-callbacks.mjs +120 -120
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/hitl.mjs +143 -143
- package/hub/lib/path-utils.mjs +167 -0
- package/hub/router.mjs +791 -791
- package/hub/session-fingerprint.mjs +1 -1
- package/hub/team/cli/commands/attach.mjs +37 -37
- package/hub/team/cli/commands/debug.mjs +74 -74
- package/hub/team/cli/commands/focus.mjs +53 -53
- package/hub/team/cli/commands/list.mjs +24 -24
- package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
- package/hub/team/cli/commands/start/start-mux.mjs +73 -73
- package/hub/team/cli/commands/start/start-wt.mjs +69 -69
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/render.mjs +30 -30
- package/hub/team/cli/services/attach-fallback.mjs +54 -54
- package/hub/team/cli/services/member-selector.mjs +30 -30
- package/hub/team/cli/services/native-control.mjs +116 -116
- package/hub/team/cli/services/task-model.mjs +30 -30
- package/hub/team/notify.mjs +1 -1
- package/hub/team/orchestrator.mjs +161 -161
- package/hub/team/runtime-strategy.mjs +74 -0
- package/hub/team/session.mjs +611 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/worktree-lifecycle.mjs +61 -2
- package/hub/tray.mjs +368 -368
- package/hub/workers/codex-mcp.mjs +507 -507
- package/hub/workers/factory.mjs +21 -21
- package/hud/hud-qos-status.mjs +17 -3
- package/hud/mission-board.mjs +53 -0
- package/hud/providers/claude.mjs +95 -22
- package/hud/renderers.mjs +39 -5
- package/mesh/index.mjs +63 -0
- package/mesh/mesh-budget.mjs +128 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +165 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +76 -0
- package/package.json +2 -1
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/demo.mjs +169 -0
- package/scripts/headless-guard.mjs +16 -4
- package/scripts/hub-ensure.mjs +120 -120
- package/scripts/keyword-detector.mjs +272 -272
- package/scripts/keyword-rules-expander.mjs +521 -521
- package/scripts/lib/mcp-server-catalog.mjs +118 -118
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/notion-read.mjs +553 -553
- package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
- package/scripts/tfx-batch-stats.mjs +96 -96
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +0 -1
- package/skills/.omc/state/idle-notif-cooldown.json +0 -3
- package/skills/.omc/state/last-tool-error.json +0 -7
- package/skills/.omc/state/subagent-tracking.json +0 -7
- package/skills/tfx-remote-spawn/references/hosts.json +0 -16
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
# Installation: ~/.config/fish/completions/에 복사
|
|
2
|
-
# e.g., cp /path/to/tfx.fish ~/.config/fish/completions/tfx.fish
|
|
3
|
-
|
|
4
|
-
set -l commands setup doctor multi hub auto codex gemini
|
|
5
|
-
set -l multi_cmds status stop kill attach list
|
|
6
|
-
set -l hub_cmds start stop status restart
|
|
7
|
-
|
|
8
|
-
complete -c tfx -f
|
|
9
|
-
|
|
10
|
-
# Subcommands
|
|
11
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "setup" -d "Setup and sync files"
|
|
12
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "doctor" -d "Diagnose CLI and issues"
|
|
13
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "multi" -d "Multi-CLI team mode"
|
|
14
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "hub" -d "MCP message bus management"
|
|
15
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "auto" -d "Auto mode"
|
|
16
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "codex" -d "Codex mode"
|
|
17
|
-
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "gemini" -d "Gemini mode"
|
|
18
|
-
|
|
19
|
-
# Doctor flags
|
|
20
|
-
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l fix -d "Auto fix issues"
|
|
21
|
-
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l reset -d "Reset all caches"
|
|
22
|
-
|
|
23
|
-
# Multi subcommands
|
|
24
|
-
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "status"
|
|
25
|
-
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "stop"
|
|
26
|
-
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "kill"
|
|
27
|
-
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "attach"
|
|
28
|
-
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "list"
|
|
29
|
-
|
|
30
|
-
# Hub subcommands
|
|
31
|
-
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "start"
|
|
32
|
-
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "stop"
|
|
33
|
-
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "status"
|
|
34
|
-
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "restart"
|
|
35
|
-
|
|
36
|
-
# Global or multi flags
|
|
37
|
-
set -l flags_cond "__fish_seen_subcommand_from setup multi auto codex gemini"
|
|
38
|
-
complete -c tfx -n "$flags_cond" -l thorough -d "Thorough execution"
|
|
39
|
-
complete -c tfx -n "$flags_cond" -l quick -d "Quick execution"
|
|
40
|
-
complete -c tfx -n "$flags_cond" -l tmux -d "Use tmux"
|
|
41
|
-
complete -c tfx -n "$flags_cond" -l psmux -d "Use psmux"
|
|
42
|
-
complete -c tfx -n "$flags_cond" -l agents -d "Specify agents"
|
|
43
|
-
complete -c tfx -n "$flags_cond" -l no-attach -d "Do not attach"
|
|
44
|
-
complete -c tfx -n "$flags_cond" -l timeout -d "Set timeout"
|
|
1
|
+
# Installation: ~/.config/fish/completions/에 복사
|
|
2
|
+
# e.g., cp /path/to/tfx.fish ~/.config/fish/completions/tfx.fish
|
|
3
|
+
|
|
4
|
+
set -l commands setup doctor multi hub auto codex gemini
|
|
5
|
+
set -l multi_cmds status stop kill attach list
|
|
6
|
+
set -l hub_cmds start stop status restart
|
|
7
|
+
|
|
8
|
+
complete -c tfx -f
|
|
9
|
+
|
|
10
|
+
# Subcommands
|
|
11
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "setup" -d "Setup and sync files"
|
|
12
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "doctor" -d "Diagnose CLI and issues"
|
|
13
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "multi" -d "Multi-CLI team mode"
|
|
14
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "hub" -d "MCP message bus management"
|
|
15
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "auto" -d "Auto mode"
|
|
16
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "codex" -d "Codex mode"
|
|
17
|
+
complete -c tfx -n "not __fish_seen_subcommand_from $commands" -a "gemini" -d "Gemini mode"
|
|
18
|
+
|
|
19
|
+
# Doctor flags
|
|
20
|
+
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l fix -d "Auto fix issues"
|
|
21
|
+
complete -c tfx -n "__fish_seen_subcommand_from doctor" -l reset -d "Reset all caches"
|
|
22
|
+
|
|
23
|
+
# Multi subcommands
|
|
24
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "status"
|
|
25
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "stop"
|
|
26
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "kill"
|
|
27
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "attach"
|
|
28
|
+
complete -c tfx -n "__fish_seen_subcommand_from multi; and not __fish_seen_subcommand_from $multi_cmds" -a "list"
|
|
29
|
+
|
|
30
|
+
# Hub subcommands
|
|
31
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "start"
|
|
32
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "stop"
|
|
33
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "status"
|
|
34
|
+
complete -c tfx -n "__fish_seen_subcommand_from hub; and not __fish_seen_subcommand_from $hub_cmds" -a "restart"
|
|
35
|
+
|
|
36
|
+
# Global or multi flags
|
|
37
|
+
set -l flags_cond "__fish_seen_subcommand_from setup multi auto codex gemini"
|
|
38
|
+
complete -c tfx -n "$flags_cond" -l thorough -d "Thorough execution"
|
|
39
|
+
complete -c tfx -n "$flags_cond" -l quick -d "Quick execution"
|
|
40
|
+
complete -c tfx -n "$flags_cond" -l tmux -d "Use tmux"
|
|
41
|
+
complete -c tfx -n "$flags_cond" -l psmux -d "Use psmux"
|
|
42
|
+
complete -c tfx -n "$flags_cond" -l agents -d "Specify agents"
|
|
43
|
+
complete -c tfx -n "$flags_cond" -l no-attach -d "Do not attach"
|
|
44
|
+
complete -c tfx -n "$flags_cond" -l timeout -d "Set timeout"
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
#compdef tfx
|
|
2
|
-
# Installation: fpath에 추가 후 compinit
|
|
3
|
-
# e.g., fpath=(/path/to/dir $fpath) && compinit
|
|
4
|
-
|
|
5
|
-
_tfx() {
|
|
6
|
-
local line state
|
|
7
|
-
local -a commands multi_cmds hub_cmds flags
|
|
8
|
-
|
|
9
|
-
commands=(
|
|
10
|
-
'setup:Setup and sync files'
|
|
11
|
-
'doctor:Diagnose CLI and issues'
|
|
12
|
-
'multi:Multi-CLI team mode'
|
|
13
|
-
'hub:MCP message bus management'
|
|
14
|
-
'auto:Auto mode'
|
|
15
|
-
'codex:Codex mode'
|
|
16
|
-
'gemini:Gemini mode'
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
multi_cmds=(
|
|
20
|
-
'status:Show status'
|
|
21
|
-
'stop:Stop multi'
|
|
22
|
-
'kill:Kill multi'
|
|
23
|
-
'attach:Attach to multi'
|
|
24
|
-
'list:List multi sessions'
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
hub_cmds=(
|
|
28
|
-
'start:Start hub'
|
|
29
|
-
'stop:Stop hub'
|
|
30
|
-
'status:Show hub status'
|
|
31
|
-
'restart:Restart hub'
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
_arguments -C \
|
|
35
|
-
'1: :->cmds' \
|
|
36
|
-
'*: :->args'
|
|
37
|
-
|
|
38
|
-
case $state in
|
|
39
|
-
cmds)
|
|
40
|
-
_describe -t commands 'tfx commands' commands
|
|
41
|
-
;;
|
|
42
|
-
args)
|
|
43
|
-
case $words[2] in
|
|
44
|
-
multi)
|
|
45
|
-
if (( CURRENT == 3 )) && [[ $words[CURRENT] != -* ]]; then
|
|
46
|
-
_describe -t multi_cmds 'multi commands' multi_cmds
|
|
47
|
-
else
|
|
48
|
-
_arguments \
|
|
49
|
-
'--thorough[Thorough execution]' \
|
|
50
|
-
'--quick[Quick execution]' \
|
|
51
|
-
'--tmux[Use tmux]' \
|
|
52
|
-
'--psmux[Use psmux]' \
|
|
53
|
-
'--agents[Specify agents]' \
|
|
54
|
-
'--no-attach[Do not attach]' \
|
|
55
|
-
'--timeout[Set timeout]'
|
|
56
|
-
fi
|
|
57
|
-
;;
|
|
58
|
-
hub)
|
|
59
|
-
if (( CURRENT == 3 )); then
|
|
60
|
-
_describe -t hub_cmds 'hub commands' hub_cmds
|
|
61
|
-
fi
|
|
62
|
-
;;
|
|
63
|
-
doctor)
|
|
64
|
-
_arguments \
|
|
65
|
-
'--fix[Auto fix issues]' \
|
|
66
|
-
'--reset[Reset all caches]'
|
|
67
|
-
;;
|
|
68
|
-
*)
|
|
69
|
-
_arguments \
|
|
70
|
-
'--thorough[Thorough execution]' \
|
|
71
|
-
'--quick[Quick execution]' \
|
|
72
|
-
'--tmux[Use tmux]' \
|
|
73
|
-
'--psmux[Use psmux]' \
|
|
74
|
-
'--agents[Specify agents]' \
|
|
75
|
-
'--no-attach[Do not attach]' \
|
|
76
|
-
'--timeout[Set timeout]'
|
|
77
|
-
;;
|
|
78
|
-
esac
|
|
79
|
-
;;
|
|
80
|
-
esac
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
_tfx "$@"
|
|
1
|
+
#compdef tfx
|
|
2
|
+
# Installation: fpath에 추가 후 compinit
|
|
3
|
+
# e.g., fpath=(/path/to/dir $fpath) && compinit
|
|
4
|
+
|
|
5
|
+
_tfx() {
|
|
6
|
+
local line state
|
|
7
|
+
local -a commands multi_cmds hub_cmds flags
|
|
8
|
+
|
|
9
|
+
commands=(
|
|
10
|
+
'setup:Setup and sync files'
|
|
11
|
+
'doctor:Diagnose CLI and issues'
|
|
12
|
+
'multi:Multi-CLI team mode'
|
|
13
|
+
'hub:MCP message bus management'
|
|
14
|
+
'auto:Auto mode'
|
|
15
|
+
'codex:Codex mode'
|
|
16
|
+
'gemini:Gemini mode'
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
multi_cmds=(
|
|
20
|
+
'status:Show status'
|
|
21
|
+
'stop:Stop multi'
|
|
22
|
+
'kill:Kill multi'
|
|
23
|
+
'attach:Attach to multi'
|
|
24
|
+
'list:List multi sessions'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
hub_cmds=(
|
|
28
|
+
'start:Start hub'
|
|
29
|
+
'stop:Stop hub'
|
|
30
|
+
'status:Show hub status'
|
|
31
|
+
'restart:Restart hub'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_arguments -C \
|
|
35
|
+
'1: :->cmds' \
|
|
36
|
+
'*: :->args'
|
|
37
|
+
|
|
38
|
+
case $state in
|
|
39
|
+
cmds)
|
|
40
|
+
_describe -t commands 'tfx commands' commands
|
|
41
|
+
;;
|
|
42
|
+
args)
|
|
43
|
+
case $words[2] in
|
|
44
|
+
multi)
|
|
45
|
+
if (( CURRENT == 3 )) && [[ $words[CURRENT] != -* ]]; then
|
|
46
|
+
_describe -t multi_cmds 'multi commands' multi_cmds
|
|
47
|
+
else
|
|
48
|
+
_arguments \
|
|
49
|
+
'--thorough[Thorough execution]' \
|
|
50
|
+
'--quick[Quick execution]' \
|
|
51
|
+
'--tmux[Use tmux]' \
|
|
52
|
+
'--psmux[Use psmux]' \
|
|
53
|
+
'--agents[Specify agents]' \
|
|
54
|
+
'--no-attach[Do not attach]' \
|
|
55
|
+
'--timeout[Set timeout]'
|
|
56
|
+
fi
|
|
57
|
+
;;
|
|
58
|
+
hub)
|
|
59
|
+
if (( CURRENT == 3 )); then
|
|
60
|
+
_describe -t hub_cmds 'hub commands' hub_cmds
|
|
61
|
+
fi
|
|
62
|
+
;;
|
|
63
|
+
doctor)
|
|
64
|
+
_arguments \
|
|
65
|
+
'--fix[Auto fix issues]' \
|
|
66
|
+
'--reset[Reset all caches]'
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
_arguments \
|
|
70
|
+
'--thorough[Thorough execution]' \
|
|
71
|
+
'--quick[Quick execution]' \
|
|
72
|
+
'--tmux[Use tmux]' \
|
|
73
|
+
'--psmux[Use psmux]' \
|
|
74
|
+
'--agents[Specify agents]' \
|
|
75
|
+
'--no-attach[Do not attach]' \
|
|
76
|
+
'--timeout[Set timeout]'
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_tfx "$@"
|
package/scripts/demo.mjs
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import childProcess from "node:child_process";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
|
|
6
|
+
const { values: flags } = parseArgs({
|
|
7
|
+
options: {
|
|
8
|
+
"dry-run": { type: "boolean", default: false },
|
|
9
|
+
keep: { type: "boolean", default: false },
|
|
10
|
+
},
|
|
11
|
+
strict: false,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const SESSION_NAME = "triflux-demo";
|
|
15
|
+
|
|
16
|
+
const WORKERS = [
|
|
17
|
+
{
|
|
18
|
+
pane: 0,
|
|
19
|
+
agent: "codex",
|
|
20
|
+
messages: [
|
|
21
|
+
"[codex] Analyzing auth module...",
|
|
22
|
+
"[codex] Refactoring JWT validation...",
|
|
23
|
+
"[codex] Done ✓",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
pane: 1,
|
|
28
|
+
agent: "gemini",
|
|
29
|
+
messages: [
|
|
30
|
+
"[gemini] Reviewing UI components...",
|
|
31
|
+
"[gemini] Optimizing render cycle...",
|
|
32
|
+
"[gemini] Done ✓",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
pane: 2,
|
|
37
|
+
agent: "claude",
|
|
38
|
+
messages: [
|
|
39
|
+
"[claude] Security audit in progress...",
|
|
40
|
+
"[claude] Found 0 vulnerabilities",
|
|
41
|
+
"[claude] Done ✓",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export function checkPsmux(opts = {}) {
|
|
47
|
+
if (opts.dryRun) return false;
|
|
48
|
+
try {
|
|
49
|
+
childProcess.execFileSync("psmux", ["-V"], { encoding: "utf8", stdio: "pipe" });
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createDemoSession(sessionName, opts = {}) {
|
|
57
|
+
if (opts.dryRun) {
|
|
58
|
+
console.log(`[dry-run] psmux new-session -d -s ${sessionName}`);
|
|
59
|
+
console.log(`[dry-run] psmux split-window -h -t ${sessionName}`);
|
|
60
|
+
console.log(`[dry-run] psmux split-window -h -t ${sessionName}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
childProcess.execFileSync("psmux", ["new-session", "-d", "-s", sessionName], { stdio: "pipe" });
|
|
64
|
+
childProcess.execFileSync("psmux", ["split-window", "-h", "-t", sessionName], { stdio: "pipe" });
|
|
65
|
+
childProcess.execFileSync("psmux", ["split-window", "-h", "-t", sessionName], { stdio: "pipe" });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function simulateWorker(pane, agentName, messages, opts = {}) {
|
|
69
|
+
const sessionName = opts.sessionName || SESSION_NAME;
|
|
70
|
+
for (const msg of messages) {
|
|
71
|
+
const escapedMsg = msg.replace(/'/g, "'\\''");
|
|
72
|
+
if (opts.dryRun) {
|
|
73
|
+
console.log(`[dry-run] psmux send-keys -t ${sessionName}:0.${pane} "echo '${escapedMsg}'" Enter`);
|
|
74
|
+
} else {
|
|
75
|
+
childProcess.execFileSync(
|
|
76
|
+
"psmux",
|
|
77
|
+
["send-keys", "-t", `${sessionName}:0.${pane}`, `echo '${escapedMsg}'`, "Enter"],
|
|
78
|
+
{ stdio: "pipe" },
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function showSummary() {
|
|
85
|
+
const lines = [
|
|
86
|
+
"",
|
|
87
|
+
"=== triflux demo summary ===",
|
|
88
|
+
" codex → JWT auth refactor [done]",
|
|
89
|
+
" gemini → UI render optimize [done]",
|
|
90
|
+
" claude → Security audit [done]",
|
|
91
|
+
"============================",
|
|
92
|
+
"",
|
|
93
|
+
];
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
console.log(line);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function cleanup(sessionName, opts = {}) {
|
|
100
|
+
if (opts.dryRun) {
|
|
101
|
+
console.log(`[dry-run] psmux kill-session -t ${sessionName}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
childProcess.execFileSync("psmux", ["kill-session", "-t", sessionName], { stdio: "pipe" });
|
|
106
|
+
} catch {
|
|
107
|
+
// session may already be gone
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function wait(ms) {
|
|
112
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function main() {
|
|
116
|
+
const { values: flags } = parseArgs({
|
|
117
|
+
options: {
|
|
118
|
+
"dry-run": { type: "boolean", default: false },
|
|
119
|
+
keep: { type: "boolean", default: false },
|
|
120
|
+
},
|
|
121
|
+
strict: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const psmuxAvailable = checkPsmux({ dryRun: flags["dry-run"] });
|
|
125
|
+
const dryRun = flags["dry-run"] || !psmuxAvailable;
|
|
126
|
+
|
|
127
|
+
if (!psmuxAvailable && !flags["dry-run"]) {
|
|
128
|
+
console.log("[demo] psmux not found — switching to dry-run mode");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const opts = {
|
|
132
|
+
dryRun,
|
|
133
|
+
keep: flags.keep,
|
|
134
|
+
sessionName: SESSION_NAME,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
createDemoSession(SESSION_NAME, opts);
|
|
138
|
+
|
|
139
|
+
for (const { pane, agent, messages } of WORKERS) {
|
|
140
|
+
simulateWorker(pane, agent, messages, opts);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!opts.dryRun) {
|
|
144
|
+
await wait(2000);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
showSummary();
|
|
148
|
+
|
|
149
|
+
if (!opts.keep) {
|
|
150
|
+
cleanup(SESSION_NAME, opts);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Only run main when executed directly (not imported as a module)
|
|
155
|
+
// Normalize both paths to forward-slash for cross-platform comparison
|
|
156
|
+
function isDirectExec() {
|
|
157
|
+
if (!process.argv[1]) return false;
|
|
158
|
+
const scriptPath = new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1");
|
|
159
|
+
const argv1 = process.argv[1].replace(/\\/g, "/");
|
|
160
|
+
const norm = scriptPath.replace(/\\/g, "/");
|
|
161
|
+
return argv1 === norm || argv1.endsWith(norm);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (isDirectExec()) {
|
|
165
|
+
main().catch((err) => {
|
|
166
|
+
console.error("demo error:", err.message);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
@@ -202,8 +202,12 @@ async function main() {
|
|
|
202
202
|
// codex/gemini 직접 CLI 호출 → deny (인라인 TFX_ALLOW_DIRECT_CLI=1 우회 허용)
|
|
203
203
|
// 복합 명령(&&, ||, ;, |) 분리 후 각 세그먼트의 커맨드 위치만 검사 (args/quotes 안의 codex는 무시)
|
|
204
204
|
// NOTE: || 는 | 보다 먼저 매칭되므로 logical OR이 단일 pipe로 잘못 분리되지 않음
|
|
205
|
+
// #37 Bug4: gh/git 명령은 본문에 codex/gemini 문자열이 있어도 차단하지 않음
|
|
206
|
+
const SAFE_CMD_RE = /^\s*(?:[\w_]+=\S+\s+)*\s*(gh|git)\b/;
|
|
205
207
|
const cmdParts = cmd.split(/\s*(?:&&|\|\||\||;)\s*/);
|
|
206
208
|
let hasDirectCli = cmdParts.some(part => {
|
|
209
|
+
// gh/git 세그먼트는 건너뜀 (이슈 본문/커밋 메시지 내 codex/gemini 언급은 정상)
|
|
210
|
+
if (SAFE_CMD_RE.test(part)) return false;
|
|
207
211
|
// 1단계: env var prefix 제거 (FOO=bar ...)
|
|
208
212
|
// 2단계: wrapper prefix 제거 (env, command, nohup, timeout N, 절대경로, bash -c/-lc "...")
|
|
209
213
|
const stripped = part
|
|
@@ -216,11 +220,19 @@ async function main() {
|
|
|
216
220
|
});
|
|
217
221
|
// 2차 휴리스틱: 1차 세그먼트 검사를 통과한 간접 실행 패턴 탐지
|
|
218
222
|
// full AST 파서 대신 현실적 위협 벡터만 커버 — eval, subshell, variable 확장
|
|
223
|
+
// 2차 휴리스틱: 간접 실행 패턴 탐지 (eval, subshell, variable 확장)
|
|
219
224
|
if (!hasDirectCli) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
const isAllSafeCmd = cmdParts.every(p => SAFE_CMD_RE.test(p));
|
|
226
|
+
if (isAllSafeCmd) {
|
|
227
|
+
// gh/git 전용: $(codex exec ...) 직접 명령 치환만 차단
|
|
228
|
+
// $(cat <<'EOF'\n...codex exec text...\nEOF) 같은 heredoc 텍스트는 허용
|
|
229
|
+
hasDirectCli = /\$\(\s*(codex\s+exec|gemini\s+(-p|--prompt))\b/i.test(cmd);
|
|
230
|
+
} else {
|
|
231
|
+
hasDirectCli = (
|
|
232
|
+
/\beval\b.*\b(codex\s+exec|gemini\s+(-p|--prompt))\b/i.test(cmd) ||
|
|
233
|
+
/\$[({].*\b(codex\s+exec|gemini\s+(-p|--prompt))\b/i.test(cmd)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
224
236
|
}
|
|
225
237
|
|
|
226
238
|
if (hasDirectCli) {
|