sootsim 0.0.1 → 0.0.2

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 (122) hide show
  1. package/README.md +12 -0
  2. package/dist-cli/bin.js +16 -10
  3. package/dist-cli/chunks/agent-3T4BJEZM.js +61 -0
  4. package/dist-cli/chunks/agent-wrapper-WCYNLWHZ.js +15 -0
  5. package/dist-cli/chunks/assert-FPFJEFF3.js +47 -0
  6. package/dist-cli/chunks/auto-bootstrap-HDW6N77H.js +2 -0
  7. package/dist-cli/chunks/chunk-3HBBSRLE.js +2 -0
  8. package/dist-cli/chunks/chunk-4372UQHZ.js +308 -0
  9. package/dist-cli/chunks/chunk-4GWEO5CL.js +1 -0
  10. package/dist-cli/chunks/{chunk-G5MR66EB.js → chunk-5C5I5OFM.js} +2 -2
  11. package/dist-cli/chunks/chunk-6IPY24VM.js +11 -0
  12. package/dist-cli/chunks/chunk-AS4V7TZU.js +2 -0
  13. package/dist-cli/chunks/chunk-B5R4K2DG.js +5 -0
  14. package/dist-cli/chunks/chunk-CXTA5VGA.js +4 -0
  15. package/dist-cli/chunks/chunk-CZZB4DWG.js +3 -0
  16. package/dist-cli/chunks/chunk-DW54UPRZ.js +119 -0
  17. package/dist-cli/chunks/chunk-EIZCWDRE.js +1 -0
  18. package/dist-cli/chunks/{chunk-KSACMDXK.js → chunk-ET3NNZAR.js} +2 -2
  19. package/dist-cli/chunks/chunk-EWEKADK4.js +5 -0
  20. package/dist-cli/chunks/{chunk-JSF5LPNT.js → chunk-EWMYTXM2.js} +5 -5
  21. package/dist-cli/chunks/chunk-FUQ4XA6I.js +16 -0
  22. package/dist-cli/chunks/chunk-GQUOQNTP.js +27 -0
  23. package/dist-cli/chunks/chunk-HBNVKYSC.js +2 -0
  24. package/dist-cli/chunks/{chunk-64TOMNZX.js → chunk-HORCHQT7.js} +2 -2
  25. package/dist-cli/chunks/{chunk-YCETS3B3.js → chunk-ISAMAM3I.js} +2 -2
  26. package/dist-cli/chunks/{chunk-GPVPHE2B.js → chunk-K6YUSCAC.js} +2 -2
  27. package/dist-cli/chunks/{chunk-E522F5JW.js → chunk-K7LDP7JL.js} +1 -1
  28. package/dist-cli/chunks/chunk-KZ2LIDW6.js +2 -0
  29. package/dist-cli/chunks/{chunk-J2S3OCWA.js → chunk-LOV766MI.js} +1 -1
  30. package/dist-cli/chunks/{chunk-OROM7DZI.js → chunk-LXCFGKL2.js} +1 -1
  31. package/dist-cli/chunks/{chunk-PWXPA745.js → chunk-NE62JSI6.js} +1 -1
  32. package/dist-cli/chunks/chunk-NHA3G6A3.js +22 -0
  33. package/dist-cli/chunks/chunk-NXWCDGWS.js +2 -0
  34. package/dist-cli/chunks/{chunk-QOBRRY5X.js → chunk-RJUBGX5M.js} +1 -1
  35. package/dist-cli/chunks/chunk-SLCVEGTW.js +4 -0
  36. package/dist-cli/chunks/chunk-TGDP3D3V.js +34 -0
  37. package/dist-cli/chunks/chunk-TSZBQS6W.js +62 -0
  38. package/dist-cli/chunks/chunk-XKDQEYTE.js +1 -0
  39. package/dist-cli/chunks/chunk-XXUAOYYT.js +4 -0
  40. package/dist-cli/chunks/{chunk-7X6OPSRD.js → chunk-YVSZHVLU.js} +2 -2
  41. package/dist-cli/chunks/{compat-MRN2ORY5.js → compat-3HMKLGXL.js} +4 -4
  42. package/dist-cli/chunks/{config-CO5IYWUY.js → config-IJQ3KANN.js} +5 -5
  43. package/dist-cli/chunks/control-3RAFI4AW.js +2 -0
  44. package/dist-cli/chunks/{daemon-G4XVRFHM.js → daemon-BBEQJLRY.js} +2 -2
  45. package/dist-cli/chunks/{debug-ZNSZTWT6.js → debug-SGZ5ZFQI.js} +4 -4
  46. package/dist-cli/chunks/demo-app-registry-NCYP3WA6.js +2 -0
  47. package/dist-cli/chunks/{detox-JEGYNTYV.js → detox-PK74V2Y7.js} +2 -2
  48. package/dist-cli/chunks/{device-BS34FAFM.js → device-MWNFX54L.js} +2 -2
  49. package/dist-cli/chunks/drivers-EXUREU4B.js +2 -0
  50. package/dist-cli/chunks/electron-3NIHSU2K.js +15 -0
  51. package/dist-cli/chunks/flow-6Y3E6E5P.js +2 -0
  52. package/dist-cli/chunks/{hints-7Z656W4H.js → hints-XZJLBIXW.js} +2 -2
  53. package/dist-cli/chunks/home-paths-BNRMUBJA.js +2 -0
  54. package/dist-cli/chunks/{inspect-NAHXP2M5.js → inspect-FGTUAK4C.js} +153 -165
  55. package/dist-cli/chunks/install-LCXALH26.js +65 -0
  56. package/dist-cli/chunks/{install-desktop-PYIZIH67.js → install-desktop-U3RQ6XUX.js} +8 -4
  57. package/dist-cli/chunks/install-dev-desktop-BLKRFI42.js +100 -0
  58. package/dist-cli/chunks/keys-N5LBDSD5.js +19 -0
  59. package/dist-cli/chunks/launch-NIMSJH5I.js +16 -0
  60. package/dist-cli/chunks/{login-Z5Z54HUJ.js → login-CQV2XBRM.js} +5 -5
  61. package/dist-cli/chunks/{logout-T2QDYGCB.js → logout-R56NWAWQ.js} +2 -2
  62. package/dist-cli/chunks/{maestro-4AXTS7OE.js → maestro-ZYUVTM7H.js} +2 -2
  63. package/dist-cli/chunks/{preview-NMGWHWMX.js → preview-AOAWAYEQ.js} +2 -2
  64. package/dist-cli/chunks/{profile-6RGJA4FR.js → profile-DDADDPRW.js} +3 -3
  65. package/dist-cli/chunks/record-3OIOTHP6.js +37 -0
  66. package/dist-cli/chunks/runtime-JTLZYEXK.js +25 -0
  67. package/dist-cli/chunks/{screenshot-R3GCCSCI.js → screenshot-Q6N2V5LL.js} +3 -3
  68. package/dist-cli/chunks/screenshot-mode-WWLWJWQD.js +17 -0
  69. package/dist-cli/chunks/{screenshots-4UQJE4NC.js → screenshots-2JEPJGZO.js} +2 -2
  70. package/dist-cli/chunks/server-VH34RVAX.js +29 -0
  71. package/dist-cli/chunks/{skills-2PPKPL4B.js → skills-PU4627FY.js} +2 -2
  72. package/dist-cli/chunks/store-U2VDD2S4.js +2 -0
  73. package/dist-cli/chunks/{test-5LFKOQ4M.js → test-AECE56E7.js} +3 -3
  74. package/dist-cli/chunks/upload-KPP7KG6E.js +2 -0
  75. package/dist-cli/chunks/{whoami-H6FW34JS.js → whoami-NCGRRR7X.js} +2 -2
  76. package/dist-lib/agent-daemon-client.cjs +414 -0
  77. package/dist-lib/agent-events.cjs +48 -0
  78. package/dist-lib/agent-sessions.cjs +692 -0
  79. package/dist-lib/attached-projects.cjs +448 -0
  80. package/dist-lib/auth/shared-session.cjs +174 -0
  81. package/dist-lib/backend-origin.cjs +70 -0
  82. package/dist-lib/bridge-constants.cjs +32 -0
  83. package/dist-lib/cli-constants.cjs +32 -0
  84. package/dist-lib/config.cjs +88 -0
  85. package/dist-lib/dev-bundle-resolution.cjs +236 -0
  86. package/dist-lib/home-paths.cjs +234 -0
  87. package/dist-lib/host/bridge-host.cjs +3458 -0
  88. package/dist-lib/index.cjs +361 -0
  89. package/dist-lib/metro.cjs +215 -0
  90. package/dist-lib/render-mode.cjs +54 -0
  91. package/dist-lib/vite-base.cjs +4217 -0
  92. package/dist-lib/vite.cjs +178 -0
  93. package/package.json +80 -13
  94. package/scripts/postinstall.cjs +70 -0
  95. package/dist-cli/chunks/bridge-host-2EY7Z4AO.js +0 -2
  96. package/dist-cli/chunks/chunk-3C3ZH7PP.js +0 -4
  97. package/dist-cli/chunks/chunk-3R4ZZESY.js +0 -119
  98. package/dist-cli/chunks/chunk-74XPLOV4.js +0 -2
  99. package/dist-cli/chunks/chunk-7LMDCMSI.js +0 -8
  100. package/dist-cli/chunks/chunk-A2CZQIWO.js +0 -1
  101. package/dist-cli/chunks/chunk-CKZ376AY.js +0 -322
  102. package/dist-cli/chunks/chunk-E5UBZEYR.js +0 -2
  103. package/dist-cli/chunks/chunk-HOIHCO7S.js +0 -3
  104. package/dist-cli/chunks/chunk-KQWZZ56P.js +0 -2
  105. package/dist-cli/chunks/chunk-KSB6MSZ4.js +0 -34
  106. package/dist-cli/chunks/chunk-KXYKAYYB.js +0 -51
  107. package/dist-cli/chunks/chunk-MBFP2LVH.js +0 -3
  108. package/dist-cli/chunks/chunk-MPSZ5EWF.js +0 -16
  109. package/dist-cli/chunks/chunk-X2U72K7X.js +0 -1
  110. package/dist-cli/chunks/control-Y7TKKB6D.js +0 -2
  111. package/dist-cli/chunks/dev-ZUKCZQEX.js +0 -25
  112. package/dist-cli/chunks/dev-checkout-IEZVVTCN.js +0 -2
  113. package/dist-cli/chunks/drivers-46PFFIDF.js +0 -2
  114. package/dist-cli/chunks/electron-P2KOPX2S.js +0 -15
  115. package/dist-cli/chunks/flow-VVOF6UNC.js +0 -2
  116. package/dist-cli/chunks/install-EPUJX4AT.js +0 -67
  117. package/dist-cli/chunks/record-IE27Z2GA.js +0 -37
  118. package/dist-cli/chunks/screenshot-mode-SZQDNGYE.js +0 -17
  119. package/dist-cli/chunks/server-AN2G5KO4.js +0 -21
  120. package/dist-cli/chunks/store-PU5ES4YQ.js +0 -2
  121. package/dist-cli/chunks/upload-BYNPC54C.js +0 -2
  122. package/dist-cli/chunks/vite-plugin-5AEUUBKP.js +0 -9
