@ouro.bot/cli 0.1.0-alpha.445 → 0.1.0-alpha.447
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 +1 -1
- package/changelog.json +16 -0
- package/dist/heart/daemon/agent-config-check.js +14 -1
- package/dist/heart/daemon/agentic-repair.js +2 -0
- package/dist/heart/daemon/cli-exec.js +38 -13
- package/dist/heart/daemon/connect-bay.js +118 -251
- package/dist/heart/daemon/human-command-screens.js +112 -34
- package/dist/heart/daemon/interactive-repair.js +96 -9
- package/dist/heart/daemon/terminal-ui.js +200 -0
- package/dist/heart/provider-credentials.js +20 -4
- package/dist/repertoire/bitwarden-store.js +66 -25
- package/package.json +1 -1
|
@@ -17,6 +17,34 @@ function renderScreenEvent(screen) {
|
|
|
17
17
|
meta: { screen },
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
+
function homeActionSummary(action) {
|
|
21
|
+
switch (action.kind) {
|
|
22
|
+
case "chat":
|
|
23
|
+
return `Open chat with ${action.agent}.`;
|
|
24
|
+
case "up":
|
|
25
|
+
return "Start the local runtime and check what still needs attention.";
|
|
26
|
+
case "connect":
|
|
27
|
+
return "Set up providers, portable tools, and machine-specific attachments.";
|
|
28
|
+
case "repair":
|
|
29
|
+
return "Walk through repairs for anything blocking startup or chat.";
|
|
30
|
+
case "help":
|
|
31
|
+
return "Show the command guide.";
|
|
32
|
+
case "hatch":
|
|
33
|
+
return "Create a new agent on this machine.";
|
|
34
|
+
case "clone":
|
|
35
|
+
return "Bring an existing bundle onto this machine.";
|
|
36
|
+
case "exit":
|
|
37
|
+
return "Leave the prompt.";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function actionToWizardItem(action) {
|
|
41
|
+
return {
|
|
42
|
+
key: action.key,
|
|
43
|
+
label: action.label,
|
|
44
|
+
summary: homeActionSummary(action),
|
|
45
|
+
...(action.key === "1" ? { recommended: true } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
20
48
|
function buildOuroHomeActions(agents) {
|
|
21
49
|
if (agents.length === 0) {
|
|
22
50
|
return [
|
|
@@ -57,21 +85,28 @@ function resolveOuroHomeAction(answer, actions) {
|
|
|
57
85
|
function renderOuroHomeScreen(options) {
|
|
58
86
|
renderScreenEvent("home");
|
|
59
87
|
const actions = buildOuroHomeActions(options.agents);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
const chatActions = actions.filter((action) => action.kind === "chat");
|
|
89
|
+
const setupActions = actions.filter((action) => action.kind !== "chat");
|
|
90
|
+
const sections = options.agents.length === 0
|
|
91
|
+
? [
|
|
92
|
+
{
|
|
93
|
+
title: "Start here",
|
|
94
|
+
summary: "There are no agents on this machine yet.",
|
|
95
|
+
items: setupActions.map((action) => actionToWizardItem(action)),
|
|
96
|
+
},
|
|
97
|
+
]
|
|
98
|
+
: [
|
|
99
|
+
{
|
|
100
|
+
title: "Agents",
|
|
101
|
+
summary: "Jump straight into conversation or pick a setup path below.",
|
|
102
|
+
items: chatActions.map((action) => actionToWizardItem(action)),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: "System",
|
|
106
|
+
items: setupActions.map((action) => actionToWizardItem(action)),
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
75
110
|
isTTY: options.isTTY,
|
|
76
111
|
columns: options.columns,
|
|
77
112
|
masthead: {
|
|
@@ -84,13 +119,18 @@ function renderOuroHomeScreen(options) {
|
|
|
84
119
|
? "Create a new agent or clone an existing bundle to get started."
|
|
85
120
|
: "Choose an agent or a setup task without memorizing commands.",
|
|
86
121
|
sections,
|
|
87
|
-
|
|
122
|
+
nextStep: {
|
|
123
|
+
label: options.agents.length === 0 ? "Start by creating or cloning an agent." : `Start with ${actions[0].label}.`,
|
|
124
|
+
detail: options.agents.length === 0
|
|
125
|
+
? "Once one agent bundle exists here, the rest of the command surface becomes interactive."
|
|
126
|
+
: "You can always type the number or the agent name instead of remembering a command.",
|
|
127
|
+
},
|
|
88
128
|
prompt: `Choose [1-${actions.length}] or type a name: `,
|
|
89
129
|
});
|
|
90
130
|
}
|
|
91
131
|
function renderAgentPickerScreen(options) {
|
|
92
132
|
renderScreenEvent("agent-picker");
|
|
93
|
-
return (0, terminal_ui_1.
|
|
133
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
94
134
|
isTTY: options.isTTY,
|
|
95
135
|
columns: options.columns,
|
|
96
136
|
masthead: {
|
|
@@ -101,7 +141,12 @@ function renderAgentPickerScreen(options) {
|
|
|
101
141
|
sections: [
|
|
102
142
|
{
|
|
103
143
|
title: "Agents",
|
|
104
|
-
|
|
144
|
+
items: options.agents.map((agent, index) => ({
|
|
145
|
+
key: String(index + 1),
|
|
146
|
+
label: agent,
|
|
147
|
+
summary: "Available on this machine.",
|
|
148
|
+
...(index === 0 ? { recommended: true } : {}),
|
|
149
|
+
})),
|
|
105
150
|
},
|
|
106
151
|
],
|
|
107
152
|
prompt: `Choose [1-${options.agents.length}] or type a name: `,
|
|
@@ -116,32 +161,65 @@ function resolveNamedAgentSelection(answer, agents) {
|
|
|
116
161
|
return agents[numbered - 1];
|
|
117
162
|
return agents.find((agent) => agent.toLowerCase() === normalized);
|
|
118
163
|
}
|
|
119
|
-
function statusLabel(status) {
|
|
120
|
-
return status.replace(/-/g, " ");
|
|
121
|
-
}
|
|
122
164
|
function renderHumanReadinessBoard(options) {
|
|
123
165
|
renderScreenEvent("readiness");
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
],
|
|
166
|
+
const issueItems = options.snapshot.items.map((item) => ({
|
|
167
|
+
label: item.title,
|
|
168
|
+
status: item.status,
|
|
169
|
+
summary: item.summary,
|
|
170
|
+
detailLines: item.detailLines,
|
|
130
171
|
}));
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
172
|
+
const actionItems = options.snapshot.nextActions.map((action, index) => ({
|
|
173
|
+
key: String(index + 1),
|
|
174
|
+
label: action.label,
|
|
175
|
+
actor: action.actor,
|
|
176
|
+
command: action.command,
|
|
177
|
+
...(action.recommended ? { recommended: true } : {}),
|
|
178
|
+
}));
|
|
179
|
+
if (options.prompt) {
|
|
180
|
+
actionItems.push({
|
|
181
|
+
key: String(actionItems.length + 1),
|
|
182
|
+
label: "Skip for now",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
135
186
|
isTTY: options.isTTY,
|
|
136
187
|
columns: options.columns,
|
|
137
|
-
|
|
138
|
-
|
|
188
|
+
masthead: {
|
|
189
|
+
subtitle: options.subtitle,
|
|
190
|
+
},
|
|
191
|
+
title: options.title,
|
|
192
|
+
summary: options.snapshot.summary,
|
|
193
|
+
nextStep: options.snapshot.primaryAction
|
|
194
|
+
? {
|
|
195
|
+
label: options.snapshot.primaryAction.label,
|
|
196
|
+
detail: options.snapshot.summary,
|
|
197
|
+
command: options.snapshot.primaryAction.command,
|
|
198
|
+
}
|
|
199
|
+
: options.snapshot.status === "ready" || options.snapshot.status === "attached"
|
|
200
|
+
? {
|
|
201
|
+
label: "Everything needed here is ready.",
|
|
202
|
+
detail: "You can keep going or leave this area alone.",
|
|
203
|
+
}
|
|
204
|
+
: undefined,
|
|
205
|
+
sections: [
|
|
206
|
+
{
|
|
207
|
+
title: "What needs attention",
|
|
208
|
+
items: issueItems,
|
|
209
|
+
},
|
|
210
|
+
...(actionItems.length > 0
|
|
211
|
+
? [{
|
|
212
|
+
title: "Ways forward",
|
|
213
|
+
items: actionItems,
|
|
214
|
+
}]
|
|
215
|
+
: []),
|
|
216
|
+
],
|
|
139
217
|
prompt: options.prompt,
|
|
140
218
|
});
|
|
141
219
|
}
|
|
142
220
|
function renderHumanCommandBoard(options) {
|
|
143
221
|
renderScreenEvent("command-board");
|
|
144
|
-
return (0, terminal_ui_1.
|
|
222
|
+
return (0, terminal_ui_1.renderTerminalGuide)({
|
|
145
223
|
isTTY: options.isTTY,
|
|
146
224
|
columns: options.columns,
|
|
147
225
|
masthead: {
|
|
@@ -11,6 +11,7 @@ exports.isAffirmativeAnswer = isAffirmativeAnswer;
|
|
|
11
11
|
exports.runInteractiveRepair = runInteractiveRepair;
|
|
12
12
|
const runtime_1 = require("../../nerves/runtime");
|
|
13
13
|
const identity_1 = require("../identity");
|
|
14
|
+
const terminal_ui_1 = require("./terminal-ui");
|
|
14
15
|
function isCredentialIssue(degraded) {
|
|
15
16
|
const reason = degraded.errorReason.toLowerCase();
|
|
16
17
|
const hint = degraded.fixHint.toLowerCase();
|
|
@@ -90,6 +91,9 @@ function fallbackCommandsFor(degraded, primaryCommand) {
|
|
|
90
91
|
function renderRepairChoices(prefix, commands) {
|
|
91
92
|
return commands.map((command, index) => ` ${index === 0 ? prefix : "or"}: ${command}`);
|
|
92
93
|
}
|
|
94
|
+
function repairStatusFor(action) {
|
|
95
|
+
return action.kind === "vault-unlock" ? "locked" : "needs credentials";
|
|
96
|
+
}
|
|
93
97
|
function renderRepairQueueSummaryLines(degraded) {
|
|
94
98
|
const repairable = degraded
|
|
95
99
|
.map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
|
|
@@ -109,6 +113,34 @@ function renderRepairQueueSummaryLines(degraded) {
|
|
|
109
113
|
});
|
|
110
114
|
return lines;
|
|
111
115
|
}
|
|
116
|
+
function renderRepairQueueSummary(degraded, deps) {
|
|
117
|
+
const repairable = degraded
|
|
118
|
+
.map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
|
|
119
|
+
.filter((item) => item.action !== undefined);
|
|
120
|
+
if (!deps.isTTY) {
|
|
121
|
+
return renderRepairQueueSummaryLines(degraded).join("\n");
|
|
122
|
+
}
|
|
123
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
124
|
+
isTTY: true,
|
|
125
|
+
columns: deps.stdoutColumns,
|
|
126
|
+
masthead: {
|
|
127
|
+
subtitle: "Repair paths ready.",
|
|
128
|
+
},
|
|
129
|
+
title: "Needs attention before startup can finish",
|
|
130
|
+
summary: "Ouro found repair steps it can open right now. It will walk through them one at a time.",
|
|
131
|
+
sections: [{
|
|
132
|
+
title: "Repair queue",
|
|
133
|
+
items: repairable.map(({ entry, action }) => ({
|
|
134
|
+
label: entry.agent,
|
|
135
|
+
status: repairStatusFor(action),
|
|
136
|
+
summary: action.label,
|
|
137
|
+
detailLines: [entry.errorReason],
|
|
138
|
+
command: action.command,
|
|
139
|
+
})),
|
|
140
|
+
}],
|
|
141
|
+
suppressEvent: true,
|
|
142
|
+
}).trimEnd();
|
|
143
|
+
}
|
|
112
144
|
function renderActionPromptLines(agent, action) {
|
|
113
145
|
const lines = [
|
|
114
146
|
`${agent}`,
|
|
@@ -120,11 +152,66 @@ function renderActionPromptLines(agent, action) {
|
|
|
120
152
|
}
|
|
121
153
|
return lines;
|
|
122
154
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
155
|
+
function renderActionPrompt(entry, action, deps) {
|
|
156
|
+
if (!deps.isTTY)
|
|
157
|
+
return renderActionPromptLines(entry.agent, action).join("\n");
|
|
158
|
+
const footer = action.kind === "vault-unlock"
|
|
159
|
+
? "Only say yes if you have the saved vault unlock secret."
|
|
160
|
+
: "Say yes if you want Ouro to open the auth flow now.";
|
|
161
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
162
|
+
isTTY: true,
|
|
163
|
+
columns: deps.stdoutColumns,
|
|
164
|
+
masthead: {
|
|
165
|
+
subtitle: "One repair step at a time.",
|
|
166
|
+
},
|
|
167
|
+
title: `Repair ${entry.agent}`,
|
|
168
|
+
summary: "Ouro found one clear next move for this agent.",
|
|
169
|
+
nextStep: {
|
|
170
|
+
label: action.kind === "vault-unlock"
|
|
171
|
+
? "Unlock the credential vault on this machine."
|
|
172
|
+
: "Refresh provider authentication.",
|
|
173
|
+
detail: action.kind === "vault-unlock"
|
|
174
|
+
? "Use the saved unlock secret if you have it. If not, leave this for later and choose a replacement or recovery path."
|
|
175
|
+
: "This opens the provider login or refresh flow for the selected provider.",
|
|
176
|
+
command: action.command,
|
|
177
|
+
},
|
|
178
|
+
sections: [{
|
|
179
|
+
title: "Why startup paused",
|
|
180
|
+
items: [{
|
|
181
|
+
label: entry.agent,
|
|
182
|
+
status: repairStatusFor(action),
|
|
183
|
+
summary: entry.errorReason,
|
|
184
|
+
}],
|
|
185
|
+
}],
|
|
186
|
+
footerLines: [footer],
|
|
187
|
+
suppressEvent: true,
|
|
188
|
+
}).trimEnd();
|
|
189
|
+
}
|
|
190
|
+
function renderDeferredRepair(agent, commands, deps) {
|
|
191
|
+
if (!deps.isTTY) {
|
|
192
|
+
return [
|
|
193
|
+
`Leaving ${agent} for later.`,
|
|
194
|
+
...renderRepairChoices("next", commands),
|
|
195
|
+
].join("\n");
|
|
196
|
+
}
|
|
197
|
+
return (0, terminal_ui_1.renderTerminalWizard)({
|
|
198
|
+
isTTY: true,
|
|
199
|
+
columns: deps.stdoutColumns,
|
|
200
|
+
masthead: {
|
|
201
|
+
subtitle: "Nothing changed yet.",
|
|
202
|
+
},
|
|
203
|
+
title: `Leaving ${agent} for later`,
|
|
204
|
+
summary: "Ouro did not open this repair flow right now.",
|
|
205
|
+
sections: [{
|
|
206
|
+
title: "Next time",
|
|
207
|
+
items: commands.map((command, index) => ({
|
|
208
|
+
key: String(index + 1),
|
|
209
|
+
label: index === 0 ? "Start here" : "Or try this",
|
|
210
|
+
command,
|
|
211
|
+
})),
|
|
212
|
+
}],
|
|
213
|
+
suppressEvent: true,
|
|
214
|
+
}).trimEnd();
|
|
128
215
|
}
|
|
129
216
|
function renderManualRepairHint(agent, fixHint) {
|
|
130
217
|
return [
|
|
@@ -140,7 +227,7 @@ function renderUnknownRepair(agent, errorReason) {
|
|
|
140
227
|
].join("\n");
|
|
141
228
|
}
|
|
142
229
|
function writeDeclinedRepair(degraded, command, deps) {
|
|
143
|
-
deps.writeStdout(renderDeferredRepair(degraded.agent, fallbackCommandsFor(degraded, command)));
|
|
230
|
+
deps.writeStdout(renderDeferredRepair(degraded.agent, fallbackCommandsFor(degraded, command), deps));
|
|
144
231
|
}
|
|
145
232
|
function runnableRepairActionFor(degraded) {
|
|
146
233
|
const typedAction = degraded.issue?.actions
|
|
@@ -180,10 +267,10 @@ function typedActionToRunnable(degraded, action) {
|
|
|
180
267
|
function writeRepairQueueSummary(degraded, deps) {
|
|
181
268
|
const lines = renderRepairQueueSummaryLines(degraded);
|
|
182
269
|
if (lines.length > 0)
|
|
183
|
-
deps.writeStdout(
|
|
270
|
+
deps.writeStdout(renderRepairQueueSummary(degraded, deps));
|
|
184
271
|
}
|
|
185
272
|
async function attemptVaultUnlock(entry, action, deps) {
|
|
186
|
-
deps.writeStdout(
|
|
273
|
+
deps.writeStdout(renderActionPrompt(entry, action, deps));
|
|
187
274
|
const answer = await deps.promptInput(`Unlock ${entry.agent}'s vault now? [y/N] `);
|
|
188
275
|
if (!isAffirmativeAnswer(answer)) {
|
|
189
276
|
writeDeclinedRepair(entry, action.command, deps);
|
|
@@ -211,7 +298,7 @@ async function attemptVaultUnlock(entry, action, deps) {
|
|
|
211
298
|
}
|
|
212
299
|
}
|
|
213
300
|
async function attemptProviderAuth(entry, action, deps) {
|
|
214
|
-
deps.writeStdout(
|
|
301
|
+
deps.writeStdout(renderActionPrompt(entry, action, deps));
|
|
215
302
|
const answer = await deps.promptInput(`Open the auth flow for ${entry.agent} now? [y/N] `);
|
|
216
303
|
if (!isAffirmativeAnswer(answer)) {
|
|
217
304
|
writeDeclinedRepair(entry, action.command, deps);
|
|
@@ -7,7 +7,9 @@ exports.wrapPlain = wrapPlain;
|
|
|
7
7
|
exports.renderOverwriteFrame = renderOverwriteFrame;
|
|
8
8
|
exports.renderOuroMasthead = renderOuroMasthead;
|
|
9
9
|
exports.formatActionActorLabel = formatActionActorLabel;
|
|
10
|
+
exports.renderTerminalWizard = renderTerminalWizard;
|
|
10
11
|
exports.renderTerminalBoard = renderTerminalBoard;
|
|
12
|
+
exports.renderTerminalGuide = renderTerminalGuide;
|
|
11
13
|
exports.renderTerminalOperation = renderTerminalOperation;
|
|
12
14
|
const runtime_1 = require("../../nerves/runtime");
|
|
13
15
|
const RESET = "\x1b[0m";
|
|
@@ -145,6 +147,154 @@ function renderOuroMasthead(options) {
|
|
|
145
147
|
function formatActionActorLabel(actor) {
|
|
146
148
|
return actor.replace(/-/g, " ");
|
|
147
149
|
}
|
|
150
|
+
function formatWizardStatusLabel(status) {
|
|
151
|
+
return status;
|
|
152
|
+
}
|
|
153
|
+
function isQuietWizardStatus(status) {
|
|
154
|
+
return status === "ready" || status === "attached" || status === "not attached";
|
|
155
|
+
}
|
|
156
|
+
function renderWizardStatusBadge(status, isTTY) {
|
|
157
|
+
const symbol = status === "ready" || status === "attached"
|
|
158
|
+
? "●"
|
|
159
|
+
: status === "not attached"
|
|
160
|
+
? "◌"
|
|
161
|
+
: "◆";
|
|
162
|
+
const label = `${symbol} ${formatWizardStatusLabel(status)}`;
|
|
163
|
+
if (!isTTY)
|
|
164
|
+
return label;
|
|
165
|
+
if (status === "ready" || status === "attached")
|
|
166
|
+
return color(label, GLOW, true);
|
|
167
|
+
if (status === "not attached")
|
|
168
|
+
return color(label, MIST);
|
|
169
|
+
return color(label, ALERT, true);
|
|
170
|
+
}
|
|
171
|
+
function renderWizardActorBadge(actor, isTTY) {
|
|
172
|
+
const label = `[${formatActionActorLabel(actor)}]`;
|
|
173
|
+
if (!isTTY)
|
|
174
|
+
return label;
|
|
175
|
+
if (actor === "human-required")
|
|
176
|
+
return color(label, ALERT, true);
|
|
177
|
+
if (actor === "human-choice")
|
|
178
|
+
return color(label, SCALE, true);
|
|
179
|
+
return color(label, MIST);
|
|
180
|
+
}
|
|
181
|
+
function renderWizardRecommendedBadge(isTTY) {
|
|
182
|
+
if (!isTTY)
|
|
183
|
+
return "[recommended]";
|
|
184
|
+
return color("[recommended]", GLOW, true);
|
|
185
|
+
}
|
|
186
|
+
function wrapWizardDetailLines(lines, width, isTTY, tone) {
|
|
187
|
+
const rendered = [];
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
const wrapped = wrapPlain(line, width);
|
|
190
|
+
for (const segment of wrapped) {
|
|
191
|
+
rendered.push(isTTY ? color(segment, tone) : segment);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return rendered;
|
|
195
|
+
}
|
|
196
|
+
function renderWizardItem(item, width, isTTY) {
|
|
197
|
+
const badges = [
|
|
198
|
+
...(item.status ? [renderWizardStatusBadge(item.status, isTTY)] : []),
|
|
199
|
+
...(item.actor ? [renderWizardActorBadge(item.actor, isTTY)] : []),
|
|
200
|
+
...(item.recommended ? [renderWizardRecommendedBadge(isTTY)] : []),
|
|
201
|
+
];
|
|
202
|
+
const keyPrefix = item.key ? `${item.key}. ` : "";
|
|
203
|
+
const header = `${keyPrefix}${item.label}${badges.length > 0 ? ` ${badges.join(" ")}` : ""}`;
|
|
204
|
+
const detailWidth = Math.max(18, width - 6);
|
|
205
|
+
const detailTone = item.status && !isQuietWizardStatus(item.status) ? BONE : MIST;
|
|
206
|
+
const lines = [isTTY ? color(header, BONE, true) : header];
|
|
207
|
+
if (item.summary) {
|
|
208
|
+
lines.push(...wrapWizardDetailLines([item.summary], detailWidth, isTTY, detailTone));
|
|
209
|
+
}
|
|
210
|
+
if (item.detailLines && item.detailLines.length > 0) {
|
|
211
|
+
lines.push(...wrapWizardDetailLines(item.detailLines, detailWidth, isTTY, detailTone));
|
|
212
|
+
}
|
|
213
|
+
if (item.command) {
|
|
214
|
+
lines.push(...wrapWizardDetailLines([`run: ${item.command}`], detailWidth, isTTY, MIST));
|
|
215
|
+
}
|
|
216
|
+
return lines.flatMap((line, index) => index === 0 ? [line] : [` ${line}`]);
|
|
217
|
+
}
|
|
218
|
+
function renderWizardSectionTTY(section, width) {
|
|
219
|
+
const lines = [];
|
|
220
|
+
if (section.summary) {
|
|
221
|
+
lines.push(...wrapWizardDetailLines([section.summary], Math.max(18, width - 4), true, MIST));
|
|
222
|
+
}
|
|
223
|
+
for (const [index, item] of section.items.entries()) {
|
|
224
|
+
if (index > 0)
|
|
225
|
+
lines.push("");
|
|
226
|
+
lines.push(...renderWizardItem(item, width, true));
|
|
227
|
+
}
|
|
228
|
+
return renderOperationSectionTTY(section.title, lines, width);
|
|
229
|
+
}
|
|
230
|
+
function renderWizardSectionPlain(section, width) {
|
|
231
|
+
const lines = [];
|
|
232
|
+
if (section.summary) {
|
|
233
|
+
lines.push(...wrapWizardDetailLines([section.summary], Math.max(18, width - 4), false, MIST));
|
|
234
|
+
}
|
|
235
|
+
for (const [index, item] of section.items.entries()) {
|
|
236
|
+
if (index > 0)
|
|
237
|
+
lines.push("");
|
|
238
|
+
lines.push(...renderWizardItem(item, width, false));
|
|
239
|
+
}
|
|
240
|
+
return renderOperationSectionPlain(section.title, lines);
|
|
241
|
+
}
|
|
242
|
+
function renderTerminalWizard(options) {
|
|
243
|
+
if (!options.suppressEvent) {
|
|
244
|
+
(0, runtime_1.emitNervesEvent)({
|
|
245
|
+
component: "daemon",
|
|
246
|
+
event: "daemon.terminal_wizard_rendered",
|
|
247
|
+
message: "rendered shared terminal wizard",
|
|
248
|
+
meta: {
|
|
249
|
+
title: options.title,
|
|
250
|
+
sections: options.sections?.length ?? 0,
|
|
251
|
+
items: options.sections?.reduce((count, section) => count + section.items.length, 0) ?? 0,
|
|
252
|
+
hasNextStep: !!options.nextStep,
|
|
253
|
+
tty: options.isTTY,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const width = boardWidth(options.columns);
|
|
258
|
+
const blocks = [];
|
|
259
|
+
blocks.push(renderOuroMasthead({
|
|
260
|
+
isTTY: options.isTTY,
|
|
261
|
+
columns: width,
|
|
262
|
+
subtitle: options.masthead?.subtitle,
|
|
263
|
+
}).trimEnd());
|
|
264
|
+
const introLines = [
|
|
265
|
+
options.isTTY ? color(options.title, BONE, true) : options.title,
|
|
266
|
+
...(options.summary
|
|
267
|
+
? wrapPlain(options.summary, Math.max(20, width - 2)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
268
|
+
: []),
|
|
269
|
+
];
|
|
270
|
+
blocks.push(introLines.join("\n"));
|
|
271
|
+
if (options.nextStep) {
|
|
272
|
+
const nextStepLines = [
|
|
273
|
+
options.isTTY ? color(options.nextStep.label, BONE, true) : options.nextStep.label,
|
|
274
|
+
...(options.nextStep.detail
|
|
275
|
+
? wrapPlain(options.nextStep.detail, Math.max(18, width - 4)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
276
|
+
: []),
|
|
277
|
+
...(options.nextStep.command
|
|
278
|
+
? wrapPlain(`run: ${options.nextStep.command}`, Math.max(18, width - 4)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
279
|
+
: []),
|
|
280
|
+
];
|
|
281
|
+
blocks.push((options.isTTY
|
|
282
|
+
? renderOperationSectionTTY("Recommended next step", nextStepLines, width)
|
|
283
|
+
: renderOperationSectionPlain("Recommended next step", nextStepLines)).join("\n"));
|
|
284
|
+
}
|
|
285
|
+
for (const section of options.sections ?? []) {
|
|
286
|
+
blocks.push((options.isTTY
|
|
287
|
+
? renderWizardSectionTTY(section, width)
|
|
288
|
+
: renderWizardSectionPlain(section, width)).join("\n"));
|
|
289
|
+
}
|
|
290
|
+
if (options.footerLines && options.footerLines.length > 0) {
|
|
291
|
+
blocks.push(options.footerLines.map((line) => options.isTTY ? color(line, MIST) : line).join("\n"));
|
|
292
|
+
}
|
|
293
|
+
if (options.prompt) {
|
|
294
|
+
blocks.push(options.isTTY ? color(options.prompt, BONE, true) : options.prompt);
|
|
295
|
+
}
|
|
296
|
+
return `${blocks.join("\n\n")}\n`;
|
|
297
|
+
}
|
|
148
298
|
function renderActionLine(action) {
|
|
149
299
|
const chips = [`[${formatActionActorLabel(action.actor)}]`];
|
|
150
300
|
if (action.recommended)
|
|
@@ -195,6 +345,56 @@ function renderTerminalBoard(options) {
|
|
|
195
345
|
}
|
|
196
346
|
return `${blocks.join("\n\n")}\n`;
|
|
197
347
|
}
|
|
348
|
+
function renderTerminalGuide(options) {
|
|
349
|
+
if (!options.suppressEvent) {
|
|
350
|
+
(0, runtime_1.emitNervesEvent)({
|
|
351
|
+
component: "daemon",
|
|
352
|
+
event: "daemon.terminal_guide_rendered",
|
|
353
|
+
message: "rendered shared terminal guide",
|
|
354
|
+
meta: {
|
|
355
|
+
title: options.title,
|
|
356
|
+
sections: options.sections?.length ?? 0,
|
|
357
|
+
actions: options.actions?.length ?? 0,
|
|
358
|
+
tty: options.isTTY,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const width = boardWidth(options.columns);
|
|
363
|
+
const blocks = [];
|
|
364
|
+
blocks.push(renderOuroMasthead({
|
|
365
|
+
isTTY: options.isTTY,
|
|
366
|
+
columns: width,
|
|
367
|
+
subtitle: options.masthead?.subtitle,
|
|
368
|
+
}).trimEnd());
|
|
369
|
+
const introLines = [
|
|
370
|
+
options.isTTY ? color(options.title, BONE, true) : options.title,
|
|
371
|
+
...(options.summary
|
|
372
|
+
? wrapPlain(options.summary, Math.max(20, width - 2)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
373
|
+
: []),
|
|
374
|
+
];
|
|
375
|
+
blocks.push(introLines.join("\n"));
|
|
376
|
+
for (const section of options.sections ?? []) {
|
|
377
|
+
const lines = section.lines.map((line) => options.isTTY ? color(line, BONE) : line);
|
|
378
|
+
blocks.push((options.isTTY
|
|
379
|
+
? renderOperationSectionTTY(section.title, lines, width)
|
|
380
|
+
: renderOperationSectionPlain(section.title, lines)).join("\n"));
|
|
381
|
+
}
|
|
382
|
+
const actionList = options.actions ?? [];
|
|
383
|
+
if (actionList.length > 0) {
|
|
384
|
+
const lines = [];
|
|
385
|
+
for (const [index, action] of actionList.entries()) {
|
|
386
|
+
lines.push(options.isTTY ? color(`${index + 1}. ${renderActionLine(action)}`, BONE, true) : `${index + 1}. ${renderActionLine(action)}`);
|
|
387
|
+
lines.push(options.isTTY ? color(`run: ${action.command}`, MIST) : `run: ${action.command}`);
|
|
388
|
+
}
|
|
389
|
+
blocks.push((options.isTTY
|
|
390
|
+
? renderOperationSectionTTY("Next moves", lines, width)
|
|
391
|
+
: renderOperationSectionPlain("Next moves", lines)).join("\n"));
|
|
392
|
+
}
|
|
393
|
+
if (options.prompt) {
|
|
394
|
+
blocks.push(options.isTTY ? color(options.prompt, BONE, true) : options.prompt);
|
|
395
|
+
}
|
|
396
|
+
return `${blocks.join("\n\n")}\n`;
|
|
397
|
+
}
|
|
198
398
|
function formatOperationStep(step) {
|
|
199
399
|
const marker = step.status === "done"
|
|
200
400
|
? "✓"
|
|
@@ -209,6 +209,11 @@ function cacheResult(agentName, result) {
|
|
|
209
209
|
cachedPools.set(agentName, result);
|
|
210
210
|
return result;
|
|
211
211
|
}
|
|
212
|
+
function selectedProviders(providers) {
|
|
213
|
+
if (!providers || providers.length === 0)
|
|
214
|
+
return [...VALID_PROVIDERS];
|
|
215
|
+
return [...new Set(providers)];
|
|
216
|
+
}
|
|
212
217
|
function resultForProvider(poolResult, provider) {
|
|
213
218
|
if (!poolResult.ok)
|
|
214
219
|
return poolResult;
|
|
@@ -227,12 +232,13 @@ function readProviderCredentialPool(agentName) {
|
|
|
227
232
|
return cachedPools.get(agentName) ?? missingPool(agentName);
|
|
228
233
|
}
|
|
229
234
|
async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
235
|
+
const providersToRead = selectedProviders(options.providers);
|
|
230
236
|
try {
|
|
231
237
|
const store = (0, credential_access_1.getCredentialStore)(agentName);
|
|
232
238
|
options.onProgress?.(`reading vault items for ${agentName}...`);
|
|
233
239
|
const providers = {};
|
|
234
240
|
let updatedAt = new Date(0).toISOString();
|
|
235
|
-
for (const provider of
|
|
241
|
+
for (const provider of providersToRead) {
|
|
236
242
|
const itemName = providerCredentialItemName(provider);
|
|
237
243
|
options.onProgress?.(`reading ${provider} credentials...`);
|
|
238
244
|
let raw;
|
|
@@ -261,9 +267,19 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
|
261
267
|
component: "config/identity",
|
|
262
268
|
event: "config.provider_credentials_loaded",
|
|
263
269
|
message: "loaded provider credentials from vault",
|
|
264
|
-
meta: {
|
|
270
|
+
meta: {
|
|
271
|
+
agentName,
|
|
272
|
+
providerCount: Object.keys(providers).length,
|
|
273
|
+
requestedProviderCount: providersToRead.length,
|
|
274
|
+
cacheSkipped: options.skipCache === true,
|
|
275
|
+
},
|
|
265
276
|
});
|
|
266
|
-
|
|
277
|
+
const result = {
|
|
278
|
+
ok: true,
|
|
279
|
+
poolPath: providerCredentialsVaultPath(agentName),
|
|
280
|
+
pool,
|
|
281
|
+
};
|
|
282
|
+
return options.skipCache ? result : cacheResult(agentName, result);
|
|
267
283
|
}
|
|
268
284
|
catch (error) {
|
|
269
285
|
const cached = cachedPools.get(agentName);
|
|
@@ -282,7 +298,7 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
|
282
298
|
});
|
|
283
299
|
if (options.preserveCachedOnFailure && cached?.ok)
|
|
284
300
|
return cached;
|
|
285
|
-
return cacheResult(agentName, result);
|
|
301
|
+
return options.skipCache ? result : cacheResult(agentName, result);
|
|
286
302
|
}
|
|
287
303
|
}
|
|
288
304
|
function createProviderCredentialRecord(input) {
|