@glbgpt/cli 0.1.0-test.1
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 +111 -0
- package/dist/src/auth/authClient.d.ts +32 -0
- package/dist/src/auth/authClient.js +88 -0
- package/dist/src/auth/authClient.js.map +1 -0
- package/dist/src/auth/credentialStore.d.ts +22 -0
- package/dist/src/auth/credentialStore.js +51 -0
- package/dist/src/auth/credentialStore.js.map +1 -0
- package/dist/src/auth/loginFlow.d.ts +30 -0
- package/dist/src/auth/loginFlow.js +43 -0
- package/dist/src/auth/loginFlow.js.map +1 -0
- package/dist/src/auth/loopbackServer.d.ts +41 -0
- package/dist/src/auth/loopbackServer.js +141 -0
- package/dist/src/auth/loopbackServer.js.map +1 -0
- package/dist/src/auth/token.d.ts +8 -0
- package/dist/src/auth/token.js +15 -0
- package/dist/src/auth/token.js.map +1 -0
- package/dist/src/commands/accountCommand.d.ts +2 -0
- package/dist/src/commands/accountCommand.js +56 -0
- package/dist/src/commands/accountCommand.js.map +1 -0
- package/dist/src/commands/configCommand.d.ts +4 -0
- package/dist/src/commands/configCommand.js +53 -0
- package/dist/src/commands/configCommand.js.map +1 -0
- package/dist/src/commands/execCommand.d.ts +32 -0
- package/dist/src/commands/execCommand.js +122 -0
- package/dist/src/commands/execCommand.js.map +1 -0
- package/dist/src/commands/loginCommand.d.ts +11 -0
- package/dist/src/commands/loginCommand.js +96 -0
- package/dist/src/commands/loginCommand.js.map +1 -0
- package/dist/src/commands/mediaCommand.d.ts +41 -0
- package/dist/src/commands/mediaCommand.js +277 -0
- package/dist/src/commands/mediaCommand.js.map +1 -0
- package/dist/src/config/configFile.d.ts +30 -0
- package/dist/src/config/configFile.js +54 -0
- package/dist/src/config/configFile.js.map +1 -0
- package/dist/src/config/env.d.ts +6 -0
- package/dist/src/config/env.js +24 -0
- package/dist/src/config/env.js.map +1 -0
- package/dist/src/config/paths.d.ts +6 -0
- package/dist/src/config/paths.js +16 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/core/catalog.d.ts +49 -0
- package/dist/src/core/catalog.js +96 -0
- package/dist/src/core/catalog.js.map +1 -0
- package/dist/src/core/httpChatBackend.d.ts +31 -0
- package/dist/src/core/httpChatBackend.js +134 -0
- package/dist/src/core/httpChatBackend.js.map +1 -0
- package/dist/src/core/id.d.ts +2 -0
- package/dist/src/core/id.js +5 -0
- package/dist/src/core/id.js.map +1 -0
- package/dist/src/core/runChat.d.ts +28 -0
- package/dist/src/core/runChat.js +49 -0
- package/dist/src/core/runChat.js.map +1 -0
- package/dist/src/core/session.d.ts +44 -0
- package/dist/src/core/session.js +70 -0
- package/dist/src/core/session.js.map +1 -0
- package/dist/src/core/sessionStore.d.ts +39 -0
- package/dist/src/core/sessionStore.js +106 -0
- package/dist/src/core/sessionStore.js.map +1 -0
- package/dist/src/core/toolRegistry.d.ts +19 -0
- package/dist/src/core/toolRegistry.js +53 -0
- package/dist/src/core/toolRegistry.js.map +1 -0
- package/dist/src/core/tools/generateImage.d.ts +10 -0
- package/dist/src/core/tools/generateImage.js +34 -0
- package/dist/src/core/tools/generateImage.js.map +1 -0
- package/dist/src/core/turnLoop.d.ts +31 -0
- package/dist/src/core/turnLoop.js +155 -0
- package/dist/src/core/turnLoop.js.map +1 -0
- package/dist/src/core/types.d.ts +137 -0
- package/dist/src/core/types.js +9 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/exec/input.d.ts +35 -0
- package/dist/src/exec/input.js +85 -0
- package/dist/src/exec/input.js.map +1 -0
- package/dist/src/exec/output.d.ts +39 -0
- package/dist/src/exec/output.js +84 -0
- package/dist/src/exec/output.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +253 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/server.d.ts +38 -0
- package/dist/src/mcp/server.js +99 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/media/aiApiClient.d.ts +53 -0
- package/dist/src/media/aiApiClient.js +97 -0
- package/dist/src/media/aiApiClient.js.map +1 -0
- package/dist/src/media/errors.d.ts +7 -0
- package/dist/src/media/errors.js +70 -0
- package/dist/src/media/errors.js.map +1 -0
- package/dist/src/media/executor.d.ts +50 -0
- package/dist/src/media/executor.js +55 -0
- package/dist/src/media/executor.js.map +1 -0
- package/dist/src/media/generateImageTool.d.ts +19 -0
- package/dist/src/media/generateImageTool.js +120 -0
- package/dist/src/media/generateImageTool.js.map +1 -0
- package/dist/src/media/manifest.d.ts +23 -0
- package/dist/src/media/manifest.js +558 -0
- package/dist/src/media/manifest.js.map +1 -0
- package/dist/src/media/resolution.d.ts +13 -0
- package/dist/src/media/resolution.js +42 -0
- package/dist/src/media/resolution.js.map +1 -0
- package/dist/src/media/taskLedger.d.ts +17 -0
- package/dist/src/media/taskLedger.js +44 -0
- package/dist/src/media/taskLedger.js.map +1 -0
- package/dist/src/media/taskManager.d.ts +52 -0
- package/dist/src/media/taskManager.js +130 -0
- package/dist/src/media/taskManager.js.map +1 -0
- package/dist/src/media/types.d.ts +80 -0
- package/dist/src/media/types.js +15 -0
- package/dist/src/media/types.js.map +1 -0
- package/dist/src/tui/App.d.ts +34 -0
- package/dist/src/tui/App.js +540 -0
- package/dist/src/tui/App.js.map +1 -0
- package/dist/src/tui/MultilineInput.d.ts +36 -0
- package/dist/src/tui/MultilineInput.js +221 -0
- package/dist/src/tui/MultilineInput.js.map +1 -0
- package/dist/src/tui/atPopup.d.ts +21 -0
- package/dist/src/tui/atPopup.js +55 -0
- package/dist/src/tui/atPopup.js.map +1 -0
- package/dist/src/tui/attachments.d.ts +24 -0
- package/dist/src/tui/attachments.js +91 -0
- package/dist/src/tui/attachments.js.map +1 -0
- package/dist/src/tui/branding.d.ts +22 -0
- package/dist/src/tui/branding.js +119 -0
- package/dist/src/tui/branding.js.map +1 -0
- package/dist/src/tui/chatController.d.ts +188 -0
- package/dist/src/tui/chatController.js +614 -0
- package/dist/src/tui/chatController.js.map +1 -0
- package/dist/src/tui/commands.d.ts +13 -0
- package/dist/src/tui/commands.js +22 -0
- package/dist/src/tui/commands.js.map +1 -0
- package/dist/src/tui/confirmationBridge.d.ts +23 -0
- package/dist/src/tui/confirmationBridge.js +47 -0
- package/dist/src/tui/confirmationBridge.js.map +1 -0
- package/dist/src/tui/inputHistory.d.ts +7 -0
- package/dist/src/tui/inputHistory.js +64 -0
- package/dist/src/tui/inputHistory.js.map +1 -0
- package/dist/src/tui/markdown.d.ts +1 -0
- package/dist/src/tui/markdown.js +143 -0
- package/dist/src/tui/markdown.js.map +1 -0
- package/dist/src/tui/models.d.ts +30 -0
- package/dist/src/tui/models.js +54 -0
- package/dist/src/tui/models.js.map +1 -0
- package/dist/src/tui/run.d.ts +29 -0
- package/dist/src/tui/run.js +212 -0
- package/dist/src/tui/run.js.map +1 -0
- package/dist/src/tui/slashMedia.d.ts +36 -0
- package/dist/src/tui/slashMedia.js +86 -0
- package/dist/src/tui/slashMedia.js.map +1 -0
- package/dist/src/tui/visualLines.d.ts +38 -0
- package/dist/src/tui/visualLines.js +110 -0
- package/dist/src/tui/visualLines.js.map +1 -0
- package/dist/src/util/clipboardImage.d.ts +6 -0
- package/dist/src/util/clipboardImage.js +69 -0
- package/dist/src/util/clipboardImage.js.map +1 -0
- package/dist/src/util/hyperlink.d.ts +10 -0
- package/dist/src/util/hyperlink.js +33 -0
- package/dist/src/util/hyperlink.js.map +1 -0
- package/dist/src/util/imageRef.d.ts +15 -0
- package/dist/src/util/imageRef.js +46 -0
- package/dist/src/util/imageRef.js.map +1 -0
- package/dist/src/util/inlineImage.d.ts +13 -0
- package/dist/src/util/inlineImage.js +56 -0
- package/dist/src/util/inlineImage.js.map +1 -0
- package/dist/src/util/openBrowser.d.ts +5 -0
- package/dist/src/util/openBrowser.js +23 -0
- package/dist/src/util/openBrowser.js.map +1 -0
- package/dist/src/util/retry.d.ts +43 -0
- package/dist/src/util/retry.js +116 -0
- package/dist/src/util/retry.js.map +1 -0
- package/dist/src/version.d.ts +6 -0
- package/dist/src/version.js +16 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { AiApiClient, PaymentRequiredError } from '../media/aiApiClient.js';
|
|
2
|
+
import { describePaymentRequired, describeNotSellable, describeTaskFailure } from '../media/errors.js';
|
|
3
|
+
import { AuthClient } from '../auth/authClient.js';
|
|
4
|
+
import { GenerationExecutor } from '../media/executor.js';
|
|
5
|
+
import { TaskManager } from '../media/taskManager.js';
|
|
6
|
+
import { TaskLedger } from '../media/taskLedger.js';
|
|
7
|
+
import { availableModels, findModel, isModelDisabled, DEFAULT_IMAGE_MODEL, DEFAULT_VIDEO_MODEL } from '../media/manifest.js';
|
|
8
|
+
import { getChatModels } from '../tui/models.js';
|
|
9
|
+
import { maybePrintInlinePreview } from '../util/inlineImage.js';
|
|
10
|
+
import { resolveToken } from '../auth/token.js';
|
|
11
|
+
import { apiBaseUrl } from '../config/env.js';
|
|
12
|
+
import { ensureCatalog } from '../core/catalog.js';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
/** Prints the estimate to stderr and auto-approves (command = authorization). */
|
|
15
|
+
class PrintingConfirmer {
|
|
16
|
+
async confirm(info) {
|
|
17
|
+
console.error(`Generating with ${info.displayName} · ~${info.credits} credits`);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function requireToken() {
|
|
22
|
+
const token = await resolveToken();
|
|
23
|
+
await ensureCatalog({ baseUrl: apiBaseUrl(), token });
|
|
24
|
+
if (!token) {
|
|
25
|
+
console.error('Not signed in. Run `glbgpt login` first (or set GLBGPT_TOKEN).');
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return token;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the media prompt from positional words or stdin (contract §6):
|
|
33
|
+
* a positional prompt wins; `-` forces reading stdin; with no positional and piped
|
|
34
|
+
* stdin, stdin is the prompt. Returns null when no prompt is available.
|
|
35
|
+
*/
|
|
36
|
+
async function resolveMediaPrompt(promptWords) {
|
|
37
|
+
const joined = promptWords.join(' ').trim();
|
|
38
|
+
if (joined && joined !== '-')
|
|
39
|
+
return joined;
|
|
40
|
+
if (joined !== '-' && process.stdin.isTTY)
|
|
41
|
+
return null;
|
|
42
|
+
const chunks = [];
|
|
43
|
+
for await (const chunk of process.stdin)
|
|
44
|
+
chunks.push(Buffer.from(chunk));
|
|
45
|
+
return Buffer.concat(chunks).toString('utf8').trim() || null;
|
|
46
|
+
}
|
|
47
|
+
export async function runImage(promptWords, opts) {
|
|
48
|
+
const prompt = await resolveMediaPrompt(promptWords);
|
|
49
|
+
if (!prompt) {
|
|
50
|
+
console.error('A prompt is required (argument, or piped via stdin). Example: glbgpt image "a cat"');
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await generate(prompt, opts.model ?? DEFAULT_IMAGE_MODEL, {
|
|
55
|
+
prompt,
|
|
56
|
+
aspectRatio: opts.aspectRatio,
|
|
57
|
+
resolution: opts.resolution,
|
|
58
|
+
images: opts.image,
|
|
59
|
+
n: opts.count,
|
|
60
|
+
seed: opts.seed,
|
|
61
|
+
negativePrompt: opts.negativePrompt,
|
|
62
|
+
}, opts);
|
|
63
|
+
}
|
|
64
|
+
export async function runVideo(promptWords, opts) {
|
|
65
|
+
const prompt = await resolveMediaPrompt(promptWords);
|
|
66
|
+
if (!prompt) {
|
|
67
|
+
console.error('A prompt is required (argument, or piped via stdin). Example: glbgpt video "waves on a beach"');
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
await generate(prompt, opts.model ?? DEFAULT_VIDEO_MODEL, {
|
|
72
|
+
prompt,
|
|
73
|
+
aspectRatio: opts.aspectRatio,
|
|
74
|
+
resolution: opts.resolution,
|
|
75
|
+
duration: opts.duration,
|
|
76
|
+
generateAudio: opts.audio,
|
|
77
|
+
images: opts.image,
|
|
78
|
+
startImage: opts.startImage,
|
|
79
|
+
endImage: opts.endImage,
|
|
80
|
+
}, opts);
|
|
81
|
+
}
|
|
82
|
+
/** Shared: estimate (or --estimate), submit, then block on the task and download. */
|
|
83
|
+
async function generate(prompt, modelId, params, opts) {
|
|
84
|
+
const token = await requireToken();
|
|
85
|
+
if (!token)
|
|
86
|
+
return;
|
|
87
|
+
const client = new AiApiClient({ baseUrl: apiBaseUrl(), token });
|
|
88
|
+
const executor = new GenerationExecutor(client, new PrintingConfirmer());
|
|
89
|
+
try {
|
|
90
|
+
if (opts.estimate) {
|
|
91
|
+
const est = await executor.estimate(modelId, params);
|
|
92
|
+
if (!est.sellable) {
|
|
93
|
+
console.error(describeNotSellable(est.reasonCode));
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log(`Estimated: ~${est.credits} credits (${est.spec.displayName})`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const outcome = await executor.execute(modelId, params);
|
|
101
|
+
if (outcome.status === 'not_sellable') {
|
|
102
|
+
console.error(describeNotSellable(outcome.reasonCode));
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (outcome.status === 'cancelled') {
|
|
107
|
+
console.error('Cancelled.');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const outputDir = opts.out ?? path.join(process.cwd(), 'glbgpt-output');
|
|
111
|
+
await new TaskLedger().append({ taskId: outcome.taskId, model: outcome.model, prompt, createdAt: Date.now() });
|
|
112
|
+
console.error(`Submitted task ${outcome.taskId}.`);
|
|
113
|
+
if (opts.detach) {
|
|
114
|
+
console.log(outcome.taskId);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const tm = new TaskManager(client, { outputDir });
|
|
118
|
+
tm.subscribe((e) => logProgress(e));
|
|
119
|
+
const result = await tm.register(outcome.taskId, { model: outcome.model, prompt });
|
|
120
|
+
if (result.type === 'task_done') {
|
|
121
|
+
console.log(result.localPath);
|
|
122
|
+
await maybePrintInlinePreview(result.localPath);
|
|
123
|
+
const balance = await currentBalance(token);
|
|
124
|
+
console.error(`Done · ~${outcome.credits} credits${balance ? ` · balance: ${balance}` : ''}`);
|
|
125
|
+
}
|
|
126
|
+
else if (result.type === 'task_failed') {
|
|
127
|
+
console.error(`Failed: ${result.reason}`);
|
|
128
|
+
console.error('Failed generations are not charged.');
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
console.error(err instanceof PaymentRequiredError ? describePaymentRequired(err) : err instanceof Error ? err.message : String(err));
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export async function runModelList(type) {
|
|
138
|
+
// Informational command: wait for the (10s-bounded) refresh so the list is current.
|
|
139
|
+
await (await ensureCatalog({ baseUrl: apiBaseUrl(), token: await resolveToken() })).refreshed;
|
|
140
|
+
if (type && !['chat', 'image', 'video'].includes(type)) {
|
|
141
|
+
console.error(`Unknown model type "${type}". Use: chat, image, or video.`);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!type || type === 'chat') {
|
|
146
|
+
for (const m of getChatModels()) {
|
|
147
|
+
console.log(`${m.id} — ${m.label} (chat)`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const m of availableModels().filter((m) => !type || m.type === type)) {
|
|
151
|
+
console.log(`${m.id} — ${m.displayName} (${m.type})`);
|
|
152
|
+
}
|
|
153
|
+
console.error('\nRun `glbgpt model show <model>` for parameters and limits.');
|
|
154
|
+
}
|
|
155
|
+
export async function runModelShow(id) {
|
|
156
|
+
// Informational command: wait for the (10s-bounded) refresh so the list is current.
|
|
157
|
+
await (await ensureCatalog({ baseUrl: apiBaseUrl(), token: await resolveToken() })).refreshed;
|
|
158
|
+
const spec = findModel(id);
|
|
159
|
+
if (!spec) {
|
|
160
|
+
console.error(`Unknown model "${id}". Available: ${availableModels().map((m) => m.id).join(', ')}.`);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (isModelDisabled(id)) {
|
|
165
|
+
console.error(`Note: "${id}" is temporarily unavailable (disabled server-side).`);
|
|
166
|
+
}
|
|
167
|
+
const s = spec.support;
|
|
168
|
+
console.log(`${spec.displayName} (${spec.id}) · ${spec.type}`);
|
|
169
|
+
if (s.aspectRatios)
|
|
170
|
+
console.log(` aspect ratio: ${s.aspectRatios.join(', ')}`);
|
|
171
|
+
if (s.resolutions)
|
|
172
|
+
console.log(` resolution: ${s.resolutions.join(', ')}${s.defaultResolution ? ` (default ${s.defaultResolution})` : ''}`);
|
|
173
|
+
if (s.durations)
|
|
174
|
+
console.log(` duration (s): ${s.durations.join(', ')}${s.defaultDuration ? ` (default ${s.defaultDuration})` : ''}`);
|
|
175
|
+
if (s.supportsAudio)
|
|
176
|
+
console.log(` audio: --audio / --no-audio`);
|
|
177
|
+
if (s.maxCount && s.maxCount > 1)
|
|
178
|
+
console.log(` count: 1..${s.maxCount}`);
|
|
179
|
+
if (s.maxImages)
|
|
180
|
+
console.log(` reference img: up to ${s.maxImages}`);
|
|
181
|
+
}
|
|
182
|
+
export async function runTaskList() {
|
|
183
|
+
const records = await new TaskLedger().list();
|
|
184
|
+
if (records.length === 0) {
|
|
185
|
+
console.error('No tasks yet.');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const r of records) {
|
|
189
|
+
console.log(`${r.taskId} ${r.model} ${truncate(r.prompt, 40)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export async function runTaskShow(idOrLast) {
|
|
193
|
+
const token = await requireToken();
|
|
194
|
+
if (!token)
|
|
195
|
+
return;
|
|
196
|
+
const rec = await new TaskLedger().resolve(idOrLast);
|
|
197
|
+
const taskId = rec?.taskId ?? idOrLast;
|
|
198
|
+
const info = await new AiApiClient({ baseUrl: apiBaseUrl(), token }).getTask(taskId);
|
|
199
|
+
console.log(`${taskId} status: ${info.status}${info.outputFileUrl ? `\n ${info.outputFileUrl}` : ''}${info.failedReason ? `\n reason: ${info.failedReason}` : ''}`);
|
|
200
|
+
}
|
|
201
|
+
/** `task wait <id|last>`: poll until the task reaches a terminal state, without downloading. */
|
|
202
|
+
export async function runTaskWait(idOrLast, timeoutMs = 12 * 60_000) {
|
|
203
|
+
const token = await requireToken();
|
|
204
|
+
if (!token)
|
|
205
|
+
return;
|
|
206
|
+
const rec = await new TaskLedger().resolve(idOrLast);
|
|
207
|
+
const taskId = rec?.taskId ?? idOrLast;
|
|
208
|
+
if (idOrLast === 'last')
|
|
209
|
+
console.error(`Waiting for last task: ${taskId}`);
|
|
210
|
+
const client = new AiApiClient({ baseUrl: apiBaseUrl(), token });
|
|
211
|
+
const start = Date.now();
|
|
212
|
+
for (;;) {
|
|
213
|
+
const info = await client.getTask(taskId);
|
|
214
|
+
if (info.status === 'success') {
|
|
215
|
+
process.stderr.write('\r');
|
|
216
|
+
console.log(`${taskId} status: success${info.outputFileUrl ? `\n ${info.outputFileUrl}` : ''}`);
|
|
217
|
+
console.error('Run `glbgpt task download ' + taskId + '` to save the result.');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (info.status === 'failed') {
|
|
221
|
+
process.stderr.write('\r');
|
|
222
|
+
console.error(`Failed: ${describeTaskFailure(info.errorCode, info.failedReason)}`);
|
|
223
|
+
process.exitCode = 1;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const elapsedMs = Date.now() - start;
|
|
227
|
+
if (elapsedMs >= timeoutMs) {
|
|
228
|
+
process.stderr.write('\r');
|
|
229
|
+
console.error('Timed out waiting for the task (it may still finish; check `glbgpt task show`).');
|
|
230
|
+
process.exitCode = 1;
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
process.stderr.write(`\r… ${info.status} ${Math.round(elapsedMs / 1000)}s`);
|
|
234
|
+
await new Promise((r) => setTimeout(r, 2500));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
export async function runTaskDownload(idOrLast, out) {
|
|
238
|
+
const token = await requireToken();
|
|
239
|
+
if (!token)
|
|
240
|
+
return;
|
|
241
|
+
const rec = await new TaskLedger().resolve(idOrLast);
|
|
242
|
+
const taskId = rec?.taskId ?? idOrLast;
|
|
243
|
+
const client = new AiApiClient({ baseUrl: apiBaseUrl(), token });
|
|
244
|
+
const info = await client.getTask(taskId);
|
|
245
|
+
if (info.status !== 'success' || !info.outputFileUrl) {
|
|
246
|
+
console.error(`Task ${taskId} is not ready (status: ${info.status}).`);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const tm = new TaskManager(client, { outputDir: out ?? path.join(process.cwd(), 'glbgpt-output') });
|
|
251
|
+
const result = await tm.register(taskId, { model: rec?.model });
|
|
252
|
+
if (result.type === 'task_done') {
|
|
253
|
+
console.log(result.localPath);
|
|
254
|
+
await maybePrintInlinePreview(result.localPath);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function logProgress(e) {
|
|
258
|
+
if (e.type === 'task_progress') {
|
|
259
|
+
process.stderr.write(`\r… ${e.status} ${Math.round(e.elapsedMs / 1000)}s`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
process.stderr.write('\r');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function currentBalance(token) {
|
|
266
|
+
try {
|
|
267
|
+
const info = await new AuthClient({ baseUrl: apiBaseUrl() }).userInfo(token);
|
|
268
|
+
return info.balance !== undefined ? String(info.balance) : undefined;
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function truncate(s, n) {
|
|
275
|
+
return s.length <= n ? s : s.slice(0, n - 1) + '…';
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=mediaCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaCommand.js","sourceRoot":"","sources":["../../../src/commands/mediaCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent CLI configuration at ~/.config/glbgpt/config.json.
|
|
3
|
+
*
|
|
4
|
+
* Precedence when a value is read (env.ts): explicit env var > this file > built-in default.
|
|
5
|
+
* The file holds only known keys; unknown keys are rejected at `config set` time so typos
|
|
6
|
+
* surface immediately instead of silently doing nothing.
|
|
7
|
+
*/
|
|
8
|
+
/** The configurable keys, their env-var overrides, and what they do. */
|
|
9
|
+
export declare const CONFIG_KEYS: {
|
|
10
|
+
readonly 'api-base-url': {
|
|
11
|
+
readonly env: "GLBGPT_BASE_URL";
|
|
12
|
+
readonly help: "API host serving /ai-api/* and /user-api/*";
|
|
13
|
+
};
|
|
14
|
+
readonly 'web-base-url': {
|
|
15
|
+
readonly env: "GLBGPT_WEB_URL";
|
|
16
|
+
readonly help: "Web host serving the /cli-auth page";
|
|
17
|
+
};
|
|
18
|
+
readonly lang: {
|
|
19
|
+
readonly env: "GLBGPT_LANG";
|
|
20
|
+
readonly help: "UI language segment used in web URLs";
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type ConfigKey = keyof typeof CONFIG_KEYS;
|
|
24
|
+
export declare function isConfigKey(key: string): key is ConfigKey;
|
|
25
|
+
/** Read the whole config file (missing/corrupt → empty). */
|
|
26
|
+
export declare function readConfigSync(): Partial<Record<ConfigKey, string>>;
|
|
27
|
+
/** One stored value (undefined when unset). */
|
|
28
|
+
export declare function getConfigValueSync(key: ConfigKey): string | undefined;
|
|
29
|
+
/** Persist one value (set) or remove it (value === undefined). */
|
|
30
|
+
export declare function writeConfigValue(key: ConfigKey, value: string | undefined): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { configDir } from './paths.js';
|
|
5
|
+
/**
|
|
6
|
+
* Persistent CLI configuration at ~/.config/glbgpt/config.json.
|
|
7
|
+
*
|
|
8
|
+
* Precedence when a value is read (env.ts): explicit env var > this file > built-in default.
|
|
9
|
+
* The file holds only known keys; unknown keys are rejected at `config set` time so typos
|
|
10
|
+
* surface immediately instead of silently doing nothing.
|
|
11
|
+
*/
|
|
12
|
+
/** The configurable keys, their env-var overrides, and what they do. */
|
|
13
|
+
export const CONFIG_KEYS = {
|
|
14
|
+
'api-base-url': { env: 'GLBGPT_BASE_URL', help: 'API host serving /ai-api/* and /user-api/*' },
|
|
15
|
+
'web-base-url': { env: 'GLBGPT_WEB_URL', help: 'Web host serving the /cli-auth page' },
|
|
16
|
+
lang: { env: 'GLBGPT_LANG', help: 'UI language segment used in web URLs' },
|
|
17
|
+
};
|
|
18
|
+
export function isConfigKey(key) {
|
|
19
|
+
return Object.prototype.hasOwnProperty.call(CONFIG_KEYS, key);
|
|
20
|
+
}
|
|
21
|
+
function file() {
|
|
22
|
+
return path.join(configDir(), 'config.json');
|
|
23
|
+
}
|
|
24
|
+
/** Read the whole config file (missing/corrupt → empty). */
|
|
25
|
+
export function readConfigSync() {
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(file(), 'utf8');
|
|
28
|
+
const parsed = JSON.parse(raw);
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const key of Object.keys(CONFIG_KEYS)) {
|
|
31
|
+
if (typeof parsed[key] === 'string')
|
|
32
|
+
out[key] = parsed[key];
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** One stored value (undefined when unset). */
|
|
41
|
+
export function getConfigValueSync(key) {
|
|
42
|
+
return readConfigSync()[key];
|
|
43
|
+
}
|
|
44
|
+
/** Persist one value (set) or remove it (value === undefined). */
|
|
45
|
+
export async function writeConfigValue(key, value) {
|
|
46
|
+
const current = readConfigSync();
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
delete current[key];
|
|
49
|
+
else
|
|
50
|
+
current[key] = value;
|
|
51
|
+
await fs.mkdir(configDir(), { recursive: true });
|
|
52
|
+
await fs.writeFile(file(), JSON.stringify(current, null, 2) + '\n', 'utf8');
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=configFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configFile.js","sourceRoot":"","sources":["../../../src/config/configFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,cAAc,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,4CAA4C,EAAE;IAC9F,cAAc,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,qCAAqC,EAAE;IACtF,IAAI,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,sCAAsC,EAAE;CAClE,CAAC;AAIX,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,IAAI;IACX,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,MAAM,GAAG,GAAuC,EAAE,CAAC;QACnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAgB,EAAE,CAAC;YAC1D,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAW,CAAC;QACxE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,kBAAkB,CAAC,GAAc;IAC/C,OAAO,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAc,EAAE,KAAyB;IAC9E,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;;QACxC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** GlobalGPT API base (serves /ai-api/* and /user-api/*). */
|
|
2
|
+
export declare function apiBaseUrl(): string;
|
|
3
|
+
/** Web frontend base that serves the /cli-auth authorization page. */
|
|
4
|
+
export declare function webBaseUrl(): string;
|
|
5
|
+
/** UI language segment used in web URLs. */
|
|
6
|
+
export declare function lang(): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getConfigValueSync } from './configFile.js';
|
|
2
|
+
/**
|
|
3
|
+
* Effective configuration values.
|
|
4
|
+
*
|
|
5
|
+
* Precedence: explicit env var (one-shot override / CI) > ~/.config/glbgpt/config.json
|
|
6
|
+
* (persistent, managed by `glbgpt config`) > built-in default. This build targets the TEST
|
|
7
|
+
* environment: the unified host https://tw.broly.ai serves both the API (/ai-api, /user-api)
|
|
8
|
+
* and the web frontend (/cli-auth), so both defaults point there. The prod switch happens
|
|
9
|
+
* when the CLI graduates from the test round.
|
|
10
|
+
*/
|
|
11
|
+
const TEST_HOST = 'https://tw.broly.ai';
|
|
12
|
+
/** GlobalGPT API base (serves /ai-api/* and /user-api/*). */
|
|
13
|
+
export function apiBaseUrl() {
|
|
14
|
+
return process.env.GLBGPT_BASE_URL ?? getConfigValueSync('api-base-url') ?? TEST_HOST;
|
|
15
|
+
}
|
|
16
|
+
/** Web frontend base that serves the /cli-auth authorization page. */
|
|
17
|
+
export function webBaseUrl() {
|
|
18
|
+
return process.env.GLBGPT_WEB_URL ?? getConfigValueSync('web-base-url') ?? TEST_HOST;
|
|
19
|
+
}
|
|
20
|
+
/** UI language segment used in web URLs. */
|
|
21
|
+
export function lang() {
|
|
22
|
+
return process.env.GLBGPT_LANG ?? getConfigValueSync('lang') ?? 'en';
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;GAQG;AAEH,MAAM,SAAS,GAAG,qBAAqB,CAAC;AAExC,6DAA6D;AAC7D,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,kBAAkB,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC;AACxF,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,kBAAkB,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC;AACvF,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,IAAI;IAClB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,kBAAkB,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Base config directory for the CLI: $XDG_CONFIG_HOME/glbgpt or ~/.config/glbgpt. */
|
|
2
|
+
export declare function configDir(): string;
|
|
3
|
+
/** Directory holding local session rollout files. */
|
|
4
|
+
export declare function sessionsDir(): string;
|
|
5
|
+
/** Path to the stored credentials file. */
|
|
6
|
+
export declare function credentialsPath(): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
/** Base config directory for the CLI: $XDG_CONFIG_HOME/glbgpt or ~/.config/glbgpt. */
|
|
4
|
+
export function configDir() {
|
|
5
|
+
const base = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
|
|
6
|
+
return path.join(base, 'glbgpt');
|
|
7
|
+
}
|
|
8
|
+
/** Directory holding local session rollout files. */
|
|
9
|
+
export function sessionsDir() {
|
|
10
|
+
return path.join(configDir(), 'sessions');
|
|
11
|
+
}
|
|
12
|
+
/** Path to the stored credentials file. */
|
|
13
|
+
export function credentialsPath() {
|
|
14
|
+
return path.join(configDir(), 'credentials.json');
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/config/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,sFAAsF;AACtF,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAC5C,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,kBAAkB,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ModelInfo } from '../tui/models.js';
|
|
2
|
+
/**
|
|
3
|
+
* Server-driven model catalog (GET /ai-api/cli/v1/models).
|
|
4
|
+
*
|
|
5
|
+
* The CHAT list is fully dynamic — the backend whitelist is already the authority, this
|
|
6
|
+
* endpoint just publishes it. MEDIA only carries a `disabled` availability overlay over the
|
|
7
|
+
* CLI's built-in specs (request-building behavior is code; pricing stays same-source via
|
|
8
|
+
* estimatePrice at submit time).
|
|
9
|
+
*
|
|
10
|
+
* Loading strategy (never blocks, never breaks offline): apply the cached copy immediately,
|
|
11
|
+
* then refresh in the background and persist for the next run. No cache and no network →
|
|
12
|
+
* the baked-in lists keep working exactly as before.
|
|
13
|
+
*/
|
|
14
|
+
export interface RemoteCatalog {
|
|
15
|
+
chat: {
|
|
16
|
+
default: string;
|
|
17
|
+
models: ModelInfo[];
|
|
18
|
+
};
|
|
19
|
+
media: {
|
|
20
|
+
disabled: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Parse + validate an untrusted catalog payload; null when the shape is unusable. */
|
|
24
|
+
export declare function parseCatalog(raw: unknown): RemoteCatalog | null;
|
|
25
|
+
export declare function catalogCacheFile(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch the catalog from the backend; null on any failure. The timeout is generous —
|
|
28
|
+
* this runs as a BACKGROUND refresh (startup never blocks on it), and the test backend
|
|
29
|
+
* shows multi-second cross-border jitter (measured 1.3–4.2s on 2026-07-16).
|
|
30
|
+
*/
|
|
31
|
+
export declare function fetchRemoteCatalog(baseUrl: string, token: string, fetchImpl?: typeof fetch, timeoutMs?: number): Promise<RemoteCatalog | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the model catalog: cached copy applies before this resolves; the network
|
|
34
|
+
* refresh continues in the background (await the returned `refreshed` promise in tests).
|
|
35
|
+
*/
|
|
36
|
+
export declare function initCatalog(opts: {
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
token?: string | null;
|
|
39
|
+
fetchImpl?: typeof fetch;
|
|
40
|
+
cacheFile?: string;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
refreshed: Promise<void>;
|
|
43
|
+
}>;
|
|
44
|
+
/** Initialize once per process (cache applied before resolve; network refresh in background). */
|
|
45
|
+
export declare function ensureCatalog(opts: Parameters<typeof initCatalog>[0]): Promise<{
|
|
46
|
+
refreshed: Promise<void>;
|
|
47
|
+
}>;
|
|
48
|
+
/** Test hook. */
|
|
49
|
+
export declare function resetCatalogForTests(): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { configDir } from '../config/paths.js';
|
|
4
|
+
import { applyChatCatalog } from '../tui/models.js';
|
|
5
|
+
import { applyMediaDisabled } from '../media/manifest.js';
|
|
6
|
+
/** Parse + validate an untrusted catalog payload; null when the shape is unusable. */
|
|
7
|
+
export function parseCatalog(raw) {
|
|
8
|
+
if (typeof raw !== 'object' || raw === null)
|
|
9
|
+
return null;
|
|
10
|
+
const o = raw;
|
|
11
|
+
const models = Array.isArray(o.chat?.models)
|
|
12
|
+
? o.chat.models
|
|
13
|
+
.filter((m) => typeof m?.id === 'string' && m.id.trim())
|
|
14
|
+
.map((m) => ({ id: m.id.trim(), label: typeof m.label === 'string' && m.label ? m.label : m.id.trim() }))
|
|
15
|
+
: [];
|
|
16
|
+
if (models.length === 0)
|
|
17
|
+
return null; // a catalog without chat models is never valid
|
|
18
|
+
const def = typeof o.chat?.default === 'string' && o.chat.default.trim() ? o.chat.default.trim() : models[0].id;
|
|
19
|
+
const disabled = Array.isArray(o.media?.disabled)
|
|
20
|
+
? o.media.disabled.filter((d) => typeof d === 'string' && d.trim()).map((d) => d.trim())
|
|
21
|
+
: [];
|
|
22
|
+
return { chat: { default: def, models }, media: { disabled } };
|
|
23
|
+
}
|
|
24
|
+
function applyCatalog(catalog) {
|
|
25
|
+
applyChatCatalog(catalog.chat.models, catalog.chat.default);
|
|
26
|
+
applyMediaDisabled(catalog.media.disabled);
|
|
27
|
+
}
|
|
28
|
+
export function catalogCacheFile() {
|
|
29
|
+
return path.join(configDir(), 'catalog.json');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetch the catalog from the backend; null on any failure. The timeout is generous —
|
|
33
|
+
* this runs as a BACKGROUND refresh (startup never blocks on it), and the test backend
|
|
34
|
+
* shows multi-second cross-border jitter (measured 1.3–4.2s on 2026-07-16).
|
|
35
|
+
*/
|
|
36
|
+
export async function fetchRemoteCatalog(baseUrl, token, fetchImpl = fetch, timeoutMs = 10_000) {
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetchImpl(`${baseUrl.replace(/\/+$/, '')}/ai-api/cli/v1/models`, {
|
|
39
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
40
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok)
|
|
43
|
+
return null;
|
|
44
|
+
return parseCatalog(await res.json());
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Initialize the model catalog: cached copy applies before this resolves; the network
|
|
52
|
+
* refresh continues in the background (await the returned `refreshed` promise in tests).
|
|
53
|
+
*/
|
|
54
|
+
export async function initCatalog(opts) {
|
|
55
|
+
const cacheFile = opts.cacheFile ?? catalogCacheFile();
|
|
56
|
+
try {
|
|
57
|
+
const cached = parseCatalog(JSON.parse(await fs.readFile(cacheFile, 'utf8'))?.catalog);
|
|
58
|
+
if (cached)
|
|
59
|
+
applyCatalog(cached);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// no/torn cache — baked-in lists remain in effect
|
|
63
|
+
}
|
|
64
|
+
const refreshed = (async () => {
|
|
65
|
+
if (!opts.token)
|
|
66
|
+
return; // unauthenticated → catalog endpoint would 401; baked-in is fine
|
|
67
|
+
const fresh = await fetchRemoteCatalog(opts.baseUrl, opts.token, opts.fetchImpl);
|
|
68
|
+
if (!fresh)
|
|
69
|
+
return;
|
|
70
|
+
applyCatalog(fresh);
|
|
71
|
+
try {
|
|
72
|
+
await fs.mkdir(path.dirname(cacheFile), { recursive: true });
|
|
73
|
+
const tmp = `${cacheFile}.${process.pid}.tmp`;
|
|
74
|
+
await fs.writeFile(tmp, JSON.stringify({ fetchedAt: Date.now(), catalog: fresh }, null, 2), 'utf8');
|
|
75
|
+
await fs.rename(tmp, cacheFile);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// cache write is best-effort
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
return { refreshed };
|
|
82
|
+
}
|
|
83
|
+
// Process-wide memoized init: every entry point (TUI, exec, MCP tool call, media commands)
|
|
84
|
+
// calls ensureCatalog; only the first call does work.
|
|
85
|
+
let ensured = null;
|
|
86
|
+
/** Initialize once per process (cache applied before resolve; network refresh in background). */
|
|
87
|
+
export function ensureCatalog(opts) {
|
|
88
|
+
if (!ensured)
|
|
89
|
+
ensured = initCatalog(opts);
|
|
90
|
+
return ensured;
|
|
91
|
+
}
|
|
92
|
+
/** Test hook. */
|
|
93
|
+
export function resetCatalogForTests() {
|
|
94
|
+
ensured = null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../src/core/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ChatBackend, ChatChunk, ChatRequest } from './types.js';
|
|
2
|
+
export interface HttpChatBackendOptions {
|
|
3
|
+
/** Base URL of the GlobalGPT API, e.g. https://api2.glbgpt.com (test: https://tapi2.glbgpt.com). */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** GlobalGPT user JWT. Sent as `Authorization: Bearer <token>`. */
|
|
6
|
+
token: string;
|
|
7
|
+
/** Optional fetch override (for testing). Defaults to global fetch. */
|
|
8
|
+
fetchImpl?: typeof fetch;
|
|
9
|
+
/** Max connection-level retries before the stream starts (default 3). */
|
|
10
|
+
retries?: number;
|
|
11
|
+
/** Injected backoff sleep (tests pass a no-op). */
|
|
12
|
+
sleep?: (ms: number) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Real chat backend: streams from `POST /ai-api/cli/v1/chat/completions` (OpenAI-compatible SSE).
|
|
16
|
+
*
|
|
17
|
+
* Note: the GlobalGPT gateway returns HTTP 200 for business errors with a JSON body
|
|
18
|
+
* (`{code, message}`) and Content-Type `application/json` instead of `text/event-stream`.
|
|
19
|
+
* We detect that by Content-Type and surface it as an error.
|
|
20
|
+
*/
|
|
21
|
+
export declare class HttpChatBackend implements ChatBackend {
|
|
22
|
+
private readonly baseUrl;
|
|
23
|
+
private token;
|
|
24
|
+
private readonly fetchImpl;
|
|
25
|
+
private readonly retries;
|
|
26
|
+
private readonly sleep?;
|
|
27
|
+
constructor(opts: HttpChatBackendOptions);
|
|
28
|
+
/** Hot-swap the JWT (used by the in-chat /login re-auth). */
|
|
29
|
+
setToken(token: string): void;
|
|
30
|
+
stream(req: ChatRequest, signal?: AbortSignal): AsyncIterable<ChatChunk>;
|
|
31
|
+
}
|