@mtreeai/msapling-cli 2.3.6-beta.28 → 2.3.6-beta.30
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.
- package/dist/index.js +1286 -245
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -53,11 +53,438 @@ var init_esm_shims = __esm({
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
// ../api-client/src/localLlm.ts
|
|
57
|
+
import { homedir } from "os";
|
|
58
|
+
import { readFile } from "fs/promises";
|
|
59
|
+
import { join } from "path";
|
|
60
|
+
async function probeDialect(baseUrl) {
|
|
61
|
+
const normalizedUrl = baseUrl.replace(/\/$/, "");
|
|
62
|
+
const timeout = 800;
|
|
63
|
+
try {
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
66
|
+
const response = await fetch(`${normalizedUrl}/api/tags`, { signal: controller.signal });
|
|
67
|
+
clearTimeout(timeoutId);
|
|
68
|
+
if (response.ok) return "ollama";
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
74
|
+
const response = await fetch(`${normalizedUrl}/v1/models`, { signal: controller.signal });
|
|
75
|
+
clearTimeout(timeoutId);
|
|
76
|
+
if (response.ok) return "openai_compat";
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
async function detectLocalLlm() {
|
|
82
|
+
const endpoints = [
|
|
83
|
+
process.env.MSAPLING_LOCAL_LLM_URL,
|
|
84
|
+
await readConfigLocalLlmUrl(),
|
|
85
|
+
"http://localhost:11434",
|
|
86
|
+
// Ollama
|
|
87
|
+
"http://localhost:8080",
|
|
88
|
+
// llama.cpp
|
|
89
|
+
"http://localhost:1234"
|
|
90
|
+
// LM Studio
|
|
91
|
+
].filter((url) => !!url);
|
|
92
|
+
for (const baseUrl of endpoints) {
|
|
93
|
+
try {
|
|
94
|
+
const dialect = await probeDialect(baseUrl);
|
|
95
|
+
if (dialect) {
|
|
96
|
+
return new LocalLlmClient({
|
|
97
|
+
baseUrl,
|
|
98
|
+
model: process.env.MSAPLING_LOCAL_LLM_MODEL ?? "default",
|
|
99
|
+
dialect
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
async function readConfigLocalLlmUrl() {
|
|
108
|
+
try {
|
|
109
|
+
const configPath = join(homedir(), ".msapling", "config.json");
|
|
110
|
+
const content = await readFile(configPath, "utf-8");
|
|
111
|
+
const config = JSON.parse(content);
|
|
112
|
+
return config.localLlm?.baseUrl ?? null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
var LocalLlmClient, OllamaClient;
|
|
118
|
+
var init_localLlm = __esm({
|
|
119
|
+
"../api-client/src/localLlm.ts"() {
|
|
120
|
+
"use strict";
|
|
121
|
+
init_esm_shims();
|
|
122
|
+
LocalLlmClient = class {
|
|
123
|
+
baseUrl;
|
|
124
|
+
model;
|
|
125
|
+
apiKey;
|
|
126
|
+
dialect;
|
|
127
|
+
constructor(config) {
|
|
128
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
129
|
+
this.model = config.model;
|
|
130
|
+
this.apiKey = config.apiKey;
|
|
131
|
+
this.dialect = config.dialect ?? "openai_compat";
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Detect dialect from baseUrl if not explicitly provided
|
|
135
|
+
*/
|
|
136
|
+
async detectDialect() {
|
|
137
|
+
if (this.dialect !== "openai_compat") return this.dialect;
|
|
138
|
+
const detected = await probeDialect(this.baseUrl);
|
|
139
|
+
if (detected) {
|
|
140
|
+
this.dialect = detected;
|
|
141
|
+
}
|
|
142
|
+
return this.dialect;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if local LLM server is reachable
|
|
146
|
+
*/
|
|
147
|
+
async isAvailable() {
|
|
148
|
+
try {
|
|
149
|
+
const dialect = await this.detectDialect();
|
|
150
|
+
const endpoint = dialect === "ollama" ? "/api/tags" : "/v1/models";
|
|
151
|
+
const controller = new AbortController();
|
|
152
|
+
const timeout = setTimeout(() => controller.abort(), 1e3);
|
|
153
|
+
const response = await fetch(`${this.baseUrl}${endpoint}`, { signal: controller.signal });
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
return response.ok;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* List available models
|
|
162
|
+
*/
|
|
163
|
+
async listModels() {
|
|
164
|
+
const dialect = await this.detectDialect();
|
|
165
|
+
if (dialect === "ollama") {
|
|
166
|
+
const response = await fetch(`${this.baseUrl}/api/tags`);
|
|
167
|
+
if (!response.ok) throw new Error(`Ollama /api/tags failed: ${response.status}`);
|
|
168
|
+
const data = await response.json();
|
|
169
|
+
return (data.models ?? []).map((m) => ({ name: m.name, size: m.size }));
|
|
170
|
+
} else {
|
|
171
|
+
const response = await fetch(`${this.baseUrl}/v1/models`);
|
|
172
|
+
if (!response.ok) throw new Error(`OpenAI /v1/models failed: ${response.status}`);
|
|
173
|
+
const data = await response.json();
|
|
174
|
+
return (data.data ?? []).map((m) => ({ name: m.id, size: 0 }));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Stream chat messages
|
|
179
|
+
*/
|
|
180
|
+
async *streamChat(model, messages) {
|
|
181
|
+
const dialect = await this.detectDialect();
|
|
182
|
+
if (dialect === "ollama") {
|
|
183
|
+
yield* this.streamChatOllama(model, messages);
|
|
184
|
+
} else {
|
|
185
|
+
yield* this.streamChatOpenAI(model, messages);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async *streamChatOllama(model, messages) {
|
|
189
|
+
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "Content-Type": "application/json" },
|
|
192
|
+
body: JSON.stringify({ model, messages, stream: true })
|
|
193
|
+
});
|
|
194
|
+
if (!response.ok) throw new Error(`Ollama /api/chat failed: ${response.status}`);
|
|
195
|
+
if (!response.body) throw new Error("No response body");
|
|
196
|
+
const reader = response.body.getReader();
|
|
197
|
+
const decoder = new TextDecoder();
|
|
198
|
+
let pending = "";
|
|
199
|
+
try {
|
|
200
|
+
while (true) {
|
|
201
|
+
const { done, value } = await reader.read();
|
|
202
|
+
if (done) break;
|
|
203
|
+
pending += decoder.decode(value, { stream: true });
|
|
204
|
+
let nlIdx;
|
|
205
|
+
while ((nlIdx = pending.indexOf("\n")) !== -1) {
|
|
206
|
+
const line = pending.slice(0, nlIdx).trim();
|
|
207
|
+
pending = pending.slice(nlIdx + 1);
|
|
208
|
+
if (!line) continue;
|
|
209
|
+
try {
|
|
210
|
+
const obj = JSON.parse(line);
|
|
211
|
+
yield {
|
|
212
|
+
delta: obj.message?.content ?? "",
|
|
213
|
+
done: obj.done ?? false,
|
|
214
|
+
prompt_tokens: obj.prompt_eval_count,
|
|
215
|
+
completion_tokens: obj.eval_count
|
|
216
|
+
};
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
pending += decoder.decode();
|
|
222
|
+
if (pending.trim()) {
|
|
223
|
+
try {
|
|
224
|
+
const obj = JSON.parse(pending);
|
|
225
|
+
yield {
|
|
226
|
+
delta: obj.message?.content ?? "",
|
|
227
|
+
done: obj.done ?? true,
|
|
228
|
+
prompt_tokens: obj.prompt_eval_count,
|
|
229
|
+
completion_tokens: obj.eval_count
|
|
230
|
+
};
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} finally {
|
|
235
|
+
reader.cancel();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async *streamChatOpenAI(model, messages) {
|
|
239
|
+
const headers = { "Content-Type": "application/json" };
|
|
240
|
+
if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
241
|
+
const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers,
|
|
244
|
+
body: JSON.stringify({ model, messages, stream: true })
|
|
245
|
+
});
|
|
246
|
+
if (!response.ok) throw new Error(`OpenAI /v1/chat/completions failed: ${response.status}`);
|
|
247
|
+
if (!response.body) throw new Error("No response body");
|
|
248
|
+
const reader = response.body.getReader();
|
|
249
|
+
const decoder = new TextDecoder();
|
|
250
|
+
let pending = "";
|
|
251
|
+
let promptTokens = 0;
|
|
252
|
+
let completionTokens = 0;
|
|
253
|
+
try {
|
|
254
|
+
while (true) {
|
|
255
|
+
const { done, value } = await reader.read();
|
|
256
|
+
if (done) break;
|
|
257
|
+
pending += decoder.decode(value, { stream: true });
|
|
258
|
+
let nlIdx;
|
|
259
|
+
while ((nlIdx = pending.indexOf("\n")) !== -1) {
|
|
260
|
+
const line = pending.slice(0, nlIdx).trim();
|
|
261
|
+
pending = pending.slice(nlIdx + 1);
|
|
262
|
+
if (!line || !line.startsWith("data:")) continue;
|
|
263
|
+
const data = line.slice(5).trim();
|
|
264
|
+
if (data === "[DONE]") continue;
|
|
265
|
+
try {
|
|
266
|
+
const obj = JSON.parse(data);
|
|
267
|
+
const delta = obj.choices?.[0]?.delta?.content ?? "";
|
|
268
|
+
const done2 = obj.choices?.[0]?.finish_reason !== null;
|
|
269
|
+
if (obj.usage) {
|
|
270
|
+
promptTokens = obj.usage.prompt_tokens ?? 0;
|
|
271
|
+
completionTokens = obj.usage.completion_tokens ?? 0;
|
|
272
|
+
}
|
|
273
|
+
yield {
|
|
274
|
+
delta,
|
|
275
|
+
done: done2 || obj.choices?.[0]?.finish_reason !== null && delta === "",
|
|
276
|
+
prompt_tokens: promptTokens,
|
|
277
|
+
completion_tokens: completionTokens
|
|
278
|
+
};
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} finally {
|
|
284
|
+
reader.cancel();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
OllamaClient = class extends LocalLlmClient {
|
|
289
|
+
constructor(baseUrl = process.env.OLLAMA_HOST ?? "http://localhost:11434") {
|
|
290
|
+
super({ baseUrl, model: "default", dialect: "ollama" });
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ../api-client/src/journal.ts
|
|
297
|
+
import { homedir as homedir2 } from "os";
|
|
298
|
+
import { join as join2 } from "path";
|
|
299
|
+
function getJournal() {
|
|
300
|
+
if (!journalInstance) {
|
|
301
|
+
journalInstance = new Journal();
|
|
302
|
+
}
|
|
303
|
+
return journalInstance;
|
|
304
|
+
}
|
|
305
|
+
var DatabaseConnection, Journal, journalInstance;
|
|
306
|
+
var init_journal = __esm({
|
|
307
|
+
"../api-client/src/journal.ts"() {
|
|
308
|
+
"use strict";
|
|
309
|
+
init_esm_shims();
|
|
310
|
+
try {
|
|
311
|
+
const sqlite = __require("sqlite");
|
|
312
|
+
DatabaseConnection = sqlite;
|
|
313
|
+
} catch {
|
|
314
|
+
DatabaseConnection = null;
|
|
315
|
+
}
|
|
316
|
+
Journal = class {
|
|
317
|
+
dbPath;
|
|
318
|
+
jsonlPath;
|
|
319
|
+
useJsonl = false;
|
|
320
|
+
db = null;
|
|
321
|
+
jsonlEntries = [];
|
|
322
|
+
constructor() {
|
|
323
|
+
const home = homedir2();
|
|
324
|
+
const msaplingDir = join2(home, ".msapling");
|
|
325
|
+
this.dbPath = join2(msaplingDir, "journal.sqlite");
|
|
326
|
+
this.jsonlPath = join2(msaplingDir, "journal.jsonl");
|
|
327
|
+
this.initialize();
|
|
328
|
+
}
|
|
329
|
+
initialize() {
|
|
330
|
+
if (DatabaseConnection) {
|
|
331
|
+
try {
|
|
332
|
+
const { DatabaseSync } = DatabaseConnection;
|
|
333
|
+
this.db = new DatabaseSync(this.dbPath);
|
|
334
|
+
this.initializeSchema();
|
|
335
|
+
} catch (e) {
|
|
336
|
+
console.warn("[Journal] SQLite init failed, falling back to JSONL", e);
|
|
337
|
+
this.useJsonl = true;
|
|
338
|
+
this.loadJsonl();
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
this.useJsonl = true;
|
|
342
|
+
this.loadJsonl();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
initializeSchema() {
|
|
346
|
+
if (!this.db) return;
|
|
347
|
+
try {
|
|
348
|
+
this.db.exec(`
|
|
349
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
350
|
+
id TEXT PRIMARY KEY,
|
|
351
|
+
chat_id TEXT NOT NULL,
|
|
352
|
+
project_id TEXT,
|
|
353
|
+
role TEXT NOT NULL,
|
|
354
|
+
content TEXT NOT NULL,
|
|
355
|
+
ts INTEGER NOT NULL,
|
|
356
|
+
model TEXT,
|
|
357
|
+
token_count INTEGER,
|
|
358
|
+
source TEXT NOT NULL DEFAULT 'offline',
|
|
359
|
+
synced_at INTEGER
|
|
360
|
+
);
|
|
361
|
+
CREATE INDEX IF NOT EXISTS idx_synced ON messages(synced_at, ts);
|
|
362
|
+
`);
|
|
363
|
+
} catch (e) {
|
|
364
|
+
console.warn("[Journal] Schema creation failed", e);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
loadJsonl() {
|
|
368
|
+
}
|
|
369
|
+
async journalAppend(entry) {
|
|
370
|
+
if (this.useJsonl) {
|
|
371
|
+
this.jsonlEntries.push(entry);
|
|
372
|
+
} else if (this.db) {
|
|
373
|
+
try {
|
|
374
|
+
const stmt = this.db.prepare(`
|
|
375
|
+
INSERT INTO messages (id, chat_id, project_id, role, content, ts, model, token_count, source)
|
|
376
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
377
|
+
`);
|
|
378
|
+
stmt.run(
|
|
379
|
+
entry.id,
|
|
380
|
+
entry.chat_id,
|
|
381
|
+
entry.project_id || null,
|
|
382
|
+
entry.role,
|
|
383
|
+
entry.content,
|
|
384
|
+
entry.ts,
|
|
385
|
+
entry.model || null,
|
|
386
|
+
entry.token_count || null,
|
|
387
|
+
entry.source
|
|
388
|
+
);
|
|
389
|
+
} catch (e) {
|
|
390
|
+
console.warn("[Journal] Failed to append message", e);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async journalListPending() {
|
|
395
|
+
if (this.useJsonl) {
|
|
396
|
+
return this.jsonlEntries.filter((e) => !e.synced_at);
|
|
397
|
+
}
|
|
398
|
+
if (!this.db) return [];
|
|
399
|
+
try {
|
|
400
|
+
const stmt = this.db.prepare(`
|
|
401
|
+
SELECT id, chat_id, project_id, role, content, ts, model, token_count, source, synced_at
|
|
402
|
+
FROM messages
|
|
403
|
+
WHERE synced_at IS NULL
|
|
404
|
+
ORDER BY ts ASC
|
|
405
|
+
`);
|
|
406
|
+
const rows = stmt.all();
|
|
407
|
+
return rows.map((r) => ({
|
|
408
|
+
id: r.id,
|
|
409
|
+
chat_id: r.chat_id,
|
|
410
|
+
project_id: r.project_id,
|
|
411
|
+
role: r.role,
|
|
412
|
+
content: r.content,
|
|
413
|
+
ts: r.ts,
|
|
414
|
+
model: r.model,
|
|
415
|
+
token_count: r.token_count,
|
|
416
|
+
source: r.source,
|
|
417
|
+
synced_at: r.synced_at
|
|
418
|
+
}));
|
|
419
|
+
} catch (e) {
|
|
420
|
+
console.warn("[Journal] Failed to list pending messages", e);
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async journalMarkSynced(ids) {
|
|
425
|
+
if (this.useJsonl) {
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
this.jsonlEntries = this.jsonlEntries.map(
|
|
428
|
+
(e) => ids.includes(e.id) ? { ...e, synced_at: now } : e
|
|
429
|
+
);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (!this.db) return;
|
|
433
|
+
try {
|
|
434
|
+
const now = Date.now();
|
|
435
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
436
|
+
const stmt = this.db.prepare(`
|
|
437
|
+
UPDATE messages SET synced_at = ? WHERE id IN (${placeholders})
|
|
438
|
+
`);
|
|
439
|
+
stmt.run(now, ...ids);
|
|
440
|
+
} catch (e) {
|
|
441
|
+
console.warn("[Journal] Failed to mark synced", e);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async journalCount() {
|
|
445
|
+
if (this.useJsonl) {
|
|
446
|
+
return {
|
|
447
|
+
pending: this.jsonlEntries.filter((e) => !e.synced_at).length,
|
|
448
|
+
total: this.jsonlEntries.length
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (!this.db) return { pending: 0, total: 0 };
|
|
452
|
+
try {
|
|
453
|
+
const pendingStmt = this.db.prepare("SELECT COUNT(*) as cnt FROM messages WHERE synced_at IS NULL");
|
|
454
|
+
const totalStmt = this.db.prepare("SELECT COUNT(*) as cnt FROM messages");
|
|
455
|
+
const pending = pendingStmt.get()?.cnt ?? 0;
|
|
456
|
+
const total = totalStmt.get()?.cnt ?? 0;
|
|
457
|
+
return { pending, total };
|
|
458
|
+
} catch (e) {
|
|
459
|
+
console.warn("[Journal] Failed to count messages", e);
|
|
460
|
+
return { pending: 0, total: 0 };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
close() {
|
|
464
|
+
if (this.db) {
|
|
465
|
+
try {
|
|
466
|
+
this.db.close();
|
|
467
|
+
} catch (e) {
|
|
468
|
+
console.warn("[Journal] Failed to close", e);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
journalInstance = null;
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
56
477
|
// ../api-client/src/index.ts
|
|
57
478
|
var src_exports = {};
|
|
58
479
|
__export(src_exports, {
|
|
480
|
+
Journal: () => Journal,
|
|
481
|
+
LocalLlmClient: () => LocalLlmClient,
|
|
59
482
|
MSaplingClient: () => MSaplingClient,
|
|
60
|
-
MSaplingError: () => MSaplingError
|
|
483
|
+
MSaplingError: () => MSaplingError,
|
|
484
|
+
OllamaClient: () => OllamaClient,
|
|
485
|
+
detectLocalLlm: () => detectLocalLlm,
|
|
486
|
+
getJournal: () => getJournal,
|
|
487
|
+
probeDialect: () => probeDialect
|
|
61
488
|
});
|
|
62
489
|
function normalizeDetail(detail) {
|
|
63
490
|
if (detail == null) return "";
|
|
@@ -86,6 +513,8 @@ var init_src = __esm({
|
|
|
86
513
|
"../api-client/src/index.ts"() {
|
|
87
514
|
"use strict";
|
|
88
515
|
init_esm_shims();
|
|
516
|
+
init_localLlm();
|
|
517
|
+
init_journal();
|
|
89
518
|
MSaplingError = class extends Error {
|
|
90
519
|
constructor(message, status, code) {
|
|
91
520
|
super(message);
|
|
@@ -956,6 +1385,23 @@ var init_src = __esm({
|
|
|
956
1385
|
clearTimeout(timeout);
|
|
957
1386
|
}
|
|
958
1387
|
}
|
|
1388
|
+
/**
|
|
1389
|
+
* CLI-OFFLINE-02: Import journaled messages from offline sessions
|
|
1390
|
+
* POST /api/chat/import with batch of messages
|
|
1391
|
+
*/
|
|
1392
|
+
async importChat(req) {
|
|
1393
|
+
const response = await this.request("/api/chat/import", {
|
|
1394
|
+
method: "POST",
|
|
1395
|
+
body: JSON.stringify(req)
|
|
1396
|
+
});
|
|
1397
|
+
if (!response.ok) {
|
|
1398
|
+
const err = await response.json().catch(() => ({ detail: response.statusText }));
|
|
1399
|
+
throw new MSaplingError(
|
|
1400
|
+
normalizeDetail(err.detail) || "Chat import failed",
|
|
1401
|
+
response.status
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
959
1405
|
};
|
|
960
1406
|
}
|
|
961
1407
|
});
|
|
@@ -973,7 +1419,7 @@ var init_BaseTool = __esm({
|
|
|
973
1419
|
|
|
974
1420
|
// ../core/src/tools/ReadFileTool.ts
|
|
975
1421
|
import { resolve, normalize, relative, isAbsolute } from "path";
|
|
976
|
-
import { readFile } from "fs/promises";
|
|
1422
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
977
1423
|
import { existsSync } from "fs";
|
|
978
1424
|
var ReadFileTool;
|
|
979
1425
|
var init_ReadFileTool = __esm({
|
|
@@ -1024,7 +1470,7 @@ var init_ReadFileTool = __esm({
|
|
|
1024
1470
|
const limit = args2.limit ? parseInt(args2.limit) : 0;
|
|
1025
1471
|
const MAX_BYTES = 32768;
|
|
1026
1472
|
let content = "";
|
|
1027
|
-
const fileContent = await
|
|
1473
|
+
const fileContent = await readFile2(fullPath, "utf8");
|
|
1028
1474
|
const lines = fileContent.split("\n");
|
|
1029
1475
|
let currentLine = 1;
|
|
1030
1476
|
let startLine = offset > 0 ? offset : 1;
|
|
@@ -1097,10 +1543,10 @@ var init_EditFileTool = __esm({
|
|
|
1097
1543
|
});
|
|
1098
1544
|
|
|
1099
1545
|
// ../core/src/tools/WriteFileTool.ts
|
|
1100
|
-
import { resolve as resolve2, normalize as normalize2, relative as relative2, isAbsolute as isAbsolute2, join as
|
|
1101
|
-
import { writeFile, readFile as
|
|
1546
|
+
import { resolve as resolve2, normalize as normalize2, relative as relative2, isAbsolute as isAbsolute2, join as join4 } from "path";
|
|
1547
|
+
import { writeFile, readFile as readFile3, mkdir } from "fs/promises";
|
|
1102
1548
|
import { existsSync as existsSync2 } from "fs";
|
|
1103
|
-
import { homedir } from "os";
|
|
1549
|
+
import { homedir as homedir3 } from "os";
|
|
1104
1550
|
import { randomBytes } from "crypto";
|
|
1105
1551
|
var MAX_CONTENT_BYTES, WriteFileTool;
|
|
1106
1552
|
var init_WriteFileTool = __esm({
|
|
@@ -1175,12 +1621,12 @@ var init_WriteFileTool = __esm({
|
|
|
1175
1621
|
let backedUpTo = null;
|
|
1176
1622
|
try {
|
|
1177
1623
|
if (existsSync2(resolvedTarget)) {
|
|
1178
|
-
const existingContent = await
|
|
1624
|
+
const existingContent = await readFile3(resolvedTarget, "utf8");
|
|
1179
1625
|
const filename = resolvedTarget.split(/[\\/]/).pop() ?? "file";
|
|
1180
1626
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1181
1627
|
const suffix = randomBytes(4).toString("hex");
|
|
1182
|
-
const backupPath =
|
|
1183
|
-
await mkdir(
|
|
1628
|
+
const backupPath = join4(homedir3(), ".msapling", "backups", `${filename}.backup-${stamp}-${suffix}.bak`);
|
|
1629
|
+
await mkdir(join4(homedir3(), ".msapling", "backups"), { recursive: true });
|
|
1184
1630
|
await writeFile(backupPath, existingContent, "utf8");
|
|
1185
1631
|
backedUpTo = backupPath;
|
|
1186
1632
|
}
|
|
@@ -1834,9 +2280,9 @@ var init_SubShellTool = __esm({
|
|
|
1834
2280
|
|
|
1835
2281
|
// ../core/src/tools/SearchTools.ts
|
|
1836
2282
|
import { spawn as spawn4 } from "child_process";
|
|
1837
|
-
import { readFile as
|
|
2283
|
+
import { readFile as readFile4, readdir, stat } from "fs/promises";
|
|
1838
2284
|
import { existsSync as existsSync3, statSync } from "fs";
|
|
1839
|
-
import { join as
|
|
2285
|
+
import { join as join6, relative as relative4, sep, resolve as resolve4, normalize as normalize3, isAbsolute as isAbsolute4 } from "path";
|
|
1840
2286
|
function globToRegExp(pattern) {
|
|
1841
2287
|
const normalised = pattern.replace(/\\/g, "/");
|
|
1842
2288
|
let re = "^";
|
|
@@ -1889,7 +2335,7 @@ async function* walkFiles(root, current = root) {
|
|
|
1889
2335
|
return;
|
|
1890
2336
|
}
|
|
1891
2337
|
for (const entry of entries) {
|
|
1892
|
-
const full =
|
|
2338
|
+
const full = join6(current, entry.name);
|
|
1893
2339
|
if (entry.isDirectory()) {
|
|
1894
2340
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1895
2341
|
yield* walkFiles(root, full);
|
|
@@ -1941,12 +2387,12 @@ async function nodeGrepFallback(pattern, searchRoot, maxMatches, caseSensitive)
|
|
|
1941
2387
|
const matches2 = [];
|
|
1942
2388
|
for await (const relPath of walkFiles(searchRoot)) {
|
|
1943
2389
|
if (matches2.length >= maxMatches) break;
|
|
1944
|
-
const fullPath =
|
|
2390
|
+
const fullPath = join6(searchRoot, relPath);
|
|
1945
2391
|
try {
|
|
1946
2392
|
const s = await stat(fullPath);
|
|
1947
2393
|
if (!s.isFile()) continue;
|
|
1948
2394
|
if (s.size > 524288) continue;
|
|
1949
|
-
const content = await
|
|
2395
|
+
const content = await readFile4(fullPath, "utf8");
|
|
1950
2396
|
const lines = content.split("\n");
|
|
1951
2397
|
for (let i = 0; i < lines.length; i++) {
|
|
1952
2398
|
if (regex.test(lines[i])) {
|
|
@@ -2031,7 +2477,7 @@ var init_SearchTools = __esm({
|
|
|
2031
2477
|
const files = [];
|
|
2032
2478
|
for await (const rel of walkFiles(scanRoot)) {
|
|
2033
2479
|
if (!re.test(rel)) continue;
|
|
2034
|
-
const full = scanRoot === projectRoot ? rel :
|
|
2480
|
+
const full = scanRoot === projectRoot ? rel : join6(args2.path, rel).split(sep).join("/");
|
|
2035
2481
|
files.push(full);
|
|
2036
2482
|
if (files.length >= MAX_GLOB_RESULTS) {
|
|
2037
2483
|
files.push(`... [capped at ${MAX_GLOB_RESULTS} results \u2014 refine your pattern]`);
|
|
@@ -2084,7 +2530,7 @@ var init_SearchTools = __esm({
|
|
|
2084
2530
|
isError: true
|
|
2085
2531
|
};
|
|
2086
2532
|
}
|
|
2087
|
-
const searchPath = args2?.path ?
|
|
2533
|
+
const searchPath = args2?.path ? join6(projectRoot, String(args2.path)) : projectRoot;
|
|
2088
2534
|
if (!existsSync3(searchPath)) {
|
|
2089
2535
|
return {
|
|
2090
2536
|
content: `Error: path "${args2.path}" does not exist.`,
|
|
@@ -2123,7 +2569,7 @@ var init_SearchTools = __esm({
|
|
|
2123
2569
|
const isFile = statSync(searchPath).isFile();
|
|
2124
2570
|
let matches2;
|
|
2125
2571
|
if (isFile) {
|
|
2126
|
-
const content = await
|
|
2572
|
+
const content = await readFile4(searchPath, "utf8");
|
|
2127
2573
|
const regex = new RegExp(pattern, caseSensitive ? "" : "i");
|
|
2128
2574
|
const lines = content.split("\n");
|
|
2129
2575
|
const relPath = args2?.path ?? searchPath;
|
|
@@ -2144,7 +2590,7 @@ var init_SearchTools = __esm({
|
|
|
2144
2590
|
});
|
|
2145
2591
|
|
|
2146
2592
|
// ../core/src/tools/ListDirectoryTool.ts
|
|
2147
|
-
import { resolve as resolve5, normalize as normalize4, relative as relative5, isAbsolute as isAbsolute5, join as
|
|
2593
|
+
import { resolve as resolve5, normalize as normalize4, relative as relative5, isAbsolute as isAbsolute5, join as join7, sep as sep2 } from "path";
|
|
2148
2594
|
import { readdirSync, statSync as statSync2, existsSync as existsSync4 } from "fs";
|
|
2149
2595
|
function humanSize(bytes) {
|
|
2150
2596
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -2163,7 +2609,7 @@ function scanDir(dir, relBase, recursive, showHidden, entries, cap) {
|
|
|
2163
2609
|
const fileNames = [];
|
|
2164
2610
|
for (const name of names) {
|
|
2165
2611
|
if (!showHidden && name.startsWith(".")) continue;
|
|
2166
|
-
const full =
|
|
2612
|
+
const full = join7(dir, name);
|
|
2167
2613
|
let st;
|
|
2168
2614
|
try {
|
|
2169
2615
|
st = statSync2(full);
|
|
@@ -2183,13 +2629,13 @@ function scanDir(dir, relBase, recursive, showHidden, entries, cap) {
|
|
|
2183
2629
|
const rel = relBase ? `${relBase}${sep2}${name}` : name;
|
|
2184
2630
|
entries.push({ kind: "dir", rel });
|
|
2185
2631
|
if (recursive) {
|
|
2186
|
-
scanDir(
|
|
2632
|
+
scanDir(join7(dir, name), rel, recursive, showHidden, entries, cap);
|
|
2187
2633
|
}
|
|
2188
2634
|
}
|
|
2189
2635
|
for (const name of fileNames) {
|
|
2190
2636
|
if (entries.length >= cap) return;
|
|
2191
2637
|
const rel = relBase ? `${relBase}${sep2}${name}` : name;
|
|
2192
|
-
const full =
|
|
2638
|
+
const full = join7(dir, name);
|
|
2193
2639
|
let size;
|
|
2194
2640
|
let mtime;
|
|
2195
2641
|
try {
|
|
@@ -2480,10 +2926,10 @@ var init_TodoTools = __esm({
|
|
|
2480
2926
|
});
|
|
2481
2927
|
|
|
2482
2928
|
// ../core/src/tools/PatchFileTool.ts
|
|
2483
|
-
import { resolve as resolve6, normalize as normalize5, relative as relative6, isAbsolute as isAbsolute6, join as
|
|
2484
|
-
import { readFile as
|
|
2929
|
+
import { resolve as resolve6, normalize as normalize5, relative as relative6, isAbsolute as isAbsolute6, join as join8 } from "path";
|
|
2930
|
+
import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
2485
2931
|
import { existsSync as existsSync5 } from "fs";
|
|
2486
|
-
import { homedir as
|
|
2932
|
+
import { homedir as homedir4 } from "os";
|
|
2487
2933
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2488
2934
|
var PatchFileTool;
|
|
2489
2935
|
var init_PatchFileTool = __esm({
|
|
@@ -2545,7 +2991,7 @@ var init_PatchFileTool = __esm({
|
|
|
2545
2991
|
}
|
|
2546
2992
|
let originalContent;
|
|
2547
2993
|
try {
|
|
2548
|
-
originalContent = await
|
|
2994
|
+
originalContent = await readFile5(resolvedTarget, "utf8");
|
|
2549
2995
|
} catch (e) {
|
|
2550
2996
|
return { content: `Error reading "${args2.path}": ${e.message}`, isError: true };
|
|
2551
2997
|
}
|
|
@@ -2593,8 +3039,8 @@ File preview (first 200 chars): ${JSON.stringify(preview)}`,
|
|
|
2593
3039
|
const filename = resolvedTarget.split(/[\\/]/).pop() ?? "file";
|
|
2594
3040
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2595
3041
|
const suffix = randomBytes2(4).toString("hex");
|
|
2596
|
-
const backupPath =
|
|
2597
|
-
await mkdir2(
|
|
3042
|
+
const backupPath = join8(homedir4(), ".msapling", "backups", `${filename}.backup-${stamp}-${suffix}.bak`);
|
|
3043
|
+
await mkdir2(join8(homedir4(), ".msapling", "backups"), { recursive: true });
|
|
2598
3044
|
await writeFile2(backupPath, originalContent, "utf8");
|
|
2599
3045
|
backedUpTo = backupPath;
|
|
2600
3046
|
} catch {
|
|
@@ -3225,7 +3671,7 @@ ${stderr}`;
|
|
|
3225
3671
|
|
|
3226
3672
|
// ../core/src/tools/NotebookReadTool.ts
|
|
3227
3673
|
import { resolve as resolve8, normalize as normalize7, relative as relative8, isAbsolute as isAbsolute8, extname } from "path";
|
|
3228
|
-
import { readFile as
|
|
3674
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
3229
3675
|
import { existsSync as existsSync6 } from "fs";
|
|
3230
3676
|
function joinSource(source) {
|
|
3231
3677
|
return Array.isArray(source) ? source.join("") : source ?? "";
|
|
@@ -3392,7 +3838,7 @@ var init_NotebookReadTool = __esm({
|
|
|
3392
3838
|
}
|
|
3393
3839
|
let raw;
|
|
3394
3840
|
try {
|
|
3395
|
-
raw = await
|
|
3841
|
+
raw = await readFile6(absPath, "utf8");
|
|
3396
3842
|
} catch (e) {
|
|
3397
3843
|
return { content: `Error: could not read file: ${e.message}`, isError: true };
|
|
3398
3844
|
}
|
|
@@ -3449,10 +3895,10 @@ _(Note: notebook has ${nb.cells.length} cells; only first ${MAX_CELLS} shown.)_`
|
|
|
3449
3895
|
});
|
|
3450
3896
|
|
|
3451
3897
|
// ../core/src/tools/NotebookEditTool.ts
|
|
3452
|
-
import { resolve as resolve9, normalize as normalize8, relative as relative9, isAbsolute as isAbsolute9, extname as extname2, join as
|
|
3453
|
-
import { readFile as
|
|
3898
|
+
import { resolve as resolve9, normalize as normalize8, relative as relative9, isAbsolute as isAbsolute9, extname as extname2, join as join10 } from "path";
|
|
3899
|
+
import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
3454
3900
|
import { existsSync as existsSync7 } from "fs";
|
|
3455
|
-
import { homedir as
|
|
3901
|
+
import { homedir as homedir5 } from "os";
|
|
3456
3902
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
3457
3903
|
function normaliseSource(source) {
|
|
3458
3904
|
if (source === "") return [];
|
|
@@ -3586,7 +4032,7 @@ var init_NotebookEditTool = __esm({
|
|
|
3586
4032
|
}
|
|
3587
4033
|
let rawJson;
|
|
3588
4034
|
try {
|
|
3589
|
-
rawJson = await
|
|
4035
|
+
rawJson = await readFile7(absPath, "utf8");
|
|
3590
4036
|
} catch (e) {
|
|
3591
4037
|
return { content: `Error: could not read file: ${e.message}`, isError: true };
|
|
3592
4038
|
}
|
|
@@ -3632,13 +4078,13 @@ var init_NotebookEditTool = __esm({
|
|
|
3632
4078
|
const filename = absPath.split(/[\\/]/).pop() ?? "notebook.ipynb";
|
|
3633
4079
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3634
4080
|
const suffix = randomBytes3(4).toString("hex");
|
|
3635
|
-
const backupPath =
|
|
3636
|
-
|
|
4081
|
+
const backupPath = join10(
|
|
4082
|
+
homedir5(),
|
|
3637
4083
|
".msapling",
|
|
3638
4084
|
"backups",
|
|
3639
4085
|
`${filename}.backup-${stamp}-${suffix}.bak`
|
|
3640
4086
|
);
|
|
3641
|
-
await mkdir3(
|
|
4087
|
+
await mkdir3(join10(homedir5(), ".msapling", "backups"), { recursive: true });
|
|
3642
4088
|
await writeFile3(backupPath, rawJson, "utf8");
|
|
3643
4089
|
backedUpTo = backupPath;
|
|
3644
4090
|
} catch {
|
|
@@ -3708,10 +4154,10 @@ Backup: ${backedUpTo}`;
|
|
|
3708
4154
|
});
|
|
3709
4155
|
|
|
3710
4156
|
// ../core/src/tools/MultiEditFileTool.ts
|
|
3711
|
-
import { resolve as resolve10, normalize as normalize9, relative as relative10, isAbsolute as isAbsolute10, join as
|
|
3712
|
-
import { readFile as
|
|
4157
|
+
import { resolve as resolve10, normalize as normalize9, relative as relative10, isAbsolute as isAbsolute10, join as join11 } from "path";
|
|
4158
|
+
import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
3713
4159
|
import { existsSync as existsSync8 } from "fs";
|
|
3714
|
-
import { homedir as
|
|
4160
|
+
import { homedir as homedir6 } from "os";
|
|
3715
4161
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
3716
4162
|
var MAX_EDITS, MultiEditFileTool;
|
|
3717
4163
|
var init_MultiEditFileTool = __esm({
|
|
@@ -3822,7 +4268,7 @@ var init_MultiEditFileTool = __esm({
|
|
|
3822
4268
|
}
|
|
3823
4269
|
let originalContent;
|
|
3824
4270
|
try {
|
|
3825
|
-
originalContent = await
|
|
4271
|
+
originalContent = await readFile8(resolvedTarget, "utf8");
|
|
3826
4272
|
} catch (e) {
|
|
3827
4273
|
return {
|
|
3828
4274
|
content: `Error reading "${args2.path}": ${e.message}`,
|
|
@@ -3875,13 +4321,13 @@ No changes were written (atomic: all-or-nothing).`,
|
|
|
3875
4321
|
const filename = resolvedTarget.split(/[\\/]/).pop() ?? "file";
|
|
3876
4322
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3877
4323
|
const suffix = randomBytes4(4).toString("hex");
|
|
3878
|
-
const backupPath =
|
|
3879
|
-
|
|
4324
|
+
const backupPath = join11(
|
|
4325
|
+
homedir6(),
|
|
3880
4326
|
".msapling",
|
|
3881
4327
|
"backups",
|
|
3882
4328
|
`${filename}.backup-${stamp}-${suffix}.bak`
|
|
3883
4329
|
);
|
|
3884
|
-
await mkdir4(
|
|
4330
|
+
await mkdir4(join11(homedir6(), ".msapling", "backups"), { recursive: true });
|
|
3885
4331
|
await writeFile4(backupPath, originalContent, "utf8");
|
|
3886
4332
|
backedUpTo = backupPath;
|
|
3887
4333
|
} catch {
|
|
@@ -3936,20 +4382,20 @@ async function copyDir(src, dst) {
|
|
|
3936
4382
|
}
|
|
3937
4383
|
async function backupFile(absPath) {
|
|
3938
4384
|
try {
|
|
3939
|
-
const { readFile:
|
|
3940
|
-
const { homedir:
|
|
3941
|
-
const { join:
|
|
4385
|
+
const { readFile: readFile26, writeFile: writeFile13, mkdir: mkdir9 } = await import("fs/promises");
|
|
4386
|
+
const { homedir: homedir21 } = await import("os");
|
|
4387
|
+
const { join: join34 } = await import("path");
|
|
3942
4388
|
const filename = absPath.split(/[\\/]/).pop() ?? "file";
|
|
3943
4389
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3944
4390
|
const suffix = randomBytes5(4).toString("hex");
|
|
3945
|
-
const backupPath =
|
|
3946
|
-
|
|
4391
|
+
const backupPath = join34(
|
|
4392
|
+
homedir21(),
|
|
3947
4393
|
".msapling",
|
|
3948
4394
|
"backups",
|
|
3949
4395
|
`${filename}.backup-${stamp}-${suffix}.bak`
|
|
3950
4396
|
);
|
|
3951
|
-
await mkdir9(
|
|
3952
|
-
const content = await
|
|
4397
|
+
await mkdir9(join34(homedir21(), ".msapling", "backups"), { recursive: true });
|
|
4398
|
+
const content = await readFile26(absPath, "utf8");
|
|
3953
4399
|
await writeFile13(backupPath, content, "utf8");
|
|
3954
4400
|
return backupPath;
|
|
3955
4401
|
} catch {
|
|
@@ -4100,7 +4546,7 @@ Overwritten destination backed up to: ${backedUpTo}`;
|
|
|
4100
4546
|
});
|
|
4101
4547
|
|
|
4102
4548
|
// ../core/src/tools/DeleteFileTool.ts
|
|
4103
|
-
import { resolve as resolve12, normalize as normalize11, relative as relative12, isAbsolute as isAbsolute12, join as
|
|
4549
|
+
import { resolve as resolve12, normalize as normalize11, relative as relative12, isAbsolute as isAbsolute12, join as join13 } from "path";
|
|
4104
4550
|
import { rm as rm2, stat as stat3 } from "fs/promises";
|
|
4105
4551
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
4106
4552
|
var DeleteFileTool;
|
|
@@ -4175,14 +4621,14 @@ var init_DeleteFileTool = __esm({
|
|
|
4175
4621
|
let backedUpTo = null;
|
|
4176
4622
|
if (isFile) {
|
|
4177
4623
|
try {
|
|
4178
|
-
const { readFile:
|
|
4179
|
-
const { homedir:
|
|
4180
|
-
const existingContent = await
|
|
4624
|
+
const { readFile: readFile26, writeFile: writeFile13, mkdir: mkdir9 } = await import("fs/promises");
|
|
4625
|
+
const { homedir: homedir21 } = await import("os");
|
|
4626
|
+
const existingContent = await readFile26(abs, "utf8");
|
|
4181
4627
|
const filename = abs.split(/[\\/]/).pop() ?? "file";
|
|
4182
4628
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4183
4629
|
const suffix = randomBytes6(4).toString("hex");
|
|
4184
|
-
const backupPath =
|
|
4185
|
-
await mkdir9(
|
|
4630
|
+
const backupPath = join13(homedir21(), ".msapling", "backups", `${filename}.backup-${stamp}-${suffix}.bak`);
|
|
4631
|
+
await mkdir9(join13(homedir21(), ".msapling", "backups"), { recursive: true });
|
|
4186
4632
|
await writeFile13(backupPath, existingContent, "utf8");
|
|
4187
4633
|
backedUpTo = backupPath;
|
|
4188
4634
|
} catch {
|
|
@@ -4731,7 +5177,7 @@ var init_Hooks = __esm({
|
|
|
4731
5177
|
});
|
|
4732
5178
|
|
|
4733
5179
|
// ../core/src/agent/ToolExecutor.ts
|
|
4734
|
-
import { readFile as
|
|
5180
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
4735
5181
|
var APPROVAL_GATED, ToolExecutor;
|
|
4736
5182
|
var init_ToolExecutor = __esm({
|
|
4737
5183
|
"../core/src/agent/ToolExecutor.ts"() {
|
|
@@ -4982,7 +5428,7 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
4982
5428
|
}
|
|
4983
5429
|
await this.voice.speak(`Executing ${toolName}`, "natural");
|
|
4984
5430
|
if (toolName === "edit_file") {
|
|
4985
|
-
const content = await
|
|
5431
|
+
const content = await readFile9(args2.path, "utf-8");
|
|
4986
5432
|
const parentHash = await this.mdrive.getHash(content);
|
|
4987
5433
|
const block = await this.mdrive.proposeRemoteEdit(
|
|
4988
5434
|
args2.path,
|
|
@@ -5069,14 +5515,14 @@ var init_Safety = __esm({
|
|
|
5069
5515
|
});
|
|
5070
5516
|
|
|
5071
5517
|
// ../core/src/ProjectConfig.ts
|
|
5072
|
-
import { homedir as
|
|
5073
|
-
import { join as
|
|
5518
|
+
import { homedir as homedir7 } from "os";
|
|
5519
|
+
import { join as join14, dirname as dirname2, parse as parsePath } from "path";
|
|
5074
5520
|
import { existsSync as existsSync10 } from "fs";
|
|
5075
|
-
import { readFile as
|
|
5521
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5076
5522
|
async function readIfExists(path2) {
|
|
5077
5523
|
try {
|
|
5078
5524
|
if (!existsSync10(path2)) return null;
|
|
5079
|
-
const text = await
|
|
5525
|
+
const text = await readFile10(path2, "utf8");
|
|
5080
5526
|
return text.length > TRUNCATE_AT ? text.slice(0, TRUNCATE_AT) + "\n[...truncated]" : text;
|
|
5081
5527
|
} catch {
|
|
5082
5528
|
return null;
|
|
@@ -5084,7 +5530,7 @@ async function readIfExists(path2) {
|
|
|
5084
5530
|
}
|
|
5085
5531
|
async function findInDir(dir) {
|
|
5086
5532
|
for (const filename of FILENAMES) {
|
|
5087
|
-
const path2 =
|
|
5533
|
+
const path2 = join14(dir, filename);
|
|
5088
5534
|
const content = await readIfExists(path2);
|
|
5089
5535
|
if (content !== null) {
|
|
5090
5536
|
return { path: path2, filename, content };
|
|
@@ -5107,9 +5553,9 @@ async function findProjectConfig(start) {
|
|
|
5107
5553
|
return null;
|
|
5108
5554
|
}
|
|
5109
5555
|
async function findUserConfig() {
|
|
5110
|
-
const home =
|
|
5556
|
+
const home = homedir7();
|
|
5111
5557
|
if (!home) return null;
|
|
5112
|
-
const userDir =
|
|
5558
|
+
const userDir = join14(home, ".msapling");
|
|
5113
5559
|
return findInDir(userDir);
|
|
5114
5560
|
}
|
|
5115
5561
|
function buildCombined(user, project) {
|
|
@@ -5237,6 +5683,7 @@ var init_Agent = __esm({
|
|
|
5237
5683
|
"../core/src/agent/Agent.ts"() {
|
|
5238
5684
|
"use strict";
|
|
5239
5685
|
init_esm_shims();
|
|
5686
|
+
init_src();
|
|
5240
5687
|
init_Safety();
|
|
5241
5688
|
init_ProjectConfig();
|
|
5242
5689
|
init_Hooks();
|
|
@@ -5400,15 +5847,19 @@ ${next}`;
|
|
|
5400
5847
|
}
|
|
5401
5848
|
const currentPrompt = queue.shift();
|
|
5402
5849
|
rounds++;
|
|
5403
|
-
const stream = this.
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5850
|
+
const stream = this.chatWithFallback(
|
|
5851
|
+
{
|
|
5852
|
+
chat_id: chatId,
|
|
5853
|
+
prompt: currentPrompt,
|
|
5854
|
+
model,
|
|
5855
|
+
tools: this.executor.getToolSchemas(),
|
|
5856
|
+
project_root: this.projectRoot,
|
|
5857
|
+
mode: this.executor.getMode(),
|
|
5858
|
+
...config.combined ? { project_context: config.combined } : {}
|
|
5859
|
+
},
|
|
5860
|
+
chatId,
|
|
5861
|
+
currentPrompt
|
|
5862
|
+
);
|
|
5412
5863
|
for await (const chunk of stream) {
|
|
5413
5864
|
if (chunk.content) {
|
|
5414
5865
|
onContent(chunk.content);
|
|
@@ -5449,6 +5900,156 @@ ${next}`;
|
|
|
5449
5900
|
}
|
|
5450
5901
|
return fullResponse;
|
|
5451
5902
|
}
|
|
5903
|
+
/**
|
|
5904
|
+
* CLI-OFFLINE-01: Fallback to Ollama when backend is unreachable.
|
|
5905
|
+
* On network error / 5xx / ECONNREFUSED, try local Ollama.
|
|
5906
|
+
* If Ollama unavailable, show offline message and journal the user message only.
|
|
5907
|
+
*/
|
|
5908
|
+
async *chatWithFallback(params, chatId, userMessage) {
|
|
5909
|
+
const journal = getJournal();
|
|
5910
|
+
let backendReachable = false;
|
|
5911
|
+
let connectionTimeout = null;
|
|
5912
|
+
try {
|
|
5913
|
+
const controller = new AbortController();
|
|
5914
|
+
connectionTimeout = setTimeout(() => controller.abort(), 3e3);
|
|
5915
|
+
const backendStream = this.client.streamChat(params);
|
|
5916
|
+
for await (const chunk of backendStream) {
|
|
5917
|
+
if (connectionTimeout) {
|
|
5918
|
+
clearTimeout(connectionTimeout);
|
|
5919
|
+
connectionTimeout = null;
|
|
5920
|
+
}
|
|
5921
|
+
backendReachable = true;
|
|
5922
|
+
yield chunk;
|
|
5923
|
+
}
|
|
5924
|
+
} catch (error) {
|
|
5925
|
+
if (connectionTimeout) {
|
|
5926
|
+
clearTimeout(connectionTimeout);
|
|
5927
|
+
connectionTimeout = null;
|
|
5928
|
+
}
|
|
5929
|
+
const isNetworkError = error?.code === "ECONNREFUSED" || error?.code === "ECONNRESET" || error?.code === "ETIMEDOUT" || error?.status >= 500 || error?.message?.includes("timeout");
|
|
5930
|
+
if (!isNetworkError) {
|
|
5931
|
+
throw error;
|
|
5932
|
+
}
|
|
5933
|
+
const localLlm = await detectLocalLlm();
|
|
5934
|
+
if (!localLlm) {
|
|
5935
|
+
const userEntry2 = {
|
|
5936
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
5937
|
+
chat_id: chatId,
|
|
5938
|
+
project_id: void 0,
|
|
5939
|
+
role: "user",
|
|
5940
|
+
content: userMessage,
|
|
5941
|
+
ts: Date.now(),
|
|
5942
|
+
source: "offline"
|
|
5943
|
+
};
|
|
5944
|
+
await journal.journalAppend(userEntry2);
|
|
5945
|
+
yield {
|
|
5946
|
+
content: "Offline \u2014 no local model available. Install Ollama (ollama.ai) or llama.cpp and pull a model.\n"
|
|
5947
|
+
};
|
|
5948
|
+
return;
|
|
5949
|
+
}
|
|
5950
|
+
const userEntry = {
|
|
5951
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
5952
|
+
chat_id: chatId,
|
|
5953
|
+
project_id: void 0,
|
|
5954
|
+
role: "user",
|
|
5955
|
+
content: userMessage,
|
|
5956
|
+
ts: Date.now(),
|
|
5957
|
+
source: "offline"
|
|
5958
|
+
};
|
|
5959
|
+
await journal.journalAppend(userEntry);
|
|
5960
|
+
const dialect = await localLlm.detectDialect();
|
|
5961
|
+
let models = await localLlm.listModels();
|
|
5962
|
+
let selectedModel = process.env.MSAPLING_LOCAL_LLM_MODEL || "default";
|
|
5963
|
+
if (models.length === 0) {
|
|
5964
|
+
const assistantEntry2 = {
|
|
5965
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
5966
|
+
chat_id: chatId,
|
|
5967
|
+
project_id: void 0,
|
|
5968
|
+
role: "assistant",
|
|
5969
|
+
content: `No models available. Pull one for your local LLM server.`,
|
|
5970
|
+
ts: Date.now(),
|
|
5971
|
+
model: selectedModel,
|
|
5972
|
+
source: `offline_${dialect}`
|
|
5973
|
+
};
|
|
5974
|
+
await journal.journalAppend(assistantEntry2);
|
|
5975
|
+
yield {
|
|
5976
|
+
content: `No models available. Pull one for your local LLM server.`
|
|
5977
|
+
};
|
|
5978
|
+
return;
|
|
5979
|
+
}
|
|
5980
|
+
if (!models.find((m) => m.name === selectedModel)) {
|
|
5981
|
+
selectedModel = models[0].name;
|
|
5982
|
+
}
|
|
5983
|
+
const messages = params.messages || [{ role: "user", content: userMessage }];
|
|
5984
|
+
let assistantContent = "";
|
|
5985
|
+
let promptTokens = 0;
|
|
5986
|
+
let completionTokens = 0;
|
|
5987
|
+
for await (const delta of localLlm.streamChat(selectedModel, messages)) {
|
|
5988
|
+
assistantContent += delta.delta;
|
|
5989
|
+
if (delta.done) {
|
|
5990
|
+
promptTokens = delta.prompt_tokens ?? 0;
|
|
5991
|
+
completionTokens = delta.completion_tokens ?? 0;
|
|
5992
|
+
}
|
|
5993
|
+
if (delta.delta) {
|
|
5994
|
+
yield { content: delta.delta };
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
const assistantEntry = {
|
|
5998
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
5999
|
+
chat_id: chatId,
|
|
6000
|
+
project_id: void 0,
|
|
6001
|
+
role: "assistant",
|
|
6002
|
+
content: assistantContent,
|
|
6003
|
+
ts: Date.now(),
|
|
6004
|
+
model: selectedModel,
|
|
6005
|
+
token_count: completionTokens,
|
|
6006
|
+
source: `offline_${dialect}`
|
|
6007
|
+
};
|
|
6008
|
+
await journal.journalAppend(assistantEntry);
|
|
6009
|
+
}
|
|
6010
|
+
if (backendReachable) {
|
|
6011
|
+
const pending = await journal.journalListPending();
|
|
6012
|
+
if (pending.length > 0) {
|
|
6013
|
+
this.syncPendingMessages().catch(
|
|
6014
|
+
(e) => console.warn("[Offline] Background sync failed:", e)
|
|
6015
|
+
);
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
}
|
|
6019
|
+
/**
|
|
6020
|
+
* Sync pending journal entries to backend in batches of 50
|
|
6021
|
+
*/
|
|
6022
|
+
async syncPendingMessages() {
|
|
6023
|
+
const journal = getJournal();
|
|
6024
|
+
const pending = await journal.journalListPending();
|
|
6025
|
+
if (pending.length === 0) return;
|
|
6026
|
+
const byChat = /* @__PURE__ */ new Map();
|
|
6027
|
+
for (const entry of pending) {
|
|
6028
|
+
if (!byChat.has(entry.chat_id)) {
|
|
6029
|
+
byChat.set(entry.chat_id, []);
|
|
6030
|
+
}
|
|
6031
|
+
byChat.get(entry.chat_id).push(entry);
|
|
6032
|
+
}
|
|
6033
|
+
for (const [chatId, entries] of byChat.entries()) {
|
|
6034
|
+
for (let i = 0; i < entries.length; i += 50) {
|
|
6035
|
+
const batch = entries.slice(i, i + 50);
|
|
6036
|
+
try {
|
|
6037
|
+
await this.client.importChat({
|
|
6038
|
+
chat_id: chatId,
|
|
6039
|
+
messages: batch.map((e) => ({
|
|
6040
|
+
role: e.role,
|
|
6041
|
+
content: e.content,
|
|
6042
|
+
model: e.model
|
|
6043
|
+
}))
|
|
6044
|
+
});
|
|
6045
|
+
await journal.journalMarkSynced(batch.map((e) => e.id));
|
|
6046
|
+
} catch (e) {
|
|
6047
|
+
console.warn("[Offline] Sync batch failed, will retry on next connection:", e);
|
|
6048
|
+
break;
|
|
6049
|
+
}
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
5452
6053
|
};
|
|
5453
6054
|
}
|
|
5454
6055
|
});
|
|
@@ -5518,11 +6119,11 @@ var init_Mutex = __esm({
|
|
|
5518
6119
|
* `finally` block to prevent deadlocks on throw.
|
|
5519
6120
|
*/
|
|
5520
6121
|
acquire() {
|
|
5521
|
-
let
|
|
6122
|
+
let release3;
|
|
5522
6123
|
const next = new Promise((resolve19) => {
|
|
5523
|
-
|
|
6124
|
+
release3 = resolve19;
|
|
5524
6125
|
});
|
|
5525
|
-
const entry = this._queue.then(() =>
|
|
6126
|
+
const entry = this._queue.then(() => release3);
|
|
5526
6127
|
this._queue = this._queue.then(() => next);
|
|
5527
6128
|
return entry;
|
|
5528
6129
|
}
|
|
@@ -5531,11 +6132,11 @@ var init_Mutex = __esm({
|
|
|
5531
6132
|
* release. Returns whatever `fn` returns.
|
|
5532
6133
|
*/
|
|
5533
6134
|
async run(fn) {
|
|
5534
|
-
const
|
|
6135
|
+
const release3 = await this.acquire();
|
|
5535
6136
|
try {
|
|
5536
6137
|
return await fn();
|
|
5537
6138
|
} finally {
|
|
5538
|
-
|
|
6139
|
+
release3();
|
|
5539
6140
|
}
|
|
5540
6141
|
}
|
|
5541
6142
|
};
|
|
@@ -5543,17 +6144,17 @@ var init_Mutex = __esm({
|
|
|
5543
6144
|
});
|
|
5544
6145
|
|
|
5545
6146
|
// ../core/src/TrustStore.ts
|
|
5546
|
-
import { join as
|
|
5547
|
-
import { homedir as
|
|
6147
|
+
import { join as join15 } from "path";
|
|
6148
|
+
import { homedir as homedir8, platform as platform2 } from "os";
|
|
5548
6149
|
import { existsSync as existsSync11, mkdirSync } from "fs";
|
|
5549
|
-
import { readFile as
|
|
6150
|
+
import { readFile as readFile11, writeFile as writeFile5, chmod } from "fs/promises";
|
|
5550
6151
|
var USER_SETTINGS_PATH, TrustStore;
|
|
5551
6152
|
var init_TrustStore = __esm({
|
|
5552
6153
|
"../core/src/TrustStore.ts"() {
|
|
5553
6154
|
"use strict";
|
|
5554
6155
|
init_esm_shims();
|
|
5555
6156
|
init_Mutex();
|
|
5556
|
-
USER_SETTINGS_PATH =
|
|
6157
|
+
USER_SETTINGS_PATH = join15(homedir8(), ".msapling", "settings.json");
|
|
5557
6158
|
TrustStore = class {
|
|
5558
6159
|
/** Current in-memory set of trusted `tool:command` keys. */
|
|
5559
6160
|
trusted = /* @__PURE__ */ new Set();
|
|
@@ -5571,7 +6172,7 @@ var init_TrustStore = __esm({
|
|
|
5571
6172
|
async readSettings() {
|
|
5572
6173
|
try {
|
|
5573
6174
|
if (!existsSync11(this.settingsPath)) return {};
|
|
5574
|
-
const text = await
|
|
6175
|
+
const text = await readFile11(this.settingsPath, "utf8");
|
|
5575
6176
|
if (!text.trim()) return {};
|
|
5576
6177
|
return JSON.parse(text);
|
|
5577
6178
|
} catch {
|
|
@@ -5583,7 +6184,7 @@ var init_TrustStore = __esm({
|
|
|
5583
6184
|
* updating `trustedCommands`.
|
|
5584
6185
|
*/
|
|
5585
6186
|
async writeSettings(settings) {
|
|
5586
|
-
const dir =
|
|
6187
|
+
const dir = join15(homedir8(), ".msapling");
|
|
5587
6188
|
if (!existsSync11(dir)) mkdirSync(dir, { recursive: true });
|
|
5588
6189
|
await writeFile5(this.settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
5589
6190
|
if (platform2() !== "win32") {
|
|
@@ -5669,7 +6270,7 @@ var require_polyfills = __commonJS({
|
|
|
5669
6270
|
var constants = __require("constants");
|
|
5670
6271
|
var origCwd = process.cwd;
|
|
5671
6272
|
var cwd = null;
|
|
5672
|
-
var
|
|
6273
|
+
var platform5 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
5673
6274
|
process.cwd = function() {
|
|
5674
6275
|
if (!cwd)
|
|
5675
6276
|
cwd = origCwd.call(process);
|
|
@@ -5728,7 +6329,7 @@ var require_polyfills = __commonJS({
|
|
|
5728
6329
|
fs3.lchownSync = function() {
|
|
5729
6330
|
};
|
|
5730
6331
|
}
|
|
5731
|
-
if (
|
|
6332
|
+
if (platform5 === "win32") {
|
|
5732
6333
|
fs3.rename = typeof fs3.rename !== "function" ? fs3.rename : (function(fs$rename) {
|
|
5733
6334
|
function rename2(from, to, cb) {
|
|
5734
6335
|
var start = Date.now();
|
|
@@ -6160,8 +6761,8 @@ var require_graceful_fs = __commonJS({
|
|
|
6160
6761
|
fs4.createReadStream = createReadStream;
|
|
6161
6762
|
fs4.createWriteStream = createWriteStream;
|
|
6162
6763
|
var fs$readFile = fs4.readFile;
|
|
6163
|
-
fs4.readFile =
|
|
6164
|
-
function
|
|
6764
|
+
fs4.readFile = readFile26;
|
|
6765
|
+
function readFile26(path2, options, cb) {
|
|
6165
6766
|
if (typeof options === "function")
|
|
6166
6767
|
cb = options, options = null;
|
|
6167
6768
|
return go$readFile(path2, options, cb);
|
|
@@ -7230,12 +7831,12 @@ var require_proper_lockfile = __commonJS({
|
|
|
7230
7831
|
var lockfile2 = require_lockfile();
|
|
7231
7832
|
var { toPromise, toSync, toSyncOptions } = require_adapter();
|
|
7232
7833
|
async function lock2(file, options) {
|
|
7233
|
-
const
|
|
7234
|
-
return toPromise(
|
|
7834
|
+
const release3 = await toPromise(lockfile2.lock)(file, options);
|
|
7835
|
+
return toPromise(release3);
|
|
7235
7836
|
}
|
|
7236
7837
|
function lockSync(file, options) {
|
|
7237
|
-
const
|
|
7238
|
-
return toSync(
|
|
7838
|
+
const release3 = toSync(lockfile2.lock)(file, toSyncOptions(options));
|
|
7839
|
+
return toSync(release3);
|
|
7239
7840
|
}
|
|
7240
7841
|
function unlock2(file, options) {
|
|
7241
7842
|
return toPromise(lockfile2.unlock)(file, options);
|
|
@@ -7321,10 +7922,10 @@ var require_keytar2 = __commonJS({
|
|
|
7321
7922
|
});
|
|
7322
7923
|
|
|
7323
7924
|
// ../core/src/Storage.ts
|
|
7324
|
-
import { join as
|
|
7325
|
-
import { homedir as
|
|
7925
|
+
import { join as join16 } from "path";
|
|
7926
|
+
import { homedir as homedir9 } from "os";
|
|
7326
7927
|
import { chmodSync, existsSync as existsSync12, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
7327
|
-
import { mkdir as mkdir6, writeFile as writeFile6, readFile as
|
|
7928
|
+
import { mkdir as mkdir6, writeFile as writeFile6, readFile as readFile12, appendFile } from "fs/promises";
|
|
7328
7929
|
import { randomBytes as randomBytes7, createHash as createHash3 } from "crypto";
|
|
7329
7930
|
function hashLine(line) {
|
|
7330
7931
|
return createHash3("sha256").update(line, "utf8").digest("hex");
|
|
@@ -7355,7 +7956,7 @@ var init_Storage = __esm({
|
|
|
7355
7956
|
*/
|
|
7356
7957
|
_ready;
|
|
7357
7958
|
constructor() {
|
|
7358
|
-
this.baseDir =
|
|
7959
|
+
this.baseDir = join16(homedir9(), ".msapling");
|
|
7359
7960
|
this._ready = this.ensureDirs();
|
|
7360
7961
|
}
|
|
7361
7962
|
async ensureDirs() {
|
|
@@ -7374,7 +7975,7 @@ var init_Storage = __esm({
|
|
|
7374
7975
|
"cache/recipes/objects"
|
|
7375
7976
|
];
|
|
7376
7977
|
for (const sub of subdirs) {
|
|
7377
|
-
const path2 =
|
|
7978
|
+
const path2 = join16(this.baseDir, sub);
|
|
7378
7979
|
await mkdir6(path2, { recursive: true });
|
|
7379
7980
|
}
|
|
7380
7981
|
if (process.platform !== "win32") {
|
|
@@ -7392,7 +7993,7 @@ var init_Storage = __esm({
|
|
|
7392
7993
|
await this._ready;
|
|
7393
7994
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
7394
7995
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
7395
|
-
const filePath =
|
|
7996
|
+
const filePath = join16(this.baseDir, "vault", "token");
|
|
7396
7997
|
try {
|
|
7397
7998
|
await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
|
|
7398
7999
|
try {
|
|
@@ -7412,7 +8013,7 @@ var init_Storage = __esm({
|
|
|
7412
8013
|
async loadToken() {
|
|
7413
8014
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
7414
8015
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
7415
|
-
const filePath =
|
|
8016
|
+
const filePath = join16(this.baseDir, "vault", "token");
|
|
7416
8017
|
try {
|
|
7417
8018
|
const keychainToken = await keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
7418
8019
|
if (keychainToken) {
|
|
@@ -7422,7 +8023,7 @@ var init_Storage = __esm({
|
|
|
7422
8023
|
console.debug(`Keychain unavailable (${e instanceof Error ? e.message : String(e)}), falling back to file storage`);
|
|
7423
8024
|
}
|
|
7424
8025
|
if (existsSync12(filePath)) {
|
|
7425
|
-
const text = await
|
|
8026
|
+
const text = await readFile12(filePath, "utf8");
|
|
7426
8027
|
return text.trim();
|
|
7427
8028
|
}
|
|
7428
8029
|
return null;
|
|
@@ -7433,7 +8034,7 @@ var init_Storage = __esm({
|
|
|
7433
8034
|
async clearToken() {
|
|
7434
8035
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
7435
8036
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
7436
|
-
const filePath =
|
|
8037
|
+
const filePath = join16(this.baseDir, "vault", "token");
|
|
7437
8038
|
try {
|
|
7438
8039
|
await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
7439
8040
|
} catch (e) {
|
|
@@ -7472,8 +8073,8 @@ var init_Storage = __esm({
|
|
|
7472
8073
|
async writeVaultRef(label, value) {
|
|
7473
8074
|
await this._ready;
|
|
7474
8075
|
const hash = createHash3("sha256").update(value, "utf8").digest("hex");
|
|
7475
|
-
const objectPath =
|
|
7476
|
-
const refPath =
|
|
8076
|
+
const objectPath = join16(this.baseDir, "vault", "objects", hash);
|
|
8077
|
+
const refPath = join16(this.baseDir, "vault", "refs", label);
|
|
7477
8078
|
const refTmp = `${refPath}.tmp`;
|
|
7478
8079
|
await writeFile6(objectPath, value, "utf8");
|
|
7479
8080
|
if (process.platform !== "win32") {
|
|
@@ -7489,12 +8090,12 @@ var init_Storage = __esm({
|
|
|
7489
8090
|
*/
|
|
7490
8091
|
async readVaultRef(label) {
|
|
7491
8092
|
await this._ready;
|
|
7492
|
-
const refPath =
|
|
8093
|
+
const refPath = join16(this.baseDir, "vault", "refs", label);
|
|
7493
8094
|
if (!existsSync12(refPath)) return null;
|
|
7494
|
-
const hash = (await
|
|
7495
|
-
const objectPath =
|
|
8095
|
+
const hash = (await readFile12(refPath, "utf8")).trim();
|
|
8096
|
+
const objectPath = join16(this.baseDir, "vault", "objects", hash);
|
|
7496
8097
|
if (!existsSync12(objectPath)) return null;
|
|
7497
|
-
return
|
|
8098
|
+
return readFile12(objectPath, "utf8");
|
|
7498
8099
|
}
|
|
7499
8100
|
// ─────────────────────────────────────────────────────────────────────────
|
|
7500
8101
|
// CLI-ARCH-RECIPE-AT-HASH-URI-01 — recipe hash registry helpers
|
|
@@ -7517,8 +8118,8 @@ var init_Storage = __esm({
|
|
|
7517
8118
|
async registerRecipe(name, content) {
|
|
7518
8119
|
await this._ready;
|
|
7519
8120
|
const hash = createHash3("sha256").update(content, "utf8").digest("hex");
|
|
7520
|
-
const objectPath =
|
|
7521
|
-
const indexPath =
|
|
8121
|
+
const objectPath = join16(this.baseDir, "cache", "recipes", "objects", hash);
|
|
8122
|
+
const indexPath = join16(this.baseDir, "cache", "recipes", "index.json");
|
|
7522
8123
|
const indexTmp = `${indexPath}.tmp`;
|
|
7523
8124
|
if (!existsSync12(objectPath)) {
|
|
7524
8125
|
await writeFile6(objectPath, content, "utf8");
|
|
@@ -7526,7 +8127,7 @@ var init_Storage = __esm({
|
|
|
7526
8127
|
let index = {};
|
|
7527
8128
|
if (existsSync12(indexPath)) {
|
|
7528
8129
|
try {
|
|
7529
|
-
index = JSON.parse(await
|
|
8130
|
+
index = JSON.parse(await readFile12(indexPath, "utf8"));
|
|
7530
8131
|
} catch {
|
|
7531
8132
|
index = {};
|
|
7532
8133
|
}
|
|
@@ -7542,26 +8143,26 @@ var init_Storage = __esm({
|
|
|
7542
8143
|
*/
|
|
7543
8144
|
async resolveRecipe(nameOrRef) {
|
|
7544
8145
|
await this._ready;
|
|
7545
|
-
const indexPath =
|
|
8146
|
+
const indexPath = join16(this.baseDir, "cache", "recipes", "index.json");
|
|
7546
8147
|
const atIdx = nameOrRef.indexOf("@");
|
|
7547
8148
|
if (atIdx !== -1) {
|
|
7548
8149
|
const hash2 = nameOrRef.slice(atIdx + 1);
|
|
7549
|
-
const objectPath2 =
|
|
8150
|
+
const objectPath2 = join16(this.baseDir, "cache", "recipes", "objects", hash2);
|
|
7550
8151
|
if (!existsSync12(objectPath2)) return null;
|
|
7551
|
-
return { hash: hash2, content: await
|
|
8152
|
+
return { hash: hash2, content: await readFile12(objectPath2, "utf8") };
|
|
7552
8153
|
}
|
|
7553
8154
|
if (!existsSync12(indexPath)) return null;
|
|
7554
8155
|
let index;
|
|
7555
8156
|
try {
|
|
7556
|
-
index = JSON.parse(await
|
|
8157
|
+
index = JSON.parse(await readFile12(indexPath, "utf8"));
|
|
7557
8158
|
} catch {
|
|
7558
8159
|
return null;
|
|
7559
8160
|
}
|
|
7560
8161
|
const hash = index[nameOrRef];
|
|
7561
8162
|
if (!hash) return null;
|
|
7562
|
-
const objectPath =
|
|
8163
|
+
const objectPath = join16(this.baseDir, "cache", "recipes", "objects", hash);
|
|
7563
8164
|
if (!existsSync12(objectPath)) return null;
|
|
7564
|
-
return { hash, content: await
|
|
8165
|
+
return { hash, content: await readFile12(objectPath, "utf8") };
|
|
7565
8166
|
}
|
|
7566
8167
|
// ─────────────────────────────────────────────────────────────────────────
|
|
7567
8168
|
// CLI-ARCH-HASH-CHAIN-HISTORY-01 — append-only NDJSON history with hash chain
|
|
@@ -7583,18 +8184,18 @@ var init_Storage = __esm({
|
|
|
7583
8184
|
*/
|
|
7584
8185
|
async appendHistoryEntry(content) {
|
|
7585
8186
|
await this._ready;
|
|
7586
|
-
const path2 =
|
|
8187
|
+
const path2 = join16(this.baseDir, "history", "shell_history.jsonl");
|
|
7587
8188
|
return this.historyMutex.run(async () => {
|
|
7588
|
-
let
|
|
8189
|
+
let release3 = null;
|
|
7589
8190
|
try {
|
|
7590
8191
|
if (!existsSync12(path2)) {
|
|
7591
8192
|
await writeFile6(path2, "", "utf8");
|
|
7592
8193
|
}
|
|
7593
|
-
|
|
8194
|
+
release3 = await lockfile.lock(path2, { retries: 5, retryWait: 50 });
|
|
7594
8195
|
let prevHash = null;
|
|
7595
8196
|
let seq = 1;
|
|
7596
8197
|
if (existsSync12(path2)) {
|
|
7597
|
-
const raw = (await
|
|
8198
|
+
const raw = (await readFile12(path2, "utf8")).trimEnd();
|
|
7598
8199
|
if (raw.length > 0) {
|
|
7599
8200
|
const lines = raw.split("\n");
|
|
7600
8201
|
const lastLine = lines[lines.length - 1];
|
|
@@ -7616,7 +8217,7 @@ var init_Storage = __esm({
|
|
|
7616
8217
|
await appendFile(path2, line + "\n", "utf8");
|
|
7617
8218
|
return entry;
|
|
7618
8219
|
} finally {
|
|
7619
|
-
if (
|
|
8220
|
+
if (release3) {
|
|
7620
8221
|
try {
|
|
7621
8222
|
await lockfile.unlock(path2, { skipStale: true });
|
|
7622
8223
|
} catch {
|
|
@@ -7633,9 +8234,9 @@ var init_Storage = __esm({
|
|
|
7633
8234
|
*/
|
|
7634
8235
|
async loadHistoryEntries() {
|
|
7635
8236
|
await this._ready;
|
|
7636
|
-
const path2 =
|
|
8237
|
+
const path2 = join16(this.baseDir, "history", "shell_history.jsonl");
|
|
7637
8238
|
if (!existsSync12(path2)) return [];
|
|
7638
|
-
const raw = await
|
|
8239
|
+
const raw = await readFile12(path2, "utf8");
|
|
7639
8240
|
const entries = [];
|
|
7640
8241
|
for (const line of raw.split("\n")) {
|
|
7641
8242
|
if (!line.trim()) continue;
|
|
@@ -7683,11 +8284,11 @@ var init_Storage = __esm({
|
|
|
7683
8284
|
*/
|
|
7684
8285
|
async saveHistory(history) {
|
|
7685
8286
|
await this._ready;
|
|
7686
|
-
const path2 =
|
|
7687
|
-
let
|
|
8287
|
+
const path2 = join16(this.baseDir, "history", "shell_history.json");
|
|
8288
|
+
let release3;
|
|
7688
8289
|
try {
|
|
7689
8290
|
if (!existsSync12(path2)) writeFileSync(path2, "[]", "utf8");
|
|
7690
|
-
|
|
8291
|
+
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
7691
8292
|
await this.historyMutex.run(async () => {
|
|
7692
8293
|
const tmpPath = `${path2}.tmp`;
|
|
7693
8294
|
const content = JSON.stringify(history, null, 2);
|
|
@@ -7706,7 +8307,7 @@ var init_Storage = __esm({
|
|
|
7706
8307
|
}
|
|
7707
8308
|
});
|
|
7708
8309
|
} finally {
|
|
7709
|
-
if (
|
|
8310
|
+
if (release3) {
|
|
7710
8311
|
try {
|
|
7711
8312
|
await lockfile.unlock(path2, { skipStale: true });
|
|
7712
8313
|
} catch (e) {
|
|
@@ -7724,21 +8325,21 @@ var init_Storage = __esm({
|
|
|
7724
8325
|
*/
|
|
7725
8326
|
async loadHistory() {
|
|
7726
8327
|
await this._ready;
|
|
7727
|
-
const path2 =
|
|
8328
|
+
const path2 = join16(this.baseDir, "history", "shell_history.json");
|
|
7728
8329
|
if (!existsSync12(path2)) return [];
|
|
7729
|
-
let
|
|
8330
|
+
let release3;
|
|
7730
8331
|
try {
|
|
7731
|
-
|
|
8332
|
+
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
7732
8333
|
return this.historyMutex.run(async () => {
|
|
7733
8334
|
if (existsSync12(path2)) {
|
|
7734
|
-
const text = await
|
|
8335
|
+
const text = await readFile12(path2, "utf8");
|
|
7735
8336
|
try {
|
|
7736
8337
|
return JSON.parse(text);
|
|
7737
8338
|
} catch (parseErr) {
|
|
7738
8339
|
const filename = path2.split("/").pop() || "shell_history.json";
|
|
7739
8340
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7740
8341
|
const suffix = randomBytes7(4).toString("hex");
|
|
7741
|
-
const corruptBackupPath =
|
|
8342
|
+
const corruptBackupPath = join16(
|
|
7742
8343
|
this.baseDir,
|
|
7743
8344
|
"history",
|
|
7744
8345
|
`${filename}.corrupt.${stamp}-${suffix}.bak`
|
|
@@ -7755,7 +8356,7 @@ var init_Storage = __esm({
|
|
|
7755
8356
|
return [];
|
|
7756
8357
|
});
|
|
7757
8358
|
} finally {
|
|
7758
|
-
if (
|
|
8359
|
+
if (release3) {
|
|
7759
8360
|
try {
|
|
7760
8361
|
await lockfile.unlock(path2, { skipStale: true });
|
|
7761
8362
|
} catch (e) {
|
|
@@ -7769,11 +8370,11 @@ var init_Storage = __esm({
|
|
|
7769
8370
|
* R20-CLI-2: Typed with PermissionState from Sandbox.ts.
|
|
7770
8371
|
*/
|
|
7771
8372
|
async savePermissions(permissions) {
|
|
7772
|
-
const path2 =
|
|
7773
|
-
let
|
|
8373
|
+
const path2 = join16(this.baseDir, "vault", "permissions.json");
|
|
8374
|
+
let release3;
|
|
7774
8375
|
try {
|
|
7775
8376
|
if (!existsSync12(path2)) writeFileSync(path2, "{}", "utf8");
|
|
7776
|
-
|
|
8377
|
+
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
7777
8378
|
await this.permissionsMutex.run(async () => {
|
|
7778
8379
|
const tmpPath = `${path2}.tmp`;
|
|
7779
8380
|
const content = JSON.stringify(permissions, null, 2);
|
|
@@ -7792,7 +8393,7 @@ var init_Storage = __esm({
|
|
|
7792
8393
|
}
|
|
7793
8394
|
});
|
|
7794
8395
|
} finally {
|
|
7795
|
-
if (
|
|
8396
|
+
if (release3) {
|
|
7796
8397
|
try {
|
|
7797
8398
|
await lockfile.unlock(path2, { skipStale: true });
|
|
7798
8399
|
} catch (e) {
|
|
@@ -7806,21 +8407,21 @@ var init_Storage = __esm({
|
|
|
7806
8407
|
* R20-CLI-2: Typed with PermissionState from Sandbox.ts.
|
|
7807
8408
|
*/
|
|
7808
8409
|
async loadPermissions() {
|
|
7809
|
-
const path2 =
|
|
8410
|
+
const path2 = join16(this.baseDir, "vault", "permissions.json");
|
|
7810
8411
|
if (!existsSync12(path2)) return { trustedCommands: [], trustedPaths: [] };
|
|
7811
|
-
let
|
|
8412
|
+
let release3;
|
|
7812
8413
|
try {
|
|
7813
|
-
|
|
8414
|
+
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
7814
8415
|
return this.permissionsMutex.run(async () => {
|
|
7815
8416
|
if (existsSync12(path2)) {
|
|
7816
|
-
const text = await
|
|
8417
|
+
const text = await readFile12(path2, "utf8");
|
|
7817
8418
|
try {
|
|
7818
8419
|
return JSON.parse(text);
|
|
7819
8420
|
} catch (parseErr) {
|
|
7820
8421
|
const filename = path2.split("/").pop() || "permissions.json";
|
|
7821
8422
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7822
8423
|
const suffix = randomBytes7(4).toString("hex");
|
|
7823
|
-
const corruptBackupPath =
|
|
8424
|
+
const corruptBackupPath = join16(
|
|
7824
8425
|
this.baseDir,
|
|
7825
8426
|
"vault",
|
|
7826
8427
|
`${filename}.corrupt.${stamp}-${suffix}.bak`
|
|
@@ -7837,7 +8438,7 @@ var init_Storage = __esm({
|
|
|
7837
8438
|
return { trustedCommands: [], trustedPaths: [] };
|
|
7838
8439
|
});
|
|
7839
8440
|
} finally {
|
|
7840
|
-
if (
|
|
8441
|
+
if (release3) {
|
|
7841
8442
|
try {
|
|
7842
8443
|
await lockfile.unlock(path2, { skipStale: true });
|
|
7843
8444
|
} catch (e) {
|
|
@@ -7857,7 +8458,7 @@ var init_Storage = __esm({
|
|
|
7857
8458
|
const filename = filePath.split("/").pop() || "file";
|
|
7858
8459
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7859
8460
|
const suffix = randomBytes7(4).toString("hex");
|
|
7860
|
-
const backupPath =
|
|
8461
|
+
const backupPath = join16(this.baseDir, "backups", `${filename}.backup-${stamp}-${suffix}.bak`);
|
|
7861
8462
|
await writeFile6(backupPath, content, "utf8");
|
|
7862
8463
|
if (process.platform !== "win32") {
|
|
7863
8464
|
chmodSync(backupPath, 384);
|
|
@@ -7869,11 +8470,11 @@ var init_Storage = __esm({
|
|
|
7869
8470
|
});
|
|
7870
8471
|
|
|
7871
8472
|
// ../core/src/Settings.ts
|
|
7872
|
-
import { homedir as
|
|
7873
|
-
import { join as
|
|
8473
|
+
import { homedir as homedir10 } from "os";
|
|
8474
|
+
import { join as join17 } from "path";
|
|
7874
8475
|
import { existsSync as existsSync13 } from "fs";
|
|
7875
8476
|
import * as fs from "fs";
|
|
7876
|
-
import { readFile as
|
|
8477
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
7877
8478
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
7878
8479
|
function ensureConfigDir(p) {
|
|
7879
8480
|
try {
|
|
@@ -7895,7 +8496,7 @@ function ensureConfigDir(p) {
|
|
|
7895
8496
|
async function readJson(path2) {
|
|
7896
8497
|
try {
|
|
7897
8498
|
if (!existsSync13(path2)) return null;
|
|
7898
|
-
const text = await
|
|
8499
|
+
const text = await readFile13(path2, "utf8");
|
|
7899
8500
|
if (!text.trim()) return null;
|
|
7900
8501
|
return JSON.parse(text);
|
|
7901
8502
|
} catch {
|
|
@@ -7949,8 +8550,8 @@ function mergeSettings(base, override) {
|
|
|
7949
8550
|
}
|
|
7950
8551
|
async function loadSettings(cwd = process.cwd(), env = process.env, warn) {
|
|
7951
8552
|
const sources = [];
|
|
7952
|
-
const userPath =
|
|
7953
|
-
const projectPath =
|
|
8553
|
+
const userPath = join17(homedir10() || ".", ".msapling", "settings.json");
|
|
8554
|
+
const projectPath = join17(cwd, ".msapling", "settings.json");
|
|
7954
8555
|
const [user, project] = await Promise.all([readJson(userPath), readJson(projectPath)]);
|
|
7955
8556
|
if (user) sources.push(userPath);
|
|
7956
8557
|
if (project) sources.push(projectPath);
|
|
@@ -8617,10 +9218,10 @@ var init_unlock = __esm({
|
|
|
8617
9218
|
});
|
|
8618
9219
|
|
|
8619
9220
|
// src/commands/doctor.ts
|
|
8620
|
-
import { homedir as
|
|
8621
|
-
import { join as
|
|
9221
|
+
import { homedir as homedir11 } from "os";
|
|
9222
|
+
import { join as join18 } from "path";
|
|
8622
9223
|
import { existsSync as existsSync14 } from "fs";
|
|
8623
|
-
import { readFile as
|
|
9224
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
8624
9225
|
async function checkApiHealth(client) {
|
|
8625
9226
|
try {
|
|
8626
9227
|
if (typeof client.request !== "function") {
|
|
@@ -8645,12 +9246,12 @@ async function checkAuthStatus(client) {
|
|
|
8645
9246
|
}
|
|
8646
9247
|
}
|
|
8647
9248
|
async function checkSettingsFile() {
|
|
8648
|
-
const settingsPath =
|
|
9249
|
+
const settingsPath = join18(homedir11(), ".msapling", "settings.json");
|
|
8649
9250
|
try {
|
|
8650
9251
|
if (!existsSync14(settingsPath)) {
|
|
8651
9252
|
return { ok: false, message: `Not found: ${settingsPath}` };
|
|
8652
9253
|
}
|
|
8653
|
-
const text = await
|
|
9254
|
+
const text = await readFile14(settingsPath, "utf8");
|
|
8654
9255
|
const parsed = JSON.parse(text);
|
|
8655
9256
|
return { ok: true, message: `Valid JSON (${settingsPath})`, settings: parsed };
|
|
8656
9257
|
} catch (e) {
|
|
@@ -9334,7 +9935,7 @@ var init_memories = __esm({
|
|
|
9334
9935
|
});
|
|
9335
9936
|
|
|
9336
9937
|
// src/commands/mdrive.ts
|
|
9337
|
-
import { readFile as
|
|
9938
|
+
import { readFile as readFile15, writeFile as writeFile7 } from "fs/promises";
|
|
9338
9939
|
import { existsSync as existsSync15 } from "fs";
|
|
9339
9940
|
import { basename, resolve as resolve14 } from "path";
|
|
9340
9941
|
function formatBytes(b) {
|
|
@@ -9392,7 +9993,7 @@ var init_mdrive = __esm({
|
|
|
9392
9993
|
context.addMessage("error", `Local file not found: ${absLocal}`);
|
|
9393
9994
|
return;
|
|
9394
9995
|
}
|
|
9395
|
-
const content = await
|
|
9996
|
+
const content = await readFile15(absLocal, "utf8");
|
|
9396
9997
|
const res = await context.client.mdriveWrite(remote, content);
|
|
9397
9998
|
context.addMessage("system", `Uploaded ${absLocal} \u2192 mdrive:${remote} (${formatBytes(content.length)}, hash=${res?.hash?.slice(0, 12) ?? "?"})`);
|
|
9398
9999
|
return;
|
|
@@ -9485,15 +10086,15 @@ var init_clear = __esm({
|
|
|
9485
10086
|
});
|
|
9486
10087
|
|
|
9487
10088
|
// src/commands/mode.ts
|
|
9488
|
-
import { homedir as
|
|
9489
|
-
import { join as
|
|
10089
|
+
import { homedir as homedir12 } from "os";
|
|
10090
|
+
import { join as join19 } from "path";
|
|
9490
10091
|
import { existsSync as existsSync16 } from "fs";
|
|
9491
|
-
import { readFile as
|
|
10092
|
+
import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir7 } from "fs/promises";
|
|
9492
10093
|
async function persistApprovalMode(mode, ttlMs) {
|
|
9493
10094
|
try {
|
|
9494
10095
|
let existing = {};
|
|
9495
10096
|
if (existsSync16(SETTINGS_PATH)) {
|
|
9496
|
-
const text = await
|
|
10097
|
+
const text = await readFile16(SETTINGS_PATH, "utf8");
|
|
9497
10098
|
if (text.trim()) {
|
|
9498
10099
|
existing = JSON.parse(text);
|
|
9499
10100
|
}
|
|
@@ -9504,7 +10105,7 @@ async function persistApprovalMode(mode, ttlMs) {
|
|
|
9504
10105
|
...ttlMs && { ttlMs }
|
|
9505
10106
|
};
|
|
9506
10107
|
existing.approvalMode = entry;
|
|
9507
|
-
const settingsDir =
|
|
10108
|
+
const settingsDir = join19(homedir12(), ".msapling");
|
|
9508
10109
|
if (!existsSync16(settingsDir)) {
|
|
9509
10110
|
await mkdir7(settingsDir, { recursive: true });
|
|
9510
10111
|
}
|
|
@@ -9517,7 +10118,7 @@ var init_mode = __esm({
|
|
|
9517
10118
|
"src/commands/mode.ts"() {
|
|
9518
10119
|
"use strict";
|
|
9519
10120
|
init_esm_shims();
|
|
9520
|
-
SETTINGS_PATH =
|
|
10121
|
+
SETTINGS_PATH = join19(homedir12(), ".msapling", "settings.json");
|
|
9521
10122
|
modeCommand = {
|
|
9522
10123
|
name: "mode",
|
|
9523
10124
|
args: "[default|plan|acceptEdits|bypassPermissions] [...options]",
|
|
@@ -9919,7 +10520,7 @@ var init_compact = __esm({
|
|
|
9919
10520
|
});
|
|
9920
10521
|
|
|
9921
10522
|
// src/commands/init.ts
|
|
9922
|
-
import { join as
|
|
10523
|
+
import { join as join20 } from "path";
|
|
9923
10524
|
import { existsSync as existsSync17 } from "fs";
|
|
9924
10525
|
import { writeFile as writeFile9 } from "fs/promises";
|
|
9925
10526
|
var initCommand;
|
|
@@ -9934,7 +10535,7 @@ var init_init = __esm({
|
|
|
9934
10535
|
handler: async (args2, context) => {
|
|
9935
10536
|
try {
|
|
9936
10537
|
const cwd = process.cwd();
|
|
9937
|
-
const path2 =
|
|
10538
|
+
const path2 = join20(cwd, "MSAPLING.md");
|
|
9938
10539
|
if (existsSync17(path2)) {
|
|
9939
10540
|
context.addMessage("error", "MSAPLING.md already exists in current directory.");
|
|
9940
10541
|
return;
|
|
@@ -9962,7 +10563,7 @@ var init_init = __esm({
|
|
|
9962
10563
|
|
|
9963
10564
|
// src/commands/review.ts
|
|
9964
10565
|
import { existsSync as existsSync18 } from "fs";
|
|
9965
|
-
import { readFile as
|
|
10566
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
9966
10567
|
var reviewCommand;
|
|
9967
10568
|
var init_review = __esm({
|
|
9968
10569
|
"src/commands/review.ts"() {
|
|
@@ -9982,7 +10583,7 @@ var init_review = __esm({
|
|
|
9982
10583
|
let content = "";
|
|
9983
10584
|
try {
|
|
9984
10585
|
if (existsSync18(target)) {
|
|
9985
|
-
content = await
|
|
10586
|
+
content = await readFile17(target, "utf8");
|
|
9986
10587
|
} else {
|
|
9987
10588
|
content = `Review target: ${target}`;
|
|
9988
10589
|
}
|
|
@@ -10076,13 +10677,13 @@ var init_swarm = __esm({
|
|
|
10076
10677
|
// src/commands/recipe.ts
|
|
10077
10678
|
import { parse as parseYaml } from "yaml";
|
|
10078
10679
|
import { existsSync as existsSync19 } from "fs";
|
|
10079
|
-
import { readFile as
|
|
10080
|
-
import { join as
|
|
10680
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
10681
|
+
import { join as join21 } from "path";
|
|
10081
10682
|
function findRecipe(name, cwd) {
|
|
10082
10683
|
for (const dir of RECIPE_DIRS) {
|
|
10083
10684
|
for (const suffix of NAME_SUFFIXES) {
|
|
10084
10685
|
for (const ext of FILE_EXTS) {
|
|
10085
|
-
const p =
|
|
10686
|
+
const p = join21(cwd, dir, `${name}${suffix}${ext}`);
|
|
10086
10687
|
if (existsSync19(p)) return p;
|
|
10087
10688
|
}
|
|
10088
10689
|
}
|
|
@@ -10142,7 +10743,7 @@ var init_recipe = __esm({
|
|
|
10142
10743
|
let text;
|
|
10143
10744
|
let recipe;
|
|
10144
10745
|
try {
|
|
10145
|
-
text = await
|
|
10746
|
+
text = await readFile18(path2, "utf8");
|
|
10146
10747
|
recipe = parseYaml(text);
|
|
10147
10748
|
} catch (e) {
|
|
10148
10749
|
context.addMessage("error", `Failed to load ${path2}: ${e.message}`);
|
|
@@ -10197,8 +10798,8 @@ ${rendered}` : rendered;
|
|
|
10197
10798
|
|
|
10198
10799
|
// src/commands/skill.ts
|
|
10199
10800
|
import { existsSync as existsSync20, readdirSync as readdirSync2, statSync as statSync5 } from "fs";
|
|
10200
|
-
import { readFile as
|
|
10201
|
-
import { join as
|
|
10801
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
10802
|
+
import { join as join22, resolve as resolve15 } from "path";
|
|
10202
10803
|
function findSkillsRoot(cwd) {
|
|
10203
10804
|
for (const candidate of SKILLS_DIRS) {
|
|
10204
10805
|
const full = resolve15(cwd, candidate);
|
|
@@ -10215,7 +10816,7 @@ function listAllSkills(root) {
|
|
|
10215
10816
|
return out;
|
|
10216
10817
|
}
|
|
10217
10818
|
for (const domain of domains) {
|
|
10218
|
-
const dir =
|
|
10819
|
+
const dir = join22(root, domain);
|
|
10219
10820
|
let s;
|
|
10220
10821
|
try {
|
|
10221
10822
|
s = statSync5(dir);
|
|
@@ -10231,7 +10832,7 @@ function listAllSkills(root) {
|
|
|
10231
10832
|
}
|
|
10232
10833
|
for (const f of files) {
|
|
10233
10834
|
if (!f.endsWith(".md")) continue;
|
|
10234
|
-
out.push({ domain, name: f.slice(0, -3), path:
|
|
10835
|
+
out.push({ domain, name: f.slice(0, -3), path: join22(dir, f) });
|
|
10235
10836
|
}
|
|
10236
10837
|
}
|
|
10237
10838
|
return out.sort(
|
|
@@ -10296,7 +10897,7 @@ var init_skill = __esm({
|
|
|
10296
10897
|
}
|
|
10297
10898
|
let body;
|
|
10298
10899
|
try {
|
|
10299
|
-
body = await
|
|
10900
|
+
body = await readFile19(skill.path, "utf8");
|
|
10300
10901
|
} catch (e) {
|
|
10301
10902
|
context.addMessage("error", `Failed to load skill ${skill.path}: ${e.message}`);
|
|
10302
10903
|
return;
|
|
@@ -10320,8 +10921,8 @@ ${prompt4}`;
|
|
|
10320
10921
|
});
|
|
10321
10922
|
|
|
10322
10923
|
// src/commands/benchmark.ts
|
|
10323
|
-
import { homedir as
|
|
10324
|
-
import { join as
|
|
10924
|
+
import { homedir as homedir13 } from "os";
|
|
10925
|
+
import { join as join23 } from "path";
|
|
10325
10926
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
10326
10927
|
import * as fs2 from "fs";
|
|
10327
10928
|
function parseArgs(args2) {
|
|
@@ -10441,10 +11042,10 @@ HW at start: ${hw.cores}-core ${hw.platform} | CPU ${hw.cpuPct}% | RAM ${hw.ramP
|
|
|
10441
11042
|
`[HW at run time: CPU ${hwAtEnd.cpuPct}% / RAM ${hwAtEnd.ramPct}% | ${hw.ramGiB} GiB RAM, ${hw.cores} cores]`
|
|
10442
11043
|
);
|
|
10443
11044
|
try {
|
|
10444
|
-
const dir =
|
|
11045
|
+
const dir = join23(homedir13(), ".msapling", "benchmarks");
|
|
10445
11046
|
mkdirSync4(dir, { recursive: true });
|
|
10446
11047
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
10447
|
-
const file =
|
|
11048
|
+
const file = join23(dir, `${ts}.json`);
|
|
10448
11049
|
const run = {
|
|
10449
11050
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10450
11051
|
rounds,
|
|
@@ -10690,22 +11291,22 @@ var init_theme = __esm({
|
|
|
10690
11291
|
});
|
|
10691
11292
|
|
|
10692
11293
|
// src/commands/theme.ts
|
|
10693
|
-
import { join as
|
|
10694
|
-
import { homedir as
|
|
11294
|
+
import { join as join24 } from "path";
|
|
11295
|
+
import { homedir as homedir14 } from "os";
|
|
10695
11296
|
import { existsSync as existsSync21 } from "fs";
|
|
10696
|
-
import { readFile as
|
|
11297
|
+
import { readFile as readFile20, writeFile as writeFile10 } from "fs/promises";
|
|
10697
11298
|
async function persistTheme(storage, themeName) {
|
|
10698
|
-
const settingsPath =
|
|
11299
|
+
const settingsPath = join24(homedir14(), ".msapling", "settings.json");
|
|
10699
11300
|
let existing = {};
|
|
10700
11301
|
try {
|
|
10701
11302
|
if (existsSync21(settingsPath)) {
|
|
10702
|
-
const text = await
|
|
11303
|
+
const text = await readFile20(settingsPath, "utf8");
|
|
10703
11304
|
if (text.trim()) existing = JSON.parse(text);
|
|
10704
11305
|
}
|
|
10705
11306
|
} catch {
|
|
10706
11307
|
}
|
|
10707
11308
|
existing["theme"] = themeName;
|
|
10708
|
-
ensureConfigDir(
|
|
11309
|
+
ensureConfigDir(join24(homedir14(), ".msapling"));
|
|
10709
11310
|
await writeFile10(settingsPath, JSON.stringify(existing, null, 2), "utf8");
|
|
10710
11311
|
}
|
|
10711
11312
|
var VALID_THEMES, themeCommand;
|
|
@@ -10777,7 +11378,7 @@ var init_version = __esm({
|
|
|
10777
11378
|
description: "Show version information for CLI and core packages",
|
|
10778
11379
|
category: "debug",
|
|
10779
11380
|
handler: async (_args, context) => {
|
|
10780
|
-
const cliVersion = true ? "2.3.6-beta.
|
|
11381
|
+
const cliVersion = true ? "2.3.6-beta.30" : "(dev)";
|
|
10781
11382
|
const coreVersion = true ? "2.3.2" : "(dev)";
|
|
10782
11383
|
const runtime = process.version;
|
|
10783
11384
|
context.addMessage("system", "MSapling Version Info");
|
|
@@ -10786,7 +11387,7 @@ var init_version = __esm({
|
|
|
10786
11387
|
context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
|
|
10787
11388
|
context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
|
|
10788
11389
|
try {
|
|
10789
|
-
const ts = "2026-05-
|
|
11390
|
+
const ts = "2026-05-29T20:13:59.523Z";
|
|
10790
11391
|
if (ts && ts !== "__BUILD_TIMESTAMP__") {
|
|
10791
11392
|
context.addMessage("system", row2("Build Timestamp", ts));
|
|
10792
11393
|
}
|
|
@@ -10799,15 +11400,15 @@ var init_version = __esm({
|
|
|
10799
11400
|
});
|
|
10800
11401
|
|
|
10801
11402
|
// src/commands/feedback.ts
|
|
10802
|
-
import { join as
|
|
11403
|
+
import { join as join25 } from "path";
|
|
10803
11404
|
import { existsSync as existsSync22 } from "fs";
|
|
10804
|
-
import { readFile as
|
|
11405
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
10805
11406
|
async function readCliVersion() {
|
|
10806
11407
|
try {
|
|
10807
11408
|
const baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
10808
|
-
const pkgPath =
|
|
11409
|
+
const pkgPath = join25(baseDir, "..", "..", "package.json");
|
|
10809
11410
|
if (!existsSync22(pkgPath)) return "unknown";
|
|
10810
|
-
const text = await
|
|
11411
|
+
const text = await readFile21(pkgPath, "utf8");
|
|
10811
11412
|
const json = JSON.parse(text);
|
|
10812
11413
|
return json.version ?? "unknown";
|
|
10813
11414
|
} catch {
|
|
@@ -10847,8 +11448,8 @@ var init_feedback = __esm({
|
|
|
10847
11448
|
});
|
|
10848
11449
|
|
|
10849
11450
|
// src/commands/export.ts
|
|
10850
|
-
import { homedir as
|
|
10851
|
-
import { join as
|
|
11451
|
+
import { homedir as homedir15 } from "os";
|
|
11452
|
+
import { join as join26 } from "path";
|
|
10852
11453
|
import { writeFile as writeFile11, mkdir as mkdir8 } from "fs/promises";
|
|
10853
11454
|
function formatTimestamp(date) {
|
|
10854
11455
|
return date.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
@@ -10898,10 +11499,10 @@ var init_export = __esm({
|
|
|
10898
11499
|
let outputPath;
|
|
10899
11500
|
let content;
|
|
10900
11501
|
if (arg === "" || arg === "json") {
|
|
10901
|
-
outputPath =
|
|
11502
|
+
outputPath = join26(homedir15(), `msapling-export-${timestamp}.json`);
|
|
10902
11503
|
content = buildJsonExport(history);
|
|
10903
11504
|
} else if (arg === "markdown" || arg === "md") {
|
|
10904
|
-
outputPath =
|
|
11505
|
+
outputPath = join26(homedir15(), `msapling-export-${timestamp}.md`);
|
|
10905
11506
|
content = buildMarkdownExport(history);
|
|
10906
11507
|
} else {
|
|
10907
11508
|
outputPath = arg;
|
|
@@ -10913,7 +11514,7 @@ var init_export = __esm({
|
|
|
10913
11514
|
}
|
|
10914
11515
|
}
|
|
10915
11516
|
try {
|
|
10916
|
-
const dir =
|
|
11517
|
+
const dir = join26(outputPath, "..");
|
|
10917
11518
|
await mkdir8(dir, { recursive: true });
|
|
10918
11519
|
await writeFile11(outputPath, content, "utf8");
|
|
10919
11520
|
context.addMessage("system", `Exported to: ${outputPath}`);
|
|
@@ -11108,17 +11709,17 @@ var init_plan = __esm({
|
|
|
11108
11709
|
});
|
|
11109
11710
|
|
|
11110
11711
|
// src/commands/note.ts
|
|
11111
|
-
import { homedir as
|
|
11112
|
-
import { join as
|
|
11712
|
+
import { homedir as homedir16 } from "os";
|
|
11713
|
+
import { join as join27 } from "path";
|
|
11113
11714
|
import { existsSync as existsSync23 } from "fs";
|
|
11114
|
-
import { readFile as
|
|
11715
|
+
import { readFile as readFile22, writeFile as writeFile12 } from "fs/promises";
|
|
11115
11716
|
function getNotesFilePath() {
|
|
11116
|
-
return
|
|
11717
|
+
return join27(homedir16(), ".msapling", "notes.json");
|
|
11117
11718
|
}
|
|
11118
11719
|
async function readNotes(filePath = getNotesFilePath()) {
|
|
11119
11720
|
try {
|
|
11120
11721
|
if (!existsSync23(filePath)) return [];
|
|
11121
|
-
const raw = await
|
|
11722
|
+
const raw = await readFile22(filePath, "utf8");
|
|
11122
11723
|
const parsed = JSON.parse(raw);
|
|
11123
11724
|
if (!Array.isArray(parsed)) return [];
|
|
11124
11725
|
return parsed;
|
|
@@ -11127,7 +11728,7 @@ async function readNotes(filePath = getNotesFilePath()) {
|
|
|
11127
11728
|
}
|
|
11128
11729
|
}
|
|
11129
11730
|
async function writeNotes(notes, filePath = getNotesFilePath()) {
|
|
11130
|
-
const dir =
|
|
11731
|
+
const dir = join27(homedir16(), ".msapling");
|
|
11131
11732
|
ensureConfigDir(dir);
|
|
11132
11733
|
await writeFile12(filePath, JSON.stringify(notes, null, 2), "utf8");
|
|
11133
11734
|
}
|
|
@@ -11273,14 +11874,14 @@ var init_todo = __esm({
|
|
|
11273
11874
|
});
|
|
11274
11875
|
|
|
11275
11876
|
// src/commands/outputStyle.ts
|
|
11276
|
-
import { homedir as
|
|
11277
|
-
import { join as
|
|
11877
|
+
import { homedir as homedir17 } from "os";
|
|
11878
|
+
import { join as join28, basename as basename2, extname as extname3 } from "path";
|
|
11278
11879
|
import { existsSync as existsSync24, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
11279
11880
|
function stylesDir() {
|
|
11280
|
-
return
|
|
11881
|
+
return join28(homedir17(), ".msapling", "output-styles");
|
|
11281
11882
|
}
|
|
11282
11883
|
function activeFile() {
|
|
11283
|
-
return
|
|
11884
|
+
return join28(stylesDir(), ".active");
|
|
11284
11885
|
}
|
|
11285
11886
|
function parseStyleFile(text) {
|
|
11286
11887
|
const fm = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
@@ -11305,7 +11906,7 @@ function listUserStyles() {
|
|
|
11305
11906
|
const out = [];
|
|
11306
11907
|
for (const entry of readdirSync3(dir)) {
|
|
11307
11908
|
if (extname3(entry).toLowerCase() !== ".md") continue;
|
|
11308
|
-
const full =
|
|
11909
|
+
const full = join28(dir, entry);
|
|
11309
11910
|
try {
|
|
11310
11911
|
const text = readFileSync(full, "utf8");
|
|
11311
11912
|
const { description, body } = parseStyleFile(text);
|
|
@@ -11355,7 +11956,7 @@ function createUserStyle(name, description, body) {
|
|
|
11355
11956
|
}
|
|
11356
11957
|
const dir = stylesDir();
|
|
11357
11958
|
if (!existsSync24(dir)) mkdirSync5(dir, { recursive: true });
|
|
11358
|
-
const target =
|
|
11959
|
+
const target = join28(dir, `${name}.md`);
|
|
11359
11960
|
const frontmatter = `---
|
|
11360
11961
|
description: ${description.replace(/\n/g, " ")}
|
|
11361
11962
|
---
|
|
@@ -12027,6 +12628,442 @@ var init_webhook = __esm({
|
|
|
12027
12628
|
}
|
|
12028
12629
|
});
|
|
12029
12630
|
|
|
12631
|
+
// src/commands/sync.ts
|
|
12632
|
+
var syncCommand;
|
|
12633
|
+
var init_sync = __esm({
|
|
12634
|
+
"src/commands/sync.ts"() {
|
|
12635
|
+
"use strict";
|
|
12636
|
+
init_esm_shims();
|
|
12637
|
+
init_src();
|
|
12638
|
+
syncCommand = {
|
|
12639
|
+
name: "sync",
|
|
12640
|
+
description: "Manage offline message sync (status|push)",
|
|
12641
|
+
args: "[status|push]",
|
|
12642
|
+
category: "config",
|
|
12643
|
+
handler: async (args2, context) => {
|
|
12644
|
+
const subcommand = (args2[0] || "status").toLowerCase();
|
|
12645
|
+
const journal = getJournal();
|
|
12646
|
+
switch (subcommand) {
|
|
12647
|
+
case "status": {
|
|
12648
|
+
const { pending, total } = await journal.journalCount();
|
|
12649
|
+
context.addMessage("assistant", [
|
|
12650
|
+
`Offline messages journal:`,
|
|
12651
|
+
` Total: ${total}`,
|
|
12652
|
+
` Pending sync: ${pending}`,
|
|
12653
|
+
` Synced: ${total - pending}`,
|
|
12654
|
+
...pending > 0 ? [``, `Run /sync push to sync pending messages to the backend.`] : []
|
|
12655
|
+
].join("\n"));
|
|
12656
|
+
break;
|
|
12657
|
+
}
|
|
12658
|
+
case "push": {
|
|
12659
|
+
const pending = await journal.journalListPending();
|
|
12660
|
+
if (pending.length === 0) {
|
|
12661
|
+
context.addMessage("assistant", "No pending messages to sync.");
|
|
12662
|
+
break;
|
|
12663
|
+
}
|
|
12664
|
+
context.addMessage("assistant", `Syncing ${pending.length} message(s)...`);
|
|
12665
|
+
const byChat = /* @__PURE__ */ new Map();
|
|
12666
|
+
for (const entry of pending) {
|
|
12667
|
+
if (!byChat.has(entry.chat_id)) {
|
|
12668
|
+
byChat.set(entry.chat_id, []);
|
|
12669
|
+
}
|
|
12670
|
+
byChat.get(entry.chat_id).push(entry);
|
|
12671
|
+
}
|
|
12672
|
+
let synced = 0;
|
|
12673
|
+
let failed = 0;
|
|
12674
|
+
for (const [chatId, entries] of byChat.entries()) {
|
|
12675
|
+
for (let i = 0; i < entries.length; i += 50) {
|
|
12676
|
+
const batch = entries.slice(i, i + 50);
|
|
12677
|
+
try {
|
|
12678
|
+
await context.client.importChat({
|
|
12679
|
+
chat_id: chatId,
|
|
12680
|
+
messages: batch.map((e) => ({
|
|
12681
|
+
role: e.role,
|
|
12682
|
+
content: e.content,
|
|
12683
|
+
model: e.model
|
|
12684
|
+
}))
|
|
12685
|
+
});
|
|
12686
|
+
await journal.journalMarkSynced(batch.map((e) => e.id));
|
|
12687
|
+
synced += batch.length;
|
|
12688
|
+
} catch (e) {
|
|
12689
|
+
context.addMessage(
|
|
12690
|
+
"error",
|
|
12691
|
+
`Error syncing batch from chat ${chatId}: ${String(e).slice(0, 100)}`
|
|
12692
|
+
);
|
|
12693
|
+
failed += batch.length;
|
|
12694
|
+
}
|
|
12695
|
+
}
|
|
12696
|
+
}
|
|
12697
|
+
context.addMessage("assistant", `Sync complete: ${synced} synced, ${failed} failed`);
|
|
12698
|
+
break;
|
|
12699
|
+
}
|
|
12700
|
+
default:
|
|
12701
|
+
context.addMessage("error", `Unknown sync subcommand: ${subcommand}
|
|
12702
|
+
Usage: /sync [status|push]`);
|
|
12703
|
+
}
|
|
12704
|
+
}
|
|
12705
|
+
};
|
|
12706
|
+
}
|
|
12707
|
+
});
|
|
12708
|
+
|
|
12709
|
+
// ../core/src/diagnostics/specs.ts
|
|
12710
|
+
import { cpus as cpus2, totalmem as totalmem2, freemem as freemem2, platform as platform3, arch as arch2 } from "os";
|
|
12711
|
+
import { execSync } from "child_process";
|
|
12712
|
+
async function collectSpecs() {
|
|
12713
|
+
const cpuList = cpus2();
|
|
12714
|
+
const totalMemBytes = totalmem2();
|
|
12715
|
+
const freeMemBytes = freemem2();
|
|
12716
|
+
const specs = {
|
|
12717
|
+
cpu: {
|
|
12718
|
+
cores: cpuList.length,
|
|
12719
|
+
model: cpuList[0]?.model ?? "Unknown",
|
|
12720
|
+
speed: cpuList[0]?.speed ?? 0
|
|
12721
|
+
// MHz -> convert below
|
|
12722
|
+
},
|
|
12723
|
+
memory: {
|
|
12724
|
+
totalGB: Math.round(totalMemBytes / 1024 ** 3 * 10) / 10,
|
|
12725
|
+
freeGB: Math.round(freeMemBytes / 1024 ** 3 * 10) / 10
|
|
12726
|
+
},
|
|
12727
|
+
gpu: [],
|
|
12728
|
+
disk: {
|
|
12729
|
+
freeGB: 0,
|
|
12730
|
+
totalGB: 0
|
|
12731
|
+
},
|
|
12732
|
+
platform: platform3(),
|
|
12733
|
+
arch: arch2(),
|
|
12734
|
+
nodeVersion: process.version.slice(1)
|
|
12735
|
+
// Remove 'v' prefix
|
|
12736
|
+
};
|
|
12737
|
+
specs.cpu.speed = Math.round(specs.cpu.speed / 1e3 * 10) / 10;
|
|
12738
|
+
try {
|
|
12739
|
+
const { statfs } = await import("fs/promises");
|
|
12740
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "/";
|
|
12741
|
+
const stat5 = await statfs(homeDir);
|
|
12742
|
+
specs.disk.freeGB = Math.round(stat5.bavail * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
12743
|
+
specs.disk.totalGB = Math.round(stat5.blocks * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
12744
|
+
} catch {
|
|
12745
|
+
specs.disk.freeGB = -1;
|
|
12746
|
+
specs.disk.totalGB = -1;
|
|
12747
|
+
}
|
|
12748
|
+
specs.gpu = await detectGpu();
|
|
12749
|
+
return specs;
|
|
12750
|
+
}
|
|
12751
|
+
async function detectGpu() {
|
|
12752
|
+
const currentPlatform = platform3();
|
|
12753
|
+
if (currentPlatform === "win32") {
|
|
12754
|
+
return detectGpuWindows();
|
|
12755
|
+
} else if (currentPlatform === "darwin") {
|
|
12756
|
+
return detectGpuMacOS();
|
|
12757
|
+
} else {
|
|
12758
|
+
return detectGpuLinux();
|
|
12759
|
+
}
|
|
12760
|
+
}
|
|
12761
|
+
function detectGpuWindows() {
|
|
12762
|
+
try {
|
|
12763
|
+
const output = execSync(
|
|
12764
|
+
'powershell -Command "Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM"',
|
|
12765
|
+
{ timeout: 2e3, encoding: "utf-8" }
|
|
12766
|
+
);
|
|
12767
|
+
const gpus = [];
|
|
12768
|
+
const lines = output.split("\n");
|
|
12769
|
+
for (const line of lines) {
|
|
12770
|
+
const parts = line.trim().split(/\s{2,}/);
|
|
12771
|
+
if (parts[0] && parts[0] !== "Name") {
|
|
12772
|
+
const memBytes = parseInt(parts[1] || "0");
|
|
12773
|
+
gpus.push({
|
|
12774
|
+
name: parts[0],
|
|
12775
|
+
memoryGB: memBytes > 0 ? Math.round(memBytes / 1024 ** 3 * 10) / 10 : void 0
|
|
12776
|
+
});
|
|
12777
|
+
}
|
|
12778
|
+
}
|
|
12779
|
+
return gpus;
|
|
12780
|
+
} catch {
|
|
12781
|
+
return [];
|
|
12782
|
+
}
|
|
12783
|
+
}
|
|
12784
|
+
function detectGpuMacOS() {
|
|
12785
|
+
try {
|
|
12786
|
+
const output = execSync("system_profiler SPDisplaysDataType -json", {
|
|
12787
|
+
timeout: 2e3,
|
|
12788
|
+
encoding: "utf-8"
|
|
12789
|
+
});
|
|
12790
|
+
const data = JSON.parse(output);
|
|
12791
|
+
const gpus = [];
|
|
12792
|
+
const displays = data.SPDisplaysDataType || [];
|
|
12793
|
+
for (const display of displays) {
|
|
12794
|
+
const chips = display["sppci_model_name"];
|
|
12795
|
+
if (chips) {
|
|
12796
|
+
gpus.push({ name: chips });
|
|
12797
|
+
}
|
|
12798
|
+
}
|
|
12799
|
+
return gpus;
|
|
12800
|
+
} catch {
|
|
12801
|
+
return [];
|
|
12802
|
+
}
|
|
12803
|
+
}
|
|
12804
|
+
function detectGpuLinux() {
|
|
12805
|
+
const gpus = [];
|
|
12806
|
+
try {
|
|
12807
|
+
const output = execSync(
|
|
12808
|
+
"nvidia-smi --query-gpu=name,memory.total --format=csv,noheader",
|
|
12809
|
+
{ timeout: 2e3, encoding: "utf-8" }
|
|
12810
|
+
);
|
|
12811
|
+
const lines = output.trim().split("\n");
|
|
12812
|
+
for (const line of lines) {
|
|
12813
|
+
const [name, mem] = line.split(",");
|
|
12814
|
+
const memGB = mem ? Math.round(parseInt(mem) / 1024 * 10) / 10 : void 0;
|
|
12815
|
+
gpus.push({ name: name.trim(), memoryGB: memGB });
|
|
12816
|
+
}
|
|
12817
|
+
return gpus;
|
|
12818
|
+
} catch {
|
|
12819
|
+
}
|
|
12820
|
+
try {
|
|
12821
|
+
const output = execSync("rocm-smi --showproductname", {
|
|
12822
|
+
timeout: 2e3,
|
|
12823
|
+
encoding: "utf-8"
|
|
12824
|
+
});
|
|
12825
|
+
const lines = output.trim().split("\n");
|
|
12826
|
+
for (const line of lines) {
|
|
12827
|
+
if (line.includes("GPU")) {
|
|
12828
|
+
const match = line.match(/:\s*(.+)/);
|
|
12829
|
+
if (match) gpus.push({ name: match[1].trim() });
|
|
12830
|
+
}
|
|
12831
|
+
}
|
|
12832
|
+
return gpus;
|
|
12833
|
+
} catch {
|
|
12834
|
+
}
|
|
12835
|
+
try {
|
|
12836
|
+
const output = execSync("lspci | grep -i vga", {
|
|
12837
|
+
timeout: 2e3,
|
|
12838
|
+
encoding: "utf-8"
|
|
12839
|
+
});
|
|
12840
|
+
const lines = output.trim().split("\n");
|
|
12841
|
+
for (const line of lines) {
|
|
12842
|
+
const match = line.match(/:\s*(.+)/);
|
|
12843
|
+
if (match) gpus.push({ name: match[1].trim() });
|
|
12844
|
+
}
|
|
12845
|
+
return gpus;
|
|
12846
|
+
} catch {
|
|
12847
|
+
return [];
|
|
12848
|
+
}
|
|
12849
|
+
}
|
|
12850
|
+
var init_specs = __esm({
|
|
12851
|
+
"../core/src/diagnostics/specs.ts"() {
|
|
12852
|
+
"use strict";
|
|
12853
|
+
init_esm_shims();
|
|
12854
|
+
}
|
|
12855
|
+
});
|
|
12856
|
+
|
|
12857
|
+
// ../core/src/governor/ResourceGovernor.ts
|
|
12858
|
+
import { freemem as freemem3 } from "os";
|
|
12859
|
+
import { homedir as homedir18 } from "os";
|
|
12860
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
12861
|
+
import { join as join29 } from "path";
|
|
12862
|
+
function determineTier(specs) {
|
|
12863
|
+
const memGB = specs.memory.totalGB;
|
|
12864
|
+
const cores = specs.cpu.cores;
|
|
12865
|
+
if (memGB < 8 || cores < 4) return "T1";
|
|
12866
|
+
if (memGB < 16 || cores < 8) return "T2";
|
|
12867
|
+
if (memGB < 32 || cores < 16) return "T3";
|
|
12868
|
+
return "T4";
|
|
12869
|
+
}
|
|
12870
|
+
function recommendLimits(specs) {
|
|
12871
|
+
const tier = determineTier(specs);
|
|
12872
|
+
return tierTable[tier];
|
|
12873
|
+
}
|
|
12874
|
+
async function readConfigOverrides() {
|
|
12875
|
+
try {
|
|
12876
|
+
const configPath = join29(homedir18(), ".msapling", "config.json");
|
|
12877
|
+
const content = await readFile23(configPath, "utf-8");
|
|
12878
|
+
const config = JSON.parse(content);
|
|
12879
|
+
return config.limits ?? null;
|
|
12880
|
+
} catch {
|
|
12881
|
+
return null;
|
|
12882
|
+
}
|
|
12883
|
+
}
|
|
12884
|
+
async function createResourceGovernor(specs) {
|
|
12885
|
+
const recommended = recommendLimits(specs);
|
|
12886
|
+
const overrides = await readConfigOverrides();
|
|
12887
|
+
return new ResourceGovernor(recommended, overrides ?? void 0);
|
|
12888
|
+
}
|
|
12889
|
+
var tierTable, ResourceGovernor;
|
|
12890
|
+
var init_ResourceGovernor = __esm({
|
|
12891
|
+
"../core/src/governor/ResourceGovernor.ts"() {
|
|
12892
|
+
"use strict";
|
|
12893
|
+
init_esm_shims();
|
|
12894
|
+
tierTable = {
|
|
12895
|
+
T1: { maxAgents: 1, maxParallelTools: 1, maxFileWatchers: 50, localLlmTier: "3B" },
|
|
12896
|
+
T2: { maxAgents: 2, maxParallelTools: 4, maxFileWatchers: 200, localLlmTier: "7B" },
|
|
12897
|
+
T3: { maxAgents: 4, maxParallelTools: 8, maxFileWatchers: 500, localLlmTier: "13B" },
|
|
12898
|
+
T4: { maxAgents: 8, maxParallelTools: 16, maxFileWatchers: 2e3, localLlmTier: "32B+" }
|
|
12899
|
+
};
|
|
12900
|
+
ResourceGovernor = class {
|
|
12901
|
+
activeAgents = 0;
|
|
12902
|
+
maxAgents;
|
|
12903
|
+
activeTool = 0;
|
|
12904
|
+
maxParallelTools;
|
|
12905
|
+
maxFileWatchers;
|
|
12906
|
+
memoryWarningShown = false;
|
|
12907
|
+
memoryCheckInterval = null;
|
|
12908
|
+
minFreeMemGB = 1.5;
|
|
12909
|
+
constructor(limits, overrides) {
|
|
12910
|
+
this.maxAgents = overrides?.maxAgents ?? limits.maxAgents;
|
|
12911
|
+
this.maxParallelTools = overrides?.maxParallelTools ?? limits.maxParallelTools;
|
|
12912
|
+
this.maxFileWatchers = overrides?.maxFileWatchers ?? limits.maxFileWatchers;
|
|
12913
|
+
this.startMemoryMonitor();
|
|
12914
|
+
}
|
|
12915
|
+
/**
|
|
12916
|
+
* Monitor free memory every 5s; block new acquires if below threshold
|
|
12917
|
+
*/
|
|
12918
|
+
startMemoryMonitor() {
|
|
12919
|
+
this.memoryCheckInterval = setInterval(() => {
|
|
12920
|
+
const freeMemBytes = freemem3();
|
|
12921
|
+
const freeMemGB = freeMemBytes / 1024 ** 3;
|
|
12922
|
+
if (freeMemGB < this.minFreeMemGB && !this.memoryWarningShown) {
|
|
12923
|
+
console.warn(
|
|
12924
|
+
`[ResourceGovernor] Low memory: ${freeMemGB.toFixed(2)}GB free (threshold: ${this.minFreeMemGB}GB)`
|
|
12925
|
+
);
|
|
12926
|
+
this.memoryWarningShown = true;
|
|
12927
|
+
} else if (freeMemGB >= this.minFreeMemGB) {
|
|
12928
|
+
this.memoryWarningShown = false;
|
|
12929
|
+
}
|
|
12930
|
+
}, 5e3);
|
|
12931
|
+
}
|
|
12932
|
+
/**
|
|
12933
|
+
* Attempt to acquire an agent slot
|
|
12934
|
+
*/
|
|
12935
|
+
acquireAgent() {
|
|
12936
|
+
const freeMemGB = freemem3() / 1024 ** 3;
|
|
12937
|
+
if (freeMemGB < this.minFreeMemGB) return false;
|
|
12938
|
+
if (this.activeAgents >= this.maxAgents) return false;
|
|
12939
|
+
this.activeAgents++;
|
|
12940
|
+
return true;
|
|
12941
|
+
}
|
|
12942
|
+
/**
|
|
12943
|
+
* Release an agent slot
|
|
12944
|
+
*/
|
|
12945
|
+
releaseAgent() {
|
|
12946
|
+
this.activeAgents = Math.max(0, this.activeAgents - 1);
|
|
12947
|
+
}
|
|
12948
|
+
/**
|
|
12949
|
+
* Attempt to acquire a tool execution slot
|
|
12950
|
+
*/
|
|
12951
|
+
acquireTool() {
|
|
12952
|
+
const freeMemGB = freemem3() / 1024 ** 3;
|
|
12953
|
+
if (freeMemGB < this.minFreeMemGB) return false;
|
|
12954
|
+
if (this.activeTool >= this.maxParallelTools) return false;
|
|
12955
|
+
this.activeTool++;
|
|
12956
|
+
return true;
|
|
12957
|
+
}
|
|
12958
|
+
/**
|
|
12959
|
+
* Release a tool execution slot
|
|
12960
|
+
*/
|
|
12961
|
+
releaseTool() {
|
|
12962
|
+
this.activeTool = Math.max(0, this.activeTool - 1);
|
|
12963
|
+
}
|
|
12964
|
+
/**
|
|
12965
|
+
* Get current active counts
|
|
12966
|
+
*/
|
|
12967
|
+
getStatus() {
|
|
12968
|
+
return {
|
|
12969
|
+
activeAgents: this.activeAgents,
|
|
12970
|
+
activeTool: this.activeTool,
|
|
12971
|
+
maxAgents: this.maxAgents,
|
|
12972
|
+
maxParallelTools: this.maxParallelTools
|
|
12973
|
+
};
|
|
12974
|
+
}
|
|
12975
|
+
/**
|
|
12976
|
+
* Clean up monitor interval
|
|
12977
|
+
*/
|
|
12978
|
+
destroy() {
|
|
12979
|
+
if (this.memoryCheckInterval) {
|
|
12980
|
+
clearInterval(this.memoryCheckInterval);
|
|
12981
|
+
this.memoryCheckInterval = null;
|
|
12982
|
+
}
|
|
12983
|
+
}
|
|
12984
|
+
};
|
|
12985
|
+
}
|
|
12986
|
+
});
|
|
12987
|
+
|
|
12988
|
+
// src/commands/diag.ts
|
|
12989
|
+
async function handler(args2, ctx) {
|
|
12990
|
+
const jsonMode = args2.includes("json");
|
|
12991
|
+
try {
|
|
12992
|
+
const specs = await collectSpecs();
|
|
12993
|
+
const recommended = recommendLimits(specs);
|
|
12994
|
+
const tier = determineTier(specs);
|
|
12995
|
+
const governor = await createResourceGovernor(specs);
|
|
12996
|
+
const status = governor.getStatus();
|
|
12997
|
+
governor.destroy();
|
|
12998
|
+
if (jsonMode) {
|
|
12999
|
+
const output = {
|
|
13000
|
+
specs,
|
|
13001
|
+
recommended,
|
|
13002
|
+
tier,
|
|
13003
|
+
status
|
|
13004
|
+
};
|
|
13005
|
+
console.log(JSON.stringify(output, null, 2));
|
|
13006
|
+
} else {
|
|
13007
|
+
console.log("\n=== System Diagnostics ===\n");
|
|
13008
|
+
console.log("CPU:");
|
|
13009
|
+
console.log(` Cores: ${specs.cpu.cores}`);
|
|
13010
|
+
console.log(` Model: ${specs.cpu.model}`);
|
|
13011
|
+
console.log(` Speed: ${specs.cpu.speed} GHz`);
|
|
13012
|
+
console.log("\nMemory:");
|
|
13013
|
+
console.log(` Total: ${specs.memory.totalGB} GB`);
|
|
13014
|
+
console.log(` Free: ${specs.memory.freeGB} GB`);
|
|
13015
|
+
console.log("\nGPU:");
|
|
13016
|
+
if (specs.gpu.length === 0) {
|
|
13017
|
+
console.log(" None detected");
|
|
13018
|
+
} else {
|
|
13019
|
+
for (const gpu of specs.gpu) {
|
|
13020
|
+
console.log(` ${gpu.name}${gpu.memoryGB ? ` (${gpu.memoryGB} GB)` : ""}`);
|
|
13021
|
+
}
|
|
13022
|
+
}
|
|
13023
|
+
console.log("\nDisk:");
|
|
13024
|
+
if (specs.disk.totalGB > 0) {
|
|
13025
|
+
console.log(` Total: ${specs.disk.totalGB} GB`);
|
|
13026
|
+
console.log(` Free: ${specs.disk.freeGB} GB`);
|
|
13027
|
+
} else {
|
|
13028
|
+
console.log(" Unavailable");
|
|
13029
|
+
}
|
|
13030
|
+
console.log("\nPlatform:");
|
|
13031
|
+
console.log(` OS: ${specs.platform}`);
|
|
13032
|
+
console.log(` Arch: ${specs.arch}`);
|
|
13033
|
+
console.log(` Node: ${specs.nodeVersion}`);
|
|
13034
|
+
console.log("\n=== Resource Recommendations ===\n");
|
|
13035
|
+
console.log(`Tier: ${tier}`);
|
|
13036
|
+
console.log(` Max Agents: ${recommended.maxAgents}`);
|
|
13037
|
+
console.log(` Max Parallel Tools: ${recommended.maxParallelTools}`);
|
|
13038
|
+
console.log(` Max File Watchers: ${recommended.maxFileWatchers}`);
|
|
13039
|
+
console.log(` Local LLM Tier: ${recommended.localLlmTier}`);
|
|
13040
|
+
console.log("\n=== Current Active ===\n");
|
|
13041
|
+
console.log(`Active Agents: ${status.activeAgents}/${status.maxAgents}`);
|
|
13042
|
+
console.log(`Active Tools: ${status.activeTool}/${status.maxParallelTools}`);
|
|
13043
|
+
console.log("\n");
|
|
13044
|
+
}
|
|
13045
|
+
} catch (error) {
|
|
13046
|
+
ctx.addMessage("error", `[diag] Error: ${error instanceof Error ? error.message : error}`);
|
|
13047
|
+
}
|
|
13048
|
+
}
|
|
13049
|
+
var diagCommand;
|
|
13050
|
+
var init_diag = __esm({
|
|
13051
|
+
"src/commands/diag.ts"() {
|
|
13052
|
+
"use strict";
|
|
13053
|
+
init_esm_shims();
|
|
13054
|
+
init_specs();
|
|
13055
|
+
init_ResourceGovernor();
|
|
13056
|
+
diagCommand = {
|
|
13057
|
+
name: "diag",
|
|
13058
|
+
aliases: ["diagnostics"],
|
|
13059
|
+
description: "Display system specs and resource limits",
|
|
13060
|
+
category: "debug",
|
|
13061
|
+
args: "[json]",
|
|
13062
|
+
handler
|
|
13063
|
+
};
|
|
13064
|
+
}
|
|
13065
|
+
});
|
|
13066
|
+
|
|
12030
13067
|
// src/commands/index.ts
|
|
12031
13068
|
var commands_exports = {};
|
|
12032
13069
|
__export(commands_exports, {
|
|
@@ -12090,6 +13127,8 @@ var init_commands = __esm({
|
|
|
12090
13127
|
init_mfa();
|
|
12091
13128
|
init_mcp();
|
|
12092
13129
|
init_webhook();
|
|
13130
|
+
init_sync();
|
|
13131
|
+
init_diag();
|
|
12093
13132
|
commands = [
|
|
12094
13133
|
loginCommand,
|
|
12095
13134
|
unlockCommand,
|
|
@@ -12139,7 +13178,9 @@ var init_commands = __esm({
|
|
|
12139
13178
|
mlineageCommand,
|
|
12140
13179
|
mfaCommand,
|
|
12141
13180
|
mcpCommand,
|
|
12142
|
-
webhookCommand
|
|
13181
|
+
webhookCommand,
|
|
13182
|
+
syncCommand,
|
|
13183
|
+
diagCommand
|
|
12143
13184
|
];
|
|
12144
13185
|
}
|
|
12145
13186
|
});
|
|
@@ -12210,15 +13251,15 @@ __export(exec_exports, {
|
|
|
12210
13251
|
runExec: () => runExec
|
|
12211
13252
|
});
|
|
12212
13253
|
import { existsSync as existsSync26 } from "fs";
|
|
12213
|
-
import { readFile as
|
|
12214
|
-
import { homedir as
|
|
12215
|
-
import { join as
|
|
13254
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
13255
|
+
import { homedir as homedir19 } from "os";
|
|
13256
|
+
import { join as join30 } from "path";
|
|
12216
13257
|
async function loadPersistedSettings() {
|
|
12217
13258
|
const out = { mode: "default", theme: null };
|
|
12218
13259
|
try {
|
|
12219
|
-
const p =
|
|
13260
|
+
const p = join30(homedir19(), ".msapling", "settings.json");
|
|
12220
13261
|
if (!existsSync26(p)) return out;
|
|
12221
|
-
const raw = JSON.parse(await
|
|
13262
|
+
const raw = JSON.parse(await readFile25(p, "utf8"));
|
|
12222
13263
|
const parsed = parseApprovalMode(raw, Date.now());
|
|
12223
13264
|
if (parsed.kind === "ok") out.mode = parsed.mode;
|
|
12224
13265
|
const themeRaw = raw?.theme;
|
|
@@ -12455,13 +13496,13 @@ var init_format = __esm({
|
|
|
12455
13496
|
// src/commands/billing/open-browser.ts
|
|
12456
13497
|
import { spawn as spawn10 } from "child_process";
|
|
12457
13498
|
async function openBrowser(url) {
|
|
12458
|
-
const
|
|
13499
|
+
const platform5 = process.platform;
|
|
12459
13500
|
let cmd;
|
|
12460
13501
|
let args2;
|
|
12461
|
-
if (
|
|
13502
|
+
if (platform5 === "win32") {
|
|
12462
13503
|
cmd = "cmd";
|
|
12463
13504
|
args2 = ["/c", "start", "", url];
|
|
12464
|
-
} else if (
|
|
13505
|
+
} else if (platform5 === "darwin") {
|
|
12465
13506
|
cmd = "open";
|
|
12466
13507
|
args2 = [url];
|
|
12467
13508
|
} else {
|
|
@@ -13093,8 +14134,8 @@ var doctor_exports = {};
|
|
|
13093
14134
|
__export(doctor_exports, {
|
|
13094
14135
|
runDoctor: () => runDoctor
|
|
13095
14136
|
});
|
|
13096
|
-
import { homedir as
|
|
13097
|
-
import { join as
|
|
14137
|
+
import { homedir as homedir20, platform as platform4 } from "os";
|
|
14138
|
+
import { join as join31 } from "path";
|
|
13098
14139
|
import { existsSync as existsSync27, statSync as statSync6, accessSync } from "fs";
|
|
13099
14140
|
import { readdir as readdir3 } from "fs/promises";
|
|
13100
14141
|
import { exec } from "child_process";
|
|
@@ -13118,7 +14159,7 @@ async function checkNodeVersion() {
|
|
|
13118
14159
|
};
|
|
13119
14160
|
}
|
|
13120
14161
|
async function checkConfigDir() {
|
|
13121
|
-
const configDir =
|
|
14162
|
+
const configDir = join31(homedir20(), ".msapling");
|
|
13122
14163
|
if (!existsSync27(configDir)) {
|
|
13123
14164
|
return {
|
|
13124
14165
|
name: "Config directory",
|
|
@@ -13136,7 +14177,7 @@ async function checkConfigDir() {
|
|
|
13136
14177
|
remediation: `rm "${configDir}" && mkdir -p "${configDir}"`
|
|
13137
14178
|
};
|
|
13138
14179
|
}
|
|
13139
|
-
if (
|
|
14180
|
+
if (platform4() !== "win32") {
|
|
13140
14181
|
const mode = stats.mode & 511;
|
|
13141
14182
|
const safe = (mode & 63) === 0;
|
|
13142
14183
|
if (!safe) {
|
|
@@ -13182,7 +14223,7 @@ async function checkKeytar() {
|
|
|
13182
14223
|
}
|
|
13183
14224
|
async function checkPathConflicts() {
|
|
13184
14225
|
const pathEnv = process.env.PATH || "";
|
|
13185
|
-
const paths = pathEnv.split(
|
|
14226
|
+
const paths = pathEnv.split(platform4() === "win32" ? ";" : ":");
|
|
13186
14227
|
const conflicts = [];
|
|
13187
14228
|
for (const dir of paths) {
|
|
13188
14229
|
if (!dir || !existsSync27(dir)) continue;
|
|
@@ -13190,7 +14231,7 @@ async function checkPathConflicts() {
|
|
|
13190
14231
|
const files = await readdir3(dir);
|
|
13191
14232
|
for (const file of files) {
|
|
13192
14233
|
if (file === "msapling" || file === "msapling.exe" || file === "msapling.py") {
|
|
13193
|
-
const fullPath =
|
|
14234
|
+
const fullPath = join31(dir, file);
|
|
13194
14235
|
conflicts.push(fullPath);
|
|
13195
14236
|
}
|
|
13196
14237
|
}
|
|
@@ -13290,11 +14331,11 @@ async function checkTokenValidity() {
|
|
|
13290
14331
|
}
|
|
13291
14332
|
}
|
|
13292
14333
|
async function checkOsSpecific() {
|
|
13293
|
-
if (
|
|
14334
|
+
if (platform4() === "win32") {
|
|
13294
14335
|
try {
|
|
13295
|
-
const configDir =
|
|
14336
|
+
const configDir = join31(homedir20(), ".msapling");
|
|
13296
14337
|
const longPath = "A".repeat(260);
|
|
13297
|
-
const testPath =
|
|
14338
|
+
const testPath = join31(configDir, longPath);
|
|
13298
14339
|
try {
|
|
13299
14340
|
accessSync(configDir);
|
|
13300
14341
|
} catch {
|
|
@@ -13319,14 +14360,14 @@ async function checkOsSpecific() {
|
|
|
13319
14360
|
};
|
|
13320
14361
|
}
|
|
13321
14362
|
}
|
|
13322
|
-
if (
|
|
14363
|
+
if (platform4() === "darwin") {
|
|
13323
14364
|
return {
|
|
13324
14365
|
name: "OS-specific (macOS)",
|
|
13325
14366
|
status: "PASS",
|
|
13326
14367
|
message: "macOS detected"
|
|
13327
14368
|
};
|
|
13328
14369
|
}
|
|
13329
|
-
if (
|
|
14370
|
+
if (platform4() === "linux") {
|
|
13330
14371
|
try {
|
|
13331
14372
|
await import("keytar");
|
|
13332
14373
|
return {
|
|
@@ -13346,7 +14387,7 @@ async function checkOsSpecific() {
|
|
|
13346
14387
|
return {
|
|
13347
14388
|
name: "OS-specific",
|
|
13348
14389
|
status: "PASS",
|
|
13349
|
-
message: `${
|
|
14390
|
+
message: `${platform4()} detected`
|
|
13350
14391
|
};
|
|
13351
14392
|
}
|
|
13352
14393
|
function formatCheckResult(result, maxLabelWidth) {
|
|
@@ -14956,7 +15997,7 @@ __export(server_exports, {
|
|
|
14956
15997
|
runStdioWithRegistry: () => runStdioWithRegistry
|
|
14957
15998
|
});
|
|
14958
15999
|
import { readdirSync as readdirSync4, readFileSync as readFileSync2, statSync as statSync7 } from "fs";
|
|
14959
|
-
import { join as
|
|
16000
|
+
import { join as join32, relative as relative14, resolve as resolve18 } from "path";
|
|
14960
16001
|
function asResult2(text, isError = false) {
|
|
14961
16002
|
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
14962
16003
|
}
|
|
@@ -14976,7 +16017,7 @@ function buildFileTree(root, maxFiles) {
|
|
|
14976
16017
|
for (const name of entries) {
|
|
14977
16018
|
if (out.length >= maxFiles) break;
|
|
14978
16019
|
if (SKIP_DIRS2.has(name)) continue;
|
|
14979
|
-
const full =
|
|
16020
|
+
const full = join32(dir, name);
|
|
14980
16021
|
let s;
|
|
14981
16022
|
try {
|
|
14982
16023
|
s = statSync7(full);
|
|
@@ -15742,7 +16783,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
15742
16783
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
15743
16784
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
15744
16785
|
"\u25CF MSapling CLI v",
|
|
15745
|
-
"2.3.6-beta.
|
|
16786
|
+
"2.3.6-beta.30"
|
|
15746
16787
|
] }),
|
|
15747
16788
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
15748
16789
|
] });
|
|
@@ -16266,9 +17307,9 @@ ${prompt4}` : prompt4;
|
|
|
16266
17307
|
const filePath = mention.slice(1);
|
|
16267
17308
|
try {
|
|
16268
17309
|
const { existsSync: existsSync28 } = await import("fs");
|
|
16269
|
-
const { readFile:
|
|
17310
|
+
const { readFile: readFile26 } = await import("fs/promises");
|
|
16270
17311
|
if (existsSync28(filePath)) {
|
|
16271
|
-
const content = await
|
|
17312
|
+
const content = await readFile26(filePath, "utf8");
|
|
16272
17313
|
const MAX_LEN = 32768;
|
|
16273
17314
|
const truncated = content.length > MAX_LEN ? content.slice(0, MAX_LEN) + "\n...[TRUNCATED]" : content;
|
|
16274
17315
|
finalCmd += `
|
|
@@ -16320,7 +17361,7 @@ ${finalCmd}`;
|
|
|
16320
17361
|
init_esm_shims();
|
|
16321
17362
|
init_src3();
|
|
16322
17363
|
init_parseApprovalMode();
|
|
16323
|
-
import { readFile as
|
|
17364
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
16324
17365
|
import { existsSync as existsSync25 } from "fs";
|
|
16325
17366
|
async function initSession(ctx) {
|
|
16326
17367
|
try {
|
|
@@ -16335,11 +17376,11 @@ async function initSession(ctx) {
|
|
|
16335
17376
|
ctx.setShellEscapeEnabled(settings.shellEscapeEnabled !== false);
|
|
16336
17377
|
}
|
|
16337
17378
|
try {
|
|
16338
|
-
const { homedir:
|
|
16339
|
-
const { join:
|
|
16340
|
-
const userSettingsPath =
|
|
17379
|
+
const { homedir: homedir21 } = await import("os");
|
|
17380
|
+
const { join: join34 } = await import("path");
|
|
17381
|
+
const userSettingsPath = join34(homedir21(), ".msapling", "settings.json");
|
|
16341
17382
|
if (existsSync25(userSettingsPath)) {
|
|
16342
|
-
const userText = await
|
|
17383
|
+
const userText = await readFile24(userSettingsPath, "utf8");
|
|
16343
17384
|
let parsed;
|
|
16344
17385
|
try {
|
|
16345
17386
|
parsed = JSON.parse(userText);
|
|
@@ -16644,12 +17685,12 @@ var App = ({ compact: compact2 = false }) => {
|
|
|
16644
17685
|
init_esm_shims();
|
|
16645
17686
|
import { readFileSync as readFileSync3 } from "fs";
|
|
16646
17687
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16647
|
-
import { dirname as dirname3, join as
|
|
17688
|
+
import { dirname as dirname3, join as join33 } from "path";
|
|
16648
17689
|
function readCliVersion2() {
|
|
16649
17690
|
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
16650
17691
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
16651
17692
|
try {
|
|
16652
|
-
const pkg = JSON.parse(readFileSync3(
|
|
17693
|
+
const pkg = JSON.parse(readFileSync3(join33(here, rel), "utf8"));
|
|
16653
17694
|
if (pkg.name && pkg.version) {
|
|
16654
17695
|
return { name: pkg.name, version: pkg.version };
|
|
16655
17696
|
}
|
|
@@ -16735,11 +17776,11 @@ function handleCliArgs(args2) {
|
|
|
16735
17776
|
}
|
|
16736
17777
|
if (args2[0] === "mcp" && args2[1] === "serve") {
|
|
16737
17778
|
(async () => {
|
|
16738
|
-
const { MSaplingClient:
|
|
17779
|
+
const { MSaplingClient: MSaplingClient3 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
16739
17780
|
const { runStdio: runStdio2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
16740
17781
|
const apiUrl = process.env.MSAPLING_API_URL;
|
|
16741
17782
|
const token = process.env.MSAPLING_TOKEN;
|
|
16742
|
-
const client = new
|
|
17783
|
+
const client = new MSaplingClient3({ apiUrl, token });
|
|
16743
17784
|
await runStdio2(client);
|
|
16744
17785
|
})().catch((e) => {
|
|
16745
17786
|
process.stderr.write(`[msapling-mcp] fatal: ${e?.message ?? e}
|