@ouro.bot/cli 0.1.0-alpha.420 → 0.1.0-alpha.421

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 CHANGED
@@ -1,6 +1,16 @@
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.421",
6
+ "changes": [
7
+ "`ouro vault create`, `ouro vault unlock`, `ouro vault replace`, `ouro vault recover`, `ouro vault status`, and `ouro vault config` now use the shared human CLI progress checklist for vault account creation, local unlock storage, vault probes, credential reads, runtime config writes, and recovery imports.",
8
+ "`ouro status --agent`, `ouro provider status`, `ouro config models`, and `ouro config model` now show progress while reading provider credentials, listing GitHub Copilot models, and checking selected models.",
9
+ "Hatch provider credential resolution and post-start `ouro up` repair auth now route progress through the shared command renderer instead of raw ad hoc output.",
10
+ "Credential command progress tests now cover successful phases, failure cleanup, provider model exceptions, and secret redaction so these flows stay visibly alive without leaking values.",
11
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the remaining credential command progress release."
12
+ ]
13
+ },
4
14
  {
5
15
  "version": "0.1.0-alpha.420",
6
16
  "changes": [
@@ -189,6 +189,18 @@ function createHumanCommandProgress(deps, commandName) {
189
189
  commandName,
190
190
  });
191
191
  }
