cli-jaw 1.7.10 → 1.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/bin/commands/dispatch.js +8 -1
  2. package/dist/bin/commands/dispatch.js.map +1 -1
  3. package/dist/bin/commands/launchd.js +6 -0
  4. package/dist/bin/commands/launchd.js.map +1 -1
  5. package/dist/bin/postinstall.js +181 -0
  6. package/dist/bin/postinstall.js.map +1 -1
  7. package/dist/lib/mcp/skills-utils.js +5 -1
  8. package/dist/lib/mcp/skills-utils.js.map +1 -1
  9. package/dist/server.js +4 -0
  10. package/dist/server.js.map +1 -1
  11. package/dist/src/agent/args.js +11 -6
  12. package/dist/src/agent/args.js.map +1 -1
  13. package/dist/src/agent/spawn.js +30 -1
  14. package/dist/src/agent/spawn.js.map +1 -1
  15. package/dist/src/cli/compact.js +32 -36
  16. package/dist/src/cli/compact.js.map +1 -1
  17. package/dist/src/core/boss-auth.js +38 -0
  18. package/dist/src/core/boss-auth.js.map +1 -0
  19. package/dist/src/core/compact.js +205 -0
  20. package/dist/src/core/compact.js.map +1 -1
  21. package/dist/src/core/employees.js +115 -0
  22. package/dist/src/core/employees.js.map +1 -1
  23. package/dist/src/core/launchd-plist.js +27 -10
  24. package/dist/src/core/launchd-plist.js.map +1 -1
  25. package/dist/src/core/main-session.js +14 -0
  26. package/dist/src/core/main-session.js.map +1 -1
  27. package/dist/src/core/runtime-path.js +29 -0
  28. package/dist/src/core/runtime-path.js.map +1 -1
  29. package/dist/src/orchestrator/distribute.js +40 -30
  30. package/dist/src/orchestrator/distribute.js.map +1 -1
  31. package/dist/src/orchestrator/worker-registry.js +17 -0
  32. package/dist/src/orchestrator/worker-registry.js.map +1 -1
  33. package/dist/src/prompt/builder.js +52 -2
  34. package/dist/src/prompt/builder.js.map +1 -1
  35. package/dist/src/prompt/templates/a1-system.md +87 -26
  36. package/dist/src/prompt/templates/control-system.md +48 -0
  37. package/dist/src/routes/orchestrate.js +86 -7
  38. package/dist/src/routes/orchestrate.js.map +1 -1
  39. package/package.json +1 -1
  40. package/scripts/darwin/spike-tcc-attribution.sh +333 -0
  41. package/scripts/darwin/test-computeruse-e2e.sh +204 -0
@@ -4,10 +4,12 @@ import { getLiveRun } from '../agent/live-run-state.js';
4
4
  import { orchestrateContinue, orchestrateReset } from '../orchestrator/pipeline.js';
5
5
  import { getState, getCtx, setState, resetState, canTransition } from '../orchestrator/state-machine.js';
6
6
  import { resolveOrcScope } from '../orchestrator/scope.js';
7
- import { getActiveWorkers, claimWorker, finishWorker, failWorker, markWorkerReplayed } from '../orchestrator/worker-registry.js';
7
+ import { getActiveWorkers, claimWorker, finishWorker, failWorker, markWorkerReplayed, getWorkerSlot, WorkerBusyError } from '../orchestrator/worker-registry.js';
8
8
  import { findEmployee, runSingleAgent } from '../orchestrator/distribute.js';
9
9
  import { getEmployees } from '../core/db.js';
10
10
  import { settings } from '../core/config.js';
