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
@@ -0,0 +1,122 @@
1
+ // sessionMetadataStore.ts
2
+ // tailii (TS host) — セッションメタデータ(cwd 権威記録)
3
+ // Swift 版 SessionMetadataStore.swift の移植。
4
+ // セッション名 → 作業ディレクトリ / 作成時刻を <base>/<name>.json に put/get/all で読み書きする。
5
+ import * as fs from "node:fs";
6
+ import * as os from "node:os";
7
+ import * as path from "node:path";
8
+ import { ensureDirectory0700 } from "./paths.js";
9
+ /** session 名が安全でないときに投げる型付きエラー。 */
10
+ export class InvalidSessionNameError extends Error {
11
+ constructor(name) {
12
+ super(`invalid session name: ${name}`);
13
+ this.name = "InvalidSessionNameError";
14
+ }
15
+ }
16
+ /** session 名の安全性を検証する(空 / null バイト / `/` / `.`・`..` を拒否)。 */
17
+ export function validateSessionName(name) {
18
+ if (name.length === 0)
19
+ throw new InvalidSessionNameError(name);
20
+ if (name.includes("\0"))
21
+ throw new InvalidSessionNameError(name);
22
+ if (name.includes("/"))
23
+ throw new InvalidSessionNameError(name);
24
+ if (name === "." || name === "..")
25
+ throw new InvalidSessionNameError(name);
26
+ }
27
+ /** デフォルトのベースディレクトリ(`~/.tailii/sessions`)。 */
28
+ export function defaultSessionsBase() {
29
+ return path.join(os.homedir(), ".tailii", "sessions");
30
+ }
31
+ /**
32
+ * セッションメタデータの永続ストア(cwd 権威記録)。
33
+ * 1 ファイル 1 セッション(`<base>/<name>.json`)。テストは一時 dir を注入する。
34
+ */
35
+ export class SessionMetadataStore {
36
+ base;
37
+ constructor(base) {
38
+ this.base = base ?? defaultSessionsBase();
39
+ }
40
+ /** セッションメタデータを保存する(同一 name は上書き)。 */
41
+ put(meta) {
42
+ validateSessionName(meta.name);
43
+ ensureDirectory0700(this.base);
44
+ const payload = JSON.stringify(
45
+ // sortedKeys 相当。任意キーは指定時のみ書く(後方互換: 未指定は従来どおりのキー構成)。
46
+ {
47
+ ...(meta.agent !== undefined ? { agent: meta.agent } : {}),
48
+ ...(meta.claudeSessionId !== undefined ? { claudeSessionId: meta.claudeSessionId } : {}),
49
+ createdAt: meta.createdAt,
50
+ cwd: meta.cwd,
51
+ name: meta.name,
52
+ });
53
+ const target = this.fileFor(meta.name);
54
+ const tmp = target + ".tmp";
55
+ fs.writeFileSync(tmp, payload);
56
+ fs.renameSync(tmp, target); // atomic 相当
57
+ }
58
+ /** 指定 name のメタデータ。存在しない / 読めない場合は null(throw しない設計)。 */
59
+ get(name) {
60
+ try {
61
+ validateSessionName(name);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ try {
67
+ const raw = JSON.parse(fs.readFileSync(this.fileFor(name), "utf8"));
68
+ return decodeMeta(raw);
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ /** 保存済みの全メタデータ(壊れたファイルは無視、順序不定)。 */
75
+ all() {
76
+ let entries;
77
+ try {
78
+ entries = fs.readdirSync(this.base);
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ const result = [];
84
+ for (const entry of entries) {
85
+ if (!entry.endsWith(".json") || entry.startsWith("."))
86
+ continue;
87
+ try {
88
+ const raw = JSON.parse(fs.readFileSync(path.join(this.base, entry), "utf8"));
89
+ const meta = decodeMeta(raw);
90
+ if (meta)
91
+ result.push(meta);
92
+ }
93
+ catch {
94
+ // 壊れたファイルは無視して全体を落とさない。
95
+ }
96
+ }
97
+ return result;
98
+ }
99
+ fileFor(name) {
100
+ return path.join(this.base, `${name}.json`);
101
+ }
102
+ }
103
+ function decodeMeta(raw) {
104
+ if (typeof raw !== "object" || raw === null)
105
+ return null;
106
+ const obj = raw;
107
+ if (typeof obj["name"] !== "string" ||
108
+ typeof obj["cwd"] !== "string" ||
109
+ typeof obj["createdAt"] !== "number") {
110
+ return null;
111
+ }
112
+ const agent = obj["agent"] === "codex" || obj["agent"] === "claude" ? obj["agent"] : undefined;
113
+ const claudeSessionId = typeof obj["claudeSessionId"] === "string" ? obj["claudeSessionId"] : undefined;
114
+ return {
115
+ name: obj["name"],
116
+ cwd: obj["cwd"],
117
+ createdAt: obj["createdAt"],
118
+ ...(agent !== undefined ? { agent } : {}),
119
+ ...(claudeSessionId !== undefined ? { claudeSessionId } : {}),
120
+ };
121
+ }
122
+ //# sourceMappingURL=sessionMetadataStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionMetadataStore.js","sourceRoot":"","sources":["../src/sessionMetadataStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { ClaudeSessionInfo, SessionSearchResult } from "./protocol.js";
2
+ export interface SessionSearchSource {
3
+ list(): ClaudeSessionInfo[];
4
+ transcriptPath(sessionId: string): string | null;
5
+ }
6
+ export interface SessionSearchOptions {
7
+ limit?: number;
8
+ nowMs?: () => number;
9
+ timeBudgetMs?: number;
10
+ perFileBytes?: number;
11
+ fileCountLimit?: number;
12
+ snippetContextChars?: number;
13
+ }
14
+ export interface SessionSearchStats {
15
+ scannedFiles: number;
16
+ truncated: boolean;
17
+ }
18
+ export interface SessionSearchResponse {
19
+ results: SessionSearchResult[];
20
+ stats: SessionSearchStats;
21
+ }
22
+ export declare const SESSION_SEARCH_DEFAULT_LIMIT = 20;
23
+ export declare const SESSION_SEARCH_MAX_LIMIT = 100;
24
+ export declare const SESSION_SEARCH_TIME_BUDGET_MS = 120;
25
+ export declare const SESSION_SEARCH_PER_FILE_BYTES: number;
26
+ export declare const SESSION_SEARCH_FILE_COUNT_LIMIT = 1000;
27
+ export declare const SESSION_SEARCH_SNIPPET_CONTEXT_CHARS = 40;
28
+ /** Claude 会話 jsonl の user/assistant テキストだけを対象に、大文字小文字を無視して部分一致検索する。 */
29
+ export declare function searchClaudeSessions(store: SessionSearchSource, query: string, options?: SessionSearchOptions): SessionSearchResponse;
@@ -0,0 +1,139 @@
1
+ // sessionSearch.ts
2
+ // tailii (TS host) — Claude Code 会話本文の横断検索(session_search)
3
+ import * as fs from "node:fs";
4
+ export const SESSION_SEARCH_DEFAULT_LIMIT = 20;
5
+ export const SESSION_SEARCH_MAX_LIMIT = 100;
6
+ export const SESSION_SEARCH_TIME_BUDGET_MS = 120;
7
+ export const SESSION_SEARCH_PER_FILE_BYTES = 1024 * 1024;
8
+ export const SESSION_SEARCH_FILE_COUNT_LIMIT = 1000;
9
+ export const SESSION_SEARCH_SNIPPET_CONTEXT_CHARS = 40;
10
+ /** Claude 会話 jsonl の user/assistant テキストだけを対象に、大文字小文字を無視して部分一致検索する。 */
11
+ export function searchClaudeSessions(store, query, options = {}) {
12
+ const normalizedQuery = query.trim().toLocaleLowerCase();
13
+ if (normalizedQuery.length === 0) {
14
+ return { results: [], stats: { scannedFiles: 0, truncated: false } };
15
+ }
16
+ const requested = options.limit ?? SESSION_SEARCH_DEFAULT_LIMIT;
17
+ const limit = clampLimit(requested);
18
+ const nowMs = options.nowMs ?? (() => Date.now());
19
+ const deadline = nowMs() + (options.timeBudgetMs ?? SESSION_SEARCH_TIME_BUDGET_MS);
20
+ const perFileBytes = options.perFileBytes ?? SESSION_SEARCH_PER_FILE_BYTES;
21
+ const fileCountLimit = options.fileCountLimit ?? SESSION_SEARCH_FILE_COUNT_LIMIT;
22
+ const snippetContext = options.snippetContextChars ?? SESSION_SEARCH_SNIPPET_CONTEXT_CHARS;
23
+ const sessions = store.list();
24
+ const results = [];
25
+ let scannedFiles = 0;
26
+ let truncated = false;
27
+ for (const info of sessions) {
28
+ if (results.length >= limit)
29
+ break;
30
+ if (scannedFiles >= fileCountLimit) {
31
+ truncated = true;
32
+ break;
33
+ }
34
+ if (nowMs() > deadline) {
35
+ truncated = true;
36
+ break;
37
+ }
38
+ const filePath = store.transcriptPath(info.sessionId);
39
+ if (filePath === null)
40
+ continue;
41
+ scannedFiles += 1;
42
+ const match = searchTranscriptFile(filePath, normalizedQuery, perFileBytes, snippetContext);
43
+ if (match === null)
44
+ continue;
45
+ const result = {
46
+ sessionId: info.sessionId,
47
+ title: info.title,
48
+ cwd: info.cwd,
49
+ snippet: match,
50
+ };
51
+ if (info.updatedAt !== undefined)
52
+ result.updatedAt = info.updatedAt;
53
+ results.push(result);
54
+ }
55
+ return { results, stats: { scannedFiles, truncated } };
56
+ }
57
+ function clampLimit(value) {
58
+ if (!Number.isFinite(value))
59
+ return SESSION_SEARCH_DEFAULT_LIMIT;
60
+ return Math.max(1, Math.min(SESSION_SEARCH_MAX_LIMIT, Math.floor(value)));
61
+ }
62
+ function searchTranscriptFile(filePath, normalizedQuery, byteLimit, snippetContext) {
63
+ let content;
64
+ try {
65
+ const fd = fs.openSync(filePath, "r");
66
+ try {
67
+ const size = Math.max(0, Math.min(byteLimit, fs.fstatSync(fd).size));
68
+ const buf = Buffer.alloc(size);
69
+ const bytesRead = fs.readSync(fd, buf, 0, size, 0);
70
+ content = buf.subarray(0, bytesRead).toString("utf8");
71
+ }
72
+ finally {
73
+ fs.closeSync(fd);
74
+ }
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ for (const line of content.split("\n")) {
80
+ if (!line)
81
+ continue;
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(line);
85
+ }
86
+ catch {
87
+ continue;
88
+ }
89
+ if (typeof parsed !== "object" || parsed === null)
90
+ continue;
91
+ const obj = parsed;
92
+ if (obj["type"] !== "user" && obj["type"] !== "assistant")
93
+ continue;
94
+ const text = extractMessageText(obj);
95
+ if (text === null)
96
+ continue;
97
+ const normalizedText = text.toLocaleLowerCase();
98
+ const index = normalizedText.indexOf(normalizedQuery);
99
+ if (index < 0)
100
+ continue;
101
+ return makeSnippet(text, index, normalizedQuery.length, snippetContext);
102
+ }
103
+ return null;
104
+ }
105
+ function extractMessageText(obj) {
106
+ const message = obj["message"];
107
+ if (typeof message !== "object" || message === null)
108
+ return null;
109
+ const content = message["content"];
110
+ const parts = [];
111
+ if (typeof content === "string") {
112
+ parts.push(content);
113
+ }
114
+ else if (Array.isArray(content)) {
115
+ for (const element of content) {
116
+ if (typeof element === "string") {
117
+ parts.push(element);
118
+ }
119
+ else if (typeof element === "object" && element !== null) {
120
+ const block = element;
121
+ const blockType = block["type"];
122
+ const text = block["text"];
123
+ if ((blockType === undefined || blockType === "text") && typeof text === "string") {
124
+ parts.push(text);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ const text = parts.join(" ").replaceAll("\r", " ").replaceAll("\n", " ").trim();
130
+ return text.length > 0 ? text : null;
131
+ }
132
+ function makeSnippet(text, matchIndex, matchLength, context) {
133
+ const start = Math.max(0, matchIndex - context);
134
+ const end = Math.min(text.length, matchIndex + matchLength + context);
135
+ const prefix = start > 0 ? "..." : "";
136
+ const suffix = end < text.length ? "..." : "";
137
+ return `${prefix}${text.slice(start, end)}${suffix}`.replaceAll(/\s+/g, " ").trim();
138
+ }
139
+ //# sourceMappingURL=sessionSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionSearch.js","sourceRoot":"","sources":["../src/sessionSearch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import * as os from "node:os";
2
+ /** ペアリング payload の入力(v は sessionName の有無で 2/1 が決まる)。 */
3
+ export interface PairingPayloadInput {
4
+ host: string;
5
+ port: number;
6
+ user: string;
7
+ /** 秘密鍵 PEM 全文。 */
8
+ key: string;
9
+ /** 指定で v2。 */
10
+ sessionName?: string;
11
+ /** v2 のみ。未指定なら JSON から省略される。 */
12
+ sessionCwd?: string;
13
+ }
14
+ /**
15
+ * ペアリング payload を Swift `JSONEncoder`(`.prettyPrinted + .sortedKeys`)と byte 一致で符号化する。
16
+ * 末尾改行は含めない(呼び出し側が付与)。golden `protocol/pairing-payload-v{1,2}.json` と契約整合。
17
+ */
18
+ export declare function encodePairingPayload(input: PairingPayloadInput): string;
19
+ /**
20
+ * LAN の IPv4 アドレスを検出する(en0 → en1 → 最初の非内部 IPv4 の順)。取得不能なら空文字。
21
+ * Swift 版の `ipconfig getifaddr en0/en1` 相当をクロスプラットフォームに一般化したもの。
22
+ */
23
+ export declare function detectLanIP(interfaces?: NodeJS.Dict<os.NetworkInterfaceInfo[]>): string;
24
+ /**
25
+ * Tailscale の IPv4(CGNAT レンジ `100.64.0.0/10`)を検出する。無ければ空文字。
26
+ * Tailscale は自身に 100.64.0.0〜100.127.255.255 のアドレスを割り当てる。
27
+ */
28
+ export declare function detectTailscaleIP(interfaces?: NodeJS.Dict<os.NetworkInterfaceInfo[]>): string;
29
+ /**
30
+ * ペアリングに使う「最も繋がりやすい」ホストを自動選定する。
31
+ * Tailscale が上がっていればその IP(tailnet の全端末から到達可・LAN 外でも成立)を優先し、
32
+ * 無ければ LAN IP へフォールバックする。ユーザーが経路(Tailscale か LAN か)を意識せずに済む。
33
+ */
34
+ export declare function detectPreferredIP(interfaces?: NodeJS.Dict<os.NetworkInterfaceInfo[]>): string;
35
+ /** authorized_keys 登録の結果。 */
36
+ export type AuthorizedKeyResult = "added" | "already-present";
37
+ /**
38
+ * 公開鍵行を `<sshDir>/authorized_keys` に登録する(既存なら何もしない)。
39
+ * ディレクトリは 0700、ファイルは 0600 で保存する(Swift 版パリティ)。
40
+ *
41
+ * @param sshDir `~/.ssh` 相当のディレクトリ(テストは一時 dir を注入)。
42
+ * @param pubLine 登録する公開鍵行(前後空白は呼び出し側で trim 済み想定)。
43
+ */
44
+ export declare function registerAuthorizedKey(sshDir: string, pubLine: string): AuthorizedKeyResult;
45
+ /** 鍵ペア用意の結果。 */
46
+ export interface EnsureKeypairResult {
47
+ status: "generated" | "reused";
48
+ /** 秘密鍵 PEM 全文。 */
49
+ privateKeyPem: string;
50
+ /** 公開鍵行(trim 済み)。 */
51
+ publicKeyLine: string;
52
+ }
53
+ /**
54
+ * `<base>/poc_id_ed25519{,.pub}` の鍵ペアを用意する。無ければ `keygen` を呼んで生成する。
55
+ * base は 0700 で作成する。テストは `keygen` にダミー鍵書き出しを注入して ssh-keygen を回避できる。
56
+ *
57
+ * @param base `~/.tailii` 相当のディレクトリ。
58
+ * @param keygen 鍵が無いとき `keyPath` に ed25519 鍵ペア(keyPath と keyPath+".pub")を生成する関数。
59
+ */
60
+ export declare function ensureKeypair(base: string, keygen: (keyPath: string) => void): EnsureKeypairResult;
61
+ /** 実 `ssh-keygen` で ed25519 鍵ペアを生成する(本番 keygen)。 */
62
+ export declare function sshKeygenEd25519(keyPath: string): void;
63
+ export interface EmitPayloadOptions {
64
+ /** `~/.tailii` 相当のディレクトリ。 */
65
+ base: string;
66
+ username: string;
67
+ lanIP: string;
68
+ sessionName?: string;
69
+ sessionCwd?: string;
70
+ }
71
+ /**
72
+ * `--emit-payload` の純ロジック。副作用なしで payload JSON(末尾改行なし)を返す。
73
+ * 既存の秘密鍵があれば読み、無ければ Swift 版と同じ擬似値で継続する(形状検査が目的)。
74
+ */
75
+ export declare function emitPayloadJSON(options: EmitPayloadOptions): string;
76
+ /**
77
+ * setup サブコマンドの CLI エントリポイント。
78
+ *
79
+ * `--emit-payload`: 副作用なしで payload JSON を stdout に出力(byte-exact 契約)。
80
+ * 通常モード: ed25519 鍵生成(無ければ)→ authorized_keys 登録 → payload をターミナル QR + JSON で表示。
81
+ * `--code`: 同じ副作用の後、TCP で pairing-code v1 responder を 1 回だけ実行する。
82
+ */
83
+ export declare function runSetupCommand(argv: string[]): Promise<number>;