u-foo 1.0.3 → 1.1.9

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 (179) hide show
  1. package/README.md +110 -11
  2. package/README.zh-CN.md +9 -7
  3. package/SKILLS/ufoo/SKILL.md +132 -0
  4. package/SKILLS/uinit/SKILL.md +78 -0
  5. package/SKILLS/ustatus/SKILL.md +36 -0
  6. package/bin/uclaude.js +13 -0
  7. package/bin/ucode-core.js +15 -0
  8. package/bin/ucode.js +125 -0
  9. package/bin/ucodex.js +13 -0
  10. package/bin/ufoo +9 -31
  11. package/bin/ufoo-assistant-agent.js +5 -0
  12. package/bin/ufoo-engine.js +25 -0
  13. package/bin/ufoo.js +17 -0
  14. package/modules/AGENTS.template.md +29 -11
  15. package/modules/bus/README.md +33 -25
  16. package/modules/bus/SKILLS/ubus/SKILL.md +19 -8
  17. package/modules/context/README.md +18 -40
  18. package/modules/context/SKILLS/uctx/SKILL.md +63 -1
  19. package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
  20. package/package.json +25 -4
  21. package/scripts/import-pi-mono.js +124 -0
  22. package/scripts/postinstall.js +30 -0
  23. package/scripts/sync-claude-skills.sh +21 -0
  24. package/src/agent/cliRunner.js +554 -33
  25. package/src/agent/internalRunner.js +150 -56
  26. package/src/agent/launcher.js +754 -0
  27. package/src/agent/normalizeOutput.js +1 -1
  28. package/src/agent/notifier.js +340 -0
  29. package/src/agent/ptyRunner.js +847 -0
  30. package/src/agent/ptyWrapper.js +379 -0
  31. package/src/agent/readyDetector.js +175 -0
  32. package/src/agent/ucode.js +443 -0
  33. package/src/agent/ucodeBootstrap.js +113 -0
  34. package/src/agent/ucodeBuild.js +67 -0
  35. package/src/agent/ucodeDoctor.js +184 -0
  36. package/src/agent/ucodeRuntimeConfig.js +129 -0
  37. package/src/agent/ufooAgent.js +46 -42
  38. package/src/assistant/agent.js +260 -0
  39. package/src/assistant/bridge.js +172 -0
  40. package/src/assistant/engine.js +252 -0
  41. package/src/assistant/stdio.js +58 -0
  42. package/src/assistant/ufooEngineCli.js +306 -0
  43. package/src/bus/activate.js +172 -0
  44. package/src/bus/daemon.js +436 -0
  45. package/src/bus/index.js +842 -0
  46. package/src/bus/inject.js +315 -0
  47. package/src/bus/message.js +430 -0
  48. package/src/bus/nickname.js +88 -0
  49. package/src/bus/queue.js +136 -0
  50. package/src/bus/shake.js +26 -0
  51. package/src/bus/store.js +189 -0
  52. package/src/bus/subscriber.js +312 -0
  53. package/src/bus/utils.js +363 -0
  54. package/src/chat/agentBar.js +117 -0
  55. package/src/chat/agentDirectory.js +88 -0
  56. package/src/chat/agentSockets.js +225 -0
  57. package/src/chat/agentViewController.js +298 -0
  58. package/src/chat/chatLogController.js +115 -0
  59. package/src/chat/commandExecutor.js +700 -0
  60. package/src/chat/commands.js +132 -0
  61. package/src/chat/completionController.js +414 -0
  62. package/src/chat/cronScheduler.js +160 -0
  63. package/src/chat/daemonConnection.js +166 -0
  64. package/src/chat/daemonCoordinator.js +64 -0
  65. package/src/chat/daemonMessageRouter.js +257 -0
  66. package/src/chat/daemonReconnect.js +41 -0
  67. package/src/chat/daemonTransport.js +36 -0
  68. package/src/chat/daemonTransportDefaults.js +10 -0
  69. package/src/chat/dashboardKeyController.js +480 -0
  70. package/src/chat/dashboardView.js +154 -0
  71. package/src/chat/index.js +1011 -1392
  72. package/src/chat/inputHistoryController.js +105 -0
  73. package/src/chat/inputListenerController.js +304 -0
  74. package/src/chat/inputMath.js +104 -0
  75. package/src/chat/inputSubmitHandler.js +171 -0
  76. package/src/chat/layout.js +165 -0
  77. package/src/chat/pasteController.js +81 -0
  78. package/src/chat/rawKeyMap.js +42 -0
  79. package/src/chat/settingsController.js +132 -0
  80. package/src/chat/statusLineController.js +177 -0
  81. package/src/chat/streamTracker.js +138 -0
  82. package/src/chat/text.js +70 -0
  83. package/src/chat/transport.js +61 -0
  84. package/src/cli/busCoreCommands.js +59 -0
  85. package/src/cli/ctxCoreCommands.js +199 -0
  86. package/src/cli/onlineCoreCommands.js +379 -0
  87. package/src/cli.js +1162 -96
  88. package/src/code/README.md +29 -0
  89. package/src/code/UCODE_PROMPT.md +32 -0
  90. package/src/code/agent.js +1651 -0
  91. package/src/code/cli.js +158 -0
  92. package/src/code/config +0 -0
  93. package/src/code/dispatch.js +42 -0
  94. package/src/code/index.js +70 -0
  95. package/src/code/nativeRunner.js +1213 -0
  96. package/src/code/runtime.js +154 -0
  97. package/src/code/sessionStore.js +162 -0
  98. package/src/code/taskDecomposer.js +269 -0
  99. package/src/code/tools/bash.js +53 -0
  100. package/src/code/tools/common.js +42 -0
  101. package/src/code/tools/edit.js +70 -0
  102. package/src/code/tools/read.js +44 -0
  103. package/src/code/tools/write.js +35 -0
  104. package/src/code/tui.js +1580 -0
  105. package/src/config.js +56 -3
  106. package/src/context/decisions.js +324 -0
  107. package/src/context/doctor.js +183 -0
  108. package/src/context/index.js +55 -0
  109. package/src/context/sync.js +127 -0
  110. package/src/daemon/agentProcessManager.js +74 -0
  111. package/src/daemon/cronOps.js +241 -0
  112. package/src/daemon/index.js +998 -170
  113. package/src/daemon/ipcServer.js +99 -0
  114. package/src/daemon/ops.js +630 -48
  115. package/src/daemon/promptLoop.js +319 -0
  116. package/src/daemon/promptRequest.js +101 -0
  117. package/src/daemon/providerSessions.js +306 -0
  118. package/src/daemon/reporting.js +90 -0
  119. package/src/daemon/run.js +31 -1
  120. package/src/daemon/status.js +48 -8
  121. package/src/doctor/index.js +50 -0
  122. package/src/init/index.js +318 -0
  123. package/src/online/bridge.js +663 -0
  124. package/src/online/client.js +245 -0
  125. package/src/online/runner.js +253 -0
  126. package/src/online/server.js +992 -0
  127. package/src/online/tokens.js +103 -0
  128. package/src/report/store.js +331 -0
  129. package/src/shared/eventContract.js +35 -0
  130. package/src/shared/ptySocketContract.js +21 -0
  131. package/src/skills/index.js +159 -0
  132. package/src/status/index.js +285 -0
  133. package/src/terminal/adapterContract.js +87 -0
  134. package/src/terminal/adapterRouter.js +84 -0
  135. package/src/terminal/adapters/externalAdapter.js +14 -0
  136. package/src/terminal/adapters/internalAdapter.js +13 -0
  137. package/src/terminal/adapters/internalPtyAdapter.js +42 -0
  138. package/src/terminal/adapters/internalQueueAdapter.js +37 -0
  139. package/src/terminal/adapters/terminalAdapter.js +31 -0
  140. package/src/terminal/adapters/tmuxAdapter.js +30 -0
  141. package/src/terminal/detect.js +64 -0
  142. package/src/terminal/index.js +8 -0
  143. package/src/terminal/iterm2.js +126 -0
  144. package/src/ufoo/agentsStore.js +107 -0
  145. package/src/ufoo/paths.js +46 -0
  146. package/src/utils/banner.js +76 -0
  147. package/bin/uclaude +0 -65
  148. package/bin/ucodex +0 -65
  149. package/modules/bus/scripts/bus-alert.sh +0 -185
  150. package/modules/bus/scripts/bus-listen.sh +0 -117
  151. package/modules/context/ASSUMPTIONS.md +0 -7
  152. package/modules/context/CONSTRAINTS.md +0 -7
  153. package/modules/context/CONTEXT-STRUCTURE.md +0 -49
  154. package/modules/context/DECISION-PROTOCOL.md +0 -62
  155. package/modules/context/HANDOFF.md +0 -33
  156. package/modules/context/RULES.md +0 -15
  157. package/modules/context/SKILLS/README.md +0 -14
  158. package/modules/context/SYSTEM.md +0 -18
  159. package/modules/context/TEMPLATES/assumptions.md +0 -4
  160. package/modules/context/TEMPLATES/constraints.md +0 -4
  161. package/modules/context/TEMPLATES/decision.md +0 -16
  162. package/modules/context/TEMPLATES/project-context-readme.md +0 -6
  163. package/modules/context/TEMPLATES/system.md +0 -3
  164. package/modules/context/TEMPLATES/terminology.md +0 -4
  165. package/modules/context/TERMINOLOGY.md +0 -10
  166. package/scripts/banner.sh +0 -89
  167. package/scripts/bus-alert.sh +0 -6
  168. package/scripts/bus-autotrigger.sh +0 -6
  169. package/scripts/bus-daemon.sh +0 -231
  170. package/scripts/bus-inject.sh +0 -144
  171. package/scripts/bus-listen.sh +0 -6
  172. package/scripts/bus.sh +0 -984
  173. package/scripts/context-decisions.sh +0 -167
  174. package/scripts/context-doctor.sh +0 -72
  175. package/scripts/context-lint.sh +0 -110
  176. package/scripts/doctor.sh +0 -22
  177. package/scripts/init.sh +0 -247
  178. package/scripts/skills.sh +0 -113
  179. package/scripts/status.sh +0 -125
