@ouro.bot/cli 0.1.0-alpha.72 → 0.1.0-alpha.74
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/changelog.json +14 -0
- package/dist/heart/daemon/daemon-cli.js +135 -1
- package/dist/mind/prompt.js +4 -1
- package/dist/nerves/index.js +12 -0
- package/dist/repertoire/guardrails.js +1 -0
- package/dist/senses/cli.js +38 -10
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.74",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Fix: `ouro config model` now pings the model with a tiny API call before switching, rejecting the change if the model returns an error (403, not supported, etc.)."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.73",
|
|
12
|
+
"changes": [
|
|
13
|
+
"New `ouro config models --agent <name>` command: list available models for the current provider. For github-copilot, queries the models API; other providers show a static message.",
|
|
14
|
+
"Fix: `ouro config model` now validates model availability for github-copilot before writing, showing available models if the requested one isn't found.",
|
|
15
|
+
"Fix: system prompt now tells the agent that model/provider changes take effect on the next turn automatically (no restart needed)."
|
|
16
|
+
]
|
|
17
|
+
},
|
|
4
18
|
{
|
|
5
19
|
"version": "0.1.0-alpha.72",
|
|
6
20
|
"changes": [
|
|
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
37
|
+
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
38
|
+
exports.pingGithubCopilotModel = pingGithubCopilotModel;
|
|
37
39
|
exports.parseOuroCommand = parseOuroCommand;
|
|
38
40
|
exports.discoverExistingCredentials = discoverExistingCredentials;
|
|
39
41
|
exports.createDefaultOuroCliDeps = createDefaultOuroCliDeps;
|
|
@@ -270,6 +272,7 @@ function usage() {
|
|
|
270
272
|
" ouro stop|down|status|logs|hatch",
|
|
271
273
|
" ouro -v|--version",
|
|
272
274
|
" ouro config model --agent <name> <model-name>",
|
|
275
|
+
" ouro config models --agent <name>",
|
|
273
276
|
" ouro auth --agent <name> [--provider <provider>]",
|
|
274
277
|
" ouro auth verify --agent <name> [--provider <provider>]",
|
|
275
278
|
" ouro auth switch --agent <name> --provider <provider>",
|
|
@@ -488,7 +491,73 @@ async function verifyProviderCredentials(provider, providers, fetchImpl = fetch)
|
|
|
488
491
|
return "failed (no api key)";
|
|
489
492
|
return "ok";
|
|
490
493
|
}
|
|
491
|
-
|
|
494
|
+
async function listGithubCopilotModels(baseUrl, token, fetchImpl = fetch) {
|
|
495
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
496
|
+
const response = await fetchImpl(url, {
|
|
497
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
498
|
+
});
|
|
499
|
+
if (!response.ok) {
|
|
500
|
+
throw new Error(`model listing failed (HTTP ${response.status})`);
|
|
501
|
+
}
|
|
502
|
+
const body = await response.json();
|
|
503
|
+
/* v8 ignore start -- response shape handling: tested via config-models.test.ts @preserve */
|
|
504
|
+
const items = Array.isArray(body) ? body : (body?.data ?? []);
|
|
505
|
+
return items.map((item) => {
|
|
506
|
+
const rec = item;
|
|
507
|
+
const capabilities = Array.isArray(rec.capabilities)
|
|
508
|
+
? rec.capabilities.filter((c) => typeof c === "string")
|
|
509
|
+
: undefined;
|
|
510
|
+
return {
|
|
511
|
+
id: String(rec.id ?? rec.name ?? ""),
|
|
512
|
+
name: String(rec.name ?? rec.id ?? ""),
|
|
513
|
+
...(capabilities ? { capabilities } : {}),
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
/* v8 ignore stop */
|
|
517
|
+
}
|
|
518
|
+
async function pingGithubCopilotModel(baseUrl, token, model, fetchImpl = fetch) {
|
|
519
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
520
|
+
const isClaude = model.startsWith("claude");
|
|
521
|
+
const url = isClaude ? `${base}/chat/completions` : `${base}/responses`;
|
|
522
|
+
const body = isClaude
|
|
523
|
+
? JSON.stringify({ model, messages: [{ role: "user", content: "ping" }], max_tokens: 1 })
|
|
524
|
+
: JSON.stringify({ model, input: "ping", max_output_tokens: 16 });
|
|
525
|
+
try {
|
|
526
|
+
const response = await fetchImpl(url, {
|
|
527
|
+
method: "POST",
|
|
528
|
+
headers: {
|
|
529
|
+
Authorization: `Bearer ${token}`,
|
|
530
|
+
"Content-Type": "application/json",
|
|
531
|
+
},
|
|
532
|
+
body,
|
|
533
|
+
});
|
|
534
|
+
if (response.ok)
|
|
535
|
+
return { ok: true };
|
|
536
|
+
let detail = `HTTP ${response.status}`;
|
|
537
|
+
try {
|
|
538
|
+
const json = await response.json();
|
|
539
|
+
/* v8 ignore start -- error format parsing: all branches tested via config-models.test.ts @preserve */
|
|
540
|
+
if (typeof json.error === "string")
|
|
541
|
+
detail = json.error;
|
|
542
|
+
else if (typeof json.error === "object" && json.error !== null) {
|
|
543
|
+
const errObj = json.error;
|
|
544
|
+
if (typeof errObj.message === "string")
|
|
545
|
+
detail = errObj.message;
|
|
546
|
+
}
|
|
547
|
+
else if (typeof json.message === "string")
|
|
548
|
+
detail = json.message;
|
|
549
|
+
/* v8 ignore stop */
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// response body not JSON — keep HTTP status
|
|
553
|
+
}
|
|
554
|
+
return { ok: false, error: detail };
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
/* v8 ignore next -- defensive: fetch errors are always Error instances @preserve */
|
|
558
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
492
561
|
function parseHatchCommand(args) {
|
|
493
562
|
let agentName;
|
|
494
563
|
let humanName;
|
|
@@ -805,6 +874,11 @@ function parseConfigCommand(args) {
|
|
|
805
874
|
throw new Error(`Usage: ouro config model --agent <name> <model-name>`);
|
|
806
875
|
return { kind: "config.model", agent, modelName };
|
|
807
876
|
}
|
|
877
|
+
if (sub === "models") {
|
|
878
|
+
if (!agent)
|
|
879
|
+
throw new Error("--agent is required for config models");
|
|
880
|
+
return { kind: "config.models", agent };
|
|
881
|
+
}
|
|
808
882
|
throw new Error(`Usage\n${usage()}`);
|
|
809
883
|
}
|
|
810
884
|
function parseMcpCommand(args) {
|
|
@@ -1796,9 +1870,69 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1796
1870
|
return message;
|
|
1797
1871
|
}
|
|
1798
1872
|
/* v8 ignore stop */
|
|
1873
|
+
// ── config models (local, no daemon socket needed) ──
|
|
1874
|
+
/* v8 ignore start -- config models: tested via daemon-cli.test.ts @preserve */
|
|
1875
|
+
if (command.kind === "config.models") {
|
|
1876
|
+
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
1877
|
+
const provider = config.provider;
|
|
1878
|
+
if (provider !== "github-copilot") {
|
|
1879
|
+
const message = `model listing not available for ${provider} — check provider documentation.`;
|
|
1880
|
+
deps.writeStdout(message);
|
|
1881
|
+
return message;
|
|
1882
|
+
}
|
|
1883
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1884
|
+
const ghConfig = secrets.providers["github-copilot"];
|
|
1885
|
+
if (!ghConfig.githubToken || !ghConfig.baseUrl) {
|
|
1886
|
+
throw new Error(`github-copilot credentials not configured. Run \`ouro auth --agent ${command.agent} --provider github-copilot\` first.`);
|
|
1887
|
+
}
|
|
1888
|
+
const fetchFn = deps.fetchImpl ?? fetch;
|
|
1889
|
+
const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
|
|
1890
|
+
if (models.length === 0) {
|
|
1891
|
+
const message = "no models found";
|
|
1892
|
+
deps.writeStdout(message);
|
|
1893
|
+
return message;
|
|
1894
|
+
}
|
|
1895
|
+
const lines = ["available models:"];
|
|
1896
|
+
for (const m of models) {
|
|
1897
|
+
const caps = m.capabilities?.length ? ` (${m.capabilities.join(", ")})` : "";
|
|
1898
|
+
lines.push(` ${m.id}${caps}`);
|
|
1899
|
+
}
|
|
1900
|
+
const message = lines.join("\n");
|
|
1901
|
+
deps.writeStdout(message);
|
|
1902
|
+
return message;
|
|
1903
|
+
}
|
|
1904
|
+
/* v8 ignore stop */
|
|
1799
1905
|
// ── config model (local, no daemon socket needed) ──
|
|
1800
1906
|
/* v8 ignore start -- config model: tested via daemon-cli.test.ts @preserve */
|
|
1801
1907
|
if (command.kind === "config.model") {
|
|
1908
|
+
// Validate model availability for github-copilot before writing
|
|
1909
|
+
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
|
|
1910
|
+
if (config.provider === "github-copilot") {
|
|
1911
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
|
|
1912
|
+
const ghConfig = secrets.providers["github-copilot"];
|
|
1913
|
+
if (ghConfig.githubToken && ghConfig.baseUrl) {
|
|
1914
|
+
const fetchFn = deps.fetchImpl ?? fetch;
|
|
1915
|
+
try {
|
|
1916
|
+
const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
|
|
1917
|
+
const available = models.map((m) => m.id);
|
|
1918
|
+
if (available.length > 0 && !available.includes(command.modelName)) {
|
|
1919
|
+
const message = `model '${command.modelName}' not found. available models:\n${available.map((id) => ` ${id}`).join("\n")}`;
|
|
1920
|
+
deps.writeStdout(message);
|
|
1921
|
+
return message;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
catch {
|
|
1925
|
+
// Catalog validation failed — fall through to ping test
|
|
1926
|
+
}
|
|
1927
|
+
// Ping test: verify the model actually works before switching
|
|
1928
|
+
const pingResult = await pingGithubCopilotModel(ghConfig.baseUrl, ghConfig.githubToken, command.modelName, fetchFn);
|
|
1929
|
+
if (!pingResult.ok) {
|
|
1930
|
+
const message = `model '${command.modelName}' ping failed: ${pingResult.error}\nrun \`ouro config models --agent ${command.agent}\` to see available models.`;
|
|
1931
|
+
deps.writeStdout(message);
|
|
1932
|
+
return message;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1802
1936
|
const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, command.modelName);
|
|
1803
1937
|
const message = previousModel
|
|
1804
1938
|
? `updated ${command.agent} model on ${provider}: ${previousModel} → ${command.modelName}`
|
package/dist/mind/prompt.js
CHANGED
|
@@ -182,12 +182,15 @@ my bones give me the \`ouro\` cli. always pass \`--agent ${agentName}\`:
|
|
|
182
182
|
ouro session list --agent ${agentName}
|
|
183
183
|
ouro reminder create --agent ${agentName} <title> --body <body>
|
|
184
184
|
ouro config model --agent ${agentName} <model-name>
|
|
185
|
+
ouro config models --agent ${agentName}
|
|
185
186
|
ouro auth --agent ${agentName} --provider <provider>
|
|
186
187
|
ouro auth verify --agent ${agentName} [--provider <provider>]
|
|
187
188
|
ouro auth switch --agent ${agentName} --provider <provider>
|
|
188
189
|
ouro mcp list --agent ${agentName}
|
|
189
190
|
ouro mcp call --agent ${agentName} <server> <tool> --args '{...}'
|
|
190
|
-
ouro --help
|
|
191
|
+
ouro --help
|
|
192
|
+
|
|
193
|
+
provider/model changes via \`ouro config model\` or \`ouro auth switch\` take effect on the next turn automatically — no restart needed.`;
|
|
191
194
|
}
|
|
192
195
|
function mcpToolsSection(mcpManager) {
|
|
193
196
|
if (!mcpManager)
|
package/dist/nerves/index.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.createTraceId = createTraceId;
|
|
|
4
4
|
exports.ensureTraceId = ensureTraceId;
|
|
5
5
|
exports.createFanoutSink = createFanoutSink;
|
|
6
6
|
exports.formatTerminalEntry = formatTerminalEntry;
|
|
7
|
+
exports.registerSpinnerHooks = registerSpinnerHooks;
|
|
7
8
|
exports.createTerminalSink = createTerminalSink;
|
|
8
9
|
exports.createStderrSink = createStderrSink;
|
|
9
10
|
exports.createNdjsonFileSink = createNdjsonFileSink;
|
|
@@ -73,15 +74,26 @@ function formatTerminalEntry(entry) {
|
|
|
73
74
|
const level = entry.level.toUpperCase();
|
|
74
75
|
return `${formatTerminalTime(entry.ts)} ${level} [${entry.component}] ${entry.message}${formatTerminalMeta(entry.meta)}`;
|
|
75
76
|
}
|
|
77
|
+
// Spinner coordination: the CLI sense registers these so log output
|
|
78
|
+
// doesn't interleave with the active spinner animation.
|
|
79
|
+
let _pauseSpinner = null;
|
|
80
|
+
let _resumeSpinner = null;
|
|
81
|
+
function registerSpinnerHooks(pause, resume) {
|
|
82
|
+
_pauseSpinner = pause;
|
|
83
|
+
_resumeSpinner = resume;
|
|
84
|
+
}
|
|
76
85
|
function createTerminalSink(write = (chunk) => process.stderr.write(chunk), colorize = true) {
|
|
77
86
|
return (entry) => {
|
|
87
|
+
_pauseSpinner?.();
|
|
78
88
|
const line = formatTerminalEntry(entry);
|
|
79
89
|
if (!colorize) {
|
|
80
90
|
write(`${line}\n`);
|
|
91
|
+
_resumeSpinner?.();
|
|
81
92
|
return;
|
|
82
93
|
}
|
|
83
94
|
const prefix = LEVEL_COLORS[entry.level];
|
|
84
95
|
write(`${prefix}${line}\x1b[0m\n`);
|
|
96
|
+
_resumeSpinner?.();
|
|
85
97
|
};
|
|
86
98
|
}
|
|
87
99
|
function createStderrSink(write = (chunk) => process.stderr.write(chunk)) {
|
package/dist/senses/cli.js
CHANGED
|
@@ -36,6 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
|
|
37
37
|
exports.formatPendingPrefix = formatPendingPrefix;
|
|
38
38
|
exports.getCliContinuityIngressTexts = getCliContinuityIngressTexts;
|
|
39
|
+
exports.pauseActiveSpinner = pauseActiveSpinner;
|
|
40
|
+
exports.resumeActiveSpinner = resumeActiveSpinner;
|
|
41
|
+
exports.setActiveSpinner = setActiveSpinner;
|
|
39
42
|
exports.handleSigint = handleSigint;
|
|
40
43
|
exports.addHistory = addHistory;
|
|
41
44
|
exports.renderMarkdown = renderMarkdown;
|
|
@@ -90,6 +93,14 @@ function getCliContinuityIngressTexts(input) {
|
|
|
90
93
|
const trimmed = input.trim();
|
|
91
94
|
return trimmed ? [trimmed] : [];
|
|
92
95
|
}
|
|
96
|
+
// Module-level active spinner for log coordination.
|
|
97
|
+
// The terminal log sink calls these to avoid interleaving with spinner output.
|
|
98
|
+
let _activeSpinner = null;
|
|
99
|
+
/* v8 ignore start -- spinner coordination: exercised at runtime, not unit-testable without real terminal @preserve */
|
|
100
|
+
function pauseActiveSpinner() { _activeSpinner?.pause(); }
|
|
101
|
+
function resumeActiveSpinner() { _activeSpinner?.resume(); }
|
|
102
|
+
/* v8 ignore stop */
|
|
103
|
+
function setActiveSpinner(s) { _activeSpinner = s; }
|
|
93
104
|
// spinner that only touches stderr, cleans up after itself
|
|
94
105
|
// exported for direct testability (stop-without-start branch)
|
|
95
106
|
class Spinner {
|
|
@@ -131,6 +142,20 @@ class Spinner {
|
|
|
131
142
|
this.lastPhrase = next;
|
|
132
143
|
this.msg = next;
|
|
133
144
|
}
|
|
145
|
+
/* v8 ignore start -- pause/resume: exercised at runtime via log sink coordination @preserve */
|
|
146
|
+
/** Clear the spinner line temporarily so other output can print cleanly. */
|
|
147
|
+
pause() {
|
|
148
|
+
if (this.stopped)
|
|
149
|
+
return;
|
|
150
|
+
process.stderr.write("\r\x1b[K");
|
|
151
|
+
}
|
|
152
|
+
/** Restore the spinner line after a pause. */
|
|
153
|
+
resume() {
|
|
154
|
+
if (this.stopped)
|
|
155
|
+
return;
|
|
156
|
+
this.spin();
|
|
157
|
+
}
|
|
158
|
+
/* v8 ignore stop */
|
|
134
159
|
stop(ok) {
|
|
135
160
|
this.stopped = true;
|
|
136
161
|
if (this.iv) {
|
|
@@ -319,6 +344,7 @@ function createCliCallbacks() {
|
|
|
319
344
|
meta: {},
|
|
320
345
|
});
|
|
321
346
|
let currentSpinner = null;
|
|
347
|
+
function setSpinner(s) { currentSpinner = s; setActiveSpinner(s); }
|
|
322
348
|
let hadReasoning = false;
|
|
323
349
|
let hadToolRun = false;
|
|
324
350
|
let textDirty = false; // true when text/reasoning was written without a trailing newline
|
|
@@ -326,14 +352,14 @@ function createCliCallbacks() {
|
|
|
326
352
|
return {
|
|
327
353
|
onModelStart: () => {
|
|
328
354
|
currentSpinner?.stop();
|
|
329
|
-
|
|
355
|
+
setSpinner(null);
|
|
330
356
|
hadReasoning = false;
|
|
331
357
|
textDirty = false;
|
|
332
358
|
streamer.reset();
|
|
333
359
|
const phrases = (0, phrases_1.getPhrases)();
|
|
334
360
|
const pool = hadToolRun ? phrases.followup : phrases.thinking;
|
|
335
361
|
const first = (0, phrases_1.pickPhrase)(pool);
|
|
336
|
-
|
|
362
|
+
setSpinner(new Spinner(first, pool));
|
|
337
363
|
currentSpinner.start();
|
|
338
364
|
},
|
|
339
365
|
onModelStreamStart: () => {
|
|
@@ -350,7 +376,7 @@ function createCliCallbacks() {
|
|
|
350
376
|
// otherwise keep running (and its \r writes overwrite response text).
|
|
351
377
|
if (currentSpinner) {
|
|
352
378
|
currentSpinner.stop();
|
|
353
|
-
|
|
379
|
+
setSpinner(null);
|
|
354
380
|
}
|
|
355
381
|
if (hadReasoning) {
|
|
356
382
|
// Single newline to separate reasoning from reply — reasoning
|
|
@@ -366,7 +392,7 @@ function createCliCallbacks() {
|
|
|
366
392
|
onReasoningChunk: (text) => {
|
|
367
393
|
if (currentSpinner) {
|
|
368
394
|
currentSpinner.stop();
|
|
369
|
-
|
|
395
|
+
setSpinner(null);
|
|
370
396
|
}
|
|
371
397
|
hadReasoning = true;
|
|
372
398
|
process.stdout.write(`\x1b[2m${text}\x1b[0m`);
|
|
@@ -385,13 +411,13 @@ function createCliCallbacks() {
|
|
|
385
411
|
}
|
|
386
412
|
const toolPhrases = (0, phrases_1.getPhrases)().tool;
|
|
387
413
|
const first = (0, phrases_1.pickPhrase)(toolPhrases);
|
|
388
|
-
|
|
414
|
+
setSpinner(new Spinner(first, toolPhrases));
|
|
389
415
|
currentSpinner.start();
|
|
390
416
|
hadToolRun = true;
|
|
391
417
|
},
|
|
392
418
|
onToolEnd: (name, argSummary, success) => {
|
|
393
419
|
currentSpinner?.stop();
|
|
394
|
-
|
|
420
|
+
setSpinner(null);
|
|
395
421
|
const msg = (0, format_1.formatToolResult)(name, argSummary, success);
|
|
396
422
|
const color = success ? "\x1b[32m" : "\x1b[31m";
|
|
397
423
|
process.stderr.write(`${color}${msg}\x1b[0m\n`);
|
|
@@ -399,17 +425,17 @@ function createCliCallbacks() {
|
|
|
399
425
|
onError: (error, severity) => {
|
|
400
426
|
if (severity === "transient") {
|
|
401
427
|
currentSpinner?.fail(error.message);
|
|
402
|
-
|
|
428
|
+
setSpinner(null);
|
|
403
429
|
}
|
|
404
430
|
else {
|
|
405
431
|
currentSpinner?.stop();
|
|
406
|
-
|
|
432
|
+
setSpinner(null);
|
|
407
433
|
process.stderr.write(`\x1b[31m${(0, format_1.formatError)(error)}\x1b[0m\n`);
|
|
408
434
|
}
|
|
409
435
|
},
|
|
410
436
|
onKick: () => {
|
|
411
437
|
currentSpinner?.stop();
|
|
412
|
-
|
|
438
|
+
setSpinner(null);
|
|
413
439
|
if (textDirty) {
|
|
414
440
|
process.stdout.write("\n");
|
|
415
441
|
textDirty = false;
|
|
@@ -418,7 +444,7 @@ function createCliCallbacks() {
|
|
|
418
444
|
},
|
|
419
445
|
flushMarkdown: () => {
|
|
420
446
|
currentSpinner?.stop();
|
|
421
|
-
|
|
447
|
+
setSpinner(null);
|
|
422
448
|
const remaining = streamer.flush();
|
|
423
449
|
if (remaining)
|
|
424
450
|
process.stdout.write(remaining);
|
|
@@ -694,6 +720,8 @@ async function main(agentName, options) {
|
|
|
694
720
|
if (agentName)
|
|
695
721
|
(0, identity_1.setAgentName)(agentName);
|
|
696
722
|
const pasteDebounceMs = options?.pasteDebounceMs ?? 50;
|
|
723
|
+
// Register spinner hooks so log output clears the spinner before printing
|
|
724
|
+
(0, nerves_1.registerSpinnerHooks)(pauseActiveSpinner, resumeActiveSpinner);
|
|
697
725
|
// Fallback: apply pending updates for daemon-less direct CLI usage
|
|
698
726
|
(0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
|
|
699
727
|
await (0, update_hooks_1.applyPendingUpdates)((0, identity_1.getAgentBundlesRoot)(), (0, bundle_manifest_1.getPackageVersion)());
|