tailii-host 0.1.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.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/apnsConfigStore.d.ts +22 -0
  4. package/dist/apnsConfigStore.js +50 -0
  5. package/dist/apnsConfigStore.js.map +1 -0
  6. package/dist/apnsJwtProvider.d.ts +50 -0
  7. package/dist/apnsJwtProvider.js +158 -0
  8. package/dist/apnsJwtProvider.js.map +1 -0
  9. package/dist/apnsSender.d.ts +69 -0
  10. package/dist/apnsSender.js +155 -0
  11. package/dist/apnsSender.js.map +1 -0
  12. package/dist/approvalPushNotifier.d.ts +100 -0
  13. package/dist/approvalPushNotifier.js +236 -0
  14. package/dist/approvalPushNotifier.js.map +1 -0
  15. package/dist/broker.d.ts +24 -0
  16. package/dist/broker.js +197 -0
  17. package/dist/broker.js.map +1 -0
  18. package/dist/chatTailController.d.ts +87 -0
  19. package/dist/chatTailController.js +292 -0
  20. package/dist/chatTailController.js.map +1 -0
  21. package/dist/claudeSessionStore.d.ts +15 -0
  22. package/dist/claudeSessionStore.js +201 -0
  23. package/dist/claudeSessionStore.js.map +1 -0
  24. package/dist/cli.d.ts +2 -0
  25. package/dist/cli.js +53 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/codexRolloutTailer.d.ts +56 -0
  28. package/dist/codexRolloutTailer.js +303 -0
  29. package/dist/codexRolloutTailer.js.map +1 -0
  30. package/dist/codexSessionStore.d.ts +23 -0
  31. package/dist/codexSessionStore.js +274 -0
  32. package/dist/codexSessionStore.js.map +1 -0
  33. package/dist/codexUsage.d.ts +16 -0
  34. package/dist/codexUsage.js +144 -0
  35. package/dist/codexUsage.js.map +1 -0
  36. package/dist/deviceTokenStore.d.ts +38 -0
  37. package/dist/deviceTokenStore.js +73 -0
  38. package/dist/deviceTokenStore.js.map +1 -0
  39. package/dist/dirLister.d.ts +9 -0
  40. package/dist/dirLister.js +98 -0
  41. package/dist/dirLister.js.map +1 -0
  42. package/dist/doctor.d.ts +36 -0
  43. package/dist/doctor.js +162 -0
  44. package/dist/doctor.js.map +1 -0
  45. package/dist/engine.d.ts +85 -0
  46. package/dist/engine.js +962 -0
  47. package/dist/engine.js.map +1 -0
  48. package/dist/hook.d.ts +67 -0
  49. package/dist/hook.js +511 -0
  50. package/dist/hook.js.map +1 -0
  51. package/dist/hookSettings.d.ts +40 -0
  52. package/dist/hookSettings.js +128 -0
  53. package/dist/hookSettings.js.map +1 -0
  54. package/dist/idleReaper.d.ts +13 -0
  55. package/dist/idleReaper.js +30 -0
  56. package/dist/idleReaper.js.map +1 -0
  57. package/dist/imageService.d.ts +53 -0
  58. package/dist/imageService.js +280 -0
  59. package/dist/imageService.js.map +1 -0
  60. package/dist/kick.d.ts +8 -0
  61. package/dist/kick.js +60 -0
  62. package/dist/kick.js.map +1 -0
  63. package/dist/launch.d.ts +107 -0
  64. package/dist/launch.js +396 -0
  65. package/dist/launch.js.map +1 -0
  66. package/dist/legacyHomeMigration.d.ts +1 -0
  67. package/dist/legacyHomeMigration.js +23 -0
  68. package/dist/legacyHomeMigration.js.map +1 -0
  69. package/dist/lineWriter.d.ts +9 -0
  70. package/dist/lineWriter.js +19 -0
  71. package/dist/lineWriter.js.map +1 -0
  72. package/dist/mdnsAdvertiser.d.ts +46 -0
  73. package/dist/mdnsAdvertiser.js +268 -0
  74. package/dist/mdnsAdvertiser.js.map +1 -0
  75. package/dist/observationLog.d.ts +54 -0
  76. package/dist/observationLog.js +108 -0
  77. package/dist/observationLog.js.map +1 -0
  78. package/dist/pairingCode.d.ts +60 -0
  79. package/dist/pairingCode.js +282 -0
  80. package/dist/pairingCode.js.map +1 -0
  81. package/dist/paths.d.ts +13 -0
  82. package/dist/paths.js +46 -0
  83. package/dist/paths.js.map +1 -0
  84. package/dist/permissionMode.d.ts +8 -0
  85. package/dist/permissionMode.js +32 -0
  86. package/dist/permissionMode.js.map +1 -0
  87. package/dist/planUsageFetcher.d.ts +35 -0
  88. package/dist/planUsageFetcher.js +172 -0
  89. package/dist/planUsageFetcher.js.map +1 -0
  90. package/dist/protocol.d.ts +329 -0
  91. package/dist/protocol.js +544 -0
  92. package/dist/protocol.js.map +1 -0
  93. package/dist/pushTokenCommand.d.ts +9 -0
  94. package/dist/pushTokenCommand.js +85 -0
  95. package/dist/pushTokenCommand.js.map +1 -0
  96. package/dist/pushTypes.d.ts +41 -0
  97. package/dist/pushTypes.js +68 -0
  98. package/dist/pushTypes.js.map +1 -0
  99. package/dist/sendLog.d.ts +14 -0
  100. package/dist/sendLog.js +64 -0
  101. package/dist/sendLog.js.map +1 -0
  102. package/dist/sessionActivityProvider.d.ts +7 -0
  103. package/dist/sessionActivityProvider.js +44 -0
  104. package/dist/sessionActivityProvider.js.map +1 -0
  105. package/dist/sessionIdleTracker.d.ts +19 -0
  106. package/dist/sessionIdleTracker.js +42 -0
  107. package/dist/sessionIdleTracker.js.map +1 -0
  108. package/dist/sessionListService.d.ts +27 -0
  109. package/dist/sessionListService.js +85 -0
  110. package/dist/sessionListService.js.map +1 -0
  111. package/dist/sessionMetadataStore.d.ts +33 -0
  112. package/dist/sessionMetadataStore.js +122 -0
  113. package/dist/sessionMetadataStore.js.map +1 -0
  114. package/dist/sessionSearch.d.ts +29 -0
  115. package/dist/sessionSearch.js +139 -0
  116. package/dist/sessionSearch.js.map +1 -0
  117. package/dist/setup.d.ts +83 -0
  118. package/dist/setup.js +412 -0
  119. package/dist/setup.js.map +1 -0
  120. package/dist/sleep.d.ts +4 -0
  121. package/dist/sleep.js +25 -0
  122. package/dist/sleep.js.map +1 -0
  123. package/dist/socketPath.d.ts +12 -0
  124. package/dist/socketPath.js +51 -0
  125. package/dist/socketPath.js.map +1 -0
  126. package/dist/subagentTailer.d.ts +15 -0
  127. package/dist/subagentTailer.js +399 -0
  128. package/dist/subagentTailer.js.map +1 -0
  129. package/dist/tmux.d.ts +62 -0
  130. package/dist/tmux.js +163 -0
  131. package/dist/tmux.js.map +1 -0
  132. package/dist/transcriptTailer.d.ts +61 -0
  133. package/dist/transcriptTailer.js +606 -0
  134. package/dist/transcriptTailer.js.map +1 -0
  135. package/dist/usageAggregator.d.ts +12 -0
  136. package/dist/usageAggregator.js +55 -0
  137. package/dist/usageAggregator.js.map +1 -0
  138. package/dist/version.d.ts +10 -0
  139. package/dist/version.js +45 -0
  140. package/dist/version.js.map +1 -0
  141. package/package.json +50 -0
