@stevezhou/sisu 0.1.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +12 -0
- package/README.md +3 -2
- package/dist/commands.js +58 -47
- package/dist/main.js +18 -5
- package/dist/pager/app.js +90 -5
- package/dist/pager/input.js +43 -20
- package/dist/pager/model.js +100 -25
- package/dist/runtime/adapter.js +164 -0
- package/dist/runtime/index.js +33 -0
- package/dist/runtime/launch.js +94 -0
- package/dist/runtime/loop.js +91 -0
- package/dist/runtime/models.js +56 -0
- package/dist/runtime/sessions.js +65 -0
- package/dist/runtime/suite.js +64 -0
- package/dist/runtime/tools.js +256 -0
- package/dist/runtime/transport.js +93 -0
- package/dist/runtime/types.js +2 -0
- package/dist/store.js +1 -0
- package/dist/transport.js +2 -0
- package/dist/tui.js +25 -3
- package/package.json +4 -3
package/dist/pager/model.js
CHANGED
|
@@ -9,8 +9,13 @@ exports.appendText = appendText;
|
|
|
9
9
|
exports.applyKey = applyKey;
|
|
10
10
|
exports.SLASH_COMMANDS = [
|
|
11
11
|
{ name: '/login', hint: 'Sign in with the browser' },
|
|
12
|
+
{ name: '/logout', hint: 'Sign out of this terminal' },
|
|
12
13
|
{ name: '/new', hint: 'Start a new conversation (alias: /clear)' },
|
|
13
14
|
{ name: '/resume', hint: 'Resume a conversation (alias: /history)' },
|
|
15
|
+
{ name: '/model', hint: 'Switch model (alias: /m)' },
|
|
16
|
+
{ name: '/models', hint: 'List models available to your account' },
|
|
17
|
+
{ name: '/copy', hint: 'Copy the last assistant reply' },
|
|
18
|
+
{ name: '/export', hint: 'Write this conversation to a markdown file' },
|
|
14
19
|
{ name: '/status', hint: 'Show session status' },
|
|
15
20
|
{ name: '/ls', hint: 'List local workspace files' },
|
|
16
21
|
{ name: '/training', hint: 'Training mode' },
|
|
@@ -23,6 +28,7 @@ const SLASH_ALIASES = {
|
|
|
23
28
|
'/clear': '/new',
|
|
24
29
|
'/history': '/resume',
|
|
25
30
|
'/exit': '/quit',
|
|
31
|
+
'/m': '/model',
|
|
26
32
|
};
|
|
27
33
|
let nextEntryId = 1;
|
|
28
34
|
function entryId() {
|
|
@@ -38,6 +44,9 @@ function createPagerState() {
|
|
|
38
44
|
slashOpen: false,
|
|
39
45
|
conversationId: '',
|
|
40
46
|
slashIndex: 0,
|
|
47
|
+
draftIndex: 0,
|
|
48
|
+
historyIndex: -1,
|
|
49
|
+
stashDraft: '',
|
|
41
50
|
};
|
|
42
51
|
}
|
|
43
52
|
function clampSelected(entries, selected) {
|
|
@@ -59,11 +68,40 @@ function clampSlashIndex(draft, slashIndex) {
|
|
|
59
68
|
return 0;
|
|
60
69
|
return slashIndex;
|
|
61
70
|
}
|
|
62
|
-
function
|
|
71
|
+
function draftChars(draft) {
|
|
72
|
+
return Array.from(draft);
|
|
73
|
+
}
|
|
74
|
+
function clampDraftIndex(draft, index) {
|
|
75
|
+
const n = draftChars(draft).length;
|
|
76
|
+
if (index < 0)
|
|
77
|
+
return 0;
|
|
78
|
+
if (index > n)
|
|
79
|
+
return n;
|
|
80
|
+
return index;
|
|
81
|
+
}
|
|
82
|
+
function insertAt(draft, index, value) {
|
|
83
|
+
const chars = draftChars(draft);
|
|
84
|
+
const at = clampDraftIndex(draft, index);
|
|
85
|
+
chars.splice(at, 0, ...Array.from(value));
|
|
86
|
+
return { draft: chars.join(''), draftIndex: at + Array.from(value).length };
|
|
87
|
+
}
|
|
88
|
+
function deleteBefore(draft, index) {
|
|
89
|
+
const chars = draftChars(draft);
|
|
90
|
+
const at = clampDraftIndex(draft, index);
|
|
91
|
+
if (at <= 0)
|
|
92
|
+
return { draft, draftIndex: 0 };
|
|
93
|
+
chars.splice(at - 1, 1);
|
|
94
|
+
return { draft: chars.join(''), draftIndex: at - 1 };
|
|
95
|
+
}
|
|
96
|
+
function userPrompts(state) {
|
|
97
|
+
return state.entries.filter((entry) => entry.kind === 'user').map((entry) => entry.text);
|
|
98
|
+
}
|
|
99
|
+
function withDraft(state, draft, slashOpen, draftIndex) {
|
|
63
100
|
const open = slashOpen ?? (draft.startsWith('/') ? state.slashOpen || draft === '/' : false);
|
|
64
101
|
return {
|
|
65
102
|
...state,
|
|
66
103
|
draft,
|
|
104
|
+
draftIndex: clampDraftIndex(draft, draftIndex ?? draftChars(draft).length),
|
|
67
105
|
slashOpen: open && draft.startsWith('/'),
|
|
68
106
|
slashIndex: open && draft.startsWith('/') ? clampSlashIndex(draft, state.slashIndex) : 0,
|
|
69
107
|
};
|
|
@@ -140,29 +178,57 @@ function appendText(state, text) {
|
|
|
140
178
|
const seeded = startAssistant(state);
|
|
141
179
|
return appendText(seeded, text);
|
|
142
180
|
}
|
|
181
|
+
function applyHistory(state, delta) {
|
|
182
|
+
const prompts = userPrompts(state);
|
|
183
|
+
if (prompts.length === 0) {
|
|
184
|
+
if (state.draft === '' && state.entries.length > 0) {
|
|
185
|
+
const selected = clampSelected(state.entries, state.selected + delta);
|
|
186
|
+
return { ...state, selected };
|
|
187
|
+
}
|
|
188
|
+
return state;
|
|
189
|
+
}
|
|
190
|
+
let historyIndex = state.historyIndex;
|
|
191
|
+
let stashDraft = state.stashDraft;
|
|
192
|
+
if (historyIndex < 0) {
|
|
193
|
+
if (delta > 0)
|
|
194
|
+
return state;
|
|
195
|
+
stashDraft = state.draft;
|
|
196
|
+
historyIndex = prompts.length - 1;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
historyIndex += delta;
|
|
200
|
+
}
|
|
201
|
+
if (historyIndex < 0)
|
|
202
|
+
historyIndex = 0;
|
|
203
|
+
if (historyIndex >= prompts.length) {
|
|
204
|
+
return withDraft({ ...state, historyIndex: -1, stashDraft: '' }, stashDraft, stashDraft.startsWith('/'));
|
|
205
|
+
}
|
|
206
|
+
const draft = prompts[historyIndex];
|
|
207
|
+
return withDraft({ ...state, historyIndex, stashDraft }, draft, draft.startsWith('/'));
|
|
208
|
+
}
|
|
143
209
|
function applyKey(state, key) {
|
|
144
210
|
switch (key.type) {
|
|
145
211
|
case 'char': {
|
|
146
|
-
const
|
|
212
|
+
const next = insertAt(state.draft, state.draftIndex, key.value);
|
|
147
213
|
if (key.value === '/' && state.draft === '') {
|
|
148
|
-
return { ...state, draft: '/', slashOpen: true, slashIndex: 0 };
|
|
214
|
+
return { ...state, draft: '/', draftIndex: 1, slashOpen: true, slashIndex: 0, historyIndex: -1 };
|
|
149
215
|
}
|
|
150
|
-
if (state.slashOpen || draft.startsWith('/')) {
|
|
151
|
-
return withDraft(state, draft, draft.startsWith('/'));
|
|
216
|
+
if (state.slashOpen || next.draft.startsWith('/')) {
|
|
217
|
+
return withDraft({ ...state, historyIndex: -1 }, next.draft, next.draft.startsWith('/'), next.draftIndex);
|
|
152
218
|
}
|
|
153
|
-
return { ...state, draft };
|
|
219
|
+
return { ...state, draft: next.draft, draftIndex: next.draftIndex, historyIndex: -1 };
|
|
154
220
|
}
|
|
155
221
|
case 'backspace': {
|
|
156
222
|
if (!state.draft)
|
|
157
223
|
return state;
|
|
158
|
-
const
|
|
224
|
+
const next = deleteBefore(state.draft, state.draftIndex);
|
|
159
225
|
if (state.slashOpen) {
|
|
160
|
-
if (!draft.startsWith('/')) {
|
|
161
|
-
return { ...state, draft, slashOpen: false, slashIndex: 0 };
|
|
226
|
+
if (!next.draft.startsWith('/')) {
|
|
227
|
+
return { ...state, draft: next.draft, draftIndex: next.draftIndex, slashOpen: false, slashIndex: 0 };
|
|
162
228
|
}
|
|
163
|
-
return withDraft(state, draft, true);
|
|
229
|
+
return withDraft(state, next.draft, true, next.draftIndex);
|
|
164
230
|
}
|
|
165
|
-
return { ...state, draft };
|
|
231
|
+
return { ...state, draft: next.draft, draftIndex: next.draftIndex };
|
|
166
232
|
}
|
|
167
233
|
case 'escape': {
|
|
168
234
|
if (state.slashOpen) {
|
|
@@ -171,13 +237,20 @@ function applyKey(state, key) {
|
|
|
171
237
|
return state;
|
|
172
238
|
}
|
|
173
239
|
case 'enter': {
|
|
174
|
-
|
|
175
|
-
return state;
|
|
240
|
+
return { ...state, historyIndex: -1, stashDraft: '' };
|
|
176
241
|
}
|
|
177
|
-
case 'left':
|
|
242
|
+
case 'left': {
|
|
243
|
+
if (state.draft) {
|
|
244
|
+
return { ...state, draftIndex: clampDraftIndex(state.draft, state.draftIndex - 1) };
|
|
245
|
+
}
|
|
178
246
|
return setEntryFold(state, true);
|
|
179
|
-
|
|
247
|
+
}
|
|
248
|
+
case 'right': {
|
|
249
|
+
if (state.draft) {
|
|
250
|
+
return { ...state, draftIndex: clampDraftIndex(state.draft, state.draftIndex + 1) };
|
|
251
|
+
}
|
|
180
252
|
return setEntryFold(state, false);
|
|
253
|
+
}
|
|
181
254
|
case 'up': {
|
|
182
255
|
if (state.slashOpen) {
|
|
183
256
|
const items = filterSlash(state.draft);
|
|
@@ -186,11 +259,7 @@ function applyKey(state, key) {
|
|
|
186
259
|
const slashIndex = (state.slashIndex - 1 + items.length) % items.length;
|
|
187
260
|
return { ...state, slashIndex };
|
|
188
261
|
}
|
|
189
|
-
|
|
190
|
-
const selected = clampSelected(state.entries, state.selected - 1);
|
|
191
|
-
return { ...state, selected };
|
|
192
|
-
}
|
|
193
|
-
return state;
|
|
262
|
+
return applyHistory(state, -1);
|
|
194
263
|
}
|
|
195
264
|
case 'down': {
|
|
196
265
|
if (state.slashOpen) {
|
|
@@ -200,11 +269,17 @@ function applyKey(state, key) {
|
|
|
200
269
|
const slashIndex = (state.slashIndex + 1) % items.length;
|
|
201
270
|
return { ...state, slashIndex };
|
|
202
271
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
272
|
+
return applyHistory(state, 1);
|
|
273
|
+
}
|
|
274
|
+
case 'pageup': {
|
|
275
|
+
if (state.entries.length === 0)
|
|
276
|
+
return state;
|
|
277
|
+
return { ...state, selected: clampSelected(state.entries, state.selected - 8) };
|
|
278
|
+
}
|
|
279
|
+
case 'pagedown': {
|
|
280
|
+
if (state.entries.length === 0)
|
|
281
|
+
return state;
|
|
282
|
+
return { ...state, selected: clampSelected(state.entries, state.selected + 8) };
|
|
208
283
|
}
|
|
209
284
|
default:
|
|
210
285
|
return state;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toProviderMessages = toProviderMessages;
|
|
4
|
+
exports.completeUrl = completeUrl;
|
|
5
|
+
exports.openaiCompatUrl = openaiCompatUrl;
|
|
6
|
+
exports.buildCompleteRequest = buildCompleteRequest;
|
|
7
|
+
exports.isServerSideAgentPayload = isServerSideAgentPayload;
|
|
8
|
+
exports.parseCompleteSse = parseCompleteSse;
|
|
9
|
+
exports.parseOpenAiChatCompletion = parseOpenAiChatCompletion;
|
|
10
|
+
exports.createSisuCloudModel = createSisuCloudModel;
|
|
11
|
+
const client_1 = require("../client");
|
|
12
|
+
const http_1 = require("../http");
|
|
13
|
+
const sse_1 = require("../sse");
|
|
14
|
+
const suite_1 = require("./suite");
|
|
15
|
+
/** OpenAI/Poe wire: assistant.tool_calls is {id,type,function:{name,arguments:string}}. */
|
|
16
|
+
function toProviderMessages(messages) {
|
|
17
|
+
return messages.map((row) => {
|
|
18
|
+
const out = { role: row.role, content: row.content };
|
|
19
|
+
if (row.tool_call_id)
|
|
20
|
+
out.tool_call_id = row.tool_call_id;
|
|
21
|
+
if (row.name)
|
|
22
|
+
out.name = row.name;
|
|
23
|
+
if (row.tool_calls?.length) {
|
|
24
|
+
out.tool_calls = row.tool_calls.map((call) => ({
|
|
25
|
+
id: call.id,
|
|
26
|
+
type: 'function',
|
|
27
|
+
function: {
|
|
28
|
+
name: call.name,
|
|
29
|
+
arguments: JSON.stringify(call.arguments || {}),
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function completeUrl(apiBase) {
|
|
37
|
+
return `${apiBase.replace(/\/+$/, '')}${suite_1.COMPLETE_PATH}`;
|
|
38
|
+
}
|
|
39
|
+
function openaiCompatUrl(apiBase) {
|
|
40
|
+
return `${apiBase.replace(/\/+$/, '')}${suite_1.OPENAI_COMPAT_PATH}`;
|
|
41
|
+
}
|
|
42
|
+
function buildCompleteRequest(request, options = {}) {
|
|
43
|
+
const stamp = (0, client_1.clientStamp)(options.client || 'cli');
|
|
44
|
+
return {
|
|
45
|
+
model: request.model,
|
|
46
|
+
messages: toProviderMessages(request.messages),
|
|
47
|
+
tools: request.tools,
|
|
48
|
+
stream: true,
|
|
49
|
+
client: stamp.client,
|
|
50
|
+
client_version: stamp.client_version,
|
|
51
|
+
client_request_id: stamp.client_request_id,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function isServerSideAgentPayload(body) {
|
|
55
|
+
if (!body || typeof body !== 'object')
|
|
56
|
+
return false;
|
|
57
|
+
const row = body;
|
|
58
|
+
return row.task_category === 'coding' && typeof row.message === 'string' && !Array.isArray(row.messages);
|
|
59
|
+
}
|
|
60
|
+
function asToolCall(raw, index) {
|
|
61
|
+
if (!raw || typeof raw !== 'object')
|
|
62
|
+
return null;
|
|
63
|
+
const row = raw;
|
|
64
|
+
const fn = row.function && typeof row.function === 'object' ? row.function : row;
|
|
65
|
+
const name = String(fn.name || row.name || '');
|
|
66
|
+
if (!name)
|
|
67
|
+
return null;
|
|
68
|
+
let args = {};
|
|
69
|
+
const rawArgs = fn.arguments ?? row.arguments ?? row.input;
|
|
70
|
+
if (typeof rawArgs === 'string') {
|
|
71
|
+
try {
|
|
72
|
+
args = JSON.parse(rawArgs);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
args = { raw: rawArgs };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (rawArgs && typeof rawArgs === 'object') {
|
|
79
|
+
args = rawArgs;
|
|
80
|
+
}
|
|
81
|
+
return { id: String(row.id || `call_${index}`), name, arguments: args };
|
|
82
|
+
}
|
|
83
|
+
function parseCompleteSse(buffer) {
|
|
84
|
+
const parsed = (0, sse_1.consumeSse)(buffer.endsWith('\n\n') ? buffer : `${buffer}\n\n`);
|
|
85
|
+
let text = '';
|
|
86
|
+
const tool_calls = [];
|
|
87
|
+
for (const event of parsed.events) {
|
|
88
|
+
if (event.type === 'error') {
|
|
89
|
+
throw new Error(typeof event.data === 'string' ? event.data : 'stream error');
|
|
90
|
+
}
|
|
91
|
+
if (event.name === 'tool_call') {
|
|
92
|
+
const mapped = asToolCall(event.data, tool_calls.length);
|
|
93
|
+
if (mapped)
|
|
94
|
+
tool_calls.push(mapped);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (event.name === 'delta' || event.type === 'text' || event.name === 'text') {
|
|
98
|
+
const chunk = (0, sse_1.sseEventText)(event);
|
|
99
|
+
if (chunk)
|
|
100
|
+
text += chunk;
|
|
101
|
+
}
|
|
102
|
+
if (event.data && typeof event.data === 'object') {
|
|
103
|
+
const row = event.data;
|
|
104
|
+
if (Array.isArray(row.tool_calls)) {
|
|
105
|
+
row.tool_calls.forEach((item, index) => {
|
|
106
|
+
const mapped = asToolCall(item, tool_calls.length + index);
|
|
107
|
+
if (mapped)
|
|
108
|
+
tool_calls.push(mapped);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (typeof row.content === 'string')
|
|
112
|
+
text += row.content;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { text, tool_calls };
|
|
116
|
+
}
|
|
117
|
+
function parseOpenAiChatCompletion(body) {
|
|
118
|
+
const row = body && typeof body === 'object' ? body : {};
|
|
119
|
+
const choices = Array.isArray(row.choices) ? row.choices : [];
|
|
120
|
+
const first = choices[0] && typeof choices[0] === 'object' ? choices[0] : {};
|
|
121
|
+
const message = first.message && typeof first.message === 'object' ? first.message : first;
|
|
122
|
+
const text = typeof message.content === 'string' ? message.content : '';
|
|
123
|
+
const rawCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
124
|
+
const tool_calls = rawCalls
|
|
125
|
+
.map((item, index) => asToolCall(item, index))
|
|
126
|
+
.filter((item) => Boolean(item));
|
|
127
|
+
return { text, tool_calls };
|
|
128
|
+
}
|
|
129
|
+
function createSisuCloudModel(http, options) {
|
|
130
|
+
return {
|
|
131
|
+
async complete(request) {
|
|
132
|
+
const payload = buildCompleteRequest(request, { client: options.client });
|
|
133
|
+
if (isServerSideAgentPayload(payload)) {
|
|
134
|
+
throw new Error('refusing server-side /api/chat/send agent payload');
|
|
135
|
+
}
|
|
136
|
+
const sent = await http(completeUrl(options.apiBase), {
|
|
137
|
+
method: 'POST',
|
|
138
|
+
headers: (0, http_1.authHeaders)(options.token),
|
|
139
|
+
body: JSON.stringify(payload),
|
|
140
|
+
});
|
|
141
|
+
if (!sent.ok) {
|
|
142
|
+
const body = await sent.json().catch(() => ({}));
|
|
143
|
+
throw new Error((0, http_1.errorDetail)(body, `complete failed (${sent.status})`));
|
|
144
|
+
}
|
|
145
|
+
const stream = sent.stream;
|
|
146
|
+
if (stream) {
|
|
147
|
+
let buffer = '';
|
|
148
|
+
for await (const chunk of stream())
|
|
149
|
+
buffer += chunk;
|
|
150
|
+
return parseCompleteSse(buffer);
|
|
151
|
+
}
|
|
152
|
+
const raw = await sent.text();
|
|
153
|
+
if (raw.trim().startsWith('{')) {
|
|
154
|
+
try {
|
|
155
|
+
return parseOpenAiChatCompletion(JSON.parse(raw));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// fall through to SSE
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return parseCompleteSse(raw);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.launchGrokBuildHeadless = exports.findGrokBuildBinary = exports.execLocalTurn = exports.createLocalRuntimeTransport = exports.resolveWorkspaceRoot = exports.localToolDefinitions = exports.dispatchLocalTool = exports.runLocalTurn = exports.createScriptedModel = exports.createLaunchStubModel = exports.collectLocalTurn = exports.toProviderMessages = exports.parseOpenAiChatCompletion = exports.parseCompleteSse = exports.isServerSideAgentPayload = exports.createSisuCloudModel = exports.completeUrl = exports.buildCompleteRequest = exports.PRODUCT_NAME = exports.PRODUCT_BIN = exports.grokBuildSuitePresent = exports.grokBuildRoot = exports.grokBuildPath = exports.assertGrokBuildSuite = void 0;
|
|
4
|
+
var suite_1 = require("./suite");
|
|
5
|
+
Object.defineProperty(exports, "assertGrokBuildSuite", { enumerable: true, get: function () { return suite_1.assertGrokBuildSuite; } });
|
|
6
|
+
Object.defineProperty(exports, "grokBuildPath", { enumerable: true, get: function () { return suite_1.grokBuildPath; } });
|
|
7
|
+
Object.defineProperty(exports, "grokBuildRoot", { enumerable: true, get: function () { return suite_1.grokBuildRoot; } });
|
|
8
|
+
Object.defineProperty(exports, "grokBuildSuitePresent", { enumerable: true, get: function () { return suite_1.grokBuildSuitePresent; } });
|
|
9
|
+
Object.defineProperty(exports, "PRODUCT_BIN", { enumerable: true, get: function () { return suite_1.PRODUCT_BIN; } });
|
|
10
|
+
Object.defineProperty(exports, "PRODUCT_NAME", { enumerable: true, get: function () { return suite_1.PRODUCT_NAME; } });
|
|
11
|
+
var adapter_1 = require("./adapter");
|
|
12
|
+
Object.defineProperty(exports, "buildCompleteRequest", { enumerable: true, get: function () { return adapter_1.buildCompleteRequest; } });
|
|
13
|
+
Object.defineProperty(exports, "completeUrl", { enumerable: true, get: function () { return adapter_1.completeUrl; } });
|
|
14
|
+
Object.defineProperty(exports, "createSisuCloudModel", { enumerable: true, get: function () { return adapter_1.createSisuCloudModel; } });
|
|
15
|
+
Object.defineProperty(exports, "isServerSideAgentPayload", { enumerable: true, get: function () { return adapter_1.isServerSideAgentPayload; } });
|
|
16
|
+
Object.defineProperty(exports, "parseCompleteSse", { enumerable: true, get: function () { return adapter_1.parseCompleteSse; } });
|
|
17
|
+
Object.defineProperty(exports, "parseOpenAiChatCompletion", { enumerable: true, get: function () { return adapter_1.parseOpenAiChatCompletion; } });
|
|
18
|
+
Object.defineProperty(exports, "toProviderMessages", { enumerable: true, get: function () { return adapter_1.toProviderMessages; } });
|
|
19
|
+
var loop_1 = require("./loop");
|
|
20
|
+
Object.defineProperty(exports, "collectLocalTurn", { enumerable: true, get: function () { return loop_1.collectLocalTurn; } });
|
|
21
|
+
Object.defineProperty(exports, "createLaunchStubModel", { enumerable: true, get: function () { return loop_1.createLaunchStubModel; } });
|
|
22
|
+
Object.defineProperty(exports, "createScriptedModel", { enumerable: true, get: function () { return loop_1.createScriptedModel; } });
|
|
23
|
+
Object.defineProperty(exports, "runLocalTurn", { enumerable: true, get: function () { return loop_1.runLocalTurn; } });
|
|
24
|
+
var tools_1 = require("./tools");
|
|
25
|
+
Object.defineProperty(exports, "dispatchLocalTool", { enumerable: true, get: function () { return tools_1.dispatchLocalTool; } });
|
|
26
|
+
Object.defineProperty(exports, "localToolDefinitions", { enumerable: true, get: function () { return tools_1.localToolDefinitions; } });
|
|
27
|
+
Object.defineProperty(exports, "resolveWorkspaceRoot", { enumerable: true, get: function () { return tools_1.resolveWorkspaceRoot; } });
|
|
28
|
+
var transport_1 = require("./transport");
|
|
29
|
+
Object.defineProperty(exports, "createLocalRuntimeTransport", { enumerable: true, get: function () { return transport_1.createLocalRuntimeTransport; } });
|
|
30
|
+
Object.defineProperty(exports, "execLocalTurn", { enumerable: true, get: function () { return transport_1.execLocalTurn; } });
|
|
31
|
+
var launch_1 = require("./launch");
|
|
32
|
+
Object.defineProperty(exports, "findGrokBuildBinary", { enumerable: true, get: function () { return launch_1.findGrokBuildBinary; } });
|
|
33
|
+
Object.defineProperty(exports, "launchGrokBuildHeadless", { enumerable: true, get: function () { return launch_1.launchGrokBuildHeadless; } });
|
|
@@ -0,0 +1,94 @@
|
|
|
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.grokBuildBinaryCandidates = grokBuildBinaryCandidates;
|
|
7
|
+
exports.findGrokBuildBinary = findGrokBuildBinary;
|
|
8
|
+
exports.sisuRuntimeApiBase = sisuRuntimeApiBase;
|
|
9
|
+
exports.writeSisuGrokConfig = writeSisuGrokConfig;
|
|
10
|
+
exports.sisuGrokBuildEnv = sisuGrokBuildEnv;
|
|
11
|
+
exports.launchGrokBuildHeadless = launchGrokBuildHeadless;
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const store_1 = require("../store");
|
|
16
|
+
const suite_1 = require("./suite");
|
|
17
|
+
const adapter_1 = require("./adapter");
|
|
18
|
+
function grokBuildBinaryCandidates() {
|
|
19
|
+
const env = (process.env.SISU_GROK_BIN || '').trim();
|
|
20
|
+
const root = (0, suite_1.grokBuildRoot)();
|
|
21
|
+
const packaged = path_1.default.resolve(__dirname, '..', 'bin', 'xai-grok-pager');
|
|
22
|
+
return [
|
|
23
|
+
env,
|
|
24
|
+
packaged,
|
|
25
|
+
path_1.default.join(root, 'target', 'release', 'xai-grok-pager'),
|
|
26
|
+
path_1.default.join(root, 'target', 'debug', 'xai-grok-pager'),
|
|
27
|
+
path_1.default.join(root, 'target', 'release', 'sisu-agent'),
|
|
28
|
+
].filter(Boolean);
|
|
29
|
+
}
|
|
30
|
+
function findGrokBuildBinary() {
|
|
31
|
+
for (const candidate of grokBuildBinaryCandidates()) {
|
|
32
|
+
if (candidate && fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile())
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function sisuRuntimeApiBase(apiBase) {
|
|
38
|
+
return (0, adapter_1.openaiCompatUrl)(apiBase).replace(/\/chat\/completions$/, '');
|
|
39
|
+
}
|
|
40
|
+
function writeSisuGrokConfig() {
|
|
41
|
+
const auth = (0, store_1.readAuth)();
|
|
42
|
+
const home = (0, store_1.getSisuHome)();
|
|
43
|
+
fs_1.default.mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
44
|
+
const file = path_1.default.join(home, 'config.toml');
|
|
45
|
+
const runtimeBase = sisuRuntimeApiBase(auth?.api_base || process.env.SISU_API_BASE || 'https://www.sisu.chat');
|
|
46
|
+
const body = [
|
|
47
|
+
'# sisu-managed grok-build config — SiSu auth + models + quota',
|
|
48
|
+
'[endpoints]',
|
|
49
|
+
`xai_api_base_url = "${runtimeBase}"`,
|
|
50
|
+
'',
|
|
51
|
+
].join('\n');
|
|
52
|
+
const existing = fs_1.default.existsSync(file) ? fs_1.default.readFileSync(file, 'utf8') : '';
|
|
53
|
+
if (!existing || existing.includes('sisu-managed grok-build')) {
|
|
54
|
+
fs_1.default.writeFileSync(file, `${body}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
55
|
+
}
|
|
56
|
+
return file;
|
|
57
|
+
}
|
|
58
|
+
function sisuGrokBuildEnv() {
|
|
59
|
+
const auth = (0, store_1.readAuth)();
|
|
60
|
+
const home = (0, store_1.getSisuHome)();
|
|
61
|
+
const runtimeBase = auth ? sisuRuntimeApiBase(auth.api_base) : '';
|
|
62
|
+
return {
|
|
63
|
+
...process.env,
|
|
64
|
+
GROK_HOME: process.env.GROK_HOME || home,
|
|
65
|
+
SISU_HOME: home,
|
|
66
|
+
GROK_TELEMETRY_ENABLED: process.env.GROK_TELEMETRY_ENABLED || '0',
|
|
67
|
+
XAI_API_KEY: process.env.XAI_API_KEY || auth?.token || '',
|
|
68
|
+
SISU_API_BASE: auth?.api_base || process.env.SISU_API_BASE || 'https://www.sisu.chat',
|
|
69
|
+
...(runtimeBase
|
|
70
|
+
? {
|
|
71
|
+
GROK_XAI_API_BASE_URL: runtimeBase,
|
|
72
|
+
XAI_API_BASE_URL: runtimeBase,
|
|
73
|
+
}
|
|
74
|
+
: {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function launchGrokBuildHeadless(prompt, cwd) {
|
|
78
|
+
const binary = findGrokBuildBinary();
|
|
79
|
+
if (!binary) {
|
|
80
|
+
return { status: 127, stdout: '', stderr: 'grok-build binary not built', binary: null };
|
|
81
|
+
}
|
|
82
|
+
const result = (0, child_process_1.spawnSync)(binary, ['-p', prompt], {
|
|
83
|
+
cwd,
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
env: sisuGrokBuildEnv(),
|
|
86
|
+
timeout: 30_000,
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
status: result.status ?? 1,
|
|
90
|
+
stdout: result.stdout || '',
|
|
91
|
+
stderr: result.stderr || result.error?.message || '',
|
|
92
|
+
binary,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runLocalTurn = runLocalTurn;
|
|
4
|
+
exports.collectLocalTurn = collectLocalTurn;
|
|
5
|
+
exports.createLaunchStubModel = createLaunchStubModel;
|
|
6
|
+
exports.createScriptedModel = createScriptedModel;
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const tools_1 = require("./tools");
|
|
9
|
+
const DEFAULT_MAX_ROUNDS = 8;
|
|
10
|
+
async function* runLocalTurn(options) {
|
|
11
|
+
const prompt = options.prompt.trim();
|
|
12
|
+
if (!prompt)
|
|
13
|
+
throw new Error('prompt is required');
|
|
14
|
+
const cwd = (0, tools_1.resolveWorkspaceRoot)(options.cwd);
|
|
15
|
+
const conversationId = options.conversationId || (0, crypto_1.randomUUID)();
|
|
16
|
+
yield { type: 'bound', text: conversationId };
|
|
17
|
+
const messages = [...(options.messages || []), { role: 'user', content: prompt }];
|
|
18
|
+
const tools = (0, tools_1.localToolDefinitions)();
|
|
19
|
+
const toolResults = [];
|
|
20
|
+
const requests = [];
|
|
21
|
+
let text = '';
|
|
22
|
+
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
23
|
+
for (let round = 0; round < maxRounds; round += 1) {
|
|
24
|
+
const request = { model: options.model, messages: messages.map((row) => ({ ...row })), tools };
|
|
25
|
+
requests.push(request);
|
|
26
|
+
const completion = await options.client.complete(request);
|
|
27
|
+
if (completion.text) {
|
|
28
|
+
text += completion.text;
|
|
29
|
+
yield { type: 'text', text: completion.text };
|
|
30
|
+
}
|
|
31
|
+
if (!completion.tool_calls.length) {
|
|
32
|
+
return { conversationId, text, toolResults, requests };
|
|
33
|
+
}
|
|
34
|
+
messages.push({
|
|
35
|
+
role: 'assistant',
|
|
36
|
+
content: completion.text || '',
|
|
37
|
+
tool_calls: completion.tool_calls,
|
|
38
|
+
});
|
|
39
|
+
for (const call of completion.tool_calls) {
|
|
40
|
+
const result = (0, tools_1.dispatchLocalTool)(cwd, {
|
|
41
|
+
...call,
|
|
42
|
+
id: call.id || (0, crypto_1.randomUUID)(),
|
|
43
|
+
arguments: call.arguments || {},
|
|
44
|
+
});
|
|
45
|
+
toolResults.push(result);
|
|
46
|
+
yield { type: 'tool', text: `${result.name} · ${result.ok ? 'ok' : 'error'} · ${result.content}` };
|
|
47
|
+
messages.push({
|
|
48
|
+
role: 'tool',
|
|
49
|
+
name: result.name,
|
|
50
|
+
tool_call_id: result.id,
|
|
51
|
+
content: result.content,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
yield { type: 'status', text: `stopped after ${maxRounds} tool rounds` };
|
|
56
|
+
return { conversationId, text, toolResults, requests };
|
|
57
|
+
}
|
|
58
|
+
async function collectLocalTurn(options) {
|
|
59
|
+
const gen = runLocalTurn(options);
|
|
60
|
+
let step = await gen.next();
|
|
61
|
+
while (!step.done)
|
|
62
|
+
step = await gen.next();
|
|
63
|
+
return step.value;
|
|
64
|
+
}
|
|
65
|
+
function createLaunchStubModel() {
|
|
66
|
+
let step = 0;
|
|
67
|
+
return {
|
|
68
|
+
async complete(request) {
|
|
69
|
+
step += 1;
|
|
70
|
+
if (step === 1) {
|
|
71
|
+
return {
|
|
72
|
+
text: '',
|
|
73
|
+
tool_calls: [{ id: 'stub-read', name: 'read_file', arguments: { target_file: 'hello.txt' } }],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const lastTool = [...request.messages].reverse().find((row) => row.role === 'tool');
|
|
77
|
+
return { text: `local tool result:\n${lastTool?.content || ''}`, tool_calls: [] };
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createScriptedModel(script) {
|
|
82
|
+
const remaining = [...script];
|
|
83
|
+
return {
|
|
84
|
+
async complete() {
|
|
85
|
+
const next = remaining.shift();
|
|
86
|
+
if (!next)
|
|
87
|
+
return { text: '', tool_calls: [] };
|
|
88
|
+
return { text: next.text || '', tool_calls: next.tool_calls || [] };
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchModelCatalog = fetchModelCatalog;
|
|
4
|
+
exports.resolveCatalogModel = resolveCatalogModel;
|
|
5
|
+
exports.resolveRuntimeModel = resolveRuntimeModel;
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
const store_1 = require("../store");
|
|
8
|
+
function normalizeModelKey(value) {
|
|
9
|
+
return value.toLowerCase().replace(/[-_.\s]/g, '');
|
|
10
|
+
}
|
|
11
|
+
async function fetchModelCatalog(http = http_1.defaultHttp) {
|
|
12
|
+
const auth = (0, store_1.requireAuth)();
|
|
13
|
+
const response = await http(`${auth.api_base}/api/chat/models`, { headers: (0, http_1.authHeaders)(auth.token) });
|
|
14
|
+
const body = await response.json().catch(() => ({}));
|
|
15
|
+
if (!response.ok)
|
|
16
|
+
throw new Error((0, http_1.errorDetail)(body, `models failed (${response.status})`));
|
|
17
|
+
const rows = Array.isArray(body?.models) ? body.models : [];
|
|
18
|
+
const models = rows
|
|
19
|
+
.map((row) => {
|
|
20
|
+
const name = String(row?.name || '').trim();
|
|
21
|
+
if (!name)
|
|
22
|
+
return null;
|
|
23
|
+
return { name, label: String(row.display_name || row.label || name) };
|
|
24
|
+
})
|
|
25
|
+
.filter((row) => Boolean(row));
|
|
26
|
+
return { models, defaultModel: String(body?.default_model || '') };
|
|
27
|
+
}
|
|
28
|
+
function resolveCatalogModel(query, models) {
|
|
29
|
+
const needle = normalizeModelKey(query);
|
|
30
|
+
if (!needle)
|
|
31
|
+
return undefined;
|
|
32
|
+
return (models.find((row) => normalizeModelKey(row.name) === needle) ||
|
|
33
|
+
models.find((row) => normalizeModelKey(row.label) === needle) ||
|
|
34
|
+
models.find((row) => normalizeModelKey(row.name).includes(needle) || normalizeModelKey(row.label).includes(needle)));
|
|
35
|
+
}
|
|
36
|
+
async function resolveRuntimeModel(http, options = {}) {
|
|
37
|
+
if (options.stub)
|
|
38
|
+
return (options.explicit || '').trim() || 'stub';
|
|
39
|
+
const wanted = (options.explicit || '').trim();
|
|
40
|
+
const { models, defaultModel } = await fetchModelCatalog(http);
|
|
41
|
+
if (wanted) {
|
|
42
|
+
const match = resolveCatalogModel(wanted, models);
|
|
43
|
+
if (!match)
|
|
44
|
+
throw new Error(`unknown model ${wanted}`);
|
|
45
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_model: match.name });
|
|
46
|
+
return match.name;
|
|
47
|
+
}
|
|
48
|
+
const last = ((0, store_1.readSession)().last_model || '').trim();
|
|
49
|
+
if (last && models.some((row) => row.name === last))
|
|
50
|
+
return last;
|
|
51
|
+
const name = defaultModel || models[0]?.name || '';
|
|
52
|
+
if (!name)
|
|
53
|
+
throw new Error('no SiSu model available');
|
|
54
|
+
(0, store_1.writeSession)({ ...(0, store_1.readSession)(), last_model: name });
|
|
55
|
+
return name;
|
|
56
|
+
}
|