@@ -0,0 +1,285 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const childProcess = require("child_process");
4
+ const { readJSON } = require("../bus/utils");
5
+ const { getUfooPaths } = require("../ufoo/paths");
6
+
7
+ function normalizeTty(ttyPath) {
8
+ if (!ttyPath) return "";
9
+ const trimmed = String(ttyPath).trim();
10
+ if (!trimmed || trimmed === "not a tty") return "";
11
+ if (trimmed === "/dev/tty") return "";
12
+ return trimmed;
13
+ }
14
+
15
+ function tryTtyWithFd(fd) {
16
+ try {
17
+ const res = childProcess.spawnSync("tty", {
18
+ stdio: [fd, "pipe", "ignore"],
19
+ encoding: "utf8",
20
+ });
21
+ if (res && res.status === 0) {
22
+ const tty = normalizeTty(res.stdout || "");
23
+ if (tty) return tty;
24
+ }
25
+ } catch {
26
+ // ignore
27
+ }
28
+ return "";
29
+ }
30
+
31
+ function detectCurrentTty() {
32
+ const stdinTtyPath = normalizeTty(process.stdin?.ttyPath || "");
33
+ if (stdinTtyPath) return stdinTtyPath;
34
+
35
+ const fromStdin = tryTtyWithFd(0);
36
+ if (fromStdin) return fromStdin;
37
+
38
+ try {
39
+ const fd = fs.openSync("/dev/tty", "r");
40
+ const fromTty = tryTtyWithFd(fd);
41
+ fs.closeSync(fd);
42
+ if (fromTty) return fromTty;
43
+ } catch {
44
+ // ignore
45
+ }
46
+
47
+ return "";
48
+ }
49
+
50
+ /**
51
+ * 显示项目状态
52
+ */
53
+ class StatusDisplay {
54
+ constructor(projectRoot) {
55
+ this.projectRoot = projectRoot;
56
+ this.paths = getUfooPaths(projectRoot);
57
+ this.ufooDir = this.paths.ufooDir;
58
+ }
59
+
60
+ /**
61
+ * 检查 .ufoo 目录是否存在
62
+ */
63
+ checkUfooDir() {
64
+ if (!fs.existsSync(this.ufooDir)) {
65
+ console.error("FAIL: .ufoo not found. Run: ufoo init");
66
+ process.exit(1);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 获取当前订阅者信息
72
+ */
73
+ getCurrentSubscriber() {
74
+ // 优先使用 UFOO_SUBSCRIBER_ID(daemon 启动的情况)
75
+ if (process.env.UFOO_SUBSCRIBER_ID) {
76
+ return process.env.UFOO_SUBSCRIBER_ID;
77
+ }
78
+
79
+ const agentsFile = this.paths.agentsFile;
80
+ if (!fs.existsSync(agentsFile)) {
81
+ return null;
82
+ }
83
+
84
+ // 尝试通过 tty 查找订阅者
85
+ const currentTty = detectCurrentTty();
86
+
87
+ if (currentTty && currentTty.startsWith("/dev/")) {
88
+ const busData = readJSON(agentsFile);
89
+ if (busData && busData.agents) {
90
+ for (const [id, meta] of Object.entries(busData.agents)) {
91
+ if (meta.tty === currentTty) {
92
+ return id;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ return null;
99
+ }
100
+
101
+ /**
102
+ * 统计未读消息
103
+ */
104
+ countUnreadMessages() {
105
+ const queuesDir = this.paths.busQueuesDir;
106
+ if (!fs.existsSync(queuesDir)) {
107
+ return { total: 0, details: [] };
108
+ }
109
+
110
+ const agentsFile = this.paths.agentsFile;
111
+ const busData = readJSON(agentsFile, {});
112
+
113
+ let total = 0;
114
+ const details = [];
115
+
116
+ const subscribers = fs.readdirSync(queuesDir);
117
+ for (const safeName of subscribers) {
118
+ const pendingFile = path.join(queuesDir, safeName, "pending.jsonl");
119
+ if (!fs.existsSync(pendingFile)) {
120
+ continue;
121
+ }
122
+
123
+ const stat = fs.statSync(pendingFile);
124
+ if (stat.size === 0) {
125
+ continue;
126
+ }
127
+
128
+ const content = fs.readFileSync(pendingFile, "utf8").trim();
129
+ const count = content ? content.split("\n").length : 0;
130
+
131
+ if (count > 0) {
132
+ total += count;
133
+
134
+ // 找到订阅者名称
135
+ let subscriberName = safeName.replace(/_/, ":");
136
+ if (busData.agents) {
137
+ for (const [id, meta] of Object.entries(busData.agents)) {
138
+ if (id.replace(/:/, "_") === safeName) {
139
+ subscriberName = id;
140
+ break;
141
+ }
142
+ }
143
+ }
144
+
145
+ details.push({ subscriber: subscriberName, count });
146
+ }
147
+ }
148
+
149
+ return { total, details };
150
+ }
151
+
152
+ /**
153
+ * 统计开放的决策
154
+ */
155
+ countOpenDecisions() {
156
+ const DecisionsManager = require("../context/decisions");
157
+ const manager = new DecisionsManager(this.projectRoot);
158
+ const decisionsDir = manager.decisionsDir;
159
+ if (!fs.existsSync(decisionsDir)) {
160
+ return { total: 0, details: [] };
161
+ }
162
+
163
+ let total = 0;
164
+ const details = [];
165
+
166
+ const files = fs.readdirSync(decisionsDir)
167
+ .filter((f) => f.endsWith(".md"))
168
+ .sort();
169
+
170
+ for (const file of files) {
171
+ const filePath = path.join(decisionsDir, file);
172
+ const content = fs.readFileSync(filePath, "utf8");
173
+
174
+ // 提取状态
175
+ const status = this.extractStatus(content);
176
+ if (status === "open") {
177
+ total++;
178
+
179
+ // 提取标题
180
+ const title = this.extractTitle(content);
181
+ details.push({ file, title: title || "(no title)" });
182
+ }
183
+ }
184
+
185
+ return { total, details };
186
+ }
187
+
188
+ /**
189
+ * 从决策文件提取状态
190
+ */
191
+ extractStatus(content) {
192
+ const lines = content.split("\n");
193
+ let inFrontmatter = false;
194
+ let frontmatterCount = 0;
195
+
196
+ for (const line of lines) {
197
+ if (line.trim() === "---") {
198
+ frontmatterCount++;
199
+ if (frontmatterCount === 2) {
200
+ break;
201
+ }
202
+ inFrontmatter = frontmatterCount === 1;
203
+ continue;
204
+ }
205
+
206
+ if (inFrontmatter && line.startsWith("status:")) {
207
+ return line.split(":")[1].trim();
208
+ }
209
+ }
210
+
211
+ return "open";
212
+ }
213
+
214
+ /**
215
+ * 从决策文件提取标题
216
+ */
217
+ extractTitle(content) {
218
+ const lines = content.split("\n");
219
+ for (const line of lines) {
220
+ if (line.startsWith("#")) {
221
+ return line.replace(/^#\s*/, "").trim();
222
+ }
223
+ }
224
+ return null;
225
+ }
226
+
227
+ /**
228
+ * 获取订阅者昵称(如果存在)
229
+ */
230
+ getSubscriberNickname(subscriber) {
231
+ if (!subscriber) return null;
232
+ const agentsFile = this.paths.agentsFile;
233
+ const busData = readJSON(agentsFile);
234
+ if (!busData || !busData.agents) return null;
235
+ const meta = busData.agents[subscriber];
236
+ return meta && meta.nickname ? meta.nickname : null;
237
+ }
238
+
239
+ /**
240
+ * 显示横幅
241
+ */
242
+ showBanner(subscriber) {
243
+ console.log("=== ufoo status ===");
244
+ if (subscriber) {
245
+ console.log(`Agent: ${subscriber}`);
246
+ } else {
247
+ console.log();
248
+ }
249
+ }
250
+
251
+ /**
252
+ * 显示完整状态
253
+ */
254
+ async show() {
255
+ this.checkUfooDir();
256
+
257
+ const subscriber = this.getCurrentSubscriber();
258
+
259
+ // 显示横幅
260
+ this.showBanner(subscriber);
261
+
262
+ // 显示项目路径
263
+ console.log(`Project: ${this.projectRoot}`);
264
+
265
+ // 显示未读消息
266
+ const unread = this.countUnreadMessages();
267
+ console.log(`Unread messages: ${unread.total}`);
268
+ if (unread.details.length > 0) {
269
+ for (const { subscriber: sub, count } of unread.details) {
270
+ console.log(` - ${sub}: ${count}`);
271
+ }
272
+ }
273
+
274
+ // 显示开放的决策
275
+ const decisions = this.countOpenDecisions();
276
+ console.log(`Open decisions: ${decisions.total}`);
277
+ if (decisions.details.length > 0) {
278
+ for (const { file, title } of decisions.details) {
279
+ console.log(` - ${file}: ${title}`);
280
+ }
281
+ }
282
+ }
283
+ }
284
+
285
+ module.exports = StatusDisplay;
@@ -0,0 +1,87 @@
1
+ const TERMINAL_CAPABILITY_KEYS = [
2
+ "supportsActivate",
3
+ "supportsSubscribeFull",
4
+ "supportsSubscribeScreen",
5
+ "supportsSnapshot",
6
+ "supportsReplay",
7
+ "supportsWindowClose",
8
+ "supportsSocketProtocol",
9
+ "supportsNotifierInjector",
10
+ "supportsInternalQueueLoop",
11
+ "supportsRestartFallback",
12
+ "supportsSessionReuse",
13
+ ];
14
+
15
+ const TERMINAL_ADAPTER_METHODS = [
16
+ "connect",
17
+ "disconnect",
18
+ "send",
19
+ "sendRaw",
20
+ "resize",
21
+ "snapshot",
22
+ "subscribe",
23
+ "activate",
24
+ "getState",
25
+ ];
26
+
27
+ function createTerminalCapabilities(overrides = {}) {
28
+ const capabilities = {};
29
+ for (const key of TERMINAL_CAPABILITY_KEYS) {
30
+ capabilities[key] = false;
31
+ }
32
+ return { ...capabilities, ...overrides };
33
+ }
34
+
35
+ function assertTerminalCapabilities(capabilities) {
36
+ if (!capabilities || typeof capabilities !== "object") {
37
+ throw new Error("TerminalAdapter capabilities must be an object");
38
+ }
39
+ for (const key of TERMINAL_CAPABILITY_KEYS) {
40
+ if (!(key in capabilities)) {
41
+ throw new Error(`TerminalAdapter capabilities missing: ${key}`);
42
+ }
43
+ if (typeof capabilities[key] !== "boolean") {
44
+ throw new Error(`TerminalAdapter capability must be boolean: ${key}`);
45
+ }
46
+ }
47
+ return true;
48
+ }
49
+
50
+ function createUnsupportedCapabilityError(capability, operation) {
51
+ const suffix = operation ? ` (operation: ${operation})` : "";
52
+ const err = new Error(`TerminalAdapter capability unsupported: ${capability}${suffix}`);
53
+ err.code = "UFOO_UNSUPPORTED_CAPABILITY";
54
+ err.capability = capability;
55
+ err.operation = operation || null;
56
+ return err;
57
+ }
58
+
59
+ function requireCapability(capabilities, capability, operation) {
60
+ if (!capabilities || !capabilities[capability]) {
61
+ throw createUnsupportedCapabilityError(capability, operation);
62
+ }
63
+ return true;
64
+ }
65
+
66
+ function assertTerminalAdapterContract(adapter) {
67
+ if (!adapter || typeof adapter !== "object") {
68
+ throw new Error("TerminalAdapter must be an object");
69
+ }
70
+ for (const method of TERMINAL_ADAPTER_METHODS) {
71
+ if (typeof adapter[method] !== "function") {
72
+ throw new Error(`TerminalAdapter missing method: ${method}`);
73
+ }
74
+ }
75
+ assertTerminalCapabilities(adapter.capabilities);
76
+ return true;
77
+ }
78
+
79
+ module.exports = {
80
+ TERMINAL_CAPABILITY_KEYS,
81
+ TERMINAL_ADAPTER_METHODS,
82
+ createTerminalCapabilities,
83
+ assertTerminalCapabilities,
84
+ createUnsupportedCapabilityError,
85
+ requireCapability,
86
+ assertTerminalAdapterContract,
87
+ };
@@ -0,0 +1,84 @@
1
+ const {
2
+ createTerminalCapabilities,
3
+ assertTerminalAdapterContract,
4
+ } = require("./adapterContract");
5
+ const { createTerminalAdapter } = require("./adapters/terminalAdapter");
6
+ const { createTmuxAdapter } = require("./adapters/tmuxAdapter");
7
+ const { createInternalQueueAdapter } = require("./adapters/internalQueueAdapter");
8
+ const { createInternalPtyAdapter } = require("./adapters/internalPtyAdapter");
9
+
10
+ function createTerminalAdapterRouter(options = {}) {
11
+ const {
12
+ activateAgent = () => {},
13
+ activateTerminal = null,
14
+ activateTmux = null,
15
+ sendRaw = () => {},
16
+ sendResize = () => {},
17
+ requestSnapshot = () => false,
18
+ } = options;
19
+
20
+ function createAdapter({ capabilities, handlers = {} }) {
21
+ const adapter = {
22
+ capabilities,
23
+ connect: handlers.connect || (async () => false),
24
+ disconnect: handlers.disconnect || (async () => false),
25
+ send: handlers.send || (() => false),
26
+ sendRaw: handlers.sendRaw || (() => false),
27
+ resize: handlers.resize || (() => false),
28
+ snapshot: handlers.snapshot || (() => false),
29
+ subscribe: handlers.subscribe || (() => false),
30
+ activate: handlers.activate || (() => false),
31
+ getState: handlers.getState || (() => ({})),
32
+ };
33
+ assertTerminalAdapterContract(adapter);
34
+ return adapter;
35
+ }
36
+
37
+ function getAdapter(params = {}) {
38
+ const { launchMode = "", agentId = "" } = params;
39
+
40
+ if (launchMode === "terminal") {
41
+ return createTerminalAdapter({
42
+ agentId,
43
+ activateAgent: activateTerminal || activateAgent,
44
+ createAdapter,
45
+ });
46
+ }
47
+
48
+ if (launchMode === "tmux") {
49
+ return createTmuxAdapter({
50
+ agentId,
51
+ activateAgent: activateTmux || activateAgent,
52
+ createAdapter,
53
+ });
54
+ }
55
+
56
+ if (launchMode === "internal-pty") {
57
+ return createInternalPtyAdapter({
58
+ sendRaw,
59
+ sendResize,
60
+ requestSnapshot,
61
+ createAdapter,
62
+ });
63
+ }
64
+
65
+ if (launchMode === "internal") {
66
+ return createInternalQueueAdapter({
67
+ sendRaw,
68
+ sendResize,
69
+ requestSnapshot,
70
+ createAdapter,
71
+ });
72
+ }
73
+
74
+ return createAdapter({ capabilities: createTerminalCapabilities() });
75
+ }
76
+
77
+ return {
78
+ getAdapter,
79
+ };
80
+ }
81
+
82
+ module.exports = {
83
+ createTerminalAdapterRouter,
84
+ };
@@ -0,0 +1,14 @@
1
+ const { createTerminalAdapter } = require("./terminalAdapter");
2
+ const { createTmuxAdapter } = require("./tmuxAdapter");
3
+
4
+ function createExternalAdapter(options = {}) {
5
+ const mode = options.mode || "terminal";
6
+ if (mode === "tmux") {
7
+ return createTmuxAdapter(options);
8
+ }
9
+ return createTerminalAdapter(options);
10
+ }
11
+
12
+ module.exports = {
13
+ createExternalAdapter,
14
+ };
@@ -0,0 +1,13 @@
1
+ const { createInternalPtyAdapter } = require("./internalPtyAdapter");
2
+ const { createInternalQueueAdapter } = require("./internalQueueAdapter");
3
+
4
+ function createInternalAdapter(options = {}) {
5
+ if (options.usePty) {
6
+ return createInternalPtyAdapter(options);
7
+ }
8
+ return createInternalQueueAdapter(options);
9
+ }
10
+
11
+ module.exports = {
12
+ createInternalAdapter,
13
+ };
@@ -0,0 +1,42 @@
1
+ const { createTerminalCapabilities } = require("../adapterContract");
2
+
3
+ function createInternalPtyAdapter(options = {}) {
4
+ const {
5
+ sendRaw = () => {},
6
+ sendResize = () => {},
7
+ requestSnapshot = () => false,
8
+ createAdapter = () => {},
9
+ } = options;
10
+
11
+ const capabilities = createTerminalCapabilities({
12
+ supportsInternalQueueLoop: true,
13
+ supportsSocketProtocol: true,
14
+ supportsSubscribeFull: true,
15
+ supportsSubscribeScreen: true,
16
+ supportsSnapshot: true,
17
+ });
18
+
19
+ return createAdapter({
20
+ capabilities,
21
+ handlers: {
22
+ send: (data) => {
23
+ sendRaw(data);
24
+ return true;
25
+ },
26
+ sendRaw: (data) => {
27
+ sendRaw(data);
28
+ return true;
29
+ },
30
+ resize: (cols, rows) => {
31
+ sendResize(cols, rows);
32
+ return true;
33
+ },
34
+ snapshot: () => Boolean(requestSnapshot("screen")),
35
+ subscribe: () => Boolean(requestSnapshot("full")),
36
+ },
37
+ });
38
+ }
39
+
40
+ module.exports = {
41
+ createInternalPtyAdapter,
42
+ };
@@ -0,0 +1,37 @@
1
+ const { createTerminalCapabilities } = require("../adapterContract");
2
+
3
+ function createInternalQueueAdapter(options = {}) {
4
+ const {
5
+ sendRaw = () => {},
6
+ createAdapter = () => {},
7
+ } = options;
8
+
9
+ const capabilities = createTerminalCapabilities({
10
+ supportsInternalQueueLoop: true,
11
+ supportsSocketProtocol: false,
12
+ supportsSubscribeFull: false,
13
+ supportsSubscribeScreen: false,
14
+ supportsSnapshot: false,
15
+ });
16
+
17
+ return createAdapter({
18
+ capabilities,
19
+ handlers: {
20
+ send: (data) => {
21
+ sendRaw(data);
22
+ return true;
23
+ },
24
+ sendRaw: (data) => {
25
+ sendRaw(data);
26
+ return true;
27
+ },
28
+ resize: () => false,
29
+ snapshot: () => false,
30
+ subscribe: () => false,
31
+ },
32
+ });
33
+ }
34
+
35
+ module.exports = {
36
+ createInternalQueueAdapter,
37
+ };
@@ -0,0 +1,31 @@
1
+ const { createTerminalCapabilities } = require("../adapterContract");
2
+
3
+ function createTerminalAdapter(options = {}) {
4
+ const {
5
+ agentId = "",
6
+ activateAgent = () => {},
7
+ createAdapter = () => {},
8
+ } = options;
9
+
10
+ const capabilities = createTerminalCapabilities({
11
+ supportsActivate: true,
12
+ supportsReplay: true,
13
+ supportsWindowClose: true,
14
+ supportsNotifierInjector: true,
15
+ supportsSessionReuse: true,
16
+ });
17
+
18
+ return createAdapter({
19
+ capabilities,
20
+ handlers: {
21
+ activate: () => {
22
+ activateAgent(agentId);
23
+ return true;
24
+ },
25
+ },
26
+ });
27
+ }
28
+
29
+ module.exports = {
30
+ createTerminalAdapter,
31
+ };
@@ -0,0 +1,30 @@
1
+ const { createTerminalCapabilities } = require("../adapterContract");
2
+
3
+ function createTmuxAdapter(options = {}) {
4
+ const {
5
+ agentId = "",
6
+ activateAgent = () => {},
7
+ createAdapter = () => {},
8
+ } = options;
9
+
10
+ const capabilities = createTerminalCapabilities({
11
+ supportsActivate: true,
12
+ supportsReplay: false,
13
+ supportsNotifierInjector: true,
14
+ supportsSessionReuse: true,
15
+ });
16
+
17
+ return createAdapter({
18
+ capabilities,
19
+ handlers: {
20
+ activate: () => {
21
+ activateAgent(agentId);
22
+ return true;
23
+ },
24
+ },
25
+ });
26
+ }
27
+
28
+ module.exports = {
29
+ createTmuxAdapter,
30
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Terminal type detection
3
+ *
4
+ * Detects the terminal emulator and its capabilities from environment variables.
5
+ * Results are cached for the lifetime of the process.
6
+ */
7
+
8
+ const TERMINAL_TYPES = {
9
+ ITERM2: "iterm2",
10
+ APPLE_TERMINAL: "apple-terminal",
11
+ KITTY: "kitty",
12
+ WEZTERM: "wezterm",
13
+ ALACRITTY: "alacritty",
14
+ UNKNOWN: "unknown",
15
+ };
16
+
17
+ let cached = null;
18
+
19
+ /**
20
+ * Detect the current terminal emulator.
21
+ * @returns {{ type: string, version: string, truecolor: boolean }}
22
+ */
23
+ function detect() {
24
+ if (cached) return cached;
25
+
26
+ const prog = process.env.TERM_PROGRAM || "";
27
+ const ver = process.env.TERM_PROGRAM_VERSION || "";
28
+ const colorterm = (process.env.COLORTERM || "").toLowerCase();
29
+ const truecolor = colorterm === "truecolor" || colorterm === "24bit";
30
+
31
+ let type = TERMINAL_TYPES.UNKNOWN;
32
+
33
+ if (prog === "iTerm.app" || process.env.ITERM_SESSION_ID) {
34
+ type = TERMINAL_TYPES.ITERM2;
35
+ } else if (prog === "Apple_Terminal") {
36
+ type = TERMINAL_TYPES.APPLE_TERMINAL;
37
+ } else if (prog === "kitty" || process.env.KITTY_PID) {
38
+ type = TERMINAL_TYPES.KITTY;
39
+ } else if (prog === "WezTerm") {
40
+ type = TERMINAL_TYPES.WEZTERM;
41
+ } else if (prog === "Alacritty") {
42
+ type = TERMINAL_TYPES.ALACRITTY;
43
+ }
44
+
45
+ cached = { type, version: ver, truecolor };
46
+ return cached;
47
+ }
48
+
49
+ function isITerm2() {
50
+ return detect().type === TERMINAL_TYPES.ITERM2;
51
+ }
52
+
53
+ function isAppleTerminal() {
54
+ return detect().type === TERMINAL_TYPES.APPLE_TERMINAL;
55
+ }
56
+
57
+ /**
58
+ * Reset cached detection (for testing).
59
+ */
60
+ function resetCache() {
61
+ cached = null;
62
+ }
63
+
64
+ module.exports = { detect, isITerm2, isAppleTerminal, resetCache, TERMINAL_TYPES };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Terminal detection and feature modules.
3
+ */
4
+
5
+ const detect = require("./detect");
6
+ const iterm2 = require("./iterm2");
7
+
8
+ module.exports = { ...detect, iterm2 };