192
+ async function runCommandProgressPhase(progress, label, run, detail) {
193
+ progress.startPhase(label);
194
+ try {
195
+ const result = await Promise.resolve(run());
196
+ progress.completePhase(label, detail(result));
197
+ return result;
198
+ }
199
+ catch (error) {
200
+ progress.completePhase(label, "failed");
201
+ throw error;
202
+ }
203
+ }
192
204
  function daemonProgressSummary(result) {
193
205
  if (result.verifyStartupStatus === false)
194
206
  return "not answering yet";
@@ -626,14 +638,25 @@ async function resolveHatchInput(command, deps) {
626
638
  if (!agentName || !humanName || !(0, cli_parse_2.isAgentProvider)(providerRaw)) {
627
639
  throw new Error(`Usage\n${(0, cli_parse_2.usage)()}`);
628
640
  }
629
- const credentials = await (0, auth_flow_1.resolveHatchCredentials)({
630
- agentName,
631
- provider: providerRaw,
632
- credentials: command.credentials,
633
- promptInput: prompt,
634
- runAuthFlow: deps.runAuthFlow,
635
- onProgress: deps.writeStdout,
636
- });
641
+ const progress = createHumanCommandProgress(deps, "hatch auth");
642
+ let credentials;
643
+ try {
644
+ progress.startPhase(`resolving ${providerRaw} credentials`);
645
+ credentials = await (0, auth_flow_1.resolveHatchCredentials)({
646
+ agentName,
647
+ provider: providerRaw,
648
+ credentials: command.credentials,
649
+ promptInput: prompt,
650
+ runAuthFlow: deps.runAuthFlow,
651
+ onProgress: (message) => progress.updateDetail(message),
652
+ });
653
+ progress.completePhase(`resolving ${providerRaw} credentials`, "ready");
654
+ }
655
+ catch (error) {
656
+ progress.end();
657
+ throw error;
658
+ }
659
+ progress.end();
637
660
  return {
638
661
  agentName,
639
662
  humanName,
@@ -813,7 +836,7 @@ function rejectGeneratedVaultUnlockSecret(action) {
813
836
  throw new Error(`vault ${action} no longer supports --generate-unlock-secret. Re-run without that flag and enter a human-chosen unlock secret; Ouro will not print vault unlock secrets.`);
814
837
  }
815
838
  async function createRepairVaultForAgent(input) {
816
- const result = await (0, vault_setup_1.createVaultAccount)("Ouro credential vault", input.serverUrl, input.email, input.unlockSecret);
839
+ const result = await runCommandProgressPhase(input.progress, "creating vault account", () => (0, vault_setup_1.createVaultAccount)("Ouro credential vault", input.serverUrl, input.email, input.unlockSecret), (created) => created.success ? "created" : "failed");
817
840
  if (!result.success) {
818
841
  const message = [
819
842
  `vault ${input.action} failed for ${input.agentName}: ${result.error}`,
@@ -827,14 +850,18 @@ async function createRepairVaultForAgent(input) {
827
850
  input.deps.writeStdout(message);
828
851
  return { ok: false, message };
829
852
  }
830
- writeAgentVaultConfig(input.agentName, input.configPath, input.config, { email: input.email, serverUrl: input.serverUrl });
831
- const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
832
- agentName: input.agentName,
833
- email: input.email,
834
- serverUrl: input.serverUrl,
835
- }, input.unlockSecret, { homeDir: input.deps.homeDir, store: input.store });
836
- (0, credential_access_1.resetCredentialStore)();
837
- await (0, credential_access_1.getCredentialStore)(input.agentName).get("__ouro_vault_probe__");
853
+ const store = await runCommandProgressPhase(input.progress, "saving local unlock", () => {
854
+ writeAgentVaultConfig(input.agentName, input.configPath, input.config, { email: input.email, serverUrl: input.serverUrl });
855
+ return (0, vault_unlock_1.storeVaultUnlockSecret)({
856
+ agentName: input.agentName,
857
+ email: input.email,
858
+ serverUrl: input.serverUrl,
859
+ }, input.unlockSecret, { homeDir: input.deps.homeDir, store: input.store });
860
+ }, (saved) => saved.kind);
861
+ await runCommandProgressPhase(input.progress, "checking vault access", async () => {
862
+ (0, credential_access_1.resetCredentialStore)();
863
+ await (0, credential_access_1.getCredentialStore)(input.agentName).get("__ouro_vault_probe__");
864
+ }, () => "ok");
838
865
  return { ok: true, store };
839
866
  }
840
867
  async function executeVaultUnlock(command, deps) {
@@ -845,13 +872,22 @@ async function executeVaultUnlock(command, deps) {
845
872
  const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
846
873
  const vault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
847
874
  const unlockSecret = await promptSecret(`Ouro vault unlock secret for ${vault.email}: `);
848
- const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
849
- agentName: command.agent,
850
- email: vault.email,
851
- serverUrl: vault.serverUrl,
852
- }, unlockSecret, { homeDir: deps.homeDir, store: command.store });
853
- (0, credential_access_1.resetCredentialStore)();
854
- await (0, credential_access_1.getCredentialStore)(command.agent).get("__ouro_vault_probe__");
875
+ const progress = createHumanCommandProgress(deps, "vault unlock");
876
+ let store;
877
+ try {
878
+ store = await runCommandProgressPhase(progress, "saving local unlock", () => (0, vault_unlock_1.storeVaultUnlockSecret)({
879
+ agentName: command.agent,
880
+ email: vault.email,
881
+ serverUrl: vault.serverUrl,
882
+ }, unlockSecret, { homeDir: deps.homeDir, store: command.store }), (saved) => saved.kind);
883
+ await runCommandProgressPhase(progress, "checking vault access", async () => {
884
+ (0, credential_access_1.resetCredentialStore)();
885
+ await (0, credential_access_1.getCredentialStore)(command.agent).get("__ouro_vault_probe__");
886
+ }, () => "ok");
887
+ }
888
+ finally {
889
+ progress.end();
890
+ }
855
891
  const message = [
856
892
  `vault unlocked for ${command.agent} on this machine`,
857
893
  `vault: ${vault.email} at ${vault.serverUrl}`,
@@ -877,25 +913,40 @@ async function executeVaultCreate(command, deps) {
877
913
  confirmQuestion: `Confirm Ouro vault unlock secret for ${email}: `,
878
914
  emptyError: "vault create requires an unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.",
879
915
  });
880
- const result = await (0, vault_setup_1.createVaultAccount)("Ouro credential vault", serverUrl, email, unlockSecret);
881
- if (!result.success) {
882
- const message = [
883
- `vault create failed for ${command.agent}: ${result.error}`,
884
- "",
885
- "If this vault account already exists, run:",
886
- ` ouro vault unlock --agent ${command.agent}`,
887
- ].join("\n");
888
- deps.writeStdout(message);
889
- return message;
916
+ const progress = createHumanCommandProgress(deps, "vault create");
917
+ let store;
918
+ try {
919
+ const result = await runCommandProgressPhase(progress, "creating vault account", () => (0, vault_setup_1.createVaultAccount)("Ouro credential vault", serverUrl, email, unlockSecret), (created) => created.success ? "created" : "failed");
920
+ if (!result.success) {
921
+ const message = [
922
+ `vault create failed for ${command.agent}: ${result.error}`,
923
+ "",
924
+ "If this vault account already exists, run:",
925
+ ` ouro vault unlock --agent ${command.agent}`,
926
+ ].join("\n");
927
+ progress.end();
928
+ deps.writeStdout(message);
929
+ return message;
930
+ }
931
+ store = await runCommandProgressPhase(progress, "saving local unlock", () => {
932
+ writeAgentVaultConfig(command.agent, configPath, config, { email, serverUrl });
933
+ return (0, vault_unlock_1.storeVaultUnlockSecret)({
934
+ agentName: command.agent,
935
+ email,
936
+ serverUrl,
937
+ }, unlockSecret, { homeDir: deps.homeDir, store: command.store });
938
+ }, (saved) => saved.kind);
939
+ await runCommandProgressPhase(progress, "checking vault access", async () => {
940
+ (0, credential_access_1.resetCredentialStore)();
941
+ await (0, credential_access_1.getCredentialStore)(command.agent).get("__ouro_vault_probe__");
942
+ }, () => "ok");
943
+ }
944
+ finally {
945
+ progress.end();
890
946
  }
891
- writeAgentVaultConfig(command.agent, configPath, config, { email, serverUrl });
892
- const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
893
- agentName: command.agent,
894
- email,
895
- serverUrl,
896
- }, unlockSecret, { homeDir: deps.homeDir, store: command.store });
897
- (0, credential_access_1.resetCredentialStore)();
898
- await (0, credential_access_1.getCredentialStore)(command.agent).get("__ouro_vault_probe__");
947
+ /* v8 ignore next -- defensive: success path assigns store before continuing @preserve */
948
+ if (!store)
949
+ throw new Error(`vault create failed for ${command.agent}: local unlock material was not saved`);
899
950
  const message = appendBundleSyncSummary([
900
951
  `vault created for ${command.agent}`,
901
952
  `vault: ${email} at ${serverUrl}`,
@@ -923,17 +974,28 @@ async function executeVaultReplace(command, deps) {
923
974
  confirmQuestion: `Confirm new Ouro vault unlock secret for ${email}: `,
924
975
  emptyError: "vault replace requires an unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.",
925
976
  });
926
- const repair = await createRepairVaultForAgent({
927
- action: "replace",
928
- agentName: command.agent,
929
- email,
930
- serverUrl,
931
- unlockSecret,
932
- store: command.store,
933
- deps,
934
- configPath,
935
- config,
936
- });
977
+ const progress = createHumanCommandProgress(deps, "vault replace");
978
+ let repair;
979
+ try {
980
+ repair = await createRepairVaultForAgent({
981
+ action: "replace",
982
+ agentName: command.agent,
983
+ email,
984
+ serverUrl,
985
+ unlockSecret,
986
+ store: command.store,
987
+ deps,
988
+ progress,
989
+ configPath,
990
+ config,
991
+ });
992
+ }
993
+ finally {
994
+ progress.end();
995
+ }
996
+ /* v8 ignore next -- defensive: createRepairVaultForAgent either returns or throws @preserve */
997
+ if (!repair)
998
+ throw new Error(`vault replace failed for ${command.agent}: no vault repair result`);
937
999
  if (!repair.ok)
938
1000
  return repair.message;
939
1001
  const message = appendBundleSyncSummary([
@@ -966,43 +1028,60 @@ async function executeVaultRecover(command, deps) {
966
1028
  confirmQuestion: `Confirm new Ouro vault unlock secret for ${email}: `,
967
1029
  emptyError: "vault recover requires an unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.",
968
1030
  });
969
- const repair = await createRepairVaultForAgent({
970
- action: "recover",
971
- agentName: command.agent,
972
- email,
973
- serverUrl,
974
- unlockSecret,
975
- store: command.store,
976
- deps,
977
- configPath,
978
- config,
979
- });
980
- if (!repair.ok)
981
- return repair.message;
1031
+ const progress = createHumanCommandProgress(deps, "vault recover");
1032
+ let repair;
982
1033
  const importedProviders = new Set();
983
- let mergedRecoveredRuntimeConfig = {};
984
- for (const source of sourceImports) {
985
- for (const provider of source.providers) {
986
- await (0, provider_credentials_1.upsertProviderCredential)({
987
- agentName: command.agent,
988
- provider: provider.provider,
989
- credentials: provider.credentials,
990
- config: provider.config,
991
- provenance: { source: "manual" },
992
- now,
993
- });
994
- importedProviders.add(provider.provider);
995
- }
996
- mergedRecoveredRuntimeConfig = mergeRuntimeConfig(mergedRecoveredRuntimeConfig, source.runtimeConfig);
997
- }
998
- const { agentConfig: mergedRuntimeConfig, machineConfig: mergedMachineRuntimeConfig } = splitRuntimeConfigByScope(mergedRecoveredRuntimeConfig);
999
- const runtimeFields = summarizeRuntimeConfigFields(mergedRuntimeConfig);
1000
- const machineRuntimeFields = summarizeRuntimeConfigFields(mergedMachineRuntimeConfig);
1001
- if (runtimeFields.length > 0) {
1002
- await (0, runtime_credentials_1.upsertRuntimeCredentialConfig)(command.agent, mergedRuntimeConfig, now);
1034
+ let runtimeFields = [];
1035
+ let machineRuntimeFields = [];
1036
+ try {
1037
+ repair = await createRepairVaultForAgent({
1038
+ action: "recover",
1039
+ agentName: command.agent,
1040
+ email,
1041
+ serverUrl,
1042
+ unlockSecret,
1043
+ store: command.store,
1044
+ deps,
1045
+ progress,
1046
+ configPath,
1047
+ config,
1048
+ });
1049
+ if (!repair.ok)
1050
+ return repair.message;
1051
+ await runCommandProgressPhase(progress, "importing recovered credentials", async () => {
1052
+ let mergedRecoveredRuntimeConfig = {};
1053
+ for (const source of sourceImports) {
1054
+ for (const provider of source.providers) {
1055
+ await (0, provider_credentials_1.upsertProviderCredential)({
1056
+ agentName: command.agent,
1057
+ provider: provider.provider,
1058
+ credentials: provider.credentials,
1059
+ config: provider.config,
1060
+ provenance: { source: "manual" },
1061
+ now,
1062
+ });
1063
+ importedProviders.add(provider.provider);
1064
+ }
1065
+ mergedRecoveredRuntimeConfig = mergeRuntimeConfig(mergedRecoveredRuntimeConfig, source.runtimeConfig);
1066
+ }
1067
+ const splitRuntimeConfig = splitRuntimeConfigByScope(mergedRecoveredRuntimeConfig);
1068
+ runtimeFields = summarizeRuntimeConfigFields(splitRuntimeConfig.agentConfig);
1069
+ machineRuntimeFields = summarizeRuntimeConfigFields(splitRuntimeConfig.machineConfig);
1070
+ if (runtimeFields.length > 0) {
1071
+ await (0, runtime_credentials_1.upsertRuntimeCredentialConfig)(command.agent, splitRuntimeConfig.agentConfig, now);
1072
+ }
1073
+ if (machineRuntimeFields.length > 0) {
1074
+ await (0, runtime_credentials_1.upsertMachineRuntimeCredentialConfig)(command.agent, currentMachineId(deps), splitRuntimeConfig.machineConfig, now);
1075
+ }
1076
+ return {
1077
+ providerCount: importedProviders.size,
1078
+ runtimeCount: runtimeFields.length,
1079
+ machineRuntimeCount: machineRuntimeFields.length,
1080
+ };
1081
+ }, (imported) => `${imported.providerCount} providers, ${imported.runtimeCount + imported.machineRuntimeCount} runtime fields`);
1003
1082
  }
1004
- if (machineRuntimeFields.length > 0) {
1005
- await (0, runtime_credentials_1.upsertMachineRuntimeCredentialConfig)(command.agent, currentMachineId(deps), mergedMachineRuntimeConfig, now);
1083
+ finally {
1084
+ progress.end();
1006
1085
  }
1007
1086
  const providerList = [...importedProviders].sort();
1008
1087
  const message = appendBundleSyncSummary([
@@ -1053,26 +1132,39 @@ async function executeVaultStatus(command, deps) {
1053
1132
  `local unlock: ${status.stored ? "available" : "missing"}`,
1054
1133
  ];
1055
1134
  if (status.stored) {
1056
- const runtime = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true });
1057
- if (runtime.ok) {
1058
- lines.push(`runtime credentials: ${summarizeRuntimeConfigFields(runtime.config).join(", ") || "none stored"} (${runtime.revision})`);
1059
- }
1060
- else {
1061
- lines.push(`runtime credentials: ${runtime.reason} (${runtime.error})`);
1062
- if (runtime.reason === "missing") {
1063
- lines.push(` fix: Run 'ouro vault config set --agent ${command.agent} --key <field>' to store sense/integration credentials.`);
1135
+ const progress = createHumanCommandProgress(deps, "vault status");
1136
+ try {
1137
+ const runtime = await runCommandProgressPhase(progress, "reading runtime credentials", () => (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true }), (runtimeResult) => runtimeResult.ok ? runtimeResult.revision : runtimeResult.reason);
1138
+ if (runtime.ok) {
1139
+ lines.push(`runtime credentials: ${summarizeRuntimeConfigFields(runtime.config).join(", ") || "none stored"} (${runtime.revision})`);
1064
1140
  }
1065
- }
1066
- const pool = await (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent);
1067
- if (pool.ok) {
1068
- const summary = (0, provider_credentials_1.summarizeProviderCredentialPool)(pool.pool);
1069
- lines.push(`provider credentials: ${summary.providers.length === 0 ? "none stored" : ""}`);
1070
- for (const provider of summary.providers) {
1071
- lines.push(` ${provider.provider}: credential fields ${provider.credentialFields.join(", ") || "none"}, config fields ${provider.configFields.join(", ") || "none"}`);
1141
+ else {
1142
+ lines.push(`runtime credentials: ${runtime.reason} (${runtime.error})`);
1143
+ if (runtime.reason === "missing") {
1144
+ lines.push(` fix: Run 'ouro vault config set --agent ${command.agent} --key <field>' to store sense/integration credentials.`);
1145
+ }
1146
+ }
1147
+ const pool = await runCommandProgressPhase(progress, "reading provider credentials", () => (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent, {
1148
+ onProgress: (message) => progress.updateDetail(message),
1149
+ }), (poolResult) => {
1150
+ if (!poolResult.ok)
1151
+ return poolResult.reason;
1152
+ const summary = (0, provider_credentials_1.summarizeProviderCredentialPool)(poolResult.pool);
1153
+ return summary.providers.map((provider) => provider.provider).join(", ") || "none stored";
1154
+ });
1155
+ if (pool.ok) {
1156
+ const summary = (0, provider_credentials_1.summarizeProviderCredentialPool)(pool.pool);
1157
+ lines.push(`provider credentials: ${summary.providers.length === 0 ? "none stored" : ""}`);
1158
+ for (const provider of summary.providers) {
1159
+ lines.push(` ${provider.provider}: credential fields ${provider.credentialFields.join(", ") || "none"}, config fields ${provider.configFields.join(", ") || "none"}`);
1160
+ }
1161
+ }
1162
+ else {
1163
+ lines.push(`provider credentials: unavailable (${pool.error})`);
1072
1164
  }
1073
1165
  }
1074
- else {
1075
- lines.push(`provider credentials: unavailable (${pool.error})`);
1166
+ finally {
1167
+ progress.end();
1076
1168
  }
1077
1169
  }
1078
1170
  else {
@@ -1190,13 +1282,24 @@ async function executeVaultConfigSet(command, deps) {
1190
1282
  if (!value) {
1191
1283
  throw new Error("vault config set requires --value <value> or an interactive prompt");
1192
1284
  }
1193
- const stored = await storeRuntimeConfigKey({
1194
- agent: command.agent,
1195
- key: command.key,
1196
- value,
1197
- scope,
1198
- deps,
1199
- });
1285
+ const progress = createHumanCommandProgress(deps, "vault config set");
1286
+ let stored;
1287
+ try {
1288
+ stored = await runCommandProgressPhase(progress, "storing runtime credential", () => storeRuntimeConfigKey({
1289
+ agent: command.agent,
1290
+ key: command.key,
1291
+ value,
1292
+ scope,
1293
+ deps,
1294
+ onProgress: (message) => progress.updateDetail(message),
1295
+ }), (result) => result.revision);
1296
+ }
1297
+ finally {
1298
+ progress.end();
1299
+ }
1300
+ /* v8 ignore next -- defensive: storeRuntimeConfigKey either returns or throws @preserve */
1301
+ if (!stored)
1302
+ throw new Error(`vault config set failed for ${command.agent}: no stored runtime credential result`);
1200
1303
  const message = [
1201
1304
  `stored ${command.key} for ${command.agent} in ${runtimeScopeLabel(scope)}`,
1202
1305
  `runtime credentials: ${stored.revision}`,
@@ -1214,27 +1317,33 @@ async function executeVaultConfigStatus(command, deps) {
1214
1317
  }
1215
1318
  const scopes = command.scope === "all" ? ["agent", "machine"] : [command.scope ?? "agent"];
1216
1319
  const lines = [`agent: ${command.agent}`];
1217
- for (const scope of scopes) {
1218
- const machineId = scope === "machine" ? currentMachineId(deps) : undefined;
1219
- const runtime = scope === "machine"
1220
- ? await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(command.agent, machineId, { preserveCachedOnFailure: true })
1221
- : await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true });
1222
- if (scopes.length > 1)
1223
- lines.push("");
1224
- lines.push(`${scope} runtime config item: ${runtime.itemPath}`);
1225
- if (runtime.ok) {
1226
- lines.push(`status: available (${runtime.revision})`);
1227
- const fields = summarizeRuntimeConfigFields(runtime.config);
1228
- lines.push(`fields: ${fields.length === 0 ? "none stored" : fields.join(", ")}`);
1229
- }
1230
- else {
1231
- lines.push(`status: ${runtime.reason}`);
1232
- lines.push(`error: ${runtime.error}`);
1233
- lines.push(runtime.reason === "missing"
1234
- ? `fix: Run 'ouro vault config set --agent ${command.agent} --key <field>${scope === "machine" ? " --scope machine" : ""}' to store runtime credentials.`
1235
- : `fix: ${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro vault config status'.")}`);
1320
+ const progress = createHumanCommandProgress(deps, "vault config status");
1321
+ try {
1322
+ for (const scope of scopes) {
1323
+ const machineId = scope === "machine" ? currentMachineId(deps) : undefined;
1324
+ const runtime = await runCommandProgressPhase(progress, `reading ${scope} runtime config`, () => scope === "machine"
1325
+ ? (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(command.agent, machineId, { preserveCachedOnFailure: true })
1326
+ : (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true }), (runtimeResult) => runtimeResult.ok ? runtimeResult.revision : runtimeResult.reason);
1327
+ if (scopes.length > 1)
1328
+ lines.push("");
1329
+ lines.push(`${scope} runtime config item: ${runtime.itemPath}`);
1330
+ if (runtime.ok) {
1331
+ lines.push(`status: available (${runtime.revision})`);
1332
+ const fields = summarizeRuntimeConfigFields(runtime.config);
1333
+ lines.push(`fields: ${fields.length === 0 ? "none stored" : fields.join(", ")}`);
1334
+ }
1335
+ else {
1336
+ lines.push(`status: ${runtime.reason}`);
1337
+ lines.push(`error: ${runtime.error}`);
1338
+ lines.push(runtime.reason === "missing"
1339
+ ? `fix: Run 'ouro vault config set --agent ${command.agent} --key <field>${scope === "machine" ? " --scope machine" : ""}' to store runtime credentials.`
1340
+ : `fix: ${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro vault config status'.")}`);
1341
+ }
1236
1342
  }
1237
1343
  }
1344
+ finally {
1345
+ progress.end();
1346
+ }
1238
1347
  const message = lines.join("\n");
1239
1348
  deps.writeStdout(message);
1240
1349
  return message;
@@ -1714,7 +1823,20 @@ function renderProviderCredentialLine(credential) {
1714
1823
  }
1715
1824
  async function executeProviderStatus(command, deps) {
1716
1825
  const agentRoot = providerCliAgentRoot(command, deps);
1717
- await (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent);
1826
+ const progress = createHumanCommandProgress(deps, "provider status");
1827
+ try {
1828
+ await runCommandProgressPhase(progress, "reading provider credentials", () => (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent, {
1829
+ onProgress: (message) => progress.updateDetail(message),
1830
+ }), (poolResult) => {
1831
+ if (!poolResult.ok)
1832
+ return poolResult.reason;
1833
+ const summary = (0, provider_credentials_1.summarizeProviderCredentialPool)(poolResult.pool);
1834
+ return summary.providers.map((provider) => provider.provider).join(", ") || "none stored";
1835
+ });
1836
+ }
1837
+ finally {
1838
+ progress.end();
1839
+ }
1718
1840
  const homeDir = providerCliHomeDir(deps);
1719
1841
  const lines = [`provider status: ${command.agent}`];
1720
1842
  for (const lane of ["outward", "inner"]) {
@@ -2006,36 +2128,61 @@ async function executeLegacyConfigModel(command, deps) {
2006
2128
  const lane = command.facing === "agent" ? "inner" : "outward";
2007
2129
  const { agentRoot, state } = readOrBootstrapProviderState(command.agent, deps);
2008
2130
  const binding = state.lanes[lane];
2131
+ const progress = binding.provider === "github-copilot" ? createHumanCommandProgress(deps, "config model") : null;
2132
+ const writeMessage = (message) => {
2133
+ progress?.end();
2134
+ deps.writeStdout(message);
2135
+ return message;
2136
+ };
2009
2137
  if (binding.provider === "github-copilot") {
2010
- const credential = await readProviderCredentialRecord(command.agent, "github-copilot", deps);
2011
- if (credential.ok) {
2012
- const ghConfig = {
2013
- ...credential.record.config,
2014
- ...credential.record.credentials,
2015
- };
2016
- const githubToken = ghConfig.githubToken;
2017
- const baseUrl = ghConfig.baseUrl;
2018
- if (typeof githubToken === "string" && typeof baseUrl === "string") {
2019
- const fetchFn = deps.fetchImpl ?? fetch;
2020
- try {
2021
- const models = await listGithubCopilotModels(baseUrl, githubToken, fetchFn);
2022
- const available = models.map((m) => m.id);
2023
- if (available.length > 0 && !available.includes(command.modelName)) {
2024
- const message = `model '${command.modelName}' not found. available models:\n${available.map((id) => ` ${id}`).join("\n")}`;
2025
- deps.writeStdout(message);
2026
- return message;
2138
+ try {
2139
+ progress?.startPhase("reading github-copilot credentials");
2140
+ const credential = await readProviderCredentialRecord(command.agent, "github-copilot", deps, {
2141
+ onProgress: (message) => progress?.updateDetail(message),
2142
+ });
2143
+ if (credential.ok) {
2144
+ const ghConfig = {
2145
+ ...credential.record.config,
2146
+ ...credential.record.credentials,
2147
+ };
2148
+ const githubToken = ghConfig.githubToken;
2149
+ const baseUrl = ghConfig.baseUrl;
2150
+ if (typeof githubToken === "string" && typeof baseUrl === "string") {
2151
+ progress?.completePhase("reading github-copilot credentials", "found");
2152
+ const fetchFn = deps.fetchImpl ?? fetch;
2153
+ try {
2154
+ progress?.startPhase("listing github-copilot models");
2155
+ const models = await listGithubCopilotModels(baseUrl, githubToken, fetchFn);
2156
+ const available = models.map((m) => m.id);
2157
+ progress?.completePhase("listing github-copilot models", `${available.length} model${available.length === 1 ? "" : "s"}`);
2158
+ if (available.length > 0 && !available.includes(command.modelName)) {
2159
+ const message = `model '${command.modelName}' not found. available models:\n${available.map((id) => ` ${id}`).join("\n")}`;
2160
+ return writeMessage(message);
2161
+ }
2162
+ }
2163
+ catch {
2164
+ progress?.completePhase("listing github-copilot models", "skipped");
2165
+ // Catalog validation failed; the live ping below gives the actionable result.
2166
+ }
2167
+ progress?.startPhase(`checking ${command.modelName}`);
2168
+ const pingResult = await (0, provider_ping_1.pingGithubCopilotModel)(baseUrl, githubToken, command.modelName, fetchFn);
2169
+ progress?.completePhase(`checking ${command.modelName}`, pingResult.ok ? "ok" : "failed");
2170
+ if (!pingResult.ok) {
2171
+ const message = `model '${command.modelName}' ping failed: ${pingResult.error}\nrun \`ouro config models --agent ${command.agent}\` to see available models.`;
2172
+ return writeMessage(message);
2027
2173
  }
2028
2174
  }
2029
- catch {
2030
- // Catalog validation failed; the live ping below gives the actionable result.
2031
- }
2032
- const pingResult = await (0, provider_ping_1.pingGithubCopilotModel)(baseUrl, githubToken, command.modelName, fetchFn);
2033
- if (!pingResult.ok) {
2034
- const message = `model '${command.modelName}' ping failed: ${pingResult.error}\nrun \`ouro config models --agent ${command.agent}\` to see available models.`;
2035
- deps.writeStdout(message);
2036
- return message;
2175
+ else {
2176
+ progress?.completePhase("reading github-copilot credentials", "missing fields");
2037
2177
  }
2038
2178
  }
2179
+ else {
2180
+ progress?.completePhase("reading github-copilot credentials", credential.reason);
2181
+ }
2182
+ }
2183
+ catch (error) {
2184
+ progress?.end();
2185
+ throw error;
2039
2186
  }
2040
2187
  }
2041
2188
  const updatedAt = providerCliNow(deps).toISOString();
@@ -2049,8 +2196,7 @@ async function executeLegacyConfigModel(command, deps) {
2049
2196
  delete state.readiness[lane];
2050
2197
  (0, provider_state_1.writeProviderState)(agentRoot, state);
2051
2198
  const message = `deprecated: updated ${command.agent} model on ${lane}/${binding.provider}: ${binding.model} -> ${command.modelName}\nUse \`ouro use --agent ${command.agent} --lane ${lane} --provider ${binding.provider} --model ${command.modelName}\` next time.`;
2052
- deps.writeStdout(message);
2053
- return message;
2199
+ return writeMessage(message);
2054
2200
  }
2055
2201
  // ── System setup ──
2056
2202
  async function registerOuroBundleTypeNonBlocking(deps) {
@@ -2847,11 +2993,11 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2847
2993
  promptInput: deps.promptInput ?? (async () => "n"),
2848
2994
  writeStdout: deps.writeStdout,
2849
2995
  runAuthFlow: async (agent, providerOverride) => {
2850
- const { config } = (0, auth_flow_1.readAgentConfigForAgent)(agent, deps.bundlesRoot);
2851
- const provider = providerOverride ?? config.humanFacing.provider;
2852
- /* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
2853
- const authRunner = deps.runAuthFlow ?? (await Promise.resolve().then(() => __importStar(require("../auth/auth-flow")))).runRuntimeAuthFlow;
2854
- await authRunner({ agentName: agent, provider, promptInput: deps.promptInput, onProgress: deps.writeStdout });
2996
+ await executeAuthRun({
2997
+ kind: "auth.run",
2998
+ agent,
2999
+ ...(providerOverride ? { provider: providerOverride } : {}),
3000
+ }, deps);
2855
3001
  },
2856
3002
  runVaultUnlock: async (agent) => {
2857
3003
  await executeVaultUnlock({ kind: "vault.unlock", agent }, deps);
@@ -3561,28 +3707,44 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
3561
3707
  deps.writeStdout(message);
3562
3708
  return message;
3563
3709
  }
3564
- const credential = await readProviderCredentialRecord(command.agent, "github-copilot", deps);
3565
- const ghConfig = credential.ok
3566
- ? { ...credential.record.config, ...credential.record.credentials }
3567
- : {};
3568
- if (typeof ghConfig.githubToken !== "string" || typeof ghConfig.baseUrl !== "string") {
3569
- throw new Error(`github-copilot credentials not configured. Run \`ouro auth --agent ${command.agent} --provider github-copilot\` first.`);
3570
- }
3571
- const fetchFn = deps.fetchImpl ?? fetch;
3572
- const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
3573
- if (models.length === 0) {
3574
- const message = "no models found";
3710
+ const progress = createHumanCommandProgress(deps, "config models");
3711
+ const writeMessage = (message) => {
3712
+ progress.end();
3575
3713
  deps.writeStdout(message);
3576
3714
  return message;
3715
+ };
3716
+ try {
3717
+ progress.startPhase("reading github-copilot credentials");
3718
+ const credential = await readProviderCredentialRecord(command.agent, "github-copilot", deps, {
3719
+ onProgress: (message) => progress.updateDetail(message),
3720
+ });
3721
+ const ghConfig = credential.ok
3722
+ ? { ...credential.record.config, ...credential.record.credentials }
3723
+ : {};
3724
+ if (!credential.ok || typeof ghConfig.githubToken !== "string" || typeof ghConfig.baseUrl !== "string") {
3725
+ progress.completePhase("reading github-copilot credentials", credential.ok ? "missing fields" : credential.reason);
3726
+ throw new Error(`github-copilot credentials not configured. Run \`ouro auth --agent ${command.agent} --provider github-copilot\` first.`);
3727
+ }
3728
+ progress.completePhase("reading github-copilot credentials", "found");
3729
+ const fetchFn = deps.fetchImpl ?? fetch;
3730
+ progress.startPhase("listing github-copilot models");
3731
+ const models = await listGithubCopilotModels(ghConfig.baseUrl, ghConfig.githubToken, fetchFn);
3732
+ progress.completePhase("listing github-copilot models", `${models.length} model${models.length === 1 ? "" : "s"}`);
3733
+ if (models.length === 0) {
3734
+ return writeMessage("no models found");
3735
+ }
3736
+ const lines = ["available models:"];
3737
+ for (const m of models) {
3738
+ const caps = m.capabilities?.length ? ` (${m.capabilities.join(", ")})` : "";
3739
+ lines.push(` ${m.id}${caps}`);
3740
+ }
3741
+ const message = lines.join("\n");
3742
+ return writeMessage(message);
3577
3743
  }
3578
- const lines = ["available models:"];
3579
- for (const m of models) {
3580
- const caps = m.capabilities?.length ? ` (${m.capabilities.join(", ")})` : "";
3581
- lines.push(` ${m.id}${caps}`);
3744
+ catch (error) {
3745
+ progress.end();
3746
+ throw error;
3582
3747
  }
3583
- const message = lines.join("\n");
3584
- deps.writeStdout(message);
3585
- return message;
3586
3748
  }
3587
3749
  /* v8 ignore stop */
3588
3750
  // ── config model (local, no daemon socket needed) ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.420",
3
+ "version": "0.1.0-alpha.421",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",