@@ -0,0 +1,692 @@
1
+ /*! sootsim v0.0.2 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/agent-sessions.ts
33
+ var agent_sessions_exports = {};
34
+ __export(agent_sessions_exports, {
35
+ AgentSessionError: () => AgentSessionError,
36
+ endSession: () => endSession,
37
+ eventsFifoPath: () => eventsFifoPath,
38
+ mkfifoSync: () => mkfifoSync,
39
+ pidIsAlive: () => pidIsAlive,
40
+ promptFifoPath: () => promptFifoPath,
41
+ resolveSootsimInvocation: () => resolveSootsimInvocation,
42
+ sendPrompt: () => sendPrompt,
43
+ sessionDir: () => sessionDir,
44
+ startSession: () => startSession,
45
+ subscribeEvents: () => subscribeEvents,
46
+ transcriptPath: () => transcriptPath
47
+ });
48
+ module.exports = __toCommonJS(agent_sessions_exports);
49
+ var import_node_child_process = require("node:child_process");
50
+ var import_node_crypto2 = require("node:crypto");
51
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
52
+ var import_node_path2 = __toESM(require("node:path"), 1);
53
+ var import_node_readline = __toESM(require("node:readline"), 1);
54
+
55
+ // src/agent-events.ts
56
+ function isAgentEvent(value) {
57
+ if (!value || typeof value !== "object") return false;
58
+ const v = value;
59
+ return typeof v.type === "string" && typeof v.ts === "number";
60
+ }
61
+ function parseAgentEventLine(line) {
62
+ const trimmed = line.trim();
63
+ if (!trimmed) return null;
64
+ try {
65
+ const parsed = JSON.parse(trimmed);
66
+ return isAgentEvent(parsed) ? parsed : null;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ // src/agent-prompt.ts
73
+ var ENVELOPE_MARKER = "sootsim-agent-prompt-v1";
74
+ function cleanPromptText(value) {
75
+ return typeof value === "string" ? value.trim() : "";
76
+ }
77
+ function encodeAgentPromptEnvelope(input) {
78
+ const text = cleanPromptText(input.text);
79
+ if (!text) return "";
80
+ const displayText = cleanPromptText(input.displayText);
81
+ const inspectSummary = cleanPromptText(input.inspectSummary);
82
+ const inspectTrace = cleanPromptText(input.inspectTrace);
83
+ const needsEnvelope = !!inspectSummary || !!inspectTrace || /[\r\n]/.test(text) || !!displayText && displayText !== text;
84
+ if (!needsEnvelope) return text;
85
+ return JSON.stringify({
86
+ __sootsimAgentPrompt: ENVELOPE_MARKER,
87
+ text,
88
+ displayText,
89
+ inspectSummary,
90
+ inspectTrace
91
+ });
92
+ }
93
+
94
+ // src/attached-projects.ts
95
+ var import_node_crypto = require("node:crypto");
96
+ var import_node_fs = __toESM(require("node:fs"), 1);
97
+ var import_node_os = __toESM(require("node:os"), 1);
98
+ var import_node_path = __toESM(require("node:path"), 1);
99
+ var overrideDir = null;
100
+ function userDataDir() {
101
+ if (overrideDir) return overrideDir;
102
+ const fromEnv = process.env.SOOTSIM_USER_DATA_DIR;
103
+ if (fromEnv) return fromEnv;
104
+ try {
105
+ const electron = require("electron");
106
+ if (electron.app?.getPath) return electron.app.getPath("userData");
107
+ } catch {
108
+ }
109
+ return platformDefaultUserDataDir();
110
+ }
111
+ function platformDefaultUserDataDir() {
112
+ const home = import_node_os.default.homedir();
113
+ if (process.platform === "darwin") {
114
+ return import_node_path.default.join(home, "Library", "Application Support", "sootsim");
115
+ }
116
+ if (process.platform === "win32") {
117
+ return import_node_path.default.join(process.env.APPDATA || home, "sootsim");
118
+ }
119
+ const xdg = process.env.XDG_CONFIG_HOME || import_node_path.default.join(home, ".config");
120
+ return import_node_path.default.join(xdg, "sootsim");
121
+ }
122
+ function getUserDataDir() {
123
+ return userDataDir();
124
+ }
125
+ function storeFile() {
126
+ return import_node_path.default.join(userDataDir(), "attached-projects.json");
127
+ }
128
+ function cloneEmpty() {
129
+ return {
130
+ version: 1,
131
+ attachedProjects: [],
132
+ previewAttachments: [],
133
+ agentSessions: []
134
+ };
135
+ }
136
+ function loadStore() {
137
+ const file = storeFile();
138
+ let raw;
139
+ try {
140
+ raw = import_node_fs.default.readFileSync(file, "utf8");
141
+ } catch (err) {
142
+ if (err.code === "ENOENT") return cloneEmpty();
143
+ throw err;
144
+ }
145
+ try {
146
+ const parsed = JSON.parse(raw);
147
+ if (!parsed || typeof parsed !== "object") throw new Error("not an object");
148
+ return {
149
+ version: 1,
150
+ attachedProjects: Array.isArray(parsed.attachedProjects) ? parsed.attachedProjects : [],
151
+ previewAttachments: Array.isArray(parsed.previewAttachments) ? parsed.previewAttachments : [],
152
+ agentSessions: Array.isArray(parsed.agentSessions) ? parsed.agentSessions : []
153
+ };
154
+ } catch (err) {
155
+ const quarantine = `${file}.corrupt-${Date.now()}`;
156
+ try {
157
+ import_node_fs.default.renameSync(file, quarantine);
158
+ console.warn(
159
+ `[sootsim] attached-projects.json was unparseable; quarantined to ${quarantine}. original error: ${err.message}`
160
+ );
161
+ } catch {
162
+ }
163
+ return cloneEmpty();
164
+ }
165
+ }
166
+ function writeStore(store) {
167
+ const file = storeFile();
168
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(file), { recursive: true });
169
+ const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
170
+ const fd = import_node_fs.default.openSync(tmp, "w", 384);
171
+ try {
172
+ import_node_fs.default.writeFileSync(fd, JSON.stringify(store, null, 2));
173
+ import_node_fs.default.fsyncSync(fd);
174
+ } finally {
175
+ import_node_fs.default.closeSync(fd);
176
+ }
177
+ import_node_fs.default.renameSync(tmp, file);
178
+ }
179
+ function mutateStore(fn) {
180
+ const store = loadStore();
181
+ fn(store);
182
+ writeStore(store);
183
+ return store;
184
+ }
185
+ function newSessionId() {
186
+ return `s_${(0, import_node_crypto.randomBytes)(10).toString("hex")}`;
187
+ }
188
+ function findProjectById(id) {
189
+ return loadStore().attachedProjects.find((p) => p.id === id) ?? null;
190
+ }
191
+ var COST_HISTORY_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
192
+ function upsertSession(input) {
193
+ let result;
194
+ mutateStore((store) => {
195
+ if (input.id) {
196
+ const existing = store.agentSessions.find((s) => s.id === input.id);
197
+ if (existing) {
198
+ const merged = {
199
+ ...existing,
200
+ ...input,
201
+ lastSeenAt: Date.now()
202
+ };
203
+ const idx = store.agentSessions.indexOf(existing);
204
+ store.agentSessions[idx] = merged;
205
+ result = merged;
206
+ return;
207
+ }
208
+ }
209
+ const project = store.attachedProjects.find((p) => p.id === input.projectId);
210
+ if (!project) {
211
+ throw new Error(`upsertSession: no AttachedProject with id=${input.projectId}`);
212
+ }
213
+ const now = Date.now();
214
+ const created = {
215
+ id: input.id ?? newSessionId(),
216
+ projectId: input.projectId,
217
+ provider: input.provider,
218
+ transport: input.transport ?? project.preferredTransport,
219
+ cwd: input.cwd ?? project.cwd,
220
+ claudeSessionUuid: input.claudeSessionUuid,
221
+ tmuxSessionName: input.tmuxSessionName,
222
+ wrapperPid: input.wrapperPid,
223
+ status: input.status ?? "idle",
224
+ needsAttention: input.needsAttention ?? false,
225
+ lastPrompt: input.lastPrompt,
226
+ lastSummary: input.lastSummary,
227
+ lastTurnFiles: input.lastTurnFiles,
228
+ currentlyEditing: input.currentlyEditing,
229
+ lastSeenAt: now,
230
+ createdAt: now
231
+ };
232
+ store.agentSessions.push(created);
233
+ result = created;
234
+ });
235
+ return result;
236
+ }
237
+ function findSessionById(id) {
238
+ return loadStore().agentSessions.find((s) => s.id === id) ?? null;
239
+ }
240
+ function listSessions(projectId) {
241
+ const all = loadStore().agentSessions;
242
+ return projectId ? all.filter((s) => s.projectId === projectId) : all;
243
+ }
244
+ function updateSessionStatus(id, patch) {
245
+ mutateStore((store) => {
246
+ const existing = store.agentSessions.find((s) => s.id === id);
247
+ if (!existing) return;
248
+ const idx = store.agentSessions.indexOf(existing);
249
+ store.agentSessions[idx] = {
250
+ ...existing,
251
+ ...patch,
252
+ id: existing.id,
253
+ projectId: existing.projectId,
254
+ createdAt: existing.createdAt,
255
+ lastSeenAt: Date.now()
256
+ };
257
+ });
258
+ }
259
+
260
+ // src/agent-sessions.ts
261
+ function sessionDir(sessionId) {
262
+ return import_node_path2.default.join(getUserDataDir(), "sessions", sessionId);
263
+ }
264
+ function promptFifoPath(sessionId) {
265
+ return import_node_path2.default.join(sessionDir(sessionId), "prompt.in");
266
+ }
267
+ function eventsFifoPath(sessionId) {
268
+ return import_node_path2.default.join(sessionDir(sessionId), "events.out");
269
+ }
270
+ function transcriptPath(sessionId) {
271
+ return import_node_path2.default.join(getUserDataDir(), "transcripts", `${sessionId}.log`);
272
+ }
273
+ function pidIsAlive(pid, sessionId) {
274
+ if (!pid) return false;
275
+ try {
276
+ process.kill(pid, 0);
277
+ } catch {
278
+ return false;
279
+ }
280
+ if (sessionId) {
281
+ if (!import_node_fs2.default.existsSync(sessionDir(sessionId))) return false;
282
+ }
283
+ return true;
284
+ }
285
+ function resolveSootsimInvocation() {
286
+ if (process.env.SOOTSIM_BIN) {
287
+ return { cmd: process.env.SOOTSIM_BIN, prefixArgs: [] };
288
+ }
289
+ if (process.versions.electron) {
290
+ const resourcesPath = process.resourcesPath;
291
+ if (resourcesPath) {
292
+ const candidates = [
293
+ import_node_path2.default.join(resourcesPath, "bin", "sootsim"),
294
+ import_node_path2.default.join(resourcesPath, "bin", `sootsim-${process.platform}-${process.arch}`)
295
+ ];
296
+ for (const c of candidates) {
297
+ if (import_node_fs2.default.existsSync(c)) return { cmd: c, prefixArgs: [] };
298
+ }
299
+ }
300
+ }
301
+ const workspace = tryWorkspaceSootsim();
302
+ if (workspace) return workspace;
303
+ const argv0 = process.argv[0];
304
+ const argv1 = process.argv[1];
305
+ if (argv1 && /\.(ts|tsx|mjs|cjs|js)$/.test(argv1)) {
306
+ return { cmd: argv0, prefixArgs: [argv1] };
307
+ }
308
+ if (!argv1 || argv1.includes("/.bin/")) {
309
+ throw new Error(
310
+ "sootsim CLI not found. set SOOTSIM_BIN to the path of the sootsim binary, or build the workspace CLI via `bun run --cwd packages/sootsim build:cli`."
311
+ );
312
+ }
313
+ return { cmd: argv0, prefixArgs: [] };
314
+ }
315
+ function tryWorkspaceSootsim() {
316
+ try {
317
+ const sootsimDir = resolveSootsimPackageDir();
318
+ if (!sootsimDir) return null;
319
+ const binaryName = `sootsim-${process.platform}-${process.arch}`;
320
+ const distBinary = import_node_path2.default.join(sootsimDir, "dist-bin", binaryName);
321
+ if (import_node_fs2.default.existsSync(distBinary)) return { cmd: distBinary, prefixArgs: [] };
322
+ const distBin = import_node_path2.default.join(sootsimDir, "dist-cli", "bin.js");
323
+ if (import_node_fs2.default.existsSync(distBin)) {
324
+ try {
325
+ const src = import_node_path2.default.join(sootsimDir, "cli", "commands", "agent-wrapper.ts");
326
+ if (import_node_fs2.default.existsSync(src)) {
327
+ const srcMtime = import_node_fs2.default.statSync(src).mtimeMs;
328
+ const buildMtime = import_node_fs2.default.statSync(distBin).mtimeMs;
329
+ if (buildMtime < srcMtime) {
330
+ console.warn(
331
+ `[sootsim] dist-cli/bin.js is older than agent-wrapper.ts \u2014 rebuild with \`bun run --cwd packages/sootsim build:cli\` (watch:cli:binary builds dist-bin/ instead).`
332
+ );
333
+ }
334
+ }
335
+ } catch {
336
+ }
337
+ return { cmd: process.execPath, prefixArgs: [distBin] };
338
+ }
339
+ return null;
340
+ } catch {
341
+ return null;
342
+ }
343
+ }
344
+ function resolveSootsimPackageDir() {
345
+ try {
346
+ const resolved = require.resolve("sootsim/package.json");
347
+ return import_node_path2.default.dirname(resolved);
348
+ } catch {
349
+ }
350
+ const here = fileFromImportMeta();
351
+ if (!here) return null;
352
+ let cur = import_node_path2.default.dirname(here);
353
+ for (let i = 0; i < 8; i++) {
354
+ const pkg = import_node_path2.default.join(cur, "package.json");
355
+ try {
356
+ if (import_node_fs2.default.existsSync(pkg)) {
357
+ const parsed = JSON.parse(import_node_fs2.default.readFileSync(pkg, "utf8"));
358
+ if (parsed.name === "sootsim") return cur;
359
+ }
360
+ } catch {
361
+ }
362
+ const parent = import_node_path2.default.dirname(cur);
363
+ if (parent === cur) break;
364
+ cur = parent;
365
+ }
366
+ return null;
367
+ }
368
+ function fileFromImportMeta() {
369
+ try {
370
+ const url = __sootsim_import_meta_url;
371
+ if (!url || !url.startsWith("file://")) return null;
372
+ return decodeURIComponent(url.slice("file://".length));
373
+ } catch {
374
+ return null;
375
+ }
376
+ }
377
+ async function withStartLock(projectId, provider, fn) {
378
+ const lockDir = import_node_path2.default.join(getUserDataDir(), "locks");
379
+ import_node_fs2.default.mkdirSync(lockDir, { recursive: true });
380
+ try {
381
+ import_node_fs2.default.chmodSync(lockDir, 448);
382
+ } catch {
383
+ }
384
+ const lockPath = import_node_path2.default.join(lockDir, `start-${projectId}-${provider}.lock`);
385
+ const deadline = Date.now() + 4e3;
386
+ let fd = null;
387
+ while (fd === null) {
388
+ try {
389
+ fd = import_node_fs2.default.openSync(
390
+ lockPath,
391
+ import_node_fs2.constants.O_WRONLY | import_node_fs2.constants.O_CREAT | import_node_fs2.constants.O_EXCL,
392
+ 384
393
+ );
394
+ } catch (err) {
395
+ if (err.code !== "EEXIST") throw err;
396
+ try {
397
+ const stale = Number(import_node_fs2.default.readFileSync(lockPath, "utf8").trim());
398
+ if (stale && !isProcessAlive(stale)) {
399
+ import_node_fs2.default.unlinkSync(lockPath);
400
+ continue;
401
+ }
402
+ } catch {
403
+ }
404
+ if (Date.now() > deadline) {
405
+ throw new Error(
406
+ `another start is in progress for project=${projectId} provider=${provider} (lock: ${lockPath})`
407
+ );
408
+ }
409
+ await new Promise((r) => setTimeout(r, 50));
410
+ }
411
+ }
412
+ try {
413
+ import_node_fs2.default.writeFileSync(fd, String(process.pid));
414
+ return await fn();
415
+ } finally {
416
+ try {
417
+ import_node_fs2.default.closeSync(fd);
418
+ } catch {
419
+ }
420
+ try {
421
+ import_node_fs2.default.unlinkSync(lockPath);
422
+ } catch {
423
+ }
424
+ }
425
+ }
426
+ function isProcessAlive(pid) {
427
+ try {
428
+ process.kill(pid, 0);
429
+ return true;
430
+ } catch {
431
+ return false;
432
+ }
433
+ }
434
+ function mkfifoSync(p) {
435
+ const parent = import_node_path2.default.dirname(p);
436
+ import_node_fs2.default.mkdirSync(parent, { recursive: true });
437
+ try {
438
+ import_node_fs2.default.chmodSync(parent, 448);
439
+ } catch {
440
+ }
441
+ if (import_node_fs2.default.existsSync(p)) {
442
+ try {
443
+ const stat = import_node_fs2.default.statSync(p);
444
+ if (stat.isFIFO()) {
445
+ try {
446
+ import_node_fs2.default.chmodSync(p, 384);
447
+ } catch {
448
+ }
449
+ return;
450
+ }
451
+ import_node_fs2.default.unlinkSync(p);
452
+ } catch {
453
+ import_node_fs2.default.unlinkSync(p);
454
+ }
455
+ }
456
+ const result = (0, import_node_child_process.spawnSync)("mkfifo", ["-m", "600", p]);
457
+ if (result.status !== 0) {
458
+ throw new Error(
459
+ `mkfifo(${p}) failed: ${result.stderr?.toString().trim() || "unknown error"}`
460
+ );
461
+ }
462
+ }
463
+ var AgentSessionError = class extends Error {
464
+ code;
465
+ constructor(code, message) {
466
+ super(message);
467
+ this.code = code;
468
+ }
469
+ };
470
+ async function startSession(opts) {
471
+ const project = findProjectById(opts.projectId);
472
+ if (!project) {
473
+ throw new AgentSessionError("NO_PROJECT", `no project with id=${opts.projectId}`);
474
+ }
475
+ const provider = opts.provider || project.preferredProvider || "codex";
476
+ return withStartLock(project.id, provider, async () => {
477
+ const existingLive = listSessions(project.id).find(
478
+ (s) => s.provider === provider && s.status !== "ended" && pidIsAlive(s.wrapperPid, s.id)
479
+ );
480
+ if (existingLive) {
481
+ throw new AgentSessionError(
482
+ "ALREADY_RUNNING",
483
+ `session already running for project=${project.id} provider=${provider} (session ${existingLive.id}, pid ${existingLive.wrapperPid}). end it first with \`sootsim agent end <sessionId>\`.`
484
+ );
485
+ }
486
+ const claudeSessionUuid = provider === "claude" ? (0, import_node_crypto2.randomUUID)() : void 0;
487
+ const session = upsertSession({
488
+ projectId: project.id,
489
+ provider,
490
+ transport: "pty",
491
+ cwd: project.cwd,
492
+ status: "idle",
493
+ claudeSessionUuid
494
+ });
495
+ const promptIn = promptFifoPath(session.id);
496
+ const eventsOut = eventsFifoPath(session.id);
497
+ const transcript = transcriptPath(session.id);
498
+ mkfifoSync(promptIn);
499
+ mkfifoSync(eventsOut);
500
+ const transcriptDir = import_node_path2.default.dirname(transcript);
501
+ import_node_fs2.default.mkdirSync(transcriptDir, { recursive: true });
502
+ try {
503
+ import_node_fs2.default.chmodSync(transcriptDir, 448);
504
+ } catch {
505
+ }
506
+ const { cmd, prefixArgs } = resolveSootsimInvocation();
507
+ const wrapperArgs = [
508
+ ...prefixArgs,
509
+ "agent-wrapper",
510
+ "--session-id",
511
+ session.id,
512
+ "--project-id",
513
+ project.id,
514
+ "--provider",
515
+ provider,
516
+ "--cwd",
517
+ project.cwd,
518
+ "--prompt-in",
519
+ promptIn,
520
+ "--events-out",
521
+ eventsOut,
522
+ "--transcript",
523
+ transcript
524
+ ];
525
+ if (opts.codexBin) wrapperArgs.push("--codex-bin", opts.codexBin);
526
+ if (opts.claudeBin) wrapperArgs.push("--claude-bin", opts.claudeBin);
527
+ if (claudeSessionUuid) {
528
+ wrapperArgs.push("--claude-session-uuid", claudeSessionUuid);
529
+ }
530
+ const child = (0, import_node_child_process.spawn)(cmd, wrapperArgs, {
531
+ detached: true,
532
+ stdio: "ignore",
533
+ env: {
534
+ ...process.env,
535
+ SOOTSIM_USER_DATA_DIR: getUserDataDir()
536
+ }
537
+ });
538
+ child.unref();
539
+ const readyTimeout = opts.readyTimeoutMs ?? 6e3;
540
+ const boot = await waitForFirstEvent(
541
+ eventsOut,
542
+ (e) => e.type === "ready" || e.type === "error",
543
+ readyTimeout
544
+ );
545
+ if (!boot || boot.type === "error") {
546
+ if (child.pid) {
547
+ try {
548
+ process.kill(child.pid, "SIGTERM");
549
+ } catch {
550
+ }
551
+ }
552
+ try {
553
+ import_node_fs2.default.rmSync(sessionDir(session.id), { recursive: true, force: true });
554
+ } catch {
555
+ }
556
+ updateSessionStatus(session.id, { status: "ended" });
557
+ const reason = boot && boot.type === "error" ? boot.message : `no ready event within ${readyTimeout}ms`;
558
+ throw new AgentSessionError("WRAPPER_FAILED", reason);
559
+ }
560
+ updateSessionStatus(session.id, {
561
+ wrapperPid: child.pid,
562
+ status: "idle"
563
+ });
564
+ const updated = findSessionById(session.id);
565
+ return { session: updated, wrapperPid: child.pid };
566
+ });
567
+ }
568
+ async function sendPrompt(sessionId, prompt) {
569
+ const session = findSessionById(sessionId);
570
+ if (!session) {
571
+ throw new AgentSessionError("NO_SESSION", `no session with id=${sessionId}`);
572
+ }
573
+ if (!pidIsAlive(session.wrapperPid, sessionId)) {
574
+ updateSessionStatus(sessionId, { status: "ended" });
575
+ throw new AgentSessionError(
576
+ "NOT_ALIVE",
577
+ `session wrapper is not alive (pid=${session.wrapperPid}). start a new session.`
578
+ );
579
+ }
580
+ const fifo = promptFifoPath(sessionId);
581
+ if (!import_node_fs2.default.existsSync(fifo)) {
582
+ throw new AgentSessionError("NO_FIFO", `prompt FIFO missing: ${fifo}`);
583
+ }
584
+ const fd = import_node_fs2.default.openSync(fifo, import_node_fs2.constants.O_WRONLY);
585
+ try {
586
+ const wireText = encodeAgentPromptEnvelope(prompt);
587
+ if (!wireText) {
588
+ throw new AgentSessionError("EMPTY_PROMPT", "prompt text is empty");
589
+ }
590
+ import_node_fs2.default.writeSync(fd, wireText + "\n");
591
+ } finally {
592
+ import_node_fs2.default.closeSync(fd);
593
+ }
594
+ updateSessionStatus(sessionId, {
595
+ lastPrompt: prompt.displayText ?? prompt.text,
596
+ status: "working"
597
+ });
598
+ }
599
+ async function endSession(sessionId) {
600
+ const session = findSessionById(sessionId);
601
+ if (!session) {
602
+ throw new AgentSessionError("NO_SESSION", `no session with id=${sessionId}`);
603
+ }
604
+ if (pidIsAlive(session.wrapperPid, sessionId)) {
605
+ try {
606
+ process.kill(session.wrapperPid, "SIGTERM");
607
+ } catch {
608
+ }
609
+ }
610
+ const dir = sessionDir(sessionId);
611
+ const base = getUserDataDir();
612
+ if (dir.startsWith(base)) {
613
+ try {
614
+ import_node_fs2.default.rmSync(dir, { recursive: true, force: true });
615
+ } catch {
616
+ }
617
+ }
618
+ updateSessionStatus(sessionId, { status: "ended", wrapperPid: void 0 });
619
+ }
620
+ function subscribeEvents(sessionId, onEvent) {
621
+ const fifo = eventsFifoPath(sessionId);
622
+ if (!import_node_fs2.default.existsSync(fifo)) {
623
+ throw new AgentSessionError("NO_FIFO", `events FIFO missing: ${fifo}`);
624
+ }
625
+ const fd = import_node_fs2.default.openSync(fifo, import_node_fs2.constants.O_RDWR);
626
+ const stream = import_node_fs2.default.createReadStream("", { fd, autoClose: true });
627
+ const rl = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
628
+ rl.on("line", (line) => {
629
+ const event = parseAgentEventLine(line);
630
+ if (event) onEvent(event);
631
+ });
632
+ let closed = false;
633
+ return () => {
634
+ if (closed) return;
635
+ closed = true;
636
+ try {
637
+ rl.close();
638
+ } catch {
639
+ }
640
+ try {
641
+ stream.destroy();
642
+ } catch {
643
+ }
644
+ };
645
+ }
646
+ async function waitForFirstEvent(fifo, predicate, timeoutMs) {
647
+ const fd = import_node_fs2.default.openSync(fifo, import_node_fs2.constants.O_RDWR | import_node_fs2.constants.O_NONBLOCK);
648
+ const buf = Buffer.alloc(8192);
649
+ let leftover = "";
650
+ const deadline = Date.now() + timeoutMs;
651
+ try {
652
+ while (Date.now() < deadline) {
653
+ let n = 0;
654
+ try {
655
+ n = import_node_fs2.default.readSync(fd, buf, 0, buf.length, null);
656
+ } catch (err) {
657
+ if (err.code !== "EAGAIN") throw err;
658
+ n = 0;
659
+ }
660
+ if (n > 0) {
661
+ leftover += buf.subarray(0, n).toString("utf8");
662
+ let idx;
663
+ while ((idx = leftover.indexOf("\n")) >= 0) {
664
+ const line = leftover.slice(0, idx);
665
+ leftover = leftover.slice(idx + 1);
666
+ const event = parseAgentEventLine(line);
667
+ if (event && predicate(event)) return event;
668
+ }
669
+ } else {
670
+ await new Promise((r) => setTimeout(r, 30));
671
+ }
672
+ }
673
+ return null;
674
+ } finally {
675
+ import_node_fs2.default.closeSync(fd);
676
+ }
677
+ }
678
+ // Annotate the CommonJS export names for ESM import in node:
679
+ 0 && (module.exports = {
680
+ AgentSessionError,
681
+ endSession,
682
+ eventsFifoPath,
683
+ mkfifoSync,
684
+ pidIsAlive,
685
+ promptFifoPath,
686
+ resolveSootsimInvocation,
687
+ sendPrompt,
688
+ sessionDir,
689
+ startSession,
690
+ subscribeEvents,
691
+ transcriptPath
692
+ });