package/dist/kick.js ADDED
@@ -0,0 +1,60 @@
1
+ // kick.ts
2
+ // tailii (TS host) — kick サブコマンド実装(test-only)
3
+ // Swift 版 Kick.swift の移植。
4
+ //
5
+ // 稼働中の tmux セッションへ `send-keys ... Enter` でプロンプトを注入する。
6
+ // E2E(承認1往復)の再現専用であり、プロダクトの導線には出さない非機能(test-only, Req 7.3)。
7
+ // tmux は絶対パス指定(PATH 外のため)。
8
+ import { spawnSync } from "node:child_process";
9
+ import { DEFAULT_TMUX_PATH } from "./tmux.js";
10
+ /**
11
+ * kick の純ロジック。tmux パスを注入できるためテスト可能。
12
+ * `tmux send-keys -t <session> <prompt> Enter` を実行する。指定セッションが存在しない、
13
+ * あるいは tmux が非0終了/起動失敗した場合は errorSink にメッセージを出して非0 を返す。
14
+ */
15
+ export function kickCore(session, prompt, tmuxPath, errorSink) {
16
+ // prompt は単一引数として渡す(tmux 側で1つのリテラル文字列として送出)。
17
+ // 末尾の `Enter` が改行を送り、対話入力を確定させる。
18
+ const result = spawnSync(tmuxPath, ["send-keys", "-t", session, prompt, "Enter"], {
19
+ stdio: ["ignore", "ignore", "ignore"],
20
+ });
21
+ if (result.error) {
22
+ errorSink(`tailii kick: tmux 起動失敗 (${tmuxPath}): ${result.error.message}\n`);
23
+ return 1;
24
+ }
25
+ const status = result.status ?? 1;
26
+ if (status !== 0) {
27
+ // 非存在セッション等で send-keys は非0 終了する(tmux が stderr に理由を出す)。
28
+ errorSink(`tailii kick: send-keys 失敗(セッション '${session}' が存在しない可能性): tmux exit ${status}\n`);
29
+ return status;
30
+ }
31
+ return 0;
32
+ }
33
+ /** kick サブコマンドの CLI エントリポイント。`--session <name>` と `--prompt <text>` を要求する。 */
34
+ export async function runKickCommand(args) {
35
+ let sessionArg = null;
36
+ let promptArg = null;
37
+ for (let i = 0; i < args.length; i += 1) {
38
+ const next = () => (i + 1 < args.length ? args[++i] : null);
39
+ switch (args[i]) {
40
+ case "--session":
41
+ sessionArg = next();
42
+ break;
43
+ case "--prompt":
44
+ promptArg = next();
45
+ break;
46
+ default:
47
+ break;
48
+ }
49
+ }
50
+ if (sessionArg === null || sessionArg.length === 0) {
51
+ process.stderr.write("tailii kick: --session <name> が必要です\n");
52
+ return 2;
53
+ }
54
+ if (promptArg === null) {
55
+ process.stderr.write("tailii kick: --prompt <text> が必要です\n");
56
+ return 2;
57
+ }
58
+ return kickCore(sessionArg, promptArg, DEFAULT_TMUX_PATH, (m) => process.stderr.write(m));
59
+ }
60
+ //# sourceMappingURL=kick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kick.js","sourceRoot":"","sources":["../src/kick.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,8CAA8C;AAC9C,0BAA0B;AAC1B,EAAE;AACF,sDAAsD;AACtD,4DAA4D;AAC5D,2BAA2B;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,OAAe,EACf,MAAc,EACd,QAAgB,EAChB,SAAoC;IAEpC,6CAA6C;IAC7C,iCAAiC;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAChF,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;KACtC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,SAAS,CAAC,2BAA2B,QAAQ,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QAC7E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,uDAAuD;QACvD,SAAS,CACP,oCAAoC,OAAO,2BAA2B,MAAM,IAAI,CACjF,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAc;IACjD,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,SAAS,GAAkB,IAAI,CAAC;IAEpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,GAAkB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5E,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,WAAW;gBACd,UAAU,GAAG,IAAI,EAAE,CAAC;gBACpB,MAAM;YACR,KAAK,UAAU;gBACb,SAAS,GAAG,IAAI,EAAE,CAAC;gBACnB,MAAM;YACR;gBACE,MAAM;QACV,CAAC;IACH,CAAC;IAED,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC9D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC7D,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,107 @@
1
+ import { SessionMetadataStore } from "./sessionMetadataStore.js";
2
+ /** tmux 内で起動する既定コマンド(claude TUI)。テストでは無害な値に差し替える。 */
3
+ export declare const DEFAULT_INNER_COMMAND = "claude";
4
+ /**
5
+ * codex(OpenAI Codex CLI)モードの既定コマンド。
6
+ * 承認統合は後続 Milestone のため、本スライスでは端末で承認応答できない前提で
7
+ * `-a never -s workspace-write`(workspace 内サンドボックスで承認プロンプトを出さず自律実行)にする。
8
+ */
9
+ export declare const DEFAULT_CODEX_COMMAND = "codex -a never -s workspace-write";
10
+ /**
11
+ * codex の既存会話 resume 用フラグ(サブコマンド `resume <SESSION_ID>` に前置)。
12
+ * `-a`/`-s` は resume サブコマンドでも有効(`codex resume --help` で確認済み, agent-tag)。
13
+ * resume は cwd+mtime で rollout を解決する tail の性質上、厳密な per-session 追尾は保証しない
14
+ * (同一 cwd の新しい rollout があるとそちらを追い得る。既知の制約)。
15
+ */
16
+ export declare const CODEX_RESUME_FLAGS = "-a never -s workspace-write";
17
+ /** codex サンドボックスモード(承認モード相当。承認は PreToolUse フックでゲートするため -a は常に never)。 */
18
+ export type CodexSandbox = "read-only" | "workspace-write" | "danger-full-access";
19
+ /**
20
+ * codex 新規起動の inner コマンドを組み立てる(codex-input)。
21
+ * 承認は PreToolUse フックでゲートするため `-a never` 固定。sandbox とモデルのみ可変にする。
22
+ * @param model 省略/不正文字は無視(既定モデル)。
23
+ * @param sandbox 省略時は workspace-write。
24
+ */
25
+ export declare function codexInnerCommand(opts: {
26
+ model?: string | null;
27
+ sandbox?: CodexSandbox | null;
28
+ }): string;
29
+ /** 起動対象エージェント種別(claude=既定 / codex=Codex CLI)。 */
30
+ export type LaunchAgent = "claude" | "codex";
31
+ /** SSH exec(非ログインシェル)向けに注入する PATH。 */
32
+ export declare function defaultInjectedPath(): string;
33
+ /**
34
+ * `session_start` を launch へ橋渡しする注入可能な実行子(Swift 版 EngineLauncher と対)。
35
+ * 返値は launch の終了コード(0 = 成功)と、失敗時に error 封筒へ載せる説明。
36
+ */
37
+ export type EngineLauncher = (cwd: string, name: string, baseDir: string | null, resumeSessionId: string | null,
38
+ /** 新規起動(resume なし)で claude の会話 id を固定する uuid(`--session-id`)。null なら claude 生成。 */
39
+ newSessionId?: string | null,
40
+ /** Claude の表示名(`claude --name`。会話名を付けた新規起動のみ, lazy-session)。null なら付与しない。 */
41
+ title?: string | null) => Promise<{
42
+ exitCode: number;
43
+ errorText: string;
44
+ }>;
45
+ /** シェル single-quote で安全に包む(内部の `'` は `'\''` へ)。tmux new に渡す inner コマンド用。 */
46
+ export declare function shellSingleQuote(value: string): string;
47
+ /** 子プロセス実行の注入点(テストは実 tmux を起動しないモックを注入する)。 */
48
+ export type ProcessRunner = (executable: string, args: string[], options: {
49
+ cwd?: string;
50
+ env?: NodeJS.ProcessEnv;
51
+ }) => Promise<{
52
+ exitCode: number;
53
+ stdout: string;
54
+ }>;
55
+ /** launch サブコマンドのエントリポイント(`--dir <path> --session <name>`)。 */
56
+ export declare function runLaunchCommand(args: string[]): Promise<number>;
57
+ /**
58
+ * `session_start` を `launchCore` へ橋渡しする EngineLauncher を組み立てる。
59
+ * `store` は TmuxSessionManager と同一インスタンスを渡すこと(起動直後の一覧に cwd が反映されるように)。
60
+ */
61
+ export declare function makeSessionLauncher(options?: {
62
+ store?: SessionMetadataStore;
63
+ innerCommand?: string | null;
64
+ tmuxPath?: string | null;
65
+ runner?: ProcessRunner;
66
+ /** 起動対象エージェント(既定 claude)。codex は claude 固有処理を行わない。 */
67
+ agent?: LaunchAgent;
68
+ }): EngineLauncher;
69
+ /**
70
+ * launch の純ロジック(プロセス起動とパス類を注入できるためテスト可能)。
71
+ * 1. cwd 解決 + base 配下限定自動作成 → 2. フォルダ事前信頼 → 3. `--settings` へ承認フックを合成 →
72
+ * 4. 死んだ同名セッション掃除 → 5. tmux デタッチ起動 → 6. cwd 権威記録。
73
+ */
74
+ export declare function launchCore(options: {
75
+ dir: string;
76
+ session: string;
77
+ baseDir: string | null;
78
+ binaryPath: string;
79
+ tmuxPath: string;
80
+ innerCommand: string;
81
+ path: string;
82
+ store: SessionMetadataStore;
83
+ now: () => number;
84
+ errorSink: (message: string) => void;
85
+ runner?: ProcessRunner;
86
+ /** 起動対象エージェント(既定 claude)。codex は claude 固有の事前信頼/フック注入を行わない。 */
87
+ agent?: LaunchAgent;
88
+ /** host が `--session-id` で固定した Claude 会話 id。新規 claude 起動だけに記録する。 */
89
+ claudeSessionId?: string | null;
90
+ /** `~/.claude.json`(事前信頼記録)の場所。テスト注入用。省略時は実ホーム。 */
91
+ claudeJsonPath?: string;
92
+ /** フックのグローバル無効化マーカーの場所。テスト注入用(実マシンのマーカーに左右されない密閉性)。 */
93
+ hookGlobalMarkerPath?: string;
94
+ }): Promise<number>;
95
+ /**
96
+ * `~/.claude.json` の `projects[<dir>].hasTrustDialogAccepted` を true にしておく。
97
+ * claude は canonical パス(シンボリックリンク解決後)をキーに信頼を記録する。失敗は握り潰す。
98
+ */
99
+ export declare function preTrustFolder(dir: string, claudeJsonPath?: string): void;
100
+ /**
101
+ * cwd 入力を baseDir 基準で実パスへ解決し、baseDir 配下限定で未存在を自動作成する。
102
+ * `/` 始まり → 絶対採用 / `~` 始まり → ホーム展開 / 相対(空含む)→ `baseDir/<入力>`。
103
+ * `..` 脱出は拒否。不在 + 相対(= base 内側)のみ自動作成。失敗は errorSink へ出し null。
104
+ */
105
+ export declare function resolveWorkdir(cwd: string, baseDir: string | null, errorSink: (message: string) => void): string | null;
106
+ /** THIS 実行中エントリスクリプトの絶対パスを解決する(フック command 埋め込み用)。 */
107
+ export declare function currentBinaryPath(): string;
package/dist/launch.js ADDED
@@ -0,0 +1,396 @@
1
+ // launch.ts
2
+ // tailii (TS host) — tmux 上での claude 起動(launchCore)と engine 用 launcher の組み立て
3
+ // Swift 版 Launch.swift の移植。
4
+ // 指定 dir で tmux 上に claude をデタッチ起動し、承認フックは起動時 `--settings '<json>'` で
5
+ // この起動プロセス限定に渡し(settings.json は書かない)、SessionMetadataStore に cwd を権威記録
6
+ // する。SSH exec は非ログインシェルのため PATH を明示注入する。
7
+ import { execFile } from "node:child_process";
8
+ import * as fs from "node:fs";
9
+ import * as os from "node:os";
10
+ import * as path from "node:path";
11
+ import { claudeHookLaunchSettings, installCodexHookSettings } from "./hookSettings.js";
12
+ import { isInsideBase, standardize, expandTilde } from "./paths.js";
13
+ import { SessionMetadataStore } from "./sessionMetadataStore.js";
14
+ import { DEFAULT_TMUX_PATH } from "./tmux.js";
15
+ /** tmux 内で起動する既定コマンド(claude TUI)。テストでは無害な値に差し替える。 */
16
+ export const DEFAULT_INNER_COMMAND = "claude";
17
+ /**
18
+ * codex(OpenAI Codex CLI)モードの既定コマンド。
19
+ * 承認統合は後続 Milestone のため、本スライスでは端末で承認応答できない前提で
20
+ * `-a never -s workspace-write`(workspace 内サンドボックスで承認プロンプトを出さず自律実行)にする。
21
+ */
22
+ export const DEFAULT_CODEX_COMMAND = "codex -a never -s workspace-write";
23
+ /**
24
+ * codex の既存会話 resume 用フラグ(サブコマンド `resume <SESSION_ID>` に前置)。
25
+ * `-a`/`-s` は resume サブコマンドでも有効(`codex resume --help` で確認済み, agent-tag)。
26
+ * resume は cwd+mtime で rollout を解決する tail の性質上、厳密な per-session 追尾は保証しない
27
+ * (同一 cwd の新しい rollout があるとそちらを追い得る。既知の制約)。
28
+ */
29
+ export const CODEX_RESUME_FLAGS = "-a never -s workspace-write";
30
+ /** モデル slug の安全文字(コマンド注入防止。カタログ由来だが二重に検証する)。 */
31
+ const CODEX_MODEL_SAFE = /^[A-Za-z0-9._-]+$/;
32
+ /**
33
+ * codex 新規起動の inner コマンドを組み立てる(codex-input)。
34
+ * 承認は PreToolUse フックでゲートするため `-a never` 固定。sandbox とモデルのみ可変にする。
35
+ * @param model 省略/不正文字は無視(既定モデル)。
36
+ * @param sandbox 省略時は workspace-write。
37
+ */
38
+ export function codexInnerCommand(opts) {
39
+ const sandbox = opts.sandbox ?? "workspace-write";
40
+ let cmd = `codex -a never -s ${sandbox}`;
41
+ if (opts.model && CODEX_MODEL_SAFE.test(opts.model))
42
+ cmd += ` -m ${opts.model}`;
43
+ return cmd;
44
+ }
45
+ /** SSH exec(非ログインシェル)向けに注入する PATH。 */
46
+ export function defaultInjectedPath() {
47
+ return [
48
+ "/opt/homebrew/bin",
49
+ path.join(os.homedir(), ".local/bin"),
50
+ // mise 管理ツール(例: codex)の解決用シムディレクトリ。非ログインシェルの SSH exec でも届くよう明示。
51
+ path.join(os.homedir(), ".local/share/mise/shims"),
52
+ "/usr/local/bin",
53
+ "/usr/bin",
54
+ "/bin",
55
+ "/usr/sbin",
56
+ "/sbin",
57
+ ].join(":");
58
+ }
59
+ /** シェル single-quote で安全に包む(内部の `'` は `'\''` へ)。tmux new に渡す inner コマンド用。 */
60
+ export function shellSingleQuote(value) {
61
+ return `'${value.replaceAll("'", "'\\''")}'`;
62
+ }
63
+ function defaultProcessRunner() {
64
+ return (executable, args, options) => new Promise((resolve, reject) => {
65
+ execFile(executable, args, { cwd: options.cwd, env: options.env, maxBuffer: 16 * 1024 * 1024 }, (error, stdout) => {
66
+ if (error && typeof error.code === "string") {
67
+ reject(error);
68
+ return;
69
+ }
70
+ const exitCode = error && typeof error.code === "number" ? error.code : error ? 1 : 0;
71
+ resolve({ exitCode, stdout: String(stdout) });
72
+ });
73
+ });
74
+ }
75
+ /** launch サブコマンドのエントリポイント(`--dir <path> --session <name>`)。 */
76
+ export async function runLaunchCommand(args) {
77
+ let dirArg = null;
78
+ let sessionArg = null;
79
+ for (let i = 0; i < args.length; i += 1) {
80
+ if (args[i] === "--dir" && i + 1 < args.length) {
81
+ dirArg = args[i + 1];
82
+ i += 1;
83
+ }
84
+ else if (args[i] === "--session" && i + 1 < args.length) {
85
+ sessionArg = args[i + 1];
86
+ i += 1;
87
+ }
88
+ }
89
+ if (!dirArg) {
90
+ process.stderr.write("tailii launch: --dir <path> が必要です\n");
91
+ return 2;
92
+ }
93
+ if (!sessionArg) {
94
+ process.stderr.write("tailii launch: --session <name> が必要です\n");
95
+ return 2;
96
+ }
97
+ return launchCore({
98
+ dir: dirArg,
99
+ session: sessionArg,
100
+ baseDir: null,
101
+ binaryPath: currentBinaryPath(),
102
+ tmuxPath: DEFAULT_TMUX_PATH,
103
+ innerCommand: DEFAULT_INNER_COMMAND,
104
+ path: defaultInjectedPath(),
105
+ store: new SessionMetadataStore(),
106
+ now: () => Math.floor(Date.now() / 1000),
107
+ errorSink: (message) => process.stderr.write(message),
108
+ });
109
+ }
110
+ /**
111
+ * `session_start` を `launchCore` へ橋渡しする EngineLauncher を組み立てる。
112
+ * `store` は TmuxSessionManager と同一インスタンスを渡すこと(起動直後の一覧に cwd が反映されるように)。
113
+ */
114
+ export function makeSessionLauncher(options = {}) {
115
+ const store = options.store ?? new SessionMetadataStore();
116
+ const agent = options.agent ?? "claude";
117
+ const inner = options.innerCommand ?? (agent === "codex" ? DEFAULT_CODEX_COMMAND : DEFAULT_INNER_COMMAND);
118
+ const tmux = options.tmuxPath ?? DEFAULT_TMUX_PATH;
119
+ const binary = currentBinaryPath();
120
+ const runner = options.runner ?? defaultProcessRunner();
121
+ return async (cwd, name, baseDir, resumeSessionId, newSessionId, title) => {
122
+ let errorText = "";
123
+ // resume: claude は `<inner> --resume <id>`。resume でない新規起動は
124
+ // `<inner> --session-id <uuid>` で会話 id を固定し、host が tail 対象 jsonl を
125
+ // 事前に確定できるようにする(同一 cwd の別会話ログの流入を防ぐ)。会話名を付けた
126
+ // 新規起動は `--name '<title>'`(/resume ピッカー等に出る表示名, lazy-session)を添える。
127
+ // codex はこれらの制御を持たないため従来どおり素の inner(resume 指定も本スライスでは無視)。
128
+ let effectiveInner;
129
+ if (agent === "claude") {
130
+ if (resumeSessionId) {
131
+ effectiveInner = `${inner} --resume ${resumeSessionId}`;
132
+ }
133
+ else if (newSessionId) {
134
+ effectiveInner = `${inner} --session-id ${newSessionId}`;
135
+ if (title)
136
+ effectiveInner += ` --name ${shellSingleQuote(title)}`;
137
+ }
138
+ else {
139
+ effectiveInner = inner;
140
+ }
141
+ }
142
+ else if (resumeSessionId) {
143
+ // codex は既存会話を `codex resume <SESSION_ID>` で継続する(agent-tag)。
144
+ effectiveInner = `codex resume ${CODEX_RESUME_FLAGS} ${resumeSessionId}`;
145
+ }
146
+ else {
147
+ effectiveInner = inner;
148
+ }
149
+ const exitCode = await launchCore({
150
+ dir: cwd,
151
+ session: name,
152
+ baseDir,
153
+ binaryPath: binary,
154
+ tmuxPath: tmux,
155
+ innerCommand: effectiveInner,
156
+ path: defaultInjectedPath(),
157
+ store,
158
+ now: () => Math.floor(Date.now() / 1000),
159
+ errorSink: (message) => {
160
+ errorText += message;
161
+ },
162
+ runner,
163
+ agent,
164
+ // resume は同一会話 id の jsonl に書き続ける(engine の tail 束縛と同じ前提)ため、
165
+ // resume 起動でも実効会話 id を権威記録し、以後の reattach で厳密束縛できるようにする。
166
+ claudeSessionId: agent === "claude" ? (resumeSessionId ?? newSessionId ?? null) : null,
167
+ });
168
+ return { exitCode, errorText };
169
+ };
170
+ }
171
+ /**
172
+ * launch の純ロジック(プロセス起動とパス類を注入できるためテスト可能)。
173
+ * 1. cwd 解決 + base 配下限定自動作成 → 2. フォルダ事前信頼 → 3. `--settings` へ承認フックを合成 →
174
+ * 4. 死んだ同名セッション掃除 → 5. tmux デタッチ起動 → 6. cwd 権威記録。
175
+ */
176
+ export async function launchCore(options) {
177
+ const { session, binaryPath, tmuxPath, store, now, errorSink } = options;
178
+ const agent = options.agent ?? "claude";
179
+ const runner = options.runner ?? defaultProcessRunner();
180
+ // --- 1. cwd 解決 + base 配下限定自動作成 ---
181
+ const dir = resolveWorkdir(options.dir, options.baseDir, errorSink);
182
+ if (dir === null)
183
+ return 1;
184
+ // エージェント別の起動前準備。
185
+ // claude: 「フォルダを信頼しますか?」の事前回避(~/.claude.json)と settings.json フック注入。
186
+ // codex: claude 固有処理は行わず、代わりに codex の信頼を `-c` オーバーライドで事前付与し、
187
+ // トラストダイアログを回避する(config.toml は書き換えない)。承認/フック統合は後続。
188
+ let innerCommand = options.innerCommand;
189
+ if (agent === "codex") {
190
+ // 承認ゲート: プロジェクトローカル `.codex/hooks.json` に PreToolUse フックを導入し、
191
+ // 自前フックなので `--dangerously-bypass-hook-trust` で信頼ハッシュ確認を省く。
192
+ // 併せて `-c` で信頼を事前付与しトラストダイアログを回避(config.toml は書き換えない)。
193
+ try {
194
+ installCodexHookSettings({
195
+ dir,
196
+ binaryPath,
197
+ session,
198
+ ...(options.hookGlobalMarkerPath !== undefined && {
199
+ globalMarkerPath: options.hookGlobalMarkerPath,
200
+ }),
201
+ });
202
+ }
203
+ catch (error) {
204
+ errorSink(`tailii launch: .codex/hooks.json 書込失敗: ${String(error)}\n`);
205
+ return 1;
206
+ }
207
+ innerCommand = `${innerCommand} -c projects."${dir}".trust_level="trusted" --dangerously-bypass-hook-trust`;
208
+ }
209
+ else {
210
+ // --- 1.5. claude の初回「フォルダを信頼しますか?」プロンプトを事前回避する ---
211
+ preTrustFolder(dir, options.claudeJsonPath);
212
+ // --- 2. 承認フックを settings.json へは書かず、この起動限定で `--settings` 経由で渡す ---
213
+ // (settings.json に書くと同 dir の「tailii 非経由の通常 claude 起動」もフックを拾い、
214
+ // 承認ブローカー不在で全ツールがハングするため。2026-07-07 ユーザー指摘の根治)
215
+ const hookSettings = claudeHookLaunchSettings({
216
+ dir,
217
+ binaryPath,
218
+ session,
219
+ ...(options.hookGlobalMarkerPath !== undefined && {
220
+ globalMarkerPath: options.hookGlobalMarkerPath,
221
+ }),
222
+ });
223
+ if (hookSettings !== null) {
224
+ innerCommand = `${innerCommand} --settings ${shellSingleQuote(hookSettings)}`;
225
+ }
226
+ }
227
+ const env = { ...process.env, PATH: options.path };
228
+ const runTmux = async (args) => {
229
+ try {
230
+ const result = await runner(tmuxPath, args, { env });
231
+ return { code: result.exitCode, out: result.stdout };
232
+ }
233
+ catch {
234
+ return { code: 127, out: "" };
235
+ }
236
+ };
237
+ // --- 2.5. 既存の「死んだ」同名セッションを掃除する(同名再利用を可能にする) ---
238
+ if ((await runTmux(["has-session", "-t", session])).code === 0) {
239
+ const panes = await runTmux(["list-panes", "-t", session, "-F", "#{pane_dead}"]);
240
+ const hasLive = panes.out.split("\n").some((line) => line.trim() === "0");
241
+ if (!hasLive) {
242
+ await runTmux(["kill-session", "-t", session]);
243
+ }
244
+ }
245
+ // --- 3. tmux で claude をデタッチ起動 ---
246
+ // まだ存在するなら生きた pane がある(claude 稼働中)=再作成不要。
247
+ // `-A` は付けない(sshd exec に TTY が無く attach 試行が非0終了するため)。
248
+ const alreadyLive = (await runTmux(["has-session", "-t", session])).code === 0;
249
+ if (!alreadyLive) {
250
+ let status;
251
+ try {
252
+ const result = await runner(tmuxPath, ["new", "-d", "-s", session, innerCommand], {
253
+ cwd: dir,
254
+ env,
255
+ });
256
+ status = result.exitCode;
257
+ }
258
+ catch (error) {
259
+ errorSink(`tailii launch: tmux 起動失敗 (${tmuxPath}): ${String(error)}\n`);
260
+ return 1;
261
+ }
262
+ if (status !== 0) {
263
+ errorSink(`tailii launch: tmux が非0終了 (${status})\n`);
264
+ return status;
265
+ }
266
+ }
267
+ // --- 4. cwd を権威記録(tmux は起動 cwd を安定に返さないため本ストアを権威とする) ---
268
+ try {
269
+ // 任意メタは必要なときだけ記録する(古いメタ形式との後方互換を保つ)。
270
+ store.put({
271
+ name: session,
272
+ cwd: dir,
273
+ createdAt: now(),
274
+ ...(agent === "codex" ? { agent } : {}),
275
+ ...(agent === "claude" && options.claudeSessionId
276
+ ? { claudeSessionId: options.claudeSessionId }
277
+ : {}),
278
+ });
279
+ }
280
+ catch (error) {
281
+ errorSink(`tailii launch: メタデータ保存失敗: ${String(error)}\n`);
282
+ return 1;
283
+ }
284
+ return 0;
285
+ }
286
+ /**
287
+ * `~/.claude.json` の `projects[<dir>].hasTrustDialogAccepted` を true にしておく。
288
+ * claude は canonical パス(シンボリックリンク解決後)をキーに信頼を記録する。失敗は握り潰す。
289
+ */
290
+ export function preTrustFolder(dir, claudeJsonPath) {
291
+ try {
292
+ const target = claudeJsonPath ?? path.join(os.homedir(), ".claude.json");
293
+ let key;
294
+ try {
295
+ key = fs.realpathSync.native(dir);
296
+ }
297
+ catch {
298
+ key = standardize(dir);
299
+ }
300
+ let root = {};
301
+ try {
302
+ const parsed = JSON.parse(fs.readFileSync(target, "utf8"));
303
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
304
+ root = parsed;
305
+ }
306
+ }
307
+ catch {
308
+ // 不在/壊れは空から作る。
309
+ }
310
+ const projects = typeof root["projects"] === "object" && root["projects"] !== null
311
+ ? root["projects"]
312
+ : {};
313
+ const entry = typeof projects[key] === "object" && projects[key] !== null
314
+ ? projects[key]
315
+ : {};
316
+ if (entry["hasTrustDialogAccepted"] === true)
317
+ return; // 既に信頼済み
318
+ entry["hasTrustDialogAccepted"] = true;
319
+ projects[key] = entry;
320
+ root["projects"] = projects;
321
+ const tmp = target + ".tmp";
322
+ fs.writeFileSync(tmp, JSON.stringify(root));
323
+ fs.renameSync(tmp, target);
324
+ }
325
+ catch {
326
+ // 失敗時は claude 側プロンプトへフォールバック。
327
+ }
328
+ }
329
+ /**
330
+ * cwd 入力を baseDir 基準で実パスへ解決し、baseDir 配下限定で未存在を自動作成する。
331
+ * `/` 始まり → 絶対採用 / `~` 始まり → ホーム展開 / 相対(空含む)→ `baseDir/<入力>`。
332
+ * `..` 脱出は拒否。不在 + 相対(= base 内側)のみ自動作成。失敗は errorSink へ出し null。
333
+ */
334
+ export function resolveWorkdir(cwd, baseDir, errorSink) {
335
+ let resolved;
336
+ let isRelative;
337
+ if (cwd.startsWith("/")) {
338
+ resolved = standardize(cwd);
339
+ isRelative = false;
340
+ }
341
+ else if (cwd.startsWith("~")) {
342
+ resolved = standardize(expandTilde(cwd));
343
+ isRelative = false;
344
+ }
345
+ else {
346
+ if (!baseDir) {
347
+ errorSink(`tailii launch: 相対パスにはベースディレクトリの設定が必要です: '${cwd}'\n`);
348
+ return null;
349
+ }
350
+ const joined = cwd === "" ? baseDir : baseDir + "/" + cwd;
351
+ const candidate = standardize(joined);
352
+ if (!isInsideBase(candidate, baseDir)) {
353
+ errorSink(`tailii launch: 解決先がベースディレクトリの外側です: ${candidate}\n`);
354
+ return null;
355
+ }
356
+ resolved = candidate;
357
+ isRelative = true;
358
+ }
359
+ // 存在チェック。
360
+ let stat = null;
361
+ try {
362
+ stat = fs.statSync(resolved);
363
+ }
364
+ catch {
365
+ stat = null;
366
+ }
367
+ if (stat !== null) {
368
+ if (!stat.isDirectory()) {
369
+ errorSink(`tailii launch: 作業ディレクトリがファイルです: ${resolved}\n`);
370
+ return null;
371
+ }
372
+ return resolved;
373
+ }
374
+ // 不在: base 配下(相対)のみ自動作成。絶対/`~` の不在は作成せず error。
375
+ if (!isRelative) {
376
+ errorSink(`tailii launch: 作業ディレクトリが存在しません: ${resolved}\n`);
377
+ return null;
378
+ }
379
+ try {
380
+ fs.mkdirSync(resolved, { recursive: true });
381
+ return resolved;
382
+ }
383
+ catch (error) {
384
+ errorSink(`tailii launch: 作業ディレクトリの作成に失敗しました: ${resolved}: ${String(error)}\n`);
385
+ return null;
386
+ }
387
+ }
388
+ /** THIS 実行中エントリスクリプトの絶対パスを解決する(フック command 埋め込み用)。 */
389
+ export function currentBinaryPath() {
390
+ const argv1 = process.argv[1];
391
+ if (argv1 && argv1.length > 0) {
392
+ return standardize(argv1);
393
+ }
394
+ return "tailii";
395
+ }
396
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function migrateLegacyHome(): void;
@@ -0,0 +1,23 @@
1
+ // legacyHomeMigration.ts — one-time rename of the home data dir
2
+ // ~/.pocketclaude → ~/.tailii (旧プロジェクト名 PocketClaude からのリネーム移行).
3
+ //
4
+ // Best-effort and non-fatal: a migration failure must never crash the CLI,
5
+ // so everything is wrapped in try/catch and errors are swallowed. On the
6
+ // happy path there is no output.
7
+ import fs from "node:fs";
8
+ import os from "node:os";
9
+ import path from "node:path";
10
+ export function migrateLegacyHome() {
11
+ try {
12
+ const home = os.homedir();
13
+ const oldDir = path.join(home, ".pocketclaude");
14
+ const newDir = path.join(home, ".tailii");
15
+ if (fs.existsSync(oldDir) && !fs.existsSync(newDir)) {
16
+ fs.renameSync(oldDir, newDir);
17
+ }
18
+ }
19
+ catch {
20
+ // Ignore: migration is best-effort and must never throw.
21
+ }
22
+ }
23
+ //# sourceMappingURL=legacyHomeMigration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyHomeMigration.js","sourceRoot":"","sources":["../src/legacyHomeMigration.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,iCAAiC;AAEjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Writable } from "node:stream";
2
+ import { type ControlMessage } from "./protocol.js";
3
+ /** 出力ストリームへ `ControlMessage` を NDJSON 行(末尾改行付き)で書き出す。 */
4
+ export declare class LineWriter {
5
+ private readonly out;
6
+ constructor(out: Writable);
7
+ /** message をエンコードし、末尾改行を付けて書き込む。ストリーム破棄済みは例外。 */
8
+ write(message: ControlMessage): void;
9
+ }
@@ -0,0 +1,19 @@
1
+ // lineWriter.ts
2
+ // tailii (TS host) — ControlMessage を NDJSON 行で直列に書き出す writer
3
+ // Swift 版 Engine.swift の LineWriter に対応(Node の Writable は書込順序を保証するためロック不要)。
4
+ import { encodeControlMessage } from "./protocol.js";
5
+ /** 出力ストリームへ `ControlMessage` を NDJSON 行(末尾改行付き)で書き出す。 */
6
+ export class LineWriter {
7
+ out;
8
+ constructor(out) {
9
+ this.out = out;
10
+ }
11
+ /** message をエンコードし、末尾改行を付けて書き込む。ストリーム破棄済みは例外。 */
12
+ write(message) {
13
+ if (this.out.destroyed || !this.out.writable) {
14
+ throw new Error("LineWriter: output stream is closed");
15
+ }
16
+ this.out.write(encodeControlMessage(message) + "\n");
17
+ }
18
+ }
19
+ //# sourceMappingURL=lineWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lineWriter.js","sourceRoot":"","sources":["../src/lineWriter.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,8DAA8D;AAC9D,4EAA4E;AAG5E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,eAAe,CAAC;AAE1E,yDAAyD;AACzD,MAAM,OAAO,UAAU;IACQ;IAA7B,YAA6B,GAAa;QAAb,QAAG,GAAH,GAAG,CAAU;IAAG,CAAC;IAE9C,iDAAiD;IACjD,KAAK,CAAC,OAAuB;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,CAAC;CACF"}