@stevezhou/sisu 0.3.5 → 0.3.7
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/README.md +1 -1
- package/dist/commands.js +42 -19
- package/dist/main.js +8 -5
- package/dist/runtime/adapter.js +3 -3
- package/dist/runtime/sessions.js +2 -2
- package/dist/runtime/transcriptEvents.js +195 -0
- package/dist/runtime/transport.js +21 -9
- package/dist/store.js +15 -1
- package/dist/tui.js +19 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ sisu login
|
|
|
13
13
|
sisu
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
`npm install -g` is a small JS package. postinstall fetches the stamped SiSu TUI pager for **this package version** into `~/.sisu/bin` when a prebuilt exists
|
|
16
|
+
`npm install -g` is a small JS package. postinstall fetches the stamped SiSu TUI pager for **this package version** into `~/.sisu/bin` when a prebuilt exists. GitHub Release tags ship `darwin-arm64`, `linux-x64`, and `linux-arm64`. `darwin-x64` is opt-in (`workflow_dispatch` with `platforms` containing `darwin-x64`) and often missing; platforms without a binary, or a missing GitHub Release asset, keep the Node TUI.
|
|
17
17
|
|
|
18
18
|
Requires Node.js 20 or newer. `npx sisu` works without a global install.
|
|
19
19
|
|
package/dist/commands.js
CHANGED
|
@@ -26,7 +26,6 @@ const child_process_1 = require("child_process");
|
|
|
26
26
|
const fs_1 = __importDefault(require("fs"));
|
|
27
27
|
const path_1 = __importDefault(require("path"));
|
|
28
28
|
const http_1 = require("./http");
|
|
29
|
-
const adapter_1 = require("./runtime/adapter");
|
|
30
29
|
const loop_1 = require("./runtime/loop");
|
|
31
30
|
const models_1 = require("./runtime/models");
|
|
32
31
|
Object.defineProperty(exports, "fetchModelCatalog", { enumerable: true, get: function () { return models_1.fetchModelCatalog; } });
|
|
@@ -265,9 +264,7 @@ function resolveBoundWorkspace(projectId) {
|
|
|
265
264
|
throw new Error('no local workspace — run sisu open <dir> --project <id>');
|
|
266
265
|
throw new Error('multiple workspaces — pass --project');
|
|
267
266
|
}
|
|
268
|
-
function
|
|
269
|
-
(0, store_1.requireAuth)();
|
|
270
|
-
const bound = resolveBoundWorkspace(projectId);
|
|
267
|
+
function formatDirListing(bound) {
|
|
271
268
|
const names = fs_1.default.readdirSync(bound.path).filter((name) => !name.startsWith('.'));
|
|
272
269
|
if (!names.length)
|
|
273
270
|
return `${bound.path} (empty)`;
|
|
@@ -277,24 +274,29 @@ function listLocalCommand(projectId) {
|
|
|
277
274
|
return `${name}${suffix}`;
|
|
278
275
|
}).join('\n');
|
|
279
276
|
}
|
|
277
|
+
function listLocalCommand(projectId) {
|
|
278
|
+
(0, store_1.requireAuth)();
|
|
279
|
+
const workspaces = (0, store_1.readWorkspaces)();
|
|
280
|
+
const requested = (projectId || (0, store_1.readSession)().last_project_id || '').trim();
|
|
281
|
+
if (requested && workspaces[requested]) {
|
|
282
|
+
return formatDirListing({ projectId: requested, path: workspaces[requested] });
|
|
283
|
+
}
|
|
284
|
+
const entries = Object.entries(workspaces);
|
|
285
|
+
if (!entries.length)
|
|
286
|
+
throw new Error('no local workspace — run sisu open <dir> --project <id>');
|
|
287
|
+
if (entries.length === 1)
|
|
288
|
+
return formatDirListing({ projectId: entries[0][0], path: entries[0][1] });
|
|
289
|
+
return entries.map(([id, dir]) => `${id} ${dir}`).join('\n');
|
|
290
|
+
}
|
|
280
291
|
async function execCommand(prompt, options = {}, http = http_1.defaultHttp) {
|
|
281
292
|
const text = prompt.trim();
|
|
282
293
|
if (!text)
|
|
283
294
|
throw new Error('prompt is required');
|
|
284
295
|
const stub = Boolean(options.stub || process.env.SISU_RUNTIME_STUB === '1');
|
|
285
296
|
const cwd = options.cwd || process.cwd();
|
|
286
|
-
const modelClient = options.modelClient || (stub
|
|
287
|
-
? (0, loop_1.createLaunchStubModel)()
|
|
288
|
-
: (() => {
|
|
289
|
-
const auth = (0, store_1.requireAuth)();
|
|
290
|
-
return (0, adapter_1.createSisuCloudModel)(http, {
|
|
291
|
-
apiBase: auth.api_base,
|
|
292
|
-
token: auth.token,
|
|
293
|
-
client: options.client || 'cli',
|
|
294
|
-
});
|
|
295
|
-
})());
|
|
296
297
|
if (!stub)
|
|
297
298
|
(0, store_1.requireAuth)();
|
|
299
|
+
const modelClient = options.modelClient || (stub ? (0, loop_1.createLaunchStubModel)() : undefined);
|
|
298
300
|
if (options.projectId) {
|
|
299
301
|
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_project_id: options.projectId });
|
|
300
302
|
}
|
|
@@ -312,7 +314,7 @@ async function execCommand(prompt, options = {}, http = http_1.defaultHttp) {
|
|
|
312
314
|
}
|
|
313
315
|
async function listConversationsCommand(http = http_1.defaultHttp) {
|
|
314
316
|
const auth = (0, store_1.requireAuth)();
|
|
315
|
-
const response = await http(`${auth.api_base}/api/chat/conversations?limit=30`, {
|
|
317
|
+
const response = await http(`${auth.api_base}/api/chat/conversations?source=cli&limit=30`, {
|
|
316
318
|
headers: (0, http_1.authHeaders)(auth.token),
|
|
317
319
|
});
|
|
318
320
|
const body = await response.json().catch(() => []);
|
|
@@ -322,16 +324,37 @@ async function listConversationsCommand(http = http_1.defaultHttp) {
|
|
|
322
324
|
if (!rows.length)
|
|
323
325
|
return 'no saved conversations';
|
|
324
326
|
return rows.map((row) => {
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
+
const tag = row.source || row.client;
|
|
328
|
+
const suffix = tag ? ` [${tag}]` : '';
|
|
329
|
+
return `${row.id} ${row.title || '(untitled)'}${suffix}`;
|
|
327
330
|
}).join('\n');
|
|
328
331
|
}
|
|
329
|
-
function openConversationCommand(conversationId) {
|
|
332
|
+
async function openConversationCommand(conversationId, http = http_1.defaultHttp) {
|
|
330
333
|
const id = conversationId.trim();
|
|
331
334
|
if (!id)
|
|
332
335
|
throw new Error('conversation id is required');
|
|
333
336
|
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: id });
|
|
334
|
-
|
|
337
|
+
const auth = (0, store_1.requireAuth)();
|
|
338
|
+
const response = await http(`${auth.api_base}/api/chat/conversations/${id}`, {
|
|
339
|
+
headers: (0, http_1.authHeaders)(auth.token),
|
|
340
|
+
});
|
|
341
|
+
const body = await response.json().catch(() => ({}));
|
|
342
|
+
if (!response.ok)
|
|
343
|
+
throw new Error((0, http_1.errorDetail)(body, `thread failed (${response.status})`));
|
|
344
|
+
const messages = Array.isArray(body.messages) ? body.messages : [];
|
|
345
|
+
const lines = [`opened ${id}`];
|
|
346
|
+
if (body.title)
|
|
347
|
+
lines.push(String(body.title));
|
|
348
|
+
for (const msg of messages) {
|
|
349
|
+
const role = String(msg?.role || '').trim();
|
|
350
|
+
const content = String(msg?.content || '').trim();
|
|
351
|
+
if (!role || !content)
|
|
352
|
+
continue;
|
|
353
|
+
lines.push(`${role}: ${content}`);
|
|
354
|
+
}
|
|
355
|
+
if (lines.length === 1)
|
|
356
|
+
lines.push('(no messages)');
|
|
357
|
+
return lines.join('\n');
|
|
335
358
|
}
|
|
336
359
|
async function setTrainingCommand(optIn, http = http_1.defaultHttp) {
|
|
337
360
|
const auth = (0, store_1.requireAuth)();
|
package/dist/main.js
CHANGED
|
@@ -163,18 +163,21 @@ async function runCli(argv, deps = {}) {
|
|
|
163
163
|
model: parsed.flags['--model'],
|
|
164
164
|
newConversation: parsed.switches.has('new'),
|
|
165
165
|
stub: parsed.switches.has('stub') || process.env.SISU_RUNTIME_STUB === '1',
|
|
166
|
-
});
|
|
167
|
-
if (result.text)
|
|
166
|
+
}, http);
|
|
167
|
+
if (result.text.trim()) {
|
|
168
168
|
process.stdout.write(`${result.text}\n`);
|
|
169
|
-
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
process.stderr.write('no model text\n');
|
|
172
|
+
return 1;
|
|
170
173
|
}
|
|
171
174
|
if (command === 'history') {
|
|
172
|
-
process.stdout.write(`${await (0, commands_1.listConversationsCommand)(
|
|
175
|
+
process.stdout.write(`${await (0, commands_1.listConversationsCommand)(http)}\n`);
|
|
173
176
|
return 0;
|
|
174
177
|
}
|
|
175
178
|
if (command === 'thread') {
|
|
176
179
|
const id = args.find((item) => !item.startsWith('--')) || '';
|
|
177
|
-
process.stdout.write(`${(0, commands_1.openConversationCommand)(id)}\n`);
|
|
180
|
+
process.stdout.write(`${await (0, commands_1.openConversationCommand)(id, http)}\n`);
|
|
178
181
|
return 0;
|
|
179
182
|
}
|
|
180
183
|
if (command === 'models') {
|
package/dist/runtime/adapter.js
CHANGED
|
@@ -41,10 +41,10 @@ function completeUrl(apiBase) {
|
|
|
41
41
|
function openaiCompatUrl(apiBase) {
|
|
42
42
|
return `${apiBase.replace(/\/+$/, '')}${suite_1.OPENAI_COMPAT_PATH}`;
|
|
43
43
|
}
|
|
44
|
-
function completeHeaders(token) {
|
|
44
|
+
function completeHeaders(token, conversationId) {
|
|
45
45
|
return {
|
|
46
46
|
...(0, http_1.authHeaders)(token),
|
|
47
|
-
'x-sisu-conversation-id': (0, store_1.ensureConversationId)(),
|
|
47
|
+
'x-sisu-conversation-id': conversationId || (0, store_1.ensureConversationId)(),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
function buildCompleteRequest(request, options = {}) {
|
|
@@ -143,7 +143,7 @@ function createSisuCloudModel(http, options) {
|
|
|
143
143
|
}
|
|
144
144
|
const sent = await http(completeUrl(options.apiBase), {
|
|
145
145
|
method: 'POST',
|
|
146
|
-
headers: completeHeaders(options.token),
|
|
146
|
+
headers: completeHeaders(options.token, options.conversationId),
|
|
147
147
|
body: JSON.stringify(payload),
|
|
148
148
|
});
|
|
149
149
|
if (!sent.ok) {
|
package/dist/runtime/sessions.js
CHANGED
|
@@ -17,9 +17,9 @@ function sessionsDir() {
|
|
|
17
17
|
function sessionFile(id) {
|
|
18
18
|
return path_1.default.join(sessionsDir(), `${id}.json`);
|
|
19
19
|
}
|
|
20
|
-
function createLocalSession(title, cwd, model) {
|
|
20
|
+
function createLocalSession(title, cwd, model, id) {
|
|
21
21
|
const session = {
|
|
22
|
-
id: (0, crypto_1.randomUUID)(),
|
|
22
|
+
id: id || (0, crypto_1.randomUUID)(),
|
|
23
23
|
title: title.slice(0, 80) || 'session',
|
|
24
24
|
cwd,
|
|
25
25
|
model,
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startCompactionCheckpointWatch = void 0;
|
|
7
|
+
exports.transcriptEventFromCheckpoint = transcriptEventFromCheckpoint;
|
|
8
|
+
exports.transcriptEventFromToolLog = transcriptEventFromToolLog;
|
|
9
|
+
exports.listCompactionCheckpointFiles = listCompactionCheckpointFiles;
|
|
10
|
+
exports.rememberExistingCheckpoints = rememberExistingCheckpoints;
|
|
11
|
+
exports.listTerminalLogFiles = listTerminalLogFiles;
|
|
12
|
+
exports.rememberExistingTerminalLogs = rememberExistingTerminalLogs;
|
|
13
|
+
exports.flushNewTerminalLogs = flushNewTerminalLogs;
|
|
14
|
+
exports.postTranscriptEvent = postTranscriptEvent;
|
|
15
|
+
exports.flushNewCompactionCheckpoints = flushNewCompactionCheckpoints;
|
|
16
|
+
exports.startTranscriptWatch = startTranscriptWatch;
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const http_1 = require("../http");
|
|
20
|
+
function transcriptEventFromCheckpoint(raw, conversationId, fallbackId) {
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(parsed.compacted_history))
|
|
29
|
+
return null;
|
|
30
|
+
const checkpointId = String(parsed.checkpoint_id || '').trim() || String(fallbackId || '').trim();
|
|
31
|
+
if (!checkpointId)
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
kind: 'compaction',
|
|
35
|
+
conversation_id: conversationId,
|
|
36
|
+
client_request_id: checkpointId,
|
|
37
|
+
messages: parsed.compacted_history,
|
|
38
|
+
payload: {
|
|
39
|
+
checkpoint_id: checkpointId,
|
|
40
|
+
schema_version: parsed.schema_version,
|
|
41
|
+
prompt_index_at_compaction: parsed.prompt_index_at_compaction,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function transcriptEventFromToolLog(content, conversationId, toolCallId) {
|
|
46
|
+
return {
|
|
47
|
+
kind: 'tool_result_full',
|
|
48
|
+
conversation_id: conversationId,
|
|
49
|
+
client_request_id: toolCallId,
|
|
50
|
+
payload: { tool_call_id: toolCallId, content },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function listCompactionCheckpointFiles(engineHome) {
|
|
54
|
+
const sessions = path_1.default.join(engineHome, 'sessions');
|
|
55
|
+
if (!fs_1.default.existsSync(sessions))
|
|
56
|
+
return [];
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const sessionName of fs_1.default.readdirSync(sessions)) {
|
|
59
|
+
const dir = path_1.default.join(sessions, sessionName, 'compaction_checkpoints');
|
|
60
|
+
if (!fs_1.default.existsSync(dir) || !fs_1.default.statSync(dir).isDirectory())
|
|
61
|
+
continue;
|
|
62
|
+
for (const name of fs_1.default.readdirSync(dir)) {
|
|
63
|
+
if (name.endsWith('.json'))
|
|
64
|
+
out.push(path_1.default.join(dir, name));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out.sort();
|
|
68
|
+
}
|
|
69
|
+
function rememberExistingCheckpoints(engineHome, posted) {
|
|
70
|
+
for (const file of listCompactionCheckpointFiles(engineHome))
|
|
71
|
+
posted.add(file);
|
|
72
|
+
}
|
|
73
|
+
function listTerminalLogFiles(engineHome) {
|
|
74
|
+
const sessions = path_1.default.join(engineHome, 'sessions');
|
|
75
|
+
if (!fs_1.default.existsSync(sessions))
|
|
76
|
+
return [];
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const sessionName of fs_1.default.readdirSync(sessions)) {
|
|
79
|
+
const dir = path_1.default.join(sessions, sessionName, 'terminal');
|
|
80
|
+
if (!fs_1.default.existsSync(dir) || !fs_1.default.statSync(dir).isDirectory())
|
|
81
|
+
continue;
|
|
82
|
+
for (const name of fs_1.default.readdirSync(dir)) {
|
|
83
|
+
if (name.endsWith('.log'))
|
|
84
|
+
out.push(path_1.default.join(dir, name));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out.sort();
|
|
88
|
+
}
|
|
89
|
+
function rememberExistingTerminalLogs(engineHome, posted) {
|
|
90
|
+
for (const file of listTerminalLogFiles(engineHome))
|
|
91
|
+
posted.add(file);
|
|
92
|
+
}
|
|
93
|
+
const TOOL_LOG_MAX_BYTES = 8 * 1024 * 1024;
|
|
94
|
+
function readToolLog(file) {
|
|
95
|
+
const size = fs_1.default.statSync(file).size;
|
|
96
|
+
if (size <= 0)
|
|
97
|
+
return '';
|
|
98
|
+
if (size <= TOOL_LOG_MAX_BYTES)
|
|
99
|
+
return fs_1.default.readFileSync(file, 'utf8');
|
|
100
|
+
const fd = fs_1.default.openSync(file, 'r');
|
|
101
|
+
try {
|
|
102
|
+
const buf = Buffer.alloc(TOOL_LOG_MAX_BYTES);
|
|
103
|
+
fs_1.default.readSync(fd, buf, 0, TOOL_LOG_MAX_BYTES, 0);
|
|
104
|
+
return buf.toString('utf8');
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
fs_1.default.closeSync(fd);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function flushNewTerminalLogs(options) {
|
|
111
|
+
let sent = 0;
|
|
112
|
+
for (const file of listTerminalLogFiles(options.engineHome)) {
|
|
113
|
+
if (options.posted.has(file))
|
|
114
|
+
continue;
|
|
115
|
+
let content = '';
|
|
116
|
+
try {
|
|
117
|
+
content = readToolLog(file);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!content)
|
|
123
|
+
continue;
|
|
124
|
+
const event = transcriptEventFromToolLog(content, options.conversationId, path_1.default.parse(file).name);
|
|
125
|
+
const ok = await options.post(event);
|
|
126
|
+
if (!ok)
|
|
127
|
+
continue;
|
|
128
|
+
options.posted.add(file);
|
|
129
|
+
sent += 1;
|
|
130
|
+
}
|
|
131
|
+
return sent;
|
|
132
|
+
}
|
|
133
|
+
async function postTranscriptEvent(http, apiBase, token, event) {
|
|
134
|
+
const base = apiBase.replace(/\/+$/, '');
|
|
135
|
+
const headers = { ...(0, http_1.authHeaders)(token) };
|
|
136
|
+
if (event.conversation_id)
|
|
137
|
+
headers['x-sisu-conversation-id'] = event.conversation_id;
|
|
138
|
+
const response = await http(`${base}/api/runtime/v1/transcript/events`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers,
|
|
141
|
+
body: JSON.stringify(event),
|
|
142
|
+
});
|
|
143
|
+
return Boolean(response?.ok);
|
|
144
|
+
}
|
|
145
|
+
async function flushNewCompactionCheckpoints(options) {
|
|
146
|
+
let sent = 0;
|
|
147
|
+
for (const file of listCompactionCheckpointFiles(options.engineHome)) {
|
|
148
|
+
if (options.posted.has(file))
|
|
149
|
+
continue;
|
|
150
|
+
let raw = '';
|
|
151
|
+
try {
|
|
152
|
+
raw = fs_1.default.readFileSync(file, 'utf8');
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const event = transcriptEventFromCheckpoint(raw, options.conversationId, path_1.default.parse(file).name);
|
|
158
|
+
if (!event)
|
|
159
|
+
continue;
|
|
160
|
+
const ok = await options.post(event);
|
|
161
|
+
if (!ok)
|
|
162
|
+
continue;
|
|
163
|
+
options.posted.add(file);
|
|
164
|
+
sent += 1;
|
|
165
|
+
}
|
|
166
|
+
return sent;
|
|
167
|
+
}
|
|
168
|
+
function startTranscriptWatch(options) {
|
|
169
|
+
const posted = new Set();
|
|
170
|
+
rememberExistingCheckpoints(options.engineHome, posted);
|
|
171
|
+
rememberExistingTerminalLogs(options.engineHome, posted);
|
|
172
|
+
const tick = () => Promise.all([
|
|
173
|
+
flushNewCompactionCheckpoints({
|
|
174
|
+
engineHome: options.engineHome,
|
|
175
|
+
conversationId: options.conversationId,
|
|
176
|
+
posted,
|
|
177
|
+
post: options.post,
|
|
178
|
+
}),
|
|
179
|
+
flushNewTerminalLogs({
|
|
180
|
+
engineHome: options.engineHome,
|
|
181
|
+
conversationId: options.conversationId,
|
|
182
|
+
posted,
|
|
183
|
+
post: options.post,
|
|
184
|
+
}),
|
|
185
|
+
]).catch(() => 0);
|
|
186
|
+
const timer = setInterval(() => {
|
|
187
|
+
void tick();
|
|
188
|
+
}, options.intervalMs ?? 2000);
|
|
189
|
+
void tick();
|
|
190
|
+
return async () => {
|
|
191
|
+
clearInterval(timer);
|
|
192
|
+
await tick();
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
exports.startCompactionCheckpointWatch = startTranscriptWatch;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createLocalRuntimeTransport = createLocalRuntimeTransport;
|
|
4
4
|
exports.execLocalTurn = execLocalTurn;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
5
6
|
const store_1 = require("../store");
|
|
6
7
|
const adapter_1 = require("./adapter");
|
|
7
8
|
const loop_1 = require("./loop");
|
|
@@ -15,11 +16,14 @@ function createLocalRuntimeTransport(http, options = {}) {
|
|
|
15
16
|
let conversationId = sendOptions.conversationId || (!sendOptions.newConversation ? (0, store_1.readSession)().last_conversation_id : '') || '';
|
|
16
17
|
let existing = conversationId ? (0, sessions_1.loadLocalSession)(conversationId) : null;
|
|
17
18
|
if (!existing || sendOptions.newConversation) {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
conversationId = (0, crypto_1.randomUUID)();
|
|
20
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: conversationId });
|
|
21
|
+
existing = (0, sessions_1.createLocalSession)(prompt.trim().slice(0, 50), cwd, (0, store_1.readSession)().last_model, conversationId);
|
|
20
22
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
else {
|
|
24
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: conversationId });
|
|
25
|
+
}
|
|
26
|
+
const client = options.modelClient || cloudClient(http, options.client || 'tui', conversationId);
|
|
23
27
|
const model = options.modelClient
|
|
24
28
|
? existing.model || (0, store_1.readSession)().last_model || 'stub'
|
|
25
29
|
: await (0, models_1.resolveRuntimeModel)(http, { explicit: existing.model || (0, store_1.readSession)().last_model });
|
|
@@ -64,12 +68,15 @@ function createLocalRuntimeTransport(http, options = {}) {
|
|
|
64
68
|
async function execLocalTurn(prompt, options = {}) {
|
|
65
69
|
const cwd = (0, tools_1.resolveWorkspaceRoot)(options.cwd);
|
|
66
70
|
let conversationId = options.conversationId || (!options.newConversation ? (0, store_1.readSession)().last_conversation_id : '') || '';
|
|
71
|
+
if (options.newConversation || !conversationId) {
|
|
72
|
+
conversationId = (0, crypto_1.randomUUID)();
|
|
73
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: conversationId });
|
|
74
|
+
}
|
|
67
75
|
let existing = conversationId ? (0, sessions_1.loadLocalSession)(conversationId) : null;
|
|
68
76
|
if (!existing) {
|
|
69
|
-
existing = (0, sessions_1.createLocalSession)(prompt.trim().slice(0, 50), cwd, options.model);
|
|
70
|
-
conversationId = existing.id;
|
|
77
|
+
existing = (0, sessions_1.createLocalSession)(prompt.trim().slice(0, 50), cwd, options.model, conversationId);
|
|
71
78
|
}
|
|
72
|
-
const client = options.modelClient || (options.http ? cloudClient(options.http, options.client || 'cli') : undefined);
|
|
79
|
+
const client = options.modelClient || (options.http ? cloudClient(options.http, options.client || 'cli', conversationId) : undefined);
|
|
73
80
|
if (!client)
|
|
74
81
|
throw new Error('model client required');
|
|
75
82
|
const result = await (0, loop_1.collectLocalTurn)({
|
|
@@ -87,7 +94,12 @@ async function execLocalTurn(prompt, options = {}) {
|
|
|
87
94
|
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_conversation_id: result.conversationId, last_model: options.model || (0, store_1.readSession)().last_model });
|
|
88
95
|
return { conversationId: result.conversationId, text: result.text, toolResults: result.toolResults };
|
|
89
96
|
}
|
|
90
|
-
function cloudClient(http, client) {
|
|
97
|
+
function cloudClient(http, client, conversationId) {
|
|
91
98
|
const auth = (0, store_1.requireAuth)();
|
|
92
|
-
return (0, adapter_1.createSisuCloudModel)(http, {
|
|
99
|
+
return (0, adapter_1.createSisuCloudModel)(http, {
|
|
100
|
+
apiBase: auth.api_base,
|
|
101
|
+
token: auth.token,
|
|
102
|
+
client,
|
|
103
|
+
conversationId,
|
|
104
|
+
});
|
|
93
105
|
}
|
package/dist/store.js
CHANGED
|
@@ -131,7 +131,21 @@ function clearAuth() {
|
|
|
131
131
|
}
|
|
132
132
|
function readWorkspaces() {
|
|
133
133
|
const raw = readJson(workspacePath(), {});
|
|
134
|
-
|
|
134
|
+
if (!raw || typeof raw !== 'object')
|
|
135
|
+
return {};
|
|
136
|
+
const kept = {};
|
|
137
|
+
let dirty = false;
|
|
138
|
+
for (const [id, dir] of Object.entries(raw)) {
|
|
139
|
+
if (typeof dir === 'string' && dir && fs_1.default.existsSync(dir) && fs_1.default.statSync(dir).isDirectory()) {
|
|
140
|
+
kept[id] = dir;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
dirty = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (dirty)
|
|
147
|
+
writeJson(workspacePath(), kept);
|
|
148
|
+
return kept;
|
|
135
149
|
}
|
|
136
150
|
function bindWorkspace(projectId, requestedPath) {
|
|
137
151
|
if (!projectId.trim())
|
package/dist/tui.js
CHANGED
|
@@ -20,6 +20,7 @@ const stdio_1 = require("./pager/stdio");
|
|
|
20
20
|
const store_1 = require("./store");
|
|
21
21
|
const launch_1 = require("./runtime/launch");
|
|
22
22
|
const transport_1 = require("./runtime/transport");
|
|
23
|
+
const transcriptEvents_1 = require("./runtime/transcriptEvents");
|
|
23
24
|
const child_process_1 = require("child_process");
|
|
24
25
|
/** Pager exits with this code so the host runs `sisu login` and respawns. */
|
|
25
26
|
exports.SISU_LOGIN_EXIT_CODE = 10;
|
|
@@ -273,14 +274,28 @@ async function runTui(io, deps = {}) {
|
|
|
273
274
|
(0, launch_1.purgeChangelogCache)(home, engine);
|
|
274
275
|
(0, launch_1.writeSisuGrokConfig)();
|
|
275
276
|
io.close?.();
|
|
277
|
+
const env = (0, launch_1.sisuGrokBuildEnv)();
|
|
278
|
+
const stopWatch = (0, transcriptEvents_1.startTranscriptWatch)({
|
|
279
|
+
engineHome: engine,
|
|
280
|
+
conversationId: String(env.SISU_CONVERSATION_ID || ''),
|
|
281
|
+
post: async (event) => {
|
|
282
|
+
const current = auth();
|
|
283
|
+
if (!current?.token)
|
|
284
|
+
return false;
|
|
285
|
+
return (0, transcriptEvents_1.postTranscriptEvent)(http, current.api_base || store_1.DEFAULT_API_BASE, current.token, event);
|
|
286
|
+
},
|
|
287
|
+
});
|
|
276
288
|
const child = (0, child_process_1.spawn)(grokBin, [], {
|
|
277
289
|
stdio: 'inherit',
|
|
278
|
-
env
|
|
290
|
+
env,
|
|
279
291
|
cwd: process.cwd(),
|
|
280
292
|
});
|
|
281
293
|
return new Promise((resolve) => {
|
|
282
|
-
|
|
283
|
-
|
|
294
|
+
const finish = (code) => {
|
|
295
|
+
void stopWatch().finally(() => resolve(code));
|
|
296
|
+
};
|
|
297
|
+
child.on('exit', (code) => finish(code ?? 1));
|
|
298
|
+
child.on('error', () => finish(1));
|
|
284
299
|
});
|
|
285
300
|
});
|
|
286
301
|
if (deps.spawnGrokPager || ((0, launch_1.findGrokBuildBinary)() && process.stdout.isTTY)) {
|
|
@@ -382,7 +397,7 @@ async function runTui(io, deps = {}) {
|
|
|
382
397
|
}
|
|
383
398
|
if (raw.startsWith('/open ')) {
|
|
384
399
|
try {
|
|
385
|
-
io.write(`${openThread(raw.slice(6).trim())}\n`);
|
|
400
|
+
io.write(`${await openThread(raw.slice(6).trim(), http)}\n`);
|
|
386
401
|
newConversation = false;
|
|
387
402
|
}
|
|
388
403
|
catch (error) {
|