11
+ import { verifyBossToken } from '../core/boss-auth.js';
12
+ import { resolveDispatchableEmployee, checkRuntimeHints } from '../core/employees.js';
11
13
  function getRuntimeSnapshot() {
12
14
  return {
13
15
  uptimeSec: Math.floor(process.uptime()),
@@ -56,8 +58,12 @@ export function registerOrchestrateRoutes(app, requireAuth) {
56
58
  });
57
59
  // Pipe-mode employee dispatch
58
60
  app.post('/api/orchestrate/dispatch', requireAuth, async (req, res) => {
59
- if (String(req.headers['x-jaw-dispatch-source'] || '').toLowerCase() === 'employee') {
60
- return fail(res, 409, 'Employee self-dispatch is blocked in employee sessions');
61
+ // Phase 8: server-authoritative dispatch guard. Boss-only token required.
62
+ // Employees do not have this token (stripped in spawn.ts makeCleanEnv).
63
+ const bossToken = String(req.headers['x-jaw-boss-token'] || '');
64
+ if (!verifyBossToken(bossToken)) {
65
+ console.warn(`[dispatch:deny] ip=${req.ip} ua=${String(req.headers['user-agent'] || '').slice(0, 80)}`);
66
+ return fail(res, 403, 'Dispatch requires boss-scoped token. Employees cannot dispatch.');
61
67
  }
62
68
  const { agent: agentName, task, phase } = req.body || {};
63
69
  if (!agentName || !task)
@@ -67,10 +73,59 @@ export function registerOrchestrateRoutes(app, requireAuth) {
67
73
  const currentOrcState = getState(dispatchScope);
68
74
  const resolvedPhase = phase ?? PABCD_PHASE_MAP[currentOrcState] ?? 3;
69
75
  const emps = getEmployees.all();
70
- const emp = findEmployee(emps, { agent: agentName });
76
+ // Try DB first (preserves existing id-based matching), then fall
77
+ // through to static employees for entries like Control.
78
+ let emp = findEmployee(emps, { agent: agentName });
79
+ let staticSpec = null;
80
+ if (!emp) {
81
+ staticSpec = resolveDispatchableEmployee(agentName, emps);
82
+ if (staticSpec)
83
+ emp = staticSpec.row;
84
+ }
85
+ else {
86
+ staticSpec = resolveDispatchableEmployee(emp.name, emps);
87
+ }
71
88
  if (!emp)
72
89
  return fail(res, 404, `Employee not found: ${agentName}`);
73
- const slot = claimWorker(emp, task);
90
+ // Runtime preflight for static employees (darwin/CUA/Jaw.app).
91
+ // Hard-fail on missing prerequisites; warn-only on optional ones.
92
+ if (staticSpec?.spec) {
93
+ const checks = checkRuntimeHints(staticSpec.spec);
94
+ if (checks.fail.length > 0) {
95
+ return fail(res, 412, `Preconditions not met: ${checks.fail.join('; ')}`);
96
+ }
97
+ for (const w of checks.warn)
98
+ console.warn(`[dispatch] ⚠️ ${w}`);
99
+ }
100
+ // Phase 7-2: reject concurrent dispatch of the same employee.
101
+ // Caller should poll GET /api/orchestrate/worker/:agentId/result.
102
+ let slot;
103
+ try {
104
+ slot = claimWorker(emp, task);
105
+ }
106
+ catch (err) {
107
+ if (err instanceof WorkerBusyError) {
108
+ return res.status(409).json({
109
+ ok: false,
110
+ error: 'worker_busy',
111
+ existing: {
112
+ agentId: err.existing.agentId,
113
+ employeeName: err.existing.employeeName,
114
+ task: err.existing.task.slice(0, 200),
115
+ startedAt: err.existing.startedAt,
116
+ },
117
+ hint: 'Poll GET /api/orchestrate/worker/:agentId/result for the in-flight run.',
118
+ });
119
+ }
120
+ throw err;
121
+ }
122
+ // Phase 7-1: detect client disconnect so we keep pendingReplay=true
123
+ // (so the next boss turn can drain the result) instead of discarding.
124
+ let clientDisconnected = false;
125
+ req.on('close', () => {
126
+ if (!res.writableEnded)
127
+ clientDisconnected = true;
128
+ });
74
129
  try {
75
130
  const ap = {
76
131
  agent: emp.name, role: emp.role || 'general developer',
@@ -80,13 +135,37 @@ export function registerOrchestrateRoutes(app, requireAuth) {
80
135
  };
81
136
  const result = await runSingleAgent(ap, emp, {}, 1, { origin: 'api' }, []);
82
137
  finishWorker(slot.agentId, result.text || '');
83
- markWorkerReplayed(slot.agentId);
138
+ if (clientDisconnected) {
139
+ console.warn(`[dispatch] client disconnected — keeping pendingReplay for ${slot.agentId}`);
140
+ // pendingReplay stays true; next boss orchestrate() call drains it.
141
+ return;
142
+ }
143
+ // Only clear replay flag after response is actually flushed to client.
144
+ res.on('finish', () => markWorkerReplayed(slot.agentId));
84
145
  res.json({ ok: true, result });
85
146
  }
86
147
  catch (err) {
87
148
  failWorker(slot.agentId, err.message);
88
- res.status(500).json({ ok: false, error: err.message });
149
+ if (!res.writableEnded)
150
+ res.status(500).json({ ok: false, error: err.message });
151
+ }
152
+ });
153
+ // Phase 7-4: explicit result polling for 409 retries and reconnects.
154
+ app.get('/api/orchestrate/worker/:agentId/result', requireAuth, (req, res) => {
155
+ const agentId = String(req.params.agentId || '');
156
+ if (!agentId)
157
+ return fail(res, 400, 'missing agentId');
158
+ const slot = getWorkerSlot(agentId);
159
+ if (!slot)
160
+ return fail(res, 404, 'worker not found');
161
+ if (slot.state === 'running') {
162
+ return res.json({ ok: true, state: 'running', startedAt: slot.startedAt, task: slot.task });
163
+ }
164
+ // Consume pending replay — subsequent polls will return 404.
165
+ if (slot.state === 'done' && slot.pendingReplay) {
166
+ markWorkerReplayed(slot.agentId);
89
167
  }
168
+ return res.json({ ok: true, state: slot.state, result: slot.result });
90
169
  });
91
170
  app.put('/api/orchestrate/state', requireAuth, (req, res) => {
92
171
  const target = String(req.body?.state || '').toUpperCase();
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../../src/routes/orchestrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../../src/routes/orchestrate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-jaw",
3
- "version": "1.7.10",
3
+ "version": "1.7.11",
4
4
  "description": "Personal AI assistant powered by 5 engines (Claude, Codex, Gemini, OpenCode, Copilot) — Web, Terminal, Telegram, and Discord interfaces with 107 built-in skills",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,333 @@
1
+ #!/bin/bash
2
+ # spike-tcc-attribution.sh
3
+ # 목적: macOS TCC가 `Jaw.app` bundle identity로 Automation 권한을 귀속시키는지 실측 검증.
4
+ # 이 스크립트는 31/32 계획의 핵심 가정(shell-shim launcher → com.cli-jaw.agent 책임)을
5
+ # 실제 하드웨어에서 확인한다. 계획을 B로 진입시키기 전 반드시 1회 이상 PASS해야 한다.
6
+ #
7
+ # 사용:
8
+ # bash scripts/darwin/spike-tcc-attribution.sh # dry-run check
9
+ # bash scripts/darwin/spike-tcc-attribution.sh --build # 임시 Jaw.app 번들 생성 후 검증
10
+ # bash scripts/darwin/spike-tcc-attribution.sh --cleanup # 생성된 임시 번들 제거
11
+ #
12
+ # 출력 원칙:
13
+ # - 각 단계마다 PASS/FAIL을 찍고 실패 시 즉시 exit (fail-fast 규칙)
14
+ # - 마지막에 `RESULT=...` 한 줄을 찍어 호출자가 grep하기 쉽게 한다
15
+
16
+ set -euo pipefail
17
+
18
+ # ── Colors ──
19
+ CYAN='\033[0;36m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'
20
+ DIM='\033[2m'; BOLD='\033[1m'; NC='\033[0m'
21
+ info() { echo -e "${CYAN}▸${NC} $*"; }
22
+ ok() { echo -e "${GREEN}✔${NC} $*"; }
23
+ warn() { echo -e "${YELLOW}⚠${NC} $*"; }
24
+ fail() { echo -e "${RED}✖${NC} $*"; echo "RESULT=FAIL"; exit 1; }
25
+
26
+ if [[ "$(uname -s)" != "Darwin" ]]; then fail "macOS 전용"; fi
27
+
28
+ MODE="${1:-check}"
29
+ SPIKE_APP_DIR="/Applications/JawSpike.app"
30
+ SPIKE_BUNDLE_ID="com.cli-jaw.spike"
31
+
32
+ print_header() {
33
+ echo ""
34
+ echo -e "${CYAN}${BOLD}🔬 TCC Attribution Spike${NC}"
35
+ echo -e "${DIM} 검증: shell-shim launcher가 $SPIKE_BUNDLE_ID 로 귀속되는가${NC}"
36
+ echo ""
37
+ }
38
+
39
+ step_check_prereqs() {
40
+ info "1/6 전제 조건 점검"
41
+ command -v osascript >/dev/null || fail "osascript 없음"
42
+ [[ -d "/Applications/Google Chrome.app" ]] || fail "Google Chrome 미설치 — 설치 후 재시도"
43
+ command -v /usr/libexec/PlistBuddy >/dev/null || fail "PlistBuddy 없음"
44
+ ok "prereq OK"
45
+ }
46
+
47
+ step_probe_path_sources() {
48
+ info "2/6 CLI binary PATH 감지 (bun / npm-global / nvm)"
49
+ BUN_BIN_DIR=""
50
+ NPM_BIN_DIR=""
51
+ NVM_BIN_DIR=""
52
+ if command -v bun >/dev/null; then BUN_BIN_DIR="$(dirname "$(command -v bun)")"; fi
53
+ if command -v npm >/dev/null; then NPM_BIN_DIR="$(npm config get prefix 2>/dev/null)/bin"; fi
54
+ if [[ -n "${NVM_DIR:-}" && -d "$NVM_DIR" ]]; then
55
+ NVM_BIN_DIR="$(ls -dt "$NVM_DIR"/versions/node/*/bin 2>/dev/null | head -1 || true)"
56
+ fi
57
+
58
+ echo " BUN: ${BUN_BIN_DIR:-∅}"
59
+ echo " NPM: ${NPM_BIN_DIR:-∅}"
60
+ echo " NVM: ${NVM_BIN_DIR:-∅}"
61
+
62
+ # cli-jaw가 실제로 어디서 발견되는지
63
+ JAW_BIN="$(command -v cli-jaw || true)"
64
+ [[ -n "$JAW_BIN" ]] || fail "cli-jaw가 현재 PATH에 없음 — npm i -g cli-jaw 먼저"
65
+ JAW_BIN_DIR="$(dirname "$JAW_BIN")"
66
+ echo " cli-jaw: $JAW_BIN"
67
+
68
+ # PATH 머지 (중복 제거, 존재하는 것만)
69
+ MERGED_PATH=""
70
+ for d in "$JAW_BIN_DIR" "$BUN_BIN_DIR" "$NPM_BIN_DIR" "$NVM_BIN_DIR" \
71
+ "/opt/homebrew/bin" "/usr/local/bin" "/usr/bin" "/bin"; do
72
+ [[ -z "$d" || ! -d "$d" ]] && continue
73
+ case ":$MERGED_PATH:" in *":$d:"*) ;; *) MERGED_PATH="${MERGED_PATH:+$MERGED_PATH:}$d";; esac
74
+ done
75
+ echo " MERGED_PATH=$MERGED_PATH"
76
+ ok "PATH sources detected"
77
+ }
78
+
79
+ build_spike_app() {
80
+ info "3/6 임시 Jaw.app (JawSpike.app) 생성"
81
+ if [[ -d "$SPIKE_APP_DIR" ]]; then
82
+ warn "기존 $SPIKE_APP_DIR 감지 — 삭제 후 재생성"
83
+ rm -rf "$SPIKE_APP_DIR"
84
+ fi
85
+
86
+ mkdir -p "$SPIKE_APP_DIR/Contents/MacOS"
87
+ mkdir -p "$SPIKE_APP_DIR/Contents/Resources"
88
+
89
+ cat > "$SPIKE_APP_DIR/Contents/Info.plist" <<PLIST
90
+ <?xml version="1.0" encoding="UTF-8"?>
91
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
92
+ <plist version="1.0">
93
+ <dict>
94
+ <key>CFBundleIdentifier</key><string>$SPIKE_BUNDLE_ID</string>
95
+ <key>CFBundleName</key><string>JawSpike</string>
96
+ <key>CFBundleDisplayName</key><string>Jaw Spike</string>
97
+ <key>CFBundleExecutable</key><string>jaw-spike-launcher</string>
98
+ <key>CFBundlePackageType</key><string>APPL</string>
99
+ <key>CFBundleShortVersionString</key><string>0.0.1</string>
100
+ <key>CFBundleVersion</key><string>0.0.1</string>
101
+ <key>LSBackgroundOnly</key><true/>
102
+ <key>LSUIElement</key><true/>
103
+ <key>NSAppleEventsUsageDescription</key><string>Jaw Spike — TCC attribution test</string>
104
+ </dict>
105
+ </plist>
106
+ PLIST
107
+
108
+ cat > "$SPIKE_APP_DIR/Contents/MacOS/jaw-spike-launcher" <<LAUNCHER
109
+ #!/bin/bash
110
+ # PATH pin: CLI discovery from bun / npm / nvm
111
+ export PATH="$MERGED_PATH"
112
+ # 실제 책임 주체가 누구인지 보기 위해 osascript를 직접 실행
113
+ exec /usr/bin/osascript -e 'tell application "Google Chrome" to get URL of active tab of front window'
114
+ LAUNCHER
115
+ chmod +x "$SPIKE_APP_DIR/Contents/MacOS/jaw-spike-launcher"
116
+
117
+ # Launch Services 등록
118
+ /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
119
+ -f -R "$SPIKE_APP_DIR" 2>/dev/null || warn "lsregister 실패 (무시 가능)"
120
+ xattr -dr com.apple.quarantine "$SPIKE_APP_DIR" 2>/dev/null || true
121
+
122
+ ok "JawSpike.app 생성됨: $SPIKE_APP_DIR"
123
+ }
124
+
125
+ probe_attribution() {
126
+ info "4/6 첫 실행 (TCC 프롬프트 예상)"
127
+ echo ""
128
+ echo -e "${YELLOW} 프롬프트가 뜨면 '허용'을 누르세요. 이름은 '${BOLD}Jaw Spike${NC}${YELLOW}'로 보여야 합니다.${NC}"
129
+ echo -e "${YELLOW} 만약 'Terminal' 또는 'osascript'로 뜨면 귀속 실패입니다.${NC}"
130
+ echo ""
131
+ read -r -p " 계속하려면 Enter..." _
132
+
133
+ # open -a는 LaunchServices를 통해 번들을 실행 → responsibility가 번들로 귀속됨
134
+ if ! open -W -a "$SPIKE_APP_DIR" 2>&1; then
135
+ warn "open -W가 non-zero — 권한 거부 또는 Chrome 비실행 상태일 수 있음"
136
+ fi
137
+
138
+ info "5/6 권한 DB에서 귀속 주체 조회"
139
+ DB="$HOME/Library/Application Support/com.apple.TCC/TCC.db"
140
+ if [[ ! -f "$DB" ]]; then warn "유저 TCC.db 없음 (조회 스킵)"; return; fi
141
+
142
+ # kTCCServiceAppleEvents 레코드에서 client 필드 확인 (read-only)
143
+ ATTR=$(sqlite3 "$DB" \
144
+ "SELECT client FROM access WHERE service='kTCCServiceAppleEvents' AND client LIKE '%cli-jaw%' OR client LIKE '%jaw-spike%';" \
145
+ 2>/dev/null || true)
146
+
147
+ if [[ -z "$ATTR" ]]; then
148
+ warn "TCC.db에 $SPIKE_BUNDLE_ID 관련 레코드 없음 — 아직 허용이 기록되지 않았거나 귀속 실패"
149
+ else
150
+ echo " TCC client 엔트리:"
151
+ echo "$ATTR" | sed 's/^/ /'
152
+ fi
153
+ }
154
+
155
+ verdict() {
156
+ info "6/6 결론 출력"
157
+ DB="$HOME/Library/Application Support/com.apple.TCC/TCC.db"
158
+ PASS=false
159
+ if [[ -f "$DB" ]]; then
160
+ if sqlite3 "$DB" "SELECT client FROM access WHERE service='kTCCServiceAppleEvents' AND client='$SPIKE_BUNDLE_ID';" 2>/dev/null | grep -q "$SPIKE_BUNDLE_ID"; then
161
+ PASS=true
162
+ fi
163
+ fi
164
+
165
+ if $PASS; then
166
+ ok "RESULT=PASS — shell-shim launcher가 $SPIKE_BUNDLE_ID 로 귀속됨"
167
+ echo "RESULT=PASS"
168
+ echo " → 31/32 계획의 shell-shim 전략 진행 가능"
169
+ else
170
+ warn "RESULT=INCONCLUSIVE_OR_FAIL"
171
+ echo "RESULT=INCONCLUSIVE"
172
+ echo ""
173
+ echo " 가능한 원인과 대처:"
174
+ echo " 1) 프롬프트에서 거부/무시함 → 재실행"
175
+ echo " 2) Terminal/osascript로 귀속됨 → shell-shim 전략 불가. 대안:"
176
+ echo " a. Swift/C 네이티브 mini-launcher (앱 번들 executable이 실제 네이티브 바이너리)"
177
+ echo " b. launchd ProgramArguments에 Jaw.app 자체를 지정하는 대신"
178
+ echo " /Applications/Jaw.app/Contents/MacOS/jaw-launcher 를 pid 1 자식으로 두기"
179
+ echo " 3) TCC.db 권한 없음 → Full Disk Access가 필요할 수 있음"
180
+ fi
181
+ }
182
+
183
+ build_native_spike() {
184
+ info "3/6 [V2 native] clang 확인 + Obj-C launcher 컴파일"
185
+ if ! command -v /usr/bin/clang >/dev/null; then
186
+ fail "clang 없음 — Xcode Command Line Tools 설치: xcode-select --install"
187
+ fi
188
+ ok "clang: $(/usr/bin/clang --version | head -1)"
189
+
190
+ if [[ -d "$SPIKE_APP_DIR" ]]; then
191
+ warn "기존 $SPIKE_APP_DIR 제거"
192
+ rm -rf "$SPIKE_APP_DIR"
193
+ fi
194
+ mkdir -p "$SPIKE_APP_DIR/Contents/MacOS"
195
+ mkdir -p "$SPIKE_APP_DIR/Contents/Resources"
196
+
197
+ cat > "$SPIKE_APP_DIR/Contents/Info.plist" <<PLIST
198
+ <?xml version="1.0" encoding="UTF-8"?>
199
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
200
+ <plist version="1.0">
201
+ <dict>
202
+ <key>CFBundleIdentifier</key><string>$SPIKE_BUNDLE_ID</string>
203
+ <key>CFBundleName</key><string>JawSpike</string>
204
+ <key>CFBundleDisplayName</key><string>Jaw Spike Native</string>
205
+ <key>CFBundleExecutable</key><string>jaw-spike-launcher</string>
206
+ <key>CFBundlePackageType</key><string>APPL</string>
207
+ <key>CFBundleShortVersionString</key><string>0.0.2</string>
208
+ <key>CFBundleVersion</key><string>0.0.2</string>
209
+ <key>LSBackgroundOnly</key><true/>
210
+ <key>LSUIElement</key><true/>
211
+ <key>NSAppleEventsUsageDescription</key><string>Jaw Spike — native launcher TCC test</string>
212
+ </dict>
213
+ </plist>
214
+ PLIST
215
+
216
+ # Objective-C source: Mach-O that fires an AppleEvent (TCC 진짜 트리거)
217
+ local SRC="$(mktemp -t jaw-spike-XXXXXX).m"
218
+ cat > "$SRC" <<'OBJC'
219
+ #import <Foundation/Foundation.h>
220
+ int main(void) {
221
+ @autoreleasepool {
222
+ NSAppleScript *s = [[NSAppleScript alloc] initWithSource:
223
+ @"tell application \"Google Chrome\" to get URL of active tab of front window"];
224
+ NSDictionary *err = nil;
225
+ NSAppleEventDescriptor *r = [s executeAndReturnError:&err];
226
+ if (err) {
227
+ fprintf(stderr, "ERR: %s\n", [[err description] UTF8String]);
228
+ return 1;
229
+ }
230
+ NSString *v = [r stringValue];
231
+ printf("URL=%s\n", v ? [v UTF8String] : "(null)");
232
+ return 0;
233
+ }
234
+ }
235
+ OBJC
236
+
237
+ local OUT="$SPIKE_APP_DIR/Contents/MacOS/jaw-spike-launcher"
238
+ if ! /usr/bin/clang -framework Foundation -O2 -o "$OUT" "$SRC"; then
239
+ rm -f "$SRC"
240
+ fail "clang compile 실패"
241
+ fi
242
+ rm -f "$SRC"
243
+ chmod +x "$OUT"
244
+
245
+ # ad-hoc codesign so TCC accepts the bundle identity stably
246
+ /usr/bin/codesign --force --sign - "$SPIKE_APP_DIR" 2>/dev/null || warn "codesign 실패 (무시 가능)"
247
+
248
+ /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
249
+ -f -R "$SPIKE_APP_DIR" 2>/dev/null || warn "lsregister 실패"
250
+ xattr -dr com.apple.quarantine "$SPIKE_APP_DIR" 2>/dev/null || true
251
+
252
+ ok "Native JawSpike.app 컴파일 완료: $SPIKE_APP_DIR"
253
+ file "$OUT" | sed 's/^/ /'
254
+ }
255
+
256
+ verdict_native() {
257
+ info "6/6 V2 결론"
258
+ DB="$HOME/Library/Application Support/com.apple.TCC/TCC.db"
259
+ if [[ ! -f "$DB" ]]; then
260
+ warn "TCC.db 접근 불가 — Terminal/iTerm에 Full Disk Access 필요"
261
+ echo "RESULT=INCONCLUSIVE_NO_TCC_ACCESS"
262
+ return
263
+ fi
264
+
265
+ local SPIKE_ROW
266
+ SPIKE_ROW=$(sqlite3 "$DB" \
267
+ "SELECT client FROM access WHERE service='kTCCServiceAppleEvents' AND client='$SPIKE_BUNDLE_ID';" \
268
+ 2>/dev/null || true)
269
+
270
+ if [[ -n "$SPIKE_ROW" ]]; then
271
+ ok "RESULT=PASS — Mach-O launcher가 $SPIKE_BUNDLE_ID 로 귀속됨"
272
+ echo "RESULT=PASS"
273
+ echo " → 31/32의 native launcher 전략 진행 가능"
274
+ return
275
+ fi
276
+
277
+ # 실패 상세: 실제 누구에게 귀속됐는지 힌트
278
+ warn "RESULT=FAIL — $SPIKE_BUNDLE_ID TCC 엔트리 없음"
279
+ echo "RESULT=FAIL"
280
+ echo ""
281
+ echo " TCC.db의 최근 AppleEvents 허용 client 상위 10개:"
282
+ sqlite3 "$DB" \
283
+ "SELECT client, datetime(last_modified, 'unixepoch', 'localtime') FROM access WHERE service='kTCCServiceAppleEvents' ORDER BY last_modified DESC LIMIT 10;" \
284
+ 2>/dev/null | sed 's/^/ /'
285
+ echo ""
286
+ echo " 프롬프트 제목이 무엇으로 떴는지(예: 'Jaw Spike Native' vs 'osascript' vs 'bash')가 핵심 증거입니다."
287
+ echo " 'Jaw Spike Native'였다면 유저가 허용만 안 누른 것 — 재실행하세요."
288
+ echo " 다른 이름이었다면 native launcher도 귀속 실패 → 계획 재협상 필요."
289
+ }
290
+
291
+ cleanup() {
292
+ info "cleanup"
293
+ if [[ -d "$SPIKE_APP_DIR" ]]; then
294
+ rm -rf "$SPIKE_APP_DIR"
295
+ ok "$SPIKE_APP_DIR 제거됨"
296
+ else
297
+ ok "이미 깨끗함"
298
+ fi
299
+ }
300
+
301
+ case "$MODE" in
302
+ --cleanup|cleanup) cleanup; exit 0 ;;
303
+ --build|build)
304
+ print_header
305
+ warn "V1 (shell-shim) 모드는 이미 FAIL 판정 — 참고용으로만 실행하세요."
306
+ step_check_prereqs
307
+ step_probe_path_sources
308
+ build_spike_app
309
+ probe_attribution
310
+ verdict
311
+ ;;
312
+ --build-native|build-native|--v2|v2)
313
+ print_header
314
+ echo -e "${DIM} V2: Mach-O Objective-C launcher 기반 재검증${NC}"
315
+ echo ""
316
+ step_check_prereqs
317
+ step_probe_path_sources
318
+ build_native_spike
319
+ probe_attribution
320
+ verdict_native
321
+ ;;
322
+ *)
323
+ print_header
324
+ step_check_prereqs
325
+ step_probe_path_sources
326
+ warn "dry-run 모드 — 번들 생성/권한 요청은 스킵"
327
+ echo ""
328
+ echo "다음 단계:"
329
+ echo " bash $0 --build-native # V2: clang Mach-O 기반 (권장)"
330
+ echo " bash $0 --build # V1: shell-shim (이미 FAIL, 참고용)"
331
+ echo " bash $0 --cleanup # spike 번들 제거"
332
+ ;;
333
+ esac