marifold 0.60.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/LICENSE +674 -0
- package/dist/auth/ChatGptAuth.d.ts +17 -0
- package/dist/auth/ChatGptAuth.d.ts.map +1 -0
- package/dist/auth/ChatGptAuth.js +215 -0
- package/dist/auth/ChatGptAuth.js.map +1 -0
- package/dist/auth/GitHubCopilotAuth.d.ts +10 -0
- package/dist/auth/GitHubCopilotAuth.d.ts.map +1 -0
- package/dist/auth/GitHubCopilotAuth.js +118 -0
- package/dist/auth/GitHubCopilotAuth.js.map +1 -0
- package/dist/auth/XaiAuth.d.ts +27 -0
- package/dist/auth/XaiAuth.d.ts.map +1 -0
- package/dist/auth/XaiAuth.js +280 -0
- package/dist/auth/XaiAuth.js.map +1 -0
- package/dist/commands/RuntimeFactory.d.ts +8 -0
- package/dist/commands/RuntimeFactory.d.ts.map +1 -0
- package/dist/commands/RuntimeFactory.js +13 -0
- package/dist/commands/RuntimeFactory.js.map +1 -0
- package/dist/commands/agent.d.ts +4 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +130 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/ask.d.ts +4 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +57 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/channel.d.ts +8 -0
- package/dist/commands/channel.d.ts.map +1 -0
- package/dist/commands/channel.js +114 -0
- package/dist/commands/channel.js.map +1 -0
- package/dist/commands/chat.d.ts +4 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +352 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +18 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +228 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +86 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +135 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/model.d.ts +4 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +384 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/profile.d.ts +4 -0
- package/dist/commands/profile.d.ts.map +1 -0
- package/dist/commands/profile.js +231 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/provider.d.ts +4 -0
- package/dist/commands/provider.d.ts.map +1 -0
- package/dist/commands/provider.js +254 -0
- package/dist/commands/provider.js.map +1 -0
- package/dist/commands/schedule.d.ts +4 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +141 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/service.d.ts +15 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +421 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/session.d.ts +4 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +171 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +59 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/input/InteractivePrompt.d.ts +9 -0
- package/dist/input/InteractivePrompt.d.ts.map +1 -0
- package/dist/input/InteractivePrompt.js +101 -0
- package/dist/input/InteractivePrompt.js.map +1 -0
- package/dist/input/ModelPicker.d.ts +34 -0
- package/dist/input/ModelPicker.d.ts.map +1 -0
- package/dist/input/ModelPicker.js +327 -0
- package/dist/input/ModelPicker.js.map +1 -0
- package/dist/input/PromptAbort.d.ts +11 -0
- package/dist/input/PromptAbort.d.ts.map +1 -0
- package/dist/input/PromptAbort.js +28 -0
- package/dist/input/PromptAbort.js.map +1 -0
- package/dist/input/SecretPrompt.d.ts +3 -0
- package/dist/input/SecretPrompt.d.ts.map +1 -0
- package/dist/input/SecretPrompt.js +61 -0
- package/dist/input/SecretPrompt.js.map +1 -0
- package/dist/input/TerminalSelect.d.ts +17 -0
- package/dist/input/TerminalSelect.d.ts.map +1 -0
- package/dist/input/TerminalSelect.js +153 -0
- package/dist/input/TerminalSelect.js.map +1 -0
- package/dist/output/ConsolePrinter.d.ts +12 -0
- package/dist/output/ConsolePrinter.d.ts.map +1 -0
- package/dist/output/ConsolePrinter.js +69 -0
- package/dist/output/ConsolePrinter.js.map +1 -0
- package/dist/output/TerminalStyle.d.ts +11 -0
- package/dist/output/TerminalStyle.d.ts.map +1 -0
- package/dist/output/TerminalStyle.js +41 -0
- package/dist/output/TerminalStyle.js.map +1 -0
- package/dist/service/ServiceProcess.d.ts +45 -0
- package/dist/service/ServiceProcess.d.ts.map +1 -0
- package/dist/service/ServiceProcess.js +288 -0
- package/dist/service/ServiceProcess.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerStatusCommand = registerStatusCommand;
|
|
4
|
+
const ServiceProcess_1 = require("../service/ServiceProcess");
|
|
5
|
+
const STATUS_LOG_LINES = 100;
|
|
6
|
+
function registerStatusCommand(program, printer) {
|
|
7
|
+
program
|
|
8
|
+
.command('status')
|
|
9
|
+
.description('Show the managed Marifold service status.')
|
|
10
|
+
.option('--logs', `Show the most recent ${STATUS_LOG_LINES} daemon log lines.`)
|
|
11
|
+
.action((options) => {
|
|
12
|
+
try {
|
|
13
|
+
const paths = (0, ServiceProcess_1.serviceProcessPaths)();
|
|
14
|
+
const state = (0, ServiceProcess_1.getActiveServiceProcess)(paths);
|
|
15
|
+
if (state) {
|
|
16
|
+
process.stdout.write('Marifold service: running\n');
|
|
17
|
+
process.stdout.write(`PID: ${state.pid}\n`);
|
|
18
|
+
process.stdout.write(`Mode: ${state.mode}\n`);
|
|
19
|
+
process.stdout.write(`Started: ${state.startedAt}\n`);
|
|
20
|
+
if (state.address)
|
|
21
|
+
process.stdout.write(`Address: ${state.address}\n`);
|
|
22
|
+
if (state.launch) {
|
|
23
|
+
const loopback = ['127.0.0.1', 'localhost', '::1'].includes(state.launch.host);
|
|
24
|
+
const access = state.launch.publicAccess ? 'legacy-public' : loopback ? 'loopback' : 'private';
|
|
25
|
+
process.stdout.write(`Access: ${access}\n`);
|
|
26
|
+
if (state.launch.publicAccess) {
|
|
27
|
+
process.stdout.write('Warning: restart this legacy service to enforce private-network-only access.\n');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
process.stdout.write(`Config: ${state.configPath}\n`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
process.stdout.write('Marifold service: stopped\n');
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
}
|
|
36
|
+
process.stdout.write(`Log: ${paths.log}\n`);
|
|
37
|
+
if (options.logs)
|
|
38
|
+
printLogs(paths.log);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
printer.printError(error);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function printLogs(logPath) {
|
|
47
|
+
const logs = (0, ServiceProcess_1.readRecentServiceLog)(STATUS_LOG_LINES);
|
|
48
|
+
process.stdout.write(`\nRecent logs (last ${STATUS_LOG_LINES} lines):\n`);
|
|
49
|
+
if (logs === undefined) {
|
|
50
|
+
process.stdout.write(`No daemon log found at ${logPath}.\n`);
|
|
51
|
+
}
|
|
52
|
+
else if (logs.length === 0) {
|
|
53
|
+
process.stdout.write('(empty)\n');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
process.stdout.write(`${logs}\n`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const agent_1 = require("./commands/agent");
|
|
6
|
+
const ask_1 = require("./commands/ask");
|
|
7
|
+
const chat_1 = require("./commands/chat");
|
|
8
|
+
const channel_1 = require("./commands/channel");
|
|
9
|
+
const config_1 = require("./commands/config");
|
|
10
|
+
const doctor_1 = require("./commands/doctor");
|
|
11
|
+
const init_1 = require("./commands/init");
|
|
12
|
+
const model_1 = require("./commands/model");
|
|
13
|
+
const profile_1 = require("./commands/profile");
|
|
14
|
+
const provider_1 = require("./commands/provider");
|
|
15
|
+
const schedule_1 = require("./commands/schedule");
|
|
16
|
+
const service_1 = require("./commands/service");
|
|
17
|
+
const session_1 = require("./commands/session");
|
|
18
|
+
const status_1 = require("./commands/status");
|
|
19
|
+
const ConsolePrinter_1 = require("./output/ConsolePrinter");
|
|
20
|
+
const RuntimeFactory_1 = require("./commands/RuntimeFactory");
|
|
21
|
+
const printer = new ConsolePrinter_1.ConsolePrinter();
|
|
22
|
+
const program = new commander_1.Command()
|
|
23
|
+
.name('marifold')
|
|
24
|
+
.description('Marifold local-first AI workspace CLI.')
|
|
25
|
+
.version('0.60.0')
|
|
26
|
+
// Allow a root --profile (for the bare-`marifold` TUI launch) to coexist with
|
|
27
|
+
// subcommand options of the same name: root options must precede the
|
|
28
|
+
// subcommand, and options after the subcommand bind to it.
|
|
29
|
+
.enablePositionalOptions()
|
|
30
|
+
.option('--config <path>', 'Path to Marifold config.toml.')
|
|
31
|
+
.option('--profile <name>', 'Profile to launch the TUI with.')
|
|
32
|
+
.option('--resume [id]', 'Resume a session: bare --resume continues the most recent session for the profile; --resume <id> continues that specific session.');
|
|
33
|
+
(0, init_1.registerInitCommand)(program, printer);
|
|
34
|
+
(0, agent_1.registerAgentCommand)(program, printer);
|
|
35
|
+
(0, ask_1.registerAskCommand)(program, printer);
|
|
36
|
+
(0, chat_1.registerChatCommand)(program, printer);
|
|
37
|
+
(0, channel_1.registerChannelCommand)(program, printer);
|
|
38
|
+
(0, config_1.registerConfigCommand)(program, printer);
|
|
39
|
+
(0, doctor_1.registerDoctorCommand)(program, printer);
|
|
40
|
+
(0, model_1.registerModelCommand)(program, printer);
|
|
41
|
+
(0, profile_1.registerProfileCommand)(program, printer);
|
|
42
|
+
(0, provider_1.registerProviderCommand)(program, printer);
|
|
43
|
+
(0, schedule_1.registerScheduleCommand)(program, printer);
|
|
44
|
+
(0, service_1.registerServiceCommand)(program, printer);
|
|
45
|
+
(0, session_1.registerSessionCommand)(program, printer);
|
|
46
|
+
(0, status_1.registerStatusCommand)(program, printer);
|
|
47
|
+
// Bare `marifold` (or `marifold --profile x`) launches the Ink TUI. The TUI is
|
|
48
|
+
// an ESM-only package (Ink v7); the CLI is CommonJS, so import it through a
|
|
49
|
+
// real dynamic import the TypeScript CommonJS emit will not downlevel into a
|
|
50
|
+
// require() (which would throw ERR_REQUIRE_ESM).
|
|
51
|
+
const importEsm = new Function('specifier', 'return import(specifier);');
|
|
52
|
+
program.action(async () => {
|
|
53
|
+
// Only a bare `marifold` (optionally with root options) launches the TUI. A
|
|
54
|
+
// stray/unknown subcommand (e.g. `marifold frobnicate`) lands here too because
|
|
55
|
+
// this is the default action — reject it instead of silently opening the TUI.
|
|
56
|
+
if (program.args.length > 0) {
|
|
57
|
+
printer.printError(new Error(`unknown command '${program.args[0]}'. Run \`marifold --help\` to see available commands.`));
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const options = program.opts();
|
|
62
|
+
const loadedConfig = (0, RuntimeFactory_1.loadConfig)(program);
|
|
63
|
+
const { runTui } = await importEsm('@marifold/tui');
|
|
64
|
+
await runTui({
|
|
65
|
+
loadedConfig,
|
|
66
|
+
...(options.profile ? { profile: options.profile } : {}),
|
|
67
|
+
...(options.resume !== undefined ? { resume: options.resume } : {}),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
program.parseAsync(process.argv).catch(error => {
|
|
71
|
+
printer.printError(error);
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class InteractivePrompt {
|
|
2
|
+
private readonly interface;
|
|
3
|
+
readUserMessage(label?: string, options?: {
|
|
4
|
+
onEscape?: 'cancel' | 'back';
|
|
5
|
+
}): Promise<string | undefined>;
|
|
6
|
+
readMultilineMessage(label?: string, continuationLabel?: string): Promise<string | undefined>;
|
|
7
|
+
close(): void;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=InteractivePrompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractivePrompt.d.ts","sourceRoot":"","sources":["../../src/input/InteractivePrompt.ts"],"names":[],"mappings":"AAIA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAIvB;IAEG,eAAe,CACnB,KAAK,SAAW,EAChB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;KAAO,GAC7C,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA2B7B;IAEK,oBAAoB,CAAC,KAAK,SAAY,EAAE,iBAAiB,SAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgBvG;IAED,KAAK,IAAI,IAAI,CAEZ;CACF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.InteractivePrompt = void 0;
|
|
37
|
+
const readline = __importStar(require("readline/promises"));
|
|
38
|
+
const process_1 = require("process");
|
|
39
|
+
const PromptAbort_1 = require("./PromptAbort");
|
|
40
|
+
class InteractivePrompt {
|
|
41
|
+
interface = readline.createInterface({
|
|
42
|
+
input: process_1.stdin,
|
|
43
|
+
output: process_1.stdout,
|
|
44
|
+
terminal: Boolean(process_1.stdin.isTTY && process_1.stdout.isTTY),
|
|
45
|
+
});
|
|
46
|
+
async readUserMessage(label = 'user> ', options = {}) {
|
|
47
|
+
// readline does not treat a lone Esc as cancel. When asked, wire a keypress
|
|
48
|
+
// listener to an AbortController so Esc rejects the question — letting callers
|
|
49
|
+
// either cancel the command or step back a wizard stage. (readline emits
|
|
50
|
+
// 'keypress' on the input in terminal mode; arrow keys carry names like 'up',
|
|
51
|
+
// so only a bare Esc matches 'escape'.)
|
|
52
|
+
const controller = options.onEscape && process_1.stdin.isTTY ? new AbortController() : undefined;
|
|
53
|
+
const onKeypress = (_str, key) => {
|
|
54
|
+
if (key?.name === 'escape')
|
|
55
|
+
controller?.abort();
|
|
56
|
+
};
|
|
57
|
+
if (controller)
|
|
58
|
+
process_1.stdin.on('keypress', onKeypress);
|
|
59
|
+
try {
|
|
60
|
+
return await (controller
|
|
61
|
+
? this.interface.question(label, { signal: controller.signal })
|
|
62
|
+
: this.interface.question(label));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Esc fired our controller → distinguish "step back" from a hard cancel.
|
|
66
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
67
|
+
if (options.onEscape === 'back')
|
|
68
|
+
throw new PromptAbort_1.PromptBackError();
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
// Ctrl+C / Ctrl+D close the interface → always a cancel.
|
|
72
|
+
if (error instanceof Error && error.message === 'readline was closed')
|
|
73
|
+
return undefined;
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
if (controller)
|
|
78
|
+
process_1.stdin.off('keypress', onKeypress);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async readMultilineMessage(label = 'user > ', continuationLabel = '... > ') {
|
|
82
|
+
const lines = [];
|
|
83
|
+
let prompt = label;
|
|
84
|
+
while (true) {
|
|
85
|
+
const line = await this.readUserMessage(prompt);
|
|
86
|
+
if (line === undefined)
|
|
87
|
+
return lines.length > 0 ? lines.join('\n') : undefined;
|
|
88
|
+
if (!line.endsWith('\\')) {
|
|
89
|
+
lines.push(line);
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
lines.push(line.slice(0, -1));
|
|
93
|
+
prompt = continuationLabel;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
close() {
|
|
97
|
+
this.interface.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.InteractivePrompt = InteractivePrompt;
|
|
101
|
+
//# sourceMappingURL=InteractivePrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InteractivePrompt.js","sourceRoot":"","sources":["../../src/input/InteractivePrompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LoadedMarifoldConfig, MarifoldProviderConfig, ProviderRegistryEntry } from '@marifold/core';
|
|
2
|
+
import { InteractivePrompt } from './InteractivePrompt';
|
|
3
|
+
import { TerminalStyle } from '../output/TerminalStyle';
|
|
4
|
+
/** Lazily-created prompt, so an interactive picker only opens stdin when a
|
|
5
|
+
* fallback line read is actually needed. Shared by `model add` and `init`. */
|
|
6
|
+
export type PromptFactory = () => InteractivePrompt;
|
|
7
|
+
/** The provider-setup flags `model add` accepts; the picker reads only these. */
|
|
8
|
+
export interface ModelAddSetupOptions {
|
|
9
|
+
providerType?: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
apiKeyEnv?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a provider/model pair for `marifold model add` (and `marifold init`'s
|
|
15
|
+
* model step). Falls back to interactive provider/model pickers when an argument
|
|
16
|
+
* is omitted, prompting for OAuth credentials when the chosen provider needs them.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveModelAddTarget(loadedConfig: LoadedMarifoldConfig, getPrompt: PromptFactory, style: TerminalStyle, providerArg?: string, modelArg?: string, options?: ModelAddSetupOptions): Promise<{
|
|
19
|
+
provider: string;
|
|
20
|
+
model: string;
|
|
21
|
+
}>;
|
|
22
|
+
/** Read a numbered choice (or custom name when allowed) from a text prompt —
|
|
23
|
+
* the keyboard-less fallback when an arrow-key selector is unavailable. */
|
|
24
|
+
export declare function readChoice(prompt: InteractivePrompt, style: TerminalStyle, label: string, values: string[], options?: {
|
|
25
|
+
defaultIndex?: number;
|
|
26
|
+
allowCustom?: boolean;
|
|
27
|
+
}): Promise<string>;
|
|
28
|
+
/** Force a fresh credential flow for a registry-managed OAuth provider while
|
|
29
|
+
* preserving its non-credential settings (for example proxy and base URL). */
|
|
30
|
+
export declare function reauthenticateOAuthProvider(loadedConfig: LoadedMarifoldConfig, getPrompt: PromptFactory, style: TerminalStyle, provider: string): Promise<MarifoldProviderConfig>;
|
|
31
|
+
/** Pure credential replacement used by setup and explicit re-authentication. */
|
|
32
|
+
export declare function applyOAuthCredentials(current: MarifoldProviderConfig, entry: ProviderRegistryEntry, credentials: Partial<MarifoldProviderConfig>): MarifoldProviderConfig;
|
|
33
|
+
export declare function providerHasUsableCredential(provider: MarifoldProviderConfig | undefined, options: ModelAddSetupOptions): boolean;
|
|
34
|
+
//# sourceMappingURL=ModelPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelPicker.d.ts","sourceRoot":"","sources":["../../src/input/ModelPicker.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,EAEtB,qBAAqB,EAGtB,MAAM,gBAAgB,CAAC;AAQxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAIxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD;8EAC8E;AAC9E,MAAM,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC;AAEpD,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAaD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,oBAAoB,EAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAU9C;AA6ED;2EAC2E;AAC3E,wBAAsB,UAAU,CAC9B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7D,OAAO,CAAC,MAAM,CAAC,CAYjB;AAiED;8EAC8E;AAC9E,wBAAsB,2BAA2B,CAC/C,YAAY,EAAE,oBAAoB,EAClC,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,sBAAsB,CAAC,CAgBjC;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,qBAAqB,EAC5B,WAAW,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAC3C,sBAAsB,CAoBxB;AAsID,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,sBAAsB,GAAG,SAAS,EAC5C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAWT"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveModelAddTarget = resolveModelAddTarget;
|
|
4
|
+
exports.readChoice = readChoice;
|
|
5
|
+
exports.reauthenticateOAuthProvider = reauthenticateOAuthProvider;
|
|
6
|
+
exports.applyOAuthCredentials = applyOAuthCredentials;
|
|
7
|
+
exports.providerHasUsableCredential = providerHasUsableCredential;
|
|
8
|
+
const core_1 = require("@marifold/core");
|
|
9
|
+
const ChatGptAuth_1 = require("../auth/ChatGptAuth");
|
|
10
|
+
const XaiAuth_1 = require("../auth/XaiAuth");
|
|
11
|
+
const GitHubCopilotAuth_1 = require("../auth/GitHubCopilotAuth");
|
|
12
|
+
const PromptAbort_1 = require("./PromptAbort");
|
|
13
|
+
const SecretPrompt_1 = require("./SecretPrompt");
|
|
14
|
+
const TerminalSelect_1 = require("./TerminalSelect");
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a provider/model pair for `marifold model add` (and `marifold init`'s
|
|
17
|
+
* model step). Falls back to interactive provider/model pickers when an argument
|
|
18
|
+
* is omitted, prompting for OAuth credentials when the chosen provider needs them.
|
|
19
|
+
*/
|
|
20
|
+
async function resolveModelAddTarget(loadedConfig, getPrompt, style, providerArg, modelArg, options = {}) {
|
|
21
|
+
const providerWasPicked = providerArg === undefined;
|
|
22
|
+
const provider = providerArg ?? await selectProvider(loadedConfig, getPrompt, style);
|
|
23
|
+
if (providerWasPicked)
|
|
24
|
+
process.stdout.write('\n');
|
|
25
|
+
const shouldPromptProviderSetup = providerWasPicked || modelArg === undefined;
|
|
26
|
+
if (shouldPromptProviderSetup) {
|
|
27
|
+
await promptProviderSetupIfNeeded(loadedConfig, getPrompt, style, provider, options);
|
|
28
|
+
}
|
|
29
|
+
const model = modelArg ?? await selectModel(loadedConfig, getPrompt, style, provider);
|
|
30
|
+
return { provider, model };
|
|
31
|
+
}
|
|
32
|
+
async function selectProvider(loadedConfig, getPrompt, style) {
|
|
33
|
+
const providers = modelAddProviderChoices(loadedConfig);
|
|
34
|
+
if (providers.length === 0) {
|
|
35
|
+
throw core_1.MarifoldError.configInvalid('No providers are available. Run marifold init or pass provider/model explicitly.');
|
|
36
|
+
}
|
|
37
|
+
const defaultIndex = providers.findIndex(provider => provider.isDefault);
|
|
38
|
+
const selected = await (0, TerminalSelect_1.selectTerminalOption)('Select provider:', providers.map(provider => ({
|
|
39
|
+
label: formatProvider(provider),
|
|
40
|
+
value: provider.name,
|
|
41
|
+
})), {
|
|
42
|
+
defaultIndex: defaultIndex >= 0 ? defaultIndex : 0,
|
|
43
|
+
});
|
|
44
|
+
if (selected !== undefined)
|
|
45
|
+
return selected;
|
|
46
|
+
process.stdout.write('Select provider:\n');
|
|
47
|
+
providers.forEach((provider, index) => {
|
|
48
|
+
process.stdout.write(` ${index + 1}. ${formatProvider(provider)}\n`);
|
|
49
|
+
});
|
|
50
|
+
return readChoice(getPrompt(), style, 'Provider', providers.map(provider => provider.name), {
|
|
51
|
+
defaultIndex: defaultIndex >= 0 ? defaultIndex : 0,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function selectModel(loadedConfig, getPrompt, style, provider) {
|
|
55
|
+
const inspector = new core_1.ProviderInspector(loadedConfig);
|
|
56
|
+
const result = await inspector.listModels(provider);
|
|
57
|
+
const savedModels = savedModelsForProvider(loadedConfig, provider);
|
|
58
|
+
const models = uniqueSorted([...result.models, ...savedModels]);
|
|
59
|
+
if (models.length > 0) {
|
|
60
|
+
process.stdout.write(style.dim(`${result.message}\n`));
|
|
61
|
+
const customValue = Symbol('custom model');
|
|
62
|
+
const selected = await (0, TerminalSelect_1.selectTerminalOption)(`Select model for ${provider}:`, [
|
|
63
|
+
...models.map(model => ({
|
|
64
|
+
label: model,
|
|
65
|
+
value: model,
|
|
66
|
+
})),
|
|
67
|
+
{
|
|
68
|
+
label: 'Custom model...',
|
|
69
|
+
value: customValue,
|
|
70
|
+
},
|
|
71
|
+
], {
|
|
72
|
+
defaultIndex: 0,
|
|
73
|
+
});
|
|
74
|
+
if (selected !== undefined) {
|
|
75
|
+
if (selected === customValue)
|
|
76
|
+
return readRequiredLine(getPrompt(), style, `Model for ${provider}: `);
|
|
77
|
+
return selected;
|
|
78
|
+
}
|
|
79
|
+
process.stdout.write(`Select model for ${provider}:\n`);
|
|
80
|
+
models.forEach((model, index) => {
|
|
81
|
+
process.stdout.write(` ${index + 1}. ${model}\n`);
|
|
82
|
+
});
|
|
83
|
+
return readChoice(getPrompt(), style, 'Model', models, {
|
|
84
|
+
defaultIndex: 0,
|
|
85
|
+
allowCustom: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (result.message)
|
|
89
|
+
process.stdout.write(style.dim(`${result.message}\n`));
|
|
90
|
+
return readRequiredLine(getPrompt(), style, `Model for ${provider}: `);
|
|
91
|
+
}
|
|
92
|
+
/** Read a numbered choice (or custom name when allowed) from a text prompt —
|
|
93
|
+
* the keyboard-less fallback when an arrow-key selector is unavailable. */
|
|
94
|
+
async function readChoice(prompt, style, label, values, options = {}) {
|
|
95
|
+
const defaultSuffix = options.defaultIndex !== undefined ? ` [${options.defaultIndex + 1}]` : '';
|
|
96
|
+
const customSuffix = options.allowCustom ? ' (number or custom name)' : '';
|
|
97
|
+
const answer = await prompt.readUserMessage(style.bold(`${label}${customSuffix}${defaultSuffix}: `));
|
|
98
|
+
if (answer === undefined)
|
|
99
|
+
throw new PromptAbort_1.PromptAbortError();
|
|
100
|
+
const raw = answer.trim();
|
|
101
|
+
if (!raw && options.defaultIndex !== undefined)
|
|
102
|
+
return values[options.defaultIndex];
|
|
103
|
+
const index = Number(raw);
|
|
104
|
+
if (Number.isInteger(index) && index >= 1 && index <= values.length)
|
|
105
|
+
return values[index - 1];
|
|
106
|
+
if (options.allowCustom && raw)
|
|
107
|
+
return raw;
|
|
108
|
+
throw core_1.MarifoldError.configInvalid(`Invalid ${label.toLowerCase()} selection.`);
|
|
109
|
+
}
|
|
110
|
+
async function readRequiredLine(prompt, style, label) {
|
|
111
|
+
const answer = await prompt.readUserMessage(style.bold(label));
|
|
112
|
+
if (answer === undefined)
|
|
113
|
+
throw new PromptAbort_1.PromptAbortError();
|
|
114
|
+
const value = answer.trim();
|
|
115
|
+
if (!value)
|
|
116
|
+
throw core_1.MarifoldError.configInvalid(`${label.replace(/:\s*$/, '')} cannot be empty.`);
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
function modelAddProviderChoices(loadedConfig) {
|
|
120
|
+
const configured = loadedConfig.config.providers;
|
|
121
|
+
const registry = (0, core_1.listProviderRegistry)();
|
|
122
|
+
const registryNames = new Set(registry.map(provider => provider.name));
|
|
123
|
+
const choices = registry.map(entry => providerChoiceFromRegistry(entry, loadedConfig));
|
|
124
|
+
const extraConfigured = Object.entries(configured)
|
|
125
|
+
.filter(([name]) => !registryNames.has(name))
|
|
126
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
127
|
+
.map(([name, provider]) => ({
|
|
128
|
+
name,
|
|
129
|
+
label: name,
|
|
130
|
+
kind: 'configured',
|
|
131
|
+
type: provider.type,
|
|
132
|
+
baseUrl: provider.baseUrl,
|
|
133
|
+
apiKeyEnv: provider.apiKeyEnv,
|
|
134
|
+
isDefault: name === loadedConfig.config.default.provider,
|
|
135
|
+
configured: true,
|
|
136
|
+
}));
|
|
137
|
+
return [...choices, ...extraConfigured];
|
|
138
|
+
}
|
|
139
|
+
function providerChoiceFromRegistry(entry, loadedConfig) {
|
|
140
|
+
const configured = loadedConfig.config.providers[entry.name];
|
|
141
|
+
return {
|
|
142
|
+
name: entry.name,
|
|
143
|
+
label: entry.label,
|
|
144
|
+
kind: entry.kind,
|
|
145
|
+
type: configured?.type ?? entry.type,
|
|
146
|
+
baseUrl: configured?.baseUrl ?? entry.defaultBaseUrl,
|
|
147
|
+
apiKeyEnv: configured?.apiKeyEnv ?? entry.apiKeyEnv,
|
|
148
|
+
isDefault: entry.name === loadedConfig.config.default.provider,
|
|
149
|
+
configured: Boolean(configured),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function formatProvider(provider) {
|
|
153
|
+
return `${provider.name} — ${provider.label}`;
|
|
154
|
+
}
|
|
155
|
+
async function promptProviderSetupIfNeeded(loadedConfig, getPrompt, style, provider, options) {
|
|
156
|
+
const entry = (0, core_1.listProviderRegistry)().find(item => item.name === provider);
|
|
157
|
+
if (entry?.kind !== 'oauth')
|
|
158
|
+
return;
|
|
159
|
+
if (providerHasUsableCredential(loadedConfig.config.providers[provider], options))
|
|
160
|
+
return;
|
|
161
|
+
await reauthenticateOAuthProvider(loadedConfig, getPrompt, style, provider);
|
|
162
|
+
}
|
|
163
|
+
/** Force a fresh credential flow for a registry-managed OAuth provider while
|
|
164
|
+
* preserving its non-credential settings (for example proxy and base URL). */
|
|
165
|
+
async function reauthenticateOAuthProvider(loadedConfig, getPrompt, style, provider) {
|
|
166
|
+
const entry = (0, core_1.listProviderRegistry)().find(item => item.name === provider);
|
|
167
|
+
if (!entry) {
|
|
168
|
+
throw core_1.MarifoldError.configInvalid(`Unknown provider '${provider}'.`);
|
|
169
|
+
}
|
|
170
|
+
if (entry.kind !== 'oauth') {
|
|
171
|
+
throw core_1.MarifoldError.configInvalid(`Provider '${provider}' does not use Marifold-managed OAuth. Update its API key or API key environment variable instead.`);
|
|
172
|
+
}
|
|
173
|
+
const current = loadedConfig.config.providers[provider] ?? { type: entry.type };
|
|
174
|
+
const credentials = await promptOAuthCredentials(getPrompt, style, entry, current.proxy);
|
|
175
|
+
const updated = applyOAuthCredentials(current, entry, credentials);
|
|
176
|
+
loadedConfig.config.providers[provider] = updated;
|
|
177
|
+
return updated;
|
|
178
|
+
}
|
|
179
|
+
/** Pure credential replacement used by setup and explicit re-authentication. */
|
|
180
|
+
function applyOAuthCredentials(current, entry, credentials) {
|
|
181
|
+
const updated = {
|
|
182
|
+
...current,
|
|
183
|
+
type: entry.type,
|
|
184
|
+
baseUrl: credentials.baseUrl ?? current.baseUrl ?? entry.defaultBaseUrl,
|
|
185
|
+
apiKeyEnv: current.apiKeyEnv ?? entry.apiKeyEnv,
|
|
186
|
+
apiKey: credentials.apiKey,
|
|
187
|
+
oauthToken: credentials.oauthToken,
|
|
188
|
+
apiKeyExpiresAt: credentials.apiKeyExpiresAt,
|
|
189
|
+
accountId: credentials.accountId,
|
|
190
|
+
};
|
|
191
|
+
// Keep the in-memory config as clean as the serialized TOML: switching from
|
|
192
|
+
// OAuth to a manual credential must not retain an obsolete refresh token,
|
|
193
|
+
// expiry, or ChatGPT account id.
|
|
194
|
+
if (!updated.apiKeyEnv)
|
|
195
|
+
delete updated.apiKeyEnv;
|
|
196
|
+
if (!updated.apiKey)
|
|
197
|
+
delete updated.apiKey;
|
|
198
|
+
if (!updated.oauthToken)
|
|
199
|
+
delete updated.oauthToken;
|
|
200
|
+
if (updated.apiKeyExpiresAt === undefined)
|
|
201
|
+
delete updated.apiKeyExpiresAt;
|
|
202
|
+
if (!updated.accountId)
|
|
203
|
+
delete updated.accountId;
|
|
204
|
+
return updated;
|
|
205
|
+
}
|
|
206
|
+
async function promptOAuthCredentials(getPrompt, style, provider, proxy) {
|
|
207
|
+
if (provider.name === 'github_copilot')
|
|
208
|
+
return promptGitHubCopilotCredentials(getPrompt, style, provider);
|
|
209
|
+
if (provider.name === 'chatgpt')
|
|
210
|
+
return promptChatGptCredentials(getPrompt, style, provider);
|
|
211
|
+
if (provider.name === 'xai')
|
|
212
|
+
return promptXaiCredentials(getPrompt, style, provider, proxy);
|
|
213
|
+
const token = await readRequiredSecret(getPrompt, style, 'OAuth token: ');
|
|
214
|
+
return {
|
|
215
|
+
apiKey: token,
|
|
216
|
+
baseUrl: provider.defaultBaseUrl,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function promptGitHubCopilotCredentials(getPrompt, style, provider) {
|
|
220
|
+
const method = await selectLabeledChoice(getPrompt, style, 'GitHub Copilot authorization:', [
|
|
221
|
+
{ label: 'Authorize with GitHub device code (OAuth)', value: 'device' },
|
|
222
|
+
{ label: 'Paste token manually', value: 'manual' },
|
|
223
|
+
]);
|
|
224
|
+
if (method === 'device')
|
|
225
|
+
return (0, GitHubCopilotAuth_1.authorizeGitHubCopilotWithDevice)();
|
|
226
|
+
const tokenType = await selectLabeledChoice(getPrompt, style, 'Token type:', [
|
|
227
|
+
{ label: 'GitHub OAuth/PAT token (exchange now)', value: 'github' },
|
|
228
|
+
{ label: 'Copilot IDE token (starts with tid=)', value: 'copilot' },
|
|
229
|
+
]);
|
|
230
|
+
const token = await readRequiredSecret(getPrompt, style, 'Token: ');
|
|
231
|
+
if (tokenType === 'copilot') {
|
|
232
|
+
if (!(0, GitHubCopilotAuth_1.looksLikeCopilotIdeToken)(token)) {
|
|
233
|
+
process.stderr.write(style.yellow('Token does not look like a Copilot IDE token; saving it anyway.\n'));
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
apiKey: token,
|
|
237
|
+
baseUrl: provider.defaultBaseUrl,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const copilot = await (0, GitHubCopilotAuth_1.exchangeGitHubTokenForCopilotToken)(token);
|
|
241
|
+
return {
|
|
242
|
+
apiKey: copilot.token,
|
|
243
|
+
baseUrl: copilot.baseUrl,
|
|
244
|
+
oauthToken: token,
|
|
245
|
+
apiKeyExpiresAt: copilot.expiresAt,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async function promptChatGptCredentials(getPrompt, style, provider) {
|
|
249
|
+
const method = await selectLabeledChoice(getPrompt, style, 'ChatGPT credential:', [
|
|
250
|
+
{ label: 'Sign in with ChatGPT in browser (OAuth)', value: 'oauth' },
|
|
251
|
+
{ label: 'Paste OpenAI API key', value: 'api_key' },
|
|
252
|
+
]);
|
|
253
|
+
if (method === 'oauth') {
|
|
254
|
+
const tokens = await (0, ChatGptAuth_1.authorizeChatGptWithBrowser)();
|
|
255
|
+
return {
|
|
256
|
+
// The OAuth access token is the Codex-backend bearer credential; ChatGPT
|
|
257
|
+
// subscription accounts have no platform API key to exchange for.
|
|
258
|
+
apiKey: tokens.accessToken,
|
|
259
|
+
baseUrl: provider.defaultBaseUrl,
|
|
260
|
+
oauthToken: tokens.refreshToken,
|
|
261
|
+
apiKeyExpiresAt: tokens.expiresAt,
|
|
262
|
+
accountId: tokens.accountId,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
apiKey: await readRequiredSecret(getPrompt, style, 'OpenAI API key: '),
|
|
267
|
+
baseUrl: provider.defaultBaseUrl,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
async function promptXaiCredentials(getPrompt, style, provider, proxy) {
|
|
271
|
+
// SuperGrok / X Premium+ subscription OAuth is the intended path. A raw key is
|
|
272
|
+
// still honored via the XAI_API_KEY env var (apiKeyEnv), so the picker stays
|
|
273
|
+
// OAuth-only rather than prompting for a key to store in config. The paste
|
|
274
|
+
// callback lets the user finish when xAI shows a code instead of redirecting.
|
|
275
|
+
const tokens = await (0, XaiAuth_1.authorizeXaiWithBrowser)(text => process.stdout.write(text), async (label) => getPrompt().readUserMessage(style.bold(label)), { proxy });
|
|
276
|
+
return {
|
|
277
|
+
// api.x.ai is plain OpenAI-compatible: the OAuth access token is the Bearer
|
|
278
|
+
// credential directly, refreshed from the stored refresh token.
|
|
279
|
+
apiKey: tokens.accessToken,
|
|
280
|
+
baseUrl: provider.defaultBaseUrl,
|
|
281
|
+
oauthToken: tokens.refreshToken,
|
|
282
|
+
apiKeyExpiresAt: tokens.expiresAt,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async function selectLabeledChoice(getPrompt, style, message, options) {
|
|
286
|
+
const selected = await (0, TerminalSelect_1.selectTerminalOption)(message, options);
|
|
287
|
+
if (selected !== undefined)
|
|
288
|
+
return selected;
|
|
289
|
+
process.stdout.write(`${message}\n`);
|
|
290
|
+
options.forEach((option, index) => {
|
|
291
|
+
process.stdout.write(` ${index + 1}. ${option.label}\n`);
|
|
292
|
+
});
|
|
293
|
+
return readChoice(getPrompt(), style, message.replace(/:\s*$/, ''), options.map(option => option.value), {
|
|
294
|
+
defaultIndex: 0,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async function readRequiredSecret(getPrompt, style, label) {
|
|
298
|
+
const value = await (0, SecretPrompt_1.readSecretLine)(style.bold(label), getPrompt);
|
|
299
|
+
if (!value)
|
|
300
|
+
throw core_1.MarifoldError.configInvalid(`${label.replace(/:\s*$/, '')} cannot be empty.`);
|
|
301
|
+
return value;
|
|
302
|
+
}
|
|
303
|
+
function providerHasUsableCredential(provider, options) {
|
|
304
|
+
if (options.apiKeyEnv)
|
|
305
|
+
return true;
|
|
306
|
+
if (!provider)
|
|
307
|
+
return false;
|
|
308
|
+
if (provider.apiKeyEnv && process.env[provider.apiKeyEnv])
|
|
309
|
+
return true;
|
|
310
|
+
if (provider.apiKey) {
|
|
311
|
+
const refreshWindowSeconds = 60;
|
|
312
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
313
|
+
return provider.apiKeyExpiresAt === undefined
|
|
314
|
+
|| provider.apiKeyExpiresAt > nowSeconds + refreshWindowSeconds;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function savedModelsForProvider(loadedConfig, provider) {
|
|
319
|
+
return loadedConfig.config.models.options
|
|
320
|
+
.filter(option => option.startsWith(`${provider}/`))
|
|
321
|
+
.map(option => option.slice(provider.length + 1))
|
|
322
|
+
.filter(model => model.length > 0);
|
|
323
|
+
}
|
|
324
|
+
function uniqueSorted(values) {
|
|
325
|
+
return [...new Set(values)].sort();
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=ModelPicker.js.map
|