ima2-gen 1.1.9 → 1.1.11
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 +10 -1
- package/bin/commands/annotate.js +5 -3
- package/bin/commands/annotate.ts +13 -12
- package/bin/commands/cancel.js +5 -2
- package/bin/commands/cancel.ts +6 -3
- package/bin/commands/canvas-versions.js +4 -4
- package/bin/commands/canvas-versions.ts +10 -10
- package/bin/commands/capabilities.js +90 -0
- package/bin/commands/capabilities.ts +93 -0
- package/bin/commands/cardnews.js +3 -1
- package/bin/commands/cardnews.ts +18 -17
- package/bin/commands/comfy.js +2 -2
- package/bin/commands/comfy.ts +4 -4
- package/bin/commands/config.js +11 -143
- package/bin/commands/config.ts +33 -147
- package/bin/commands/defaults.js +180 -0
- package/bin/commands/defaults.ts +192 -0
- package/bin/commands/edit.js +45 -9
- package/bin/commands/edit.ts +44 -10
- package/bin/commands/gen.js +55 -14
- package/bin/commands/gen.ts +56 -18
- package/bin/commands/history.js +2 -1
- package/bin/commands/history.ts +11 -10
- package/bin/commands/ls.js +10 -5
- package/bin/commands/ls.ts +15 -11
- package/bin/commands/metadata.js +4 -1
- package/bin/commands/metadata.ts +5 -2
- package/bin/commands/multimode.js +64 -7
- package/bin/commands/multimode.ts +59 -9
- package/bin/commands/node.js +18 -8
- package/bin/commands/node.ts +24 -15
- package/bin/commands/observability.js +7 -7
- package/bin/commands/observability.ts +21 -21
- package/bin/commands/ping.js +4 -2
- package/bin/commands/ping.ts +5 -3
- package/bin/commands/prompt.js +14 -11
- package/bin/commands/prompt.ts +40 -38
- package/bin/commands/ps.js +10 -7
- package/bin/commands/ps.ts +15 -12
- package/bin/commands/session.js +4 -3
- package/bin/commands/session.ts +23 -22
- package/bin/commands/show.js +5 -2
- package/bin/commands/show.ts +8 -5
- package/bin/commands/skill.js +62 -0
- package/bin/commands/skill.ts +70 -0
- package/bin/ima2.js +14 -5
- package/bin/ima2.ts +16 -7
- package/bin/lib/args.ts +20 -1
- package/bin/lib/client.ts +20 -7
- package/bin/lib/config-store.js +156 -0
- package/bin/lib/config-store.ts +163 -0
- package/bin/lib/error-hints.ts +3 -3
- package/bin/lib/files.ts +8 -8
- package/bin/lib/output.js +19 -17
- package/bin/lib/output.ts +38 -23
- package/bin/lib/platform.js +3 -1
- package/bin/lib/platform.ts +9 -7
- package/bin/lib/recover-output.js +104 -0
- package/bin/lib/recover-output.ts +139 -0
- package/bin/lib/star-prompt.ts +1 -1
- package/bin/lib/storage-doctor.ts +3 -2
- package/config.js +1 -1
- package/config.ts +8 -6
- package/docs/CLI.md +64 -8
- package/docs/migration/runtime-test-inventory.md +146 -0
- package/lib/assetLifecycle.ts +8 -8
- package/lib/canvasVersionStore.js +2 -0
- package/lib/canvasVersionStore.ts +54 -12
- package/lib/capabilities.js +70 -0
- package/lib/capabilities.ts +84 -0
- package/lib/cardNewsGenerator.js +13 -3
- package/lib/cardNewsGenerator.ts +123 -14
- package/lib/cardNewsJobStore.ts +47 -12
- package/lib/cardNewsManifestStore.js +13 -6
- package/lib/cardNewsManifestStore.ts +56 -14
- package/lib/cardNewsPlanner.js +11 -9
- package/lib/cardNewsPlanner.ts +86 -30
- package/lib/cardNewsPlannerClient.js +23 -10
- package/lib/cardNewsPlannerClient.ts +58 -17
- package/lib/cardNewsPlannerPrompt.js +2 -0
- package/lib/cardNewsPlannerPrompt.ts +2 -0
- package/lib/cardNewsPlannerSchema.js +43 -36
- package/lib/cardNewsPlannerSchema.ts +120 -58
- package/lib/cardNewsTemplateStore.js +20 -35
- package/lib/cardNewsTemplateStore.ts +100 -58
- package/lib/codexDetect.js +5 -3
- package/lib/codexDetect.ts +5 -3
- package/lib/comfyBridge.js +3 -1
- package/lib/comfyBridge.ts +37 -16
- package/lib/db.ts +5 -5
- package/lib/errInfo.js +32 -0
- package/lib/errInfo.ts +43 -0
- package/lib/errorClassify.ts +2 -2
- package/lib/generationCancel.js +18 -0
- package/lib/generationCancel.ts +28 -0
- package/lib/generationErrors.ts +37 -11
- package/lib/historyIndex.js +34 -0
- package/lib/historyIndex.ts +51 -0
- package/lib/historyList.js +10 -6
- package/lib/historyList.ts +17 -13
- package/lib/imageMetadata.js +1 -1
- package/lib/imageMetadata.ts +8 -8
- package/lib/imageMetadataStore.ts +6 -6
- package/lib/imageModels.ts +6 -4
- package/lib/inflight.js +32 -8
- package/lib/inflight.ts +93 -16
- package/lib/localImportStore.js +2 -0
- package/lib/localImportStore.ts +8 -5
- package/lib/logger.js +7 -5
- package/lib/logger.ts +34 -23
- package/lib/nodeStore.js +2 -0
- package/lib/nodeStore.ts +20 -10
- package/lib/oauthLauncher.js +2 -2
- package/lib/oauthLauncher.ts +7 -6
- package/lib/oauthNormalize.ts +1 -1
- package/lib/oauthProxy/errors.js +93 -0
- package/lib/oauthProxy/errors.ts +128 -0
- package/lib/oauthProxy/generators.js +426 -0
- package/lib/oauthProxy/generators.ts +494 -0
- package/lib/oauthProxy/index.js +8 -0
- package/lib/oauthProxy/index.ts +28 -0
- package/lib/oauthProxy/prompts.js +99 -0
- package/lib/oauthProxy/prompts.ts +123 -0
- package/lib/oauthProxy/references.js +32 -0
- package/lib/oauthProxy/references.ts +45 -0
- package/lib/oauthProxy/runtime.js +101 -0
- package/lib/oauthProxy/runtime.ts +115 -0
- package/lib/oauthProxy/streams.js +211 -0
- package/lib/oauthProxy/streams.ts +232 -0
- package/lib/oauthProxy/types.js +6 -0
- package/lib/oauthProxy/types.ts +9 -0
- package/lib/oauthProxy.js +3 -911
- package/lib/oauthProxy.ts +3 -995
- package/lib/openDirectory.js +5 -3
- package/lib/openDirectory.ts +9 -7
- package/lib/pngInfo.ts +2 -2
- package/lib/promptImport/curatedSources.ts +4 -2
- package/lib/promptImport/discoveryRegistry.js +17 -9
- package/lib/promptImport/discoveryRegistry.ts +121 -28
- package/lib/promptImport/errors.ts +6 -6
- package/lib/promptImport/githubDiscovery.js +13 -7
- package/lib/promptImport/githubDiscovery.ts +84 -23
- package/lib/promptImport/githubFolder.js +22 -14
- package/lib/promptImport/githubFolder.ts +130 -41
- package/lib/promptImport/githubSource.js +3 -1
- package/lib/promptImport/githubSource.ts +32 -14
- package/lib/promptImport/gptImageHints.ts +10 -8
- package/lib/promptImport/parsePromptCandidates.js +2 -0
- package/lib/promptImport/parsePromptCandidates.ts +43 -17
- package/lib/promptImport/promptIndex.js +33 -23
- package/lib/promptImport/promptIndex.ts +124 -46
- package/lib/promptImport/rankPromptCandidates.js +2 -2
- package/lib/promptImport/rankPromptCandidates.ts +22 -6
- package/lib/promptImport/types.js +1 -0
- package/lib/promptImport/types.ts +103 -0
- package/lib/promptSafetyPolicy.js +5 -0
- package/lib/promptSafetyPolicy.ts +5 -0
- package/lib/providerOptions.ts +3 -2
- package/lib/referenceImageCompress.ts +15 -6
- package/lib/refs.js +2 -0
- package/lib/refs.ts +27 -11
- package/lib/requestLogger.ts +4 -3
- package/lib/responsesImageAdapter.js +54 -17
- package/lib/responsesImageAdapter.ts +169 -36
- package/lib/runtimeContext.js +100 -0
- package/lib/runtimeContext.ts +131 -0
- package/lib/runtimePorts.js +2 -1
- package/lib/runtimePorts.ts +28 -16
- package/lib/sessionStore.js +7 -5
- package/lib/sessionStore.ts +73 -37
- package/lib/storageMigration.js +18 -11
- package/lib/storageMigration.ts +63 -37
- package/lib/styleSheet.js +7 -6
- package/lib/styleSheet.ts +34 -23
- package/lib/visibleTextLanguagePolicy.js +7 -0
- package/lib/visibleTextLanguagePolicy.ts +7 -0
- package/package.json +6 -3
- package/routes/annotations.js +9 -4
- package/routes/annotations.ts +35 -12
- package/routes/canvasVersions.js +8 -3
- package/routes/canvasVersions.ts +14 -9
- package/routes/capabilities.js +13 -0
- package/routes/capabilities.ts +18 -0
- package/routes/cardNews.js +31 -18
- package/routes/cardNews.ts +66 -38
- package/routes/comfy.js +6 -3
- package/routes/comfy.ts +10 -6
- package/routes/edit.js +38 -7
- package/routes/edit.ts +63 -12
- package/routes/generate.js +71 -23
- package/routes/generate.ts +85 -31
- package/routes/health.js +9 -6
- package/routes/health.ts +17 -13
- package/routes/history.js +85 -36
- package/routes/history.ts +112 -44
- package/routes/imageImport.js +6 -2
- package/routes/imageImport.ts +9 -5
- package/routes/index.js +5 -1
- package/routes/index.ts +6 -1
- package/routes/metadata.js +9 -4
- package/routes/metadata.ts +13 -7
- package/routes/multimode.js +161 -38
- package/routes/multimode.ts +216 -58
- package/routes/nodes.js +55 -26
- package/routes/nodes.ts +108 -40
- package/routes/promptImport.js +42 -36
- package/routes/promptImport.ts +89 -64
- package/routes/prompts.js +62 -39
- package/routes/prompts.ts +120 -71
- package/routes/sessions.js +46 -24
- package/routes/sessions.ts +60 -35
- package/routes/storage.js +4 -2
- package/routes/storage.ts +13 -5
- package/server.js +25 -21
- package/server.ts +57 -37
- package/skills/ima2/SKILL.md +206 -0
- package/ui/dist/.vite/manifest.json +11 -10
- package/ui/dist/assets/{CardNewsWorkspace-BJOCey7Z.js → CardNewsWorkspace-j4ULtNdk.js} +1 -1
- package/ui/dist/assets/NodeCanvas-Bc7BUViM.js +7 -0
- package/ui/dist/assets/{PromptImportDialog-Dqu1VpUh.js → PromptImportDialog-DBKprBEo.js} +2 -2
- package/ui/dist/assets/{PromptImportDiscoverySection-Dg8T9X0L.js → PromptImportDiscoverySection-m5v55Zsy.js} +1 -1
- package/ui/dist/assets/PromptImportFolderSection-DnPvJkfJ.js +1 -0
- package/ui/dist/assets/PromptLibraryPanel-BMSqfK9C.js +2 -0
- package/ui/dist/assets/SettingsWorkspace-Cj3LD0uu.js +1 -0
- package/ui/dist/assets/{index-Cvld7dUZ.js → index-9aOJKFI-.js} +1 -1
- package/ui/dist/assets/index-De-AWE6B.css +1 -0
- package/ui/dist/assets/index-tQhOLR-C.js +28 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/NodeCanvas-C3dzYNsk.js +0 -7
- package/ui/dist/assets/PromptImportFolderSection-DBaqsFO4.js +0 -1
- package/ui/dist/assets/PromptLibraryPanel-p5QqR97M.js +0 -2
- package/ui/dist/assets/SettingsWorkspace-B5bSAZ6u.js +0 -1
- package/ui/dist/assets/index-C9cXwiWE.js +0 -25
- package/ui/dist/assets/index-CGMIkZXn.css +0 -1
package/bin/commands/comfy.ts
CHANGED
|
@@ -18,7 +18,7 @@ const FLAGS = {
|
|
|
18
18
|
help: { short: "h", type: "boolean" },
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
async function exportSub(argv) {
|
|
21
|
+
async function exportSub(argv: string[]) {
|
|
22
22
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
23
23
|
const filename = args.positional[0];
|
|
24
24
|
if (!filename) die(2, "filename required");
|
|
@@ -28,8 +28,8 @@ async function exportSub(argv) {
|
|
|
28
28
|
const resp: any = await request(server.base, "/api/comfy/export-image", {
|
|
29
29
|
method: "POST",
|
|
30
30
|
body: { filename },
|
|
31
|
-
}).catch((e) => die(exitCodeForError(e), `${
|
|
32
|
-
const target = args.out || `${filename}.workflow.json
|
|
31
|
+
}).catch((e: unknown) => { const err = e as { message?: string; code?: string }; die(exitCodeForError(e), `${err.message}${err.code ? ` (${err.code})` : ""}`); });
|
|
32
|
+
const target = String(args.out || `${filename}.workflow.json`);
|
|
33
33
|
if (!args.force) {
|
|
34
34
|
try {
|
|
35
35
|
await access(target);
|
|
@@ -45,7 +45,7 @@ const SUB: Record<string, (argv: any[]) => Promise<void>> = {
|
|
|
45
45
|
export: exportSub,
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
export default async function comfyCmd(argv) {
|
|
48
|
+
export default async function comfyCmd(argv: string[]) {
|
|
49
49
|
const sub = argv[0];
|
|
50
50
|
if (!sub || sub === "--help" || sub === "-h") { out(HELP); return; }
|
|
51
51
|
const handler = SUB[sub];
|
package/bin/commands/config.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
1
|
import { createInterface } from "readline/promises";
|
|
3
2
|
import { parseArgs } from "../lib/args.js";
|
|
4
3
|
import { out, die, color, json } from "../lib/output.js";
|
|
5
|
-
import {
|
|
6
|
-
const CONFIG_FILE = runtimeConfig.storage.configFile;
|
|
7
|
-
const CONFIG_DIR = runtimeConfig.storage.configDir;
|
|
4
|
+
import { CONFIG_FILE, buildEffectiveConfig, deleteNestedKey, displayPath, envOverrideForKey, getNestedKey, isAuthConfigKey, isSensitiveConfigKey, isWritableConfigKey, loadFileCfg, parseConfigValue, redactValue, restartNotice, saveFileCfg, setNestedKey, } from "../lib/config-store.js";
|
|
8
5
|
const HELP = `
|
|
9
6
|
ima2 config <subcommand> [options]
|
|
10
7
|
|
|
@@ -28,130 +25,6 @@ const FLAGS = {
|
|
|
28
25
|
yes: { short: "y", type: "boolean" },
|
|
29
26
|
help: { short: "h", type: "boolean" },
|
|
30
27
|
};
|
|
31
|
-
// Keys config set is allowed to write
|
|
32
|
-
const KNOWN_KEYS = new Set([
|
|
33
|
-
"imageModels.default",
|
|
34
|
-
"imageModels.reasoningEffort",
|
|
35
|
-
"log.level",
|
|
36
|
-
"features.cardNews",
|
|
37
|
-
"cardNewsPlanner.enabled",
|
|
38
|
-
"cardNewsPlanner.model",
|
|
39
|
-
"cardNewsPlanner.timeoutMs",
|
|
40
|
-
"cardNewsPlanner.deterministicFallback",
|
|
41
|
-
"comfy.defaultUrl",
|
|
42
|
-
"comfy.uploadTimeoutMs",
|
|
43
|
-
"comfy.maxUploadBytes",
|
|
44
|
-
"storage.generatedDir",
|
|
45
|
-
"storage.generatedDirName",
|
|
46
|
-
"server.port",
|
|
47
|
-
"server.host",
|
|
48
|
-
"server.bodyLimit",
|
|
49
|
-
"oauth.proxyPort",
|
|
50
|
-
"oauth.statusTimeoutMs",
|
|
51
|
-
"oauth.restartDelayMs",
|
|
52
|
-
"limits.maxRefCount",
|
|
53
|
-
"limits.maxParallel",
|
|
54
|
-
"history.defaultPageSize",
|
|
55
|
-
"history.maxPageCap",
|
|
56
|
-
]);
|
|
57
|
-
// Auth keys live in the same file but must go through setup/login
|
|
58
|
-
const AUTH_KEYS = new Set(["provider", "apiKey"]);
|
|
59
|
-
const REDACT_PATTERN = /token|secret|apikey|password/i;
|
|
60
|
-
const ALWAYS_REDACT = new Set(["provider", "apiKey", "oauth.token", "oauth.refreshToken"]);
|
|
61
|
-
// Env var that overrides each writable key
|
|
62
|
-
const KEY_TO_ENV = {
|
|
63
|
-
"imageModels.default": "IMA2_IMAGE_MODEL_DEFAULT",
|
|
64
|
-
"imageModels.reasoningEffort": "IMA2_REASONING_EFFORT",
|
|
65
|
-
"log.level": "IMA2_LOG_LEVEL",
|
|
66
|
-
"features.cardNews": "IMA2_CARD_NEWS",
|
|
67
|
-
"server.port": "IMA2_PORT",
|
|
68
|
-
"server.host": "IMA2_HOST",
|
|
69
|
-
"server.bodyLimit": "IMA2_BODY_LIMIT",
|
|
70
|
-
"oauth.proxyPort": "IMA2_OAUTH_PROXY_PORT",
|
|
71
|
-
"storage.generatedDir": "IMA2_GENERATED_DIR",
|
|
72
|
-
"cardNewsPlanner.enabled": "IMA2_CARD_NEWS_PLANNER",
|
|
73
|
-
"cardNewsPlanner.model": "IMA2_CARD_NEWS_PLANNER_MODEL",
|
|
74
|
-
"cardNewsPlanner.timeoutMs": "IMA2_CARD_NEWS_PLANNER_TIMEOUT_MS",
|
|
75
|
-
"limits.maxParallel": "IMA2_MAX_PARALLEL",
|
|
76
|
-
"limits.maxRefCount": "IMA2_MAX_REF_COUNT",
|
|
77
|
-
"history.defaultPageSize": "IMA2_HISTORY_PAGE_SIZE",
|
|
78
|
-
};
|
|
79
|
-
function redactValue(key, value) {
|
|
80
|
-
if (ALWAYS_REDACT.has(key) || REDACT_PATTERN.test(key)) {
|
|
81
|
-
return value ? "<redacted>" : value;
|
|
82
|
-
}
|
|
83
|
-
return value;
|
|
84
|
-
}
|
|
85
|
-
function loadFileCfg() {
|
|
86
|
-
if (!existsSync(CONFIG_FILE))
|
|
87
|
-
return {};
|
|
88
|
-
try {
|
|
89
|
-
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return {};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function saveFileCfg(cfg) {
|
|
96
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
97
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
98
|
-
}
|
|
99
|
-
function getNestedKey(obj, dotKey) {
|
|
100
|
-
const parts = dotKey.split(".");
|
|
101
|
-
let cur = obj;
|
|
102
|
-
for (const p of parts) {
|
|
103
|
-
if (cur == null || typeof cur !== "object")
|
|
104
|
-
return undefined;
|
|
105
|
-
cur = cur[p];
|
|
106
|
-
}
|
|
107
|
-
return cur;
|
|
108
|
-
}
|
|
109
|
-
function setNestedKey(obj, dotKey, value) {
|
|
110
|
-
const parts = dotKey.split(".");
|
|
111
|
-
let cur = obj;
|
|
112
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
113
|
-
if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object")
|
|
114
|
-
cur[parts[i]] = {};
|
|
115
|
-
cur = cur[parts[i]];
|
|
116
|
-
}
|
|
117
|
-
cur[parts[parts.length - 1]] = value;
|
|
118
|
-
}
|
|
119
|
-
function deleteNestedKey(obj, dotKey) {
|
|
120
|
-
const parts = dotKey.split(".");
|
|
121
|
-
let cur = obj;
|
|
122
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
123
|
-
if (cur == null || typeof cur !== "object")
|
|
124
|
-
return false;
|
|
125
|
-
cur = cur[parts[i]];
|
|
126
|
-
}
|
|
127
|
-
if (cur == null || typeof cur !== "object")
|
|
128
|
-
return false;
|
|
129
|
-
const last = parts[parts.length - 1];
|
|
130
|
-
if (!(last in cur))
|
|
131
|
-
return false;
|
|
132
|
-
delete cur[last];
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
function stripSets(v) {
|
|
136
|
-
if (v instanceof Set)
|
|
137
|
-
return [...v];
|
|
138
|
-
if (Array.isArray(v))
|
|
139
|
-
return v.map(stripSets);
|
|
140
|
-
if (v && typeof v === "object") {
|
|
141
|
-
const r = {};
|
|
142
|
-
for (const [k, val] of Object.entries(v))
|
|
143
|
-
r[k] = stripSets(val);
|
|
144
|
-
return r;
|
|
145
|
-
}
|
|
146
|
-
return v;
|
|
147
|
-
}
|
|
148
|
-
function buildEffectiveConfig() {
|
|
149
|
-
return stripSets(runtimeConfig);
|
|
150
|
-
}
|
|
151
|
-
function displayPath(p) {
|
|
152
|
-
const home = process.env.HOME || "";
|
|
153
|
-
return home && p.startsWith(home) ? p.replace(home, "~") : p;
|
|
154
|
-
}
|
|
155
28
|
async function pathSub(_argv) {
|
|
156
29
|
out(CONFIG_FILE);
|
|
157
30
|
}
|
|
@@ -198,26 +71,21 @@ async function setSub(argv) {
|
|
|
198
71
|
const [key, rawValue] = args.positional;
|
|
199
72
|
if (!key || rawValue === undefined)
|
|
200
73
|
die(2, "usage: config set <key> <value>");
|
|
201
|
-
if (
|
|
74
|
+
if (isAuthConfigKey(key)) {
|
|
202
75
|
die(2, `"${key}" is an auth key. Use 'ima2 setup' or 'ima2 login' to change authentication.`);
|
|
203
76
|
}
|
|
204
|
-
if (!
|
|
77
|
+
if (!isWritableConfigKey(key)) {
|
|
205
78
|
die(2, `unknown config key: "${key}". Run 'ima2 config ls --effective' to see the config structure.`);
|
|
206
79
|
}
|
|
207
|
-
|
|
208
|
-
let value = rawValue;
|
|
209
|
-
try {
|
|
210
|
-
value = JSON.parse(rawValue);
|
|
211
|
-
}
|
|
212
|
-
catch { }
|
|
80
|
+
const value = parseConfigValue(rawValue);
|
|
213
81
|
// Warn if env var is overriding this key
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
out(color.yellow(`warning: env ${envVar}=${
|
|
82
|
+
const override = envOverrideForKey(key);
|
|
83
|
+
if (override) {
|
|
84
|
+
out(color.yellow(`warning: env ${override.envVar}=${override.value} is currently overriding this value.`));
|
|
217
85
|
out(`The file change will only apply after unsetting the env var and restarting the server.`);
|
|
218
86
|
}
|
|
219
87
|
// Confirm if writing a sensitive key
|
|
220
|
-
if ((
|
|
88
|
+
if (isSensitiveConfigKey(key) && !args.yes) {
|
|
221
89
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
222
90
|
const ans = await rl.question(`warning: "${key}" is a sensitive credential. Write to config file? [y/N] `);
|
|
223
91
|
rl.close();
|
|
@@ -230,14 +98,14 @@ async function setSub(argv) {
|
|
|
230
98
|
setNestedKey(fileCfg, key, value);
|
|
231
99
|
saveFileCfg(fileCfg);
|
|
232
100
|
out(color.green("✓ ") + `wrote ${key}=${JSON.stringify(value)} to ${displayPath(CONFIG_FILE)}`);
|
|
233
|
-
out(color.dim(
|
|
101
|
+
out(color.dim(restartNotice()));
|
|
234
102
|
}
|
|
235
103
|
async function rmSub(argv) {
|
|
236
104
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
237
105
|
const key = args.positional[0];
|
|
238
106
|
if (!key)
|
|
239
107
|
die(2, "key required. Usage: config rm <key>");
|
|
240
|
-
if (
|
|
108
|
+
if (isAuthConfigKey(key)) {
|
|
241
109
|
die(2, `"${key}" is an auth key. Use 'ima2 setup' or 'ima2 login' to change authentication.`);
|
|
242
110
|
}
|
|
243
111
|
const fileCfg = loadFileCfg();
|
|
@@ -248,7 +116,7 @@ async function rmSub(argv) {
|
|
|
248
116
|
}
|
|
249
117
|
saveFileCfg(fileCfg);
|
|
250
118
|
out(color.green("✓ ") + `removed ${key} from ${displayPath(CONFIG_FILE)}`);
|
|
251
|
-
out(color.dim(
|
|
119
|
+
out(color.dim(restartNotice()));
|
|
252
120
|
}
|
|
253
121
|
const SUB = {
|
|
254
122
|
path: pathSub,
|
package/bin/commands/config.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
1
|
import { createInterface } from "readline/promises";
|
|
3
2
|
import { parseArgs } from "../lib/args.js";
|
|
4
3
|
import { out, die, color, json } from "../lib/output.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import {
|
|
5
|
+
CONFIG_FILE,
|
|
6
|
+
buildEffectiveConfig,
|
|
7
|
+
deleteNestedKey,
|
|
8
|
+
displayPath,
|
|
9
|
+
envOverrideForKey,
|
|
10
|
+
getNestedKey,
|
|
11
|
+
isAuthConfigKey,
|
|
12
|
+
isSensitiveConfigKey,
|
|
13
|
+
isWritableConfigKey,
|
|
14
|
+
loadFileCfg,
|
|
15
|
+
parseConfigValue,
|
|
16
|
+
redactValue,
|
|
17
|
+
restartNotice,
|
|
18
|
+
saveFileCfg,
|
|
19
|
+
setNestedKey,
|
|
20
|
+
} from "../lib/config-store.js";
|
|
9
21
|
|
|
10
22
|
const HELP = `
|
|
11
23
|
ima2 config <subcommand> [options]
|
|
@@ -32,135 +44,11 @@ const FLAGS = {
|
|
|
32
44
|
help: { short: "h", type: "boolean" },
|
|
33
45
|
};
|
|
34
46
|
|
|
35
|
-
|
|
36
|
-
const KNOWN_KEYS = new Set([
|
|
37
|
-
"imageModels.default",
|
|
38
|
-
"imageModels.reasoningEffort",
|
|
39
|
-
"log.level",
|
|
40
|
-
"features.cardNews",
|
|
41
|
-
"cardNewsPlanner.enabled",
|
|
42
|
-
"cardNewsPlanner.model",
|
|
43
|
-
"cardNewsPlanner.timeoutMs",
|
|
44
|
-
"cardNewsPlanner.deterministicFallback",
|
|
45
|
-
"comfy.defaultUrl",
|
|
46
|
-
"comfy.uploadTimeoutMs",
|
|
47
|
-
"comfy.maxUploadBytes",
|
|
48
|
-
"storage.generatedDir",
|
|
49
|
-
"storage.generatedDirName",
|
|
50
|
-
"server.port",
|
|
51
|
-
"server.host",
|
|
52
|
-
"server.bodyLimit",
|
|
53
|
-
"oauth.proxyPort",
|
|
54
|
-
"oauth.statusTimeoutMs",
|
|
55
|
-
"oauth.restartDelayMs",
|
|
56
|
-
"limits.maxRefCount",
|
|
57
|
-
"limits.maxParallel",
|
|
58
|
-
"history.defaultPageSize",
|
|
59
|
-
"history.maxPageCap",
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
// Auth keys live in the same file but must go through setup/login
|
|
63
|
-
const AUTH_KEYS = new Set(["provider", "apiKey"]);
|
|
64
|
-
|
|
65
|
-
const REDACT_PATTERN = /token|secret|apikey|password/i;
|
|
66
|
-
const ALWAYS_REDACT = new Set(["provider", "apiKey", "oauth.token", "oauth.refreshToken"]);
|
|
67
|
-
|
|
68
|
-
// Env var that overrides each writable key
|
|
69
|
-
const KEY_TO_ENV: Record<string, string> = {
|
|
70
|
-
"imageModels.default": "IMA2_IMAGE_MODEL_DEFAULT",
|
|
71
|
-
"imageModels.reasoningEffort": "IMA2_REASONING_EFFORT",
|
|
72
|
-
"log.level": "IMA2_LOG_LEVEL",
|
|
73
|
-
"features.cardNews": "IMA2_CARD_NEWS",
|
|
74
|
-
"server.port": "IMA2_PORT",
|
|
75
|
-
"server.host": "IMA2_HOST",
|
|
76
|
-
"server.bodyLimit": "IMA2_BODY_LIMIT",
|
|
77
|
-
"oauth.proxyPort": "IMA2_OAUTH_PROXY_PORT",
|
|
78
|
-
"storage.generatedDir": "IMA2_GENERATED_DIR",
|
|
79
|
-
"cardNewsPlanner.enabled": "IMA2_CARD_NEWS_PLANNER",
|
|
80
|
-
"cardNewsPlanner.model": "IMA2_CARD_NEWS_PLANNER_MODEL",
|
|
81
|
-
"cardNewsPlanner.timeoutMs": "IMA2_CARD_NEWS_PLANNER_TIMEOUT_MS",
|
|
82
|
-
"limits.maxParallel": "IMA2_MAX_PARALLEL",
|
|
83
|
-
"limits.maxRefCount": "IMA2_MAX_REF_COUNT",
|
|
84
|
-
"history.defaultPageSize": "IMA2_HISTORY_PAGE_SIZE",
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
function redactValue(key: string, value: any): any {
|
|
88
|
-
if (ALWAYS_REDACT.has(key) || REDACT_PATTERN.test(key)) {
|
|
89
|
-
return value ? "<redacted>" : value;
|
|
90
|
-
}
|
|
91
|
-
return value;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function loadFileCfg(): Record<string, any> {
|
|
95
|
-
if (!existsSync(CONFIG_FILE)) return {};
|
|
96
|
-
try { return JSON.parse(readFileSync(CONFIG_FILE, "utf-8")); }
|
|
97
|
-
catch { return {}; }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function saveFileCfg(cfg: Record<string, any>) {
|
|
101
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
102
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getNestedKey(obj: any, dotKey: string): any {
|
|
106
|
-
const parts = dotKey.split(".");
|
|
107
|
-
let cur = obj;
|
|
108
|
-
for (const p of parts) {
|
|
109
|
-
if (cur == null || typeof cur !== "object") return undefined;
|
|
110
|
-
cur = cur[p];
|
|
111
|
-
}
|
|
112
|
-
return cur;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function setNestedKey(obj: any, dotKey: string, value: any): void {
|
|
116
|
-
const parts = dotKey.split(".");
|
|
117
|
-
let cur = obj;
|
|
118
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
119
|
-
if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") cur[parts[i]] = {};
|
|
120
|
-
cur = cur[parts[i]];
|
|
121
|
-
}
|
|
122
|
-
cur[parts[parts.length - 1]] = value;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function deleteNestedKey(obj: any, dotKey: string): boolean {
|
|
126
|
-
const parts = dotKey.split(".");
|
|
127
|
-
let cur = obj;
|
|
128
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
129
|
-
if (cur == null || typeof cur !== "object") return false;
|
|
130
|
-
cur = cur[parts[i]];
|
|
131
|
-
}
|
|
132
|
-
if (cur == null || typeof cur !== "object") return false;
|
|
133
|
-
const last = parts[parts.length - 1];
|
|
134
|
-
if (!(last in cur)) return false;
|
|
135
|
-
delete cur[last];
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function stripSets(v: any): any {
|
|
140
|
-
if (v instanceof Set) return [...v];
|
|
141
|
-
if (Array.isArray(v)) return v.map(stripSets);
|
|
142
|
-
if (v && typeof v === "object") {
|
|
143
|
-
const r: any = {};
|
|
144
|
-
for (const [k, val] of Object.entries(v)) r[k] = stripSets(val);
|
|
145
|
-
return r;
|
|
146
|
-
}
|
|
147
|
-
return v;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function buildEffectiveConfig(): Record<string, any> {
|
|
151
|
-
return stripSets(runtimeConfig);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function displayPath(p: string): string {
|
|
155
|
-
const home = process.env.HOME || "";
|
|
156
|
-
return home && p.startsWith(home) ? p.replace(home, "~") : p;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function pathSub(_argv) {
|
|
47
|
+
async function pathSub(_argv: string[]) {
|
|
160
48
|
out(CONFIG_FILE);
|
|
161
49
|
}
|
|
162
50
|
|
|
163
|
-
async function lsSub(argv) {
|
|
51
|
+
async function lsSub(argv: string[]) {
|
|
164
52
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
165
53
|
if (args.effective) {
|
|
166
54
|
const eff = buildEffectiveConfig();
|
|
@@ -173,7 +61,7 @@ async function lsSub(argv) {
|
|
|
173
61
|
}
|
|
174
62
|
}
|
|
175
63
|
|
|
176
|
-
async function getSub(argv) {
|
|
64
|
+
async function getSub(argv: string[]) {
|
|
177
65
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
178
66
|
const key = args.positional[0];
|
|
179
67
|
if (!key) die(2, "key required. Usage: config get <dotted.key>");
|
|
@@ -188,31 +76,29 @@ async function getSub(argv) {
|
|
|
188
76
|
}
|
|
189
77
|
}
|
|
190
78
|
|
|
191
|
-
async function setSub(argv) {
|
|
79
|
+
async function setSub(argv: string[]) {
|
|
192
80
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
193
81
|
const [key, rawValue] = args.positional;
|
|
194
82
|
if (!key || rawValue === undefined) die(2, "usage: config set <key> <value>");
|
|
195
83
|
|
|
196
|
-
if (
|
|
84
|
+
if (isAuthConfigKey(key)) {
|
|
197
85
|
die(2, `"${key}" is an auth key. Use 'ima2 setup' or 'ima2 login' to change authentication.`);
|
|
198
86
|
}
|
|
199
|
-
if (!
|
|
87
|
+
if (!isWritableConfigKey(key)) {
|
|
200
88
|
die(2, `unknown config key: "${key}". Run 'ima2 config ls --effective' to see the config structure.`);
|
|
201
89
|
}
|
|
202
90
|
|
|
203
|
-
|
|
204
|
-
let value: any = rawValue;
|
|
205
|
-
try { value = JSON.parse(rawValue); } catch {}
|
|
91
|
+
const value = parseConfigValue(rawValue);
|
|
206
92
|
|
|
207
93
|
// Warn if env var is overriding this key
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
out(color.yellow(`warning: env ${envVar}=${
|
|
94
|
+
const override = envOverrideForKey(key);
|
|
95
|
+
if (override) {
|
|
96
|
+
out(color.yellow(`warning: env ${override.envVar}=${override.value} is currently overriding this value.`));
|
|
211
97
|
out(`The file change will only apply after unsetting the env var and restarting the server.`);
|
|
212
98
|
}
|
|
213
99
|
|
|
214
100
|
// Confirm if writing a sensitive key
|
|
215
|
-
if ((
|
|
101
|
+
if (isSensitiveConfigKey(key) && !args.yes) {
|
|
216
102
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
217
103
|
const ans = await rl.question(`warning: "${key}" is a sensitive credential. Write to config file? [y/N] `);
|
|
218
104
|
rl.close();
|
|
@@ -224,15 +110,15 @@ async function setSub(argv) {
|
|
|
224
110
|
saveFileCfg(fileCfg);
|
|
225
111
|
|
|
226
112
|
out(color.green("✓ ") + `wrote ${key}=${JSON.stringify(value)} to ${displayPath(CONFIG_FILE)}`);
|
|
227
|
-
out(color.dim(
|
|
113
|
+
out(color.dim(restartNotice()));
|
|
228
114
|
}
|
|
229
115
|
|
|
230
|
-
async function rmSub(argv) {
|
|
116
|
+
async function rmSub(argv: string[]) {
|
|
231
117
|
const args = parseArgs(argv, { flags: FLAGS });
|
|
232
118
|
const key = args.positional[0];
|
|
233
119
|
if (!key) die(2, "key required. Usage: config rm <key>");
|
|
234
120
|
|
|
235
|
-
if (
|
|
121
|
+
if (isAuthConfigKey(key)) {
|
|
236
122
|
die(2, `"${key}" is an auth key. Use 'ima2 setup' or 'ima2 login' to change authentication.`);
|
|
237
123
|
}
|
|
238
124
|
|
|
@@ -244,7 +130,7 @@ async function rmSub(argv) {
|
|
|
244
130
|
}
|
|
245
131
|
saveFileCfg(fileCfg);
|
|
246
132
|
out(color.green("✓ ") + `removed ${key} from ${displayPath(CONFIG_FILE)}`);
|
|
247
|
-
out(color.dim(
|
|
133
|
+
out(color.dim(restartNotice()));
|
|
248
134
|
}
|
|
249
135
|
|
|
250
136
|
type Sub = (argv: any[]) => Promise<void>;
|
|
@@ -256,7 +142,7 @@ const SUB: Record<string, Sub> = {
|
|
|
256
142
|
rm: rmSub,
|
|
257
143
|
};
|
|
258
144
|
|
|
259
|
-
export default async function configCmd(argv) {
|
|
145
|
+
export default async function configCmd(argv: string[]) {
|
|
260
146
|
const sub = argv[0];
|
|
261
147
|
if (!sub || sub === "--help" || sub === "-h") { out(HELP); return; }
|
|
262
148
|
const handler = SUB[sub];
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { config } from "../../config.js";
|
|
2
|
+
import { parseArgs } from "../lib/args.js";
|
|
3
|
+
import { resolveServer, request } from "../lib/client.js";
|
|
4
|
+
import { buildEffectiveConfig, deleteNestedKey, displayPath, envOverrideForKey, getNestedKey, loadFileCfg, restartNotice, saveFileCfg, setNestedKey, CONFIG_FILE, } from "../lib/config-store.js";
|
|
5
|
+
import { color, die, dieWithError, json, out } from "../lib/output.js";
|
|
6
|
+
const MODEL_KEYS = ["imageModels.default", "apiProvider.defaultImageModel"];
|
|
7
|
+
const REASONING_KEYS = ["imageModels.reasoningEffort", "apiProvider.defaultReasoningEffort"];
|
|
8
|
+
const HELP = `
|
|
9
|
+
ima2 defaults [subcommand] [options]
|
|
10
|
+
|
|
11
|
+
Inspect or change persistent model/reasoning defaults.
|
|
12
|
+
|
|
13
|
+
Subcommands:
|
|
14
|
+
ls Show effective defaults
|
|
15
|
+
set model <model> Persist default model for OAuth and API paths
|
|
16
|
+
set reasoning <effort> Persist default reasoning effort for OAuth and API paths
|
|
17
|
+
reset model Remove persisted model defaults
|
|
18
|
+
reset reasoning Remove persisted reasoning defaults
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--json Print JSON
|
|
22
|
+
--local Do not query running server
|
|
23
|
+
--server <url> Query a specific running server for ls/default output
|
|
24
|
+
`;
|
|
25
|
+
const FLAGS = {
|
|
26
|
+
json: { type: "boolean" },
|
|
27
|
+
local: { type: "boolean" },
|
|
28
|
+
server: { type: "string" },
|
|
29
|
+
help: { short: "h", type: "boolean" },
|
|
30
|
+
};
|
|
31
|
+
function localDefaults() {
|
|
32
|
+
const effective = buildEffectiveConfig();
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
source: "local",
|
|
36
|
+
server: null,
|
|
37
|
+
defaults: {
|
|
38
|
+
oauth: {
|
|
39
|
+
model: getNestedKey(effective, "imageModels.default"),
|
|
40
|
+
reasoningEffort: getNestedKey(effective, "imageModels.reasoningEffort"),
|
|
41
|
+
},
|
|
42
|
+
api: {
|
|
43
|
+
model: getNestedKey(effective, "apiProvider.defaultImageModel"),
|
|
44
|
+
reasoningEffort: getNestedKey(effective, "apiProvider.defaultReasoningEffort"),
|
|
45
|
+
size: getNestedKey(effective, "apiProvider.defaultSize"),
|
|
46
|
+
webSearchEnabled: getNestedKey(effective, "apiProvider.allowWebSearch"),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function readDefaults(args) {
|
|
52
|
+
if (args.local)
|
|
53
|
+
return localDefaults();
|
|
54
|
+
try {
|
|
55
|
+
const server = await resolveServer({ serverFlag: args.server });
|
|
56
|
+
const capabilities = await request(server.base, "/api/capabilities", { timeoutMs: 5000 });
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
source: "server",
|
|
60
|
+
server: server.base,
|
|
61
|
+
defaults: capabilities.defaults,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (args.server)
|
|
66
|
+
throw error;
|
|
67
|
+
return localDefaults();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function printDefaults(payload) {
|
|
71
|
+
out(`ima2 defaults (${payload.source})`);
|
|
72
|
+
out(`server: ${payload.server || "none"}`);
|
|
73
|
+
out("");
|
|
74
|
+
out(`oauth model: ${payload.defaults?.oauth?.model}`);
|
|
75
|
+
out(`oauth reasoning: ${payload.defaults?.oauth?.reasoningEffort}`);
|
|
76
|
+
out(`api model: ${payload.defaults?.api?.model}`);
|
|
77
|
+
out(`api reasoning: ${payload.defaults?.api?.reasoningEffort}`);
|
|
78
|
+
if (payload.defaults?.api?.size)
|
|
79
|
+
out(`api size: ${payload.defaults.api.size}`);
|
|
80
|
+
if (payload.defaults?.api?.webSearchEnabled !== undefined) {
|
|
81
|
+
out(`api web search: ${payload.defaults.api.webSearchEnabled ? "enabled" : "disabled"}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function validateModel(value) {
|
|
85
|
+
if (!config.imageModels.valid.has(value)) {
|
|
86
|
+
die(2, `model must be one of: ${Array.from(config.imageModels.valid).join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function validateReasoning(value) {
|
|
90
|
+
if (!config.imageModels.validReasoningEfforts.has(value)) {
|
|
91
|
+
die(2, `reasoning must be one of: ${Array.from(config.imageModels.validReasoningEfforts).join(", ")}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function warnOverrides(keys) {
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const override = envOverrideForKey(key);
|
|
97
|
+
if (!override)
|
|
98
|
+
continue;
|
|
99
|
+
out(color.yellow(`warning: env ${override.envVar}=${override.value} is currently overriding ${key}.`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function setDefaults(keys, value) {
|
|
103
|
+
const fileCfg = loadFileCfg();
|
|
104
|
+
for (const key of keys)
|
|
105
|
+
setNestedKey(fileCfg, key, value);
|
|
106
|
+
saveFileCfg(fileCfg);
|
|
107
|
+
warnOverrides(keys);
|
|
108
|
+
out(color.green("✓ ") + `wrote ${keys.join(", ")}=${JSON.stringify(value)} to ${displayPath(CONFIG_FILE)}`);
|
|
109
|
+
out(color.dim(restartNotice()));
|
|
110
|
+
}
|
|
111
|
+
function resetDefaults(keys) {
|
|
112
|
+
const fileCfg = loadFileCfg();
|
|
113
|
+
let changed = false;
|
|
114
|
+
for (const key of keys)
|
|
115
|
+
changed = deleteNestedKey(fileCfg, key) || changed;
|
|
116
|
+
if (!changed) {
|
|
117
|
+
out(color.dim(`(no persisted defaults found for ${keys.join(", ")})`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
saveFileCfg(fileCfg);
|
|
121
|
+
out(color.green("✓ ") + `removed ${keys.join(", ")} from ${displayPath(CONFIG_FILE)}`);
|
|
122
|
+
out(color.dim(restartNotice()));
|
|
123
|
+
}
|
|
124
|
+
async function listSub(argv) {
|
|
125
|
+
const args = parseArgs(argv, { flags: FLAGS });
|
|
126
|
+
try {
|
|
127
|
+
const payload = await readDefaults(args);
|
|
128
|
+
if (args.json)
|
|
129
|
+
json(payload);
|
|
130
|
+
else
|
|
131
|
+
printDefaults(payload);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
dieWithError(error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function setSub(argv) {
|
|
138
|
+
const args = parseArgs(argv, { flags: FLAGS });
|
|
139
|
+
const [target, value] = args.positional;
|
|
140
|
+
if (!target || !value)
|
|
141
|
+
die(2, "usage: defaults set <model|reasoning> <value>");
|
|
142
|
+
if (target === "model") {
|
|
143
|
+
validateModel(value);
|
|
144
|
+
setDefaults(MODEL_KEYS, value);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (target === "reasoning") {
|
|
148
|
+
validateReasoning(value);
|
|
149
|
+
setDefaults(REASONING_KEYS, value);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
die(2, "target must be one of: model, reasoning");
|
|
153
|
+
}
|
|
154
|
+
async function resetSub(argv) {
|
|
155
|
+
const args = parseArgs(argv, { flags: FLAGS });
|
|
156
|
+
const target = args.positional[0];
|
|
157
|
+
if (target === "model") {
|
|
158
|
+
resetDefaults(MODEL_KEYS);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (target === "reasoning") {
|
|
162
|
+
resetDefaults(REASONING_KEYS);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
die(2, "usage: defaults reset <model|reasoning>");
|
|
166
|
+
}
|
|
167
|
+
export default async function defaultsCmd(argv) {
|
|
168
|
+
const sub = argv[0];
|
|
169
|
+
if (sub === "--help" || sub === "-h") {
|
|
170
|
+
out(HELP);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!sub || sub === "ls" || sub.startsWith("-"))
|
|
174
|
+
return listSub(sub === "ls" ? argv.slice(1) : argv);
|
|
175
|
+
if (sub === "set")
|
|
176
|
+
return setSub(argv.slice(1));
|
|
177
|
+
if (sub === "reset")
|
|
178
|
+
return resetSub(argv.slice(1));
|
|
179
|
+
die(2, `unknown subcommand: ${sub}\n${HELP}`);
|
|
180
|
+
}
|