@tiens.nguyen/gu-cli 1.0.695 → 1.0.696
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/coding-key-prompt.mjs +44 -3
- package/gu-cli.mjs +3 -17
- package/gu-repl.mjs +8 -4
- package/package.json +1 -1
package/coding-key-prompt.mjs
CHANGED
|
@@ -52,6 +52,34 @@ export function isKeylessCodingHost(url) {
|
|
|
52
52
|
return host === "gomarsic.cc" || host.endsWith(".gomarsic.cc");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* TRUE only for the Ollama CLOUD (https://ollama.com, or a subdomain of it). Mirrors
|
|
57
|
+
* isOllamaCloudUrl in api/src/store/settingsTypes.ts, web/src/lib/coderChoices.mjs and
|
|
58
|
+
* _isOllamaCloudUrl in gu-cli.mjs — keep them in step.
|
|
59
|
+
*/
|
|
60
|
+
export function isOllamaCloudUrl(url) {
|
|
61
|
+
const v = String(url ?? "").trim();
|
|
62
|
+
if (!/^https:\/\//i.test(v)) return false;
|
|
63
|
+
let host;
|
|
64
|
+
try {
|
|
65
|
+
host = new URL(v).hostname.toLowerCase().replace(/\.$/, "");
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return host === "ollama.com" || host.endsWith(".ollama.com");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* DOES THIS CODER NEED AN API KEY? — an OpenAI-compatible provider, or Ollama's paid CLOUD.
|
|
74
|
+
*
|
|
75
|
+
* Reported: /model on kimi-k3 at https://ollama.com said "needs no API key — nothing to set", so
|
|
76
|
+
* there was no way to give it one. Every "needs a key" check had been `kind === "openai"`, on the
|
|
77
|
+
* belief that Ollama never authenticates — true of a local or LAN Ollama, false of ollama.com.
|
|
78
|
+
*/
|
|
79
|
+
export function codingNeedsKey(kind, url) {
|
|
80
|
+
return kind === "openai" || (kind === "ollama" && isOllamaCloudUrl(url));
|
|
81
|
+
}
|
|
82
|
+
|
|
55
83
|
/**
|
|
56
84
|
* @param payload the /agent-payload `payload` object (reads codingKeyMissing/codingKeyModel).
|
|
57
85
|
* @param deps.askSecret async (prompt) => string — masked input; "" means the user skipped.
|
|
@@ -187,9 +215,10 @@ export function codingKeyFallbackLine(kj, jobId = "") {
|
|
|
187
215
|
* when a choice HAS been made, and when only one key exists — with one there is nothing to
|
|
188
216
|
* choose, and a warning on a correct setup is how warnings stop being read.
|
|
189
217
|
*/
|
|
190
|
-
export function unchosenKeyWarning({ codingKind = "", selectedKeyId = "", keys = null } = {}) {
|
|
218
|
+
export function unchosenKeyWarning({ codingKind = "", codingUrl = "", selectedKeyId = "", keys = null } = {}) {
|
|
191
219
|
const quiet = { warn: false, using: null, why: "" };
|
|
192
|
-
|
|
220
|
+
// Local Ollama and MLX need no credential; the Ollama cloud does.
|
|
221
|
+
if (!codingNeedsKey(codingKind, codingUrl)) return quiet;
|
|
193
222
|
if (!Array.isArray(keys) || keys.length < 2) return quiet;
|
|
194
223
|
if (String(selectedKeyId ?? "").trim()) return quiet;
|
|
195
224
|
return { warn: true, using: keys[0], why: "none-chosen" };
|
|
@@ -200,7 +229,8 @@ export function unchosenKeyWarning({ codingKind = "", selectedKeyId = "", keys =
|
|
|
200
229
|
*
|
|
201
230
|
* REPORTED: the bar's "key not chosen" mark only appeared after running /model. The startup gate
|
|
202
231
|
* read `payload.codingKind`, and a payload fetched with NO override describes the ACCOUNT DEFAULT
|
|
203
|
-
* — on the reporting machine
|
|
232
|
+
* — on the reporting machine kimi-k3 at ollama.com, kind "ollama" (which, it turned out later,
|
|
233
|
+
* DOES need a key: see codingNeedsKey) —
|
|
204
234
|
* while the folder was pointed at gpt-5, kind "openai". So the check answered a question about a
|
|
205
235
|
* coder the folder was not using, said "nothing to warn about", and stayed silent. /model looked
|
|
206
236
|
* right because it re-probes WITH the override.
|
|
@@ -212,6 +242,17 @@ export function unchosenKeyWarning({ codingKind = "", selectedKeyId = "", keys =
|
|
|
212
242
|
* Matched by id OR by bare model name, mirroring that validation: a session saved before ids were
|
|
213
243
|
* qualified ("<kind>::<model>", task #123) stores the bare form and is still a valid pick.
|
|
214
244
|
*/
|
|
245
|
+
/** The folder's coder — its kind AND its URL, which together decide whether it needs a key. */
|
|
246
|
+
export function effectiveCoder({ sessionCodingModel = "", choices = null, defaultKind = "", defaultUrl = "" } = {}) {
|
|
247
|
+
const want = String(sessionCodingModel ?? "").trim();
|
|
248
|
+
const list = Array.isArray(choices) ? choices : [];
|
|
249
|
+
if (want) {
|
|
250
|
+
const hit = list.find((c) => c?.id === want || c?.model === want);
|
|
251
|
+
if (hit) return { kind: String(hit.kind ?? ""), url: String(hit.url ?? "") };
|
|
252
|
+
}
|
|
253
|
+
return { kind: String(defaultKind ?? ""), url: String(defaultUrl ?? "") };
|
|
254
|
+
}
|
|
255
|
+
|
|
215
256
|
export function effectiveCoderKind({
|
|
216
257
|
sessionCodingModel = "",
|
|
217
258
|
choices = null,
|
package/gu-cli.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import { execFile as execFileCallback, spawn } from "node:child_process";
|
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
21
|
import { createTaskPool, createFolderQueue } from "./job-pools.mjs";
|
|
22
22
|
import { ragJobConfig } from "./rag-job-config.mjs";
|
|
23
|
-
import { codingKeyFallbackLine } from "./coding-key-prompt.mjs";
|
|
23
|
+
import { codingKeyFallbackLine, isOllamaCloudUrl } from "./coding-key-prompt.mjs";
|
|
24
24
|
// Pure layout helpers — safe to import here (no side effects on load, unlike gu-repl.mjs).
|
|
25
25
|
// Shared so the mid-run picker and the REPL's post-stop advice cannot word the same budget
|
|
26
26
|
// differently.
|
|
@@ -3265,22 +3265,8 @@ function _isLocalCodingUrl(baseURL) {
|
|
|
3265
3265
|
return !host.includes(".") && !host.includes(":");
|
|
3266
3266
|
}
|
|
3267
3267
|
|
|
3268
|
-
/**
|
|
3269
|
-
|
|
3270
|
-
* box, a self-hosted remote) has no auth and gets no key. Mirrors isOllamaCloudUrl in
|
|
3271
|
-
* web/src/lib/coderChoices.mjs — keep the two in step. */
|
|
3272
|
-
function _isOllamaCloudUrl(baseURL) {
|
|
3273
|
-
const v = String(baseURL || "").trim();
|
|
3274
|
-
if (!/^https:\/\//i.test(v)) return false;
|
|
3275
|
-
let host = "";
|
|
3276
|
-
try {
|
|
3277
|
-
host = new URL(v).hostname.toLowerCase().replace(/\.$/, "");
|
|
3278
|
-
} catch {
|
|
3279
|
-
return false;
|
|
3280
|
-
}
|
|
3281
|
-
return host === "ollama.com" || host.endsWith(".ollama.com");
|
|
3282
|
-
}
|
|
3283
|
-
|
|
3268
|
+
/** The Ollama cloud check lives in coding-key-prompt.mjs, shared with the terminal. */
|
|
3269
|
+
const _isOllamaCloudUrl = isOllamaCloudUrl;
|
|
3284
3270
|
|
|
3285
3271
|
/** Resolve to "ollama" | "openai" | "local". `kind` is the user's Settings choice
|
|
3286
3272
|
* ("ollama" | "openai" | "" = Auto). Only Auto costs a probe. */
|
package/gu-repl.mjs
CHANGED
|
@@ -6779,7 +6779,9 @@ async function coderKeyState(override = "") {
|
|
|
6779
6779
|
const active = override
|
|
6780
6780
|
? choices.find((c) => c.id === override)
|
|
6781
6781
|
: choices.find((c) => c.isDefault);
|
|
6782
|
-
|
|
6782
|
+
// Kind AND url: an Ollama coder at https://ollama.com needs a key; a local one does not.
|
|
6783
|
+
const { codingNeedsKey } = await import("./coding-key-prompt.mjs");
|
|
6784
|
+
const needsKey = codingNeedsKey(active?.kind, active?.url);
|
|
6783
6785
|
return {
|
|
6784
6786
|
needsKey,
|
|
6785
6787
|
model: p.codingKeyModel || active?.model || "the coding model",
|
|
@@ -9561,7 +9563,7 @@ async function main() {
|
|
|
9561
9563
|
// folder has not chosen, and more than one is stored.
|
|
9562
9564
|
try {
|
|
9563
9565
|
if (!keyRes?.saved) {
|
|
9564
|
-
const { unchosenKeyWarning,
|
|
9566
|
+
const { unchosenKeyWarning, effectiveCoder, codingNeedsKey } = await import(
|
|
9565
9567
|
"./coding-key-prompt.mjs"
|
|
9566
9568
|
);
|
|
9567
9569
|
// THE RULE LIVES IN THE MODULE so it can be tested by calling it. Open-coded here it
|
|
@@ -9574,15 +9576,17 @@ async function main() {
|
|
|
9574
9576
|
// machine an Ollama coder, which needs no key — while the folder was on gpt-5. Reading
|
|
9575
9577
|
// it directly made this check answer about a coder nobody was using, and the mark only
|
|
9576
9578
|
// appeared after /model, which re-probes WITH the override.
|
|
9577
|
-
const kind =
|
|
9579
|
+
const { kind, url } = effectiveCoder({
|
|
9578
9580
|
sessionCodingModel,
|
|
9579
9581
|
choices: probe?.codingChoices,
|
|
9580
9582
|
defaultKind: p?.codingKind,
|
|
9583
|
+
defaultUrl: probe?.codingChoices?.find?.((c) => c?.isDefault)?.url ?? "",
|
|
9581
9584
|
});
|
|
9582
9585
|
const allKeys =
|
|
9583
|
-
kind
|
|
9586
|
+
codingNeedsKey(kind, url) && !sessionCodingKeyId ? await fetchCodingKeys() : null;
|
|
9584
9587
|
const w = unchosenKeyWarning({
|
|
9585
9588
|
codingKind: kind,
|
|
9589
|
+
codingUrl: url,
|
|
9586
9590
|
selectedKeyId: sessionCodingKeyId,
|
|
9587
9591
|
keys: allKeys,
|
|
9588
9592
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.696",
|
|
4
4
|
"description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|