@ouro.bot/cli 0.1.0-alpha.344 → 0.1.0-alpha.345

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,12 @@
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.345",
6
+ "changes": [
7
+ "Machine-wide provider credentials now have a local pool at `~/.agentsecrets/providers.json` with one active record per provider, generated credential revisions, safe provenance, redacted summaries, and explicit migration from legacy per-agent `~/.agentsecrets/<agent>/secrets.json` files."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.344",
6
12
  "changes": [
@@ -0,0 +1,386 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getProviderCredentialPoolPath = getProviderCredentialPoolPath;
37
+ exports.validateProviderCredentialPool = validateProviderCredentialPool;
38
+ exports.readProviderCredentialPool = readProviderCredentialPool;
39
+ exports.writeProviderCredentialPool = writeProviderCredentialPool;
40
+ exports.upsertProviderCredential = upsertProviderCredential;
41
+ exports.redactProviderCredentialPool = redactProviderCredentialPool;
42
+ exports.summarizeProviderCredentialPool = summarizeProviderCredentialPool;
43
+ exports.readLegacyAgentProviderCredentials = readLegacyAgentProviderCredentials;
44
+ exports.migrateLegacyAgentProviderCredentials = migrateLegacyAgentProviderCredentials;
45
+ const crypto = __importStar(require("crypto"));
46
+ const fs = __importStar(require("fs"));
47
+ const os = __importStar(require("os"));
48
+ const path = __importStar(require("path"));
49
+ const runtime_1 = require("../nerves/runtime");
50
+ const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
51
+ const VALID_PROVENANCE_SOURCES = ["auth-flow", "legacy-agent-secrets", "manual"];
52
+ const PROVIDER_FIELD_SPLITS = {
53
+ anthropic: {
54
+ credentials: ["setupToken", "refreshToken", "expiresAt"],
55
+ config: [],
56
+ meaningfulCredentials: ["setupToken", "refreshToken"],
57
+ },
58
+ "openai-codex": {
59
+ credentials: ["oauthAccessToken"],
60
+ config: [],
61
+ meaningfulCredentials: ["oauthAccessToken"],
62
+ },
63
+ azure: {
64
+ credentials: ["apiKey"],
65
+ config: ["endpoint", "deployment", "apiVersion", "managedIdentityClientId"],
66
+ meaningfulCredentials: ["apiKey", "managedIdentityClientId"],
67
+ },
68
+ minimax: {
69
+ credentials: ["apiKey"],
70
+ config: [],
71
+ meaningfulCredentials: ["apiKey"],
72
+ },
73
+ "github-copilot": {
74
+ credentials: ["githubToken"],
75
+ config: ["baseUrl"],
76
+ meaningfulCredentials: ["githubToken"],
77
+ },
78
+ };
79
+ function isRecord(value) {
80
+ return !!value && typeof value === "object" && !Array.isArray(value);
81
+ }
82
+ function isAgentProvider(value) {
83
+ return VALID_PROVIDERS.includes(value);
84
+ }
85
+ function isProvenanceSource(value) {
86
+ return typeof value === "string" && VALID_PROVENANCE_SOURCES.includes(value);
87
+ }
88
+ function isCredentialValue(value) {
89
+ return typeof value === "string" || typeof value === "number";
90
+ }
91
+ function isPresentCredentialValue(value) {
92
+ if (!isCredentialValue(value))
93
+ return false;
94
+ if (typeof value === "string")
95
+ return value.trim().length > 0;
96
+ return Number.isFinite(value) && value !== 0;
97
+ }
98
+ function copyKnownFields(source, fields) {
99
+ const result = {};
100
+ for (const field of fields) {
101
+ const value = source[field];
102
+ if (isPresentCredentialValue(value)) {
103
+ result[field] = value;
104
+ }
105
+ }
106
+ return result;
107
+ }
108
+ function splitLegacyProviderConfig(provider, rawConfig) {
109
+ const fields = PROVIDER_FIELD_SPLITS[provider];
110
+ return {
111
+ credentials: copyKnownFields(rawConfig, fields.credentials),
112
+ config: copyKnownFields(rawConfig, fields.config),
113
+ };
114
+ }
115
+ function legacyProviderHasCredentialData(provider, rawConfig) {
116
+ const fields = PROVIDER_FIELD_SPLITS[provider];
117
+ return fields.meaningfulCredentials.some((field) => isPresentCredentialValue(rawConfig[field]));
118
+ }
119
+ function makeEmptyProviderCredentialPool(now) {
120
+ return {
121
+ schemaVersion: 1,
122
+ updatedAt: now.toISOString(),
123
+ providers: {},
124
+ };
125
+ }
126
+ function makeRevision() {
127
+ return `cred_${crypto.randomUUID()}`;
128
+ }
129
+ function getProviderCredentialPoolPath(homeDir = os.homedir()) {
130
+ return path.join(homeDir, ".agentsecrets", "providers.json");
131
+ }
132
+ function validateProviderCredentialPool(value) {
133
+ if (!isRecord(value)) {
134
+ throw new Error("provider credential pool must be an object");
135
+ }
136
+ if (value.schemaVersion !== 1) {
137
+ throw new Error("provider credential pool schemaVersion must be 1");
138
+ }
139
+ if (typeof value.updatedAt !== "string" || value.updatedAt.length === 0) {
140
+ throw new Error("provider credential pool updatedAt must be a non-empty string");
141
+ }
142
+ if (!isRecord(value.providers)) {
143
+ throw new Error("provider credential pool providers must be an object");
144
+ }
145
+ const providers = value.providers;
146
+ for (const [providerKey, rawRecord] of Object.entries(providers)) {
147
+ if (!isAgentProvider(providerKey)) {
148
+ throw new Error(`provider credential pool has unsupported provider ${providerKey}`);
149
+ }
150
+ if (!isRecord(rawRecord)) {
151
+ throw new Error(`${providerKey} credential record must be an object`);
152
+ }
153
+ if (rawRecord.provider !== providerKey) {
154
+ throw new Error(`${providerKey}.provider must match its provider key`);
155
+ }
156
+ if (typeof rawRecord.revision !== "string" || rawRecord.revision.length === 0) {
157
+ throw new Error(`${providerKey}.revision must be a non-empty string`);
158
+ }
159
+ if (typeof rawRecord.updatedAt !== "string" || rawRecord.updatedAt.length === 0) {
160
+ throw new Error(`${providerKey}.updatedAt must be a non-empty string`);
161
+ }
162
+ if (!isRecord(rawRecord.credentials)) {
163
+ throw new Error(`${providerKey}.credentials must be an object`);
164
+ }
165
+ if (!isRecord(rawRecord.config)) {
166
+ throw new Error(`${providerKey}.config must be an object`);
167
+ }
168
+ for (const [field, fieldValue] of Object.entries(rawRecord.credentials)) {
169
+ if (!isCredentialValue(fieldValue)) {
170
+ throw new Error(`${providerKey}.credentials.${field} must be a string or number`);
171
+ }
172
+ }
173
+ for (const [field, fieldValue] of Object.entries(rawRecord.config)) {
174
+ if (!isCredentialValue(fieldValue)) {
175
+ throw new Error(`${providerKey}.config.${field} must be a string or number`);
176
+ }
177
+ }
178
+ if (!isRecord(rawRecord.provenance)) {
179
+ throw new Error(`${providerKey}.provenance must be an object`);
180
+ }
181
+ if (!isProvenanceSource(rawRecord.provenance.source)) {
182
+ throw new Error(`${providerKey}.provenance.source must be auth-flow, legacy-agent-secrets, or manual`);
183
+ }
184
+ const contributedByAgent = rawRecord.provenance.contributedByAgent;
185
+ if (contributedByAgent !== undefined && typeof contributedByAgent !== "string") {
186
+ throw new Error(`${providerKey}.provenance.contributedByAgent must be a string when present`);
187
+ }
188
+ if (typeof rawRecord.provenance.updatedAt !== "string" || rawRecord.provenance.updatedAt.length === 0) {
189
+ throw new Error(`${providerKey}.provenance.updatedAt must be a non-empty string`);
190
+ }
191
+ }
192
+ return value;
193
+ }
194
+ function readProviderCredentialPool(homeDir = os.homedir()) {
195
+ const poolPath = getProviderCredentialPoolPath(homeDir);
196
+ try {
197
+ const raw = fs.readFileSync(poolPath, "utf-8");
198
+ const parsed = JSON.parse(raw);
199
+ const pool = validateProviderCredentialPool(parsed);
200
+ (0, runtime_1.emitNervesEvent)({
201
+ component: "config/identity",
202
+ event: "config.provider_credential_pool_read",
203
+ message: "read provider credential pool",
204
+ meta: { poolPath },
205
+ });
206
+ return { ok: true, poolPath, pool };
207
+ }
208
+ catch (error) {
209
+ if (error.code === "ENOENT") {
210
+ (0, runtime_1.emitNervesEvent)({
211
+ component: "config/identity",
212
+ event: "config.provider_credential_pool_missing",
213
+ message: "provider credential pool not found",
214
+ meta: { poolPath },
215
+ });
216
+ return {
217
+ ok: false,
218
+ reason: "missing",
219
+ poolPath,
220
+ error: "provider credential pool not found",
221
+ };
222
+ }
223
+ (0, runtime_1.emitNervesEvent)({
224
+ level: "error",
225
+ component: "config/identity",
226
+ event: "config.provider_credential_pool_invalid",
227
+ message: "provider credential pool invalid",
228
+ meta: { poolPath, reason: String(error) },
229
+ });
230
+ return {
231
+ ok: false,
232
+ reason: "invalid",
233
+ poolPath,
234
+ error: String(error),
235
+ };
236
+ }
237
+ }
238
+ function writeProviderCredentialPool(homeDir, pool) {
239
+ const validated = validateProviderCredentialPool(pool);
240
+ const poolPath = getProviderCredentialPoolPath(homeDir);
241
+ fs.mkdirSync(path.dirname(poolPath), { recursive: true });
242
+ fs.writeFileSync(poolPath, `${JSON.stringify(validated, null, 2)}\n`, "utf-8");
243
+ (0, runtime_1.emitNervesEvent)({
244
+ component: "config/identity",
245
+ event: "config.provider_credential_pool_written",
246
+ message: "wrote provider credential pool",
247
+ meta: { poolPath, providerCount: Object.keys(validated.providers).length },
248
+ });
249
+ }
250
+ function upsertProviderCredential(input) {
251
+ const homeDir = input.homeDir ?? os.homedir();
252
+ const now = input.now ?? new Date();
253
+ const updatedAt = now.toISOString();
254
+ const readResult = readProviderCredentialPool(homeDir);
255
+ const pool = readResult.ok ? readResult.pool : makeEmptyProviderCredentialPool(now);
256
+ if (!readResult.ok && readResult.reason === "invalid") {
257
+ throw new Error(`Cannot update invalid provider credential pool at ${readResult.poolPath}: ${readResult.error}`);
258
+ }
259
+ const record = {
260
+ provider: input.provider,
261
+ revision: (input.makeRevision ?? makeRevision)(),
262
+ updatedAt,
263
+ credentials: { ...input.credentials },
264
+ config: { ...input.config },
265
+ provenance: {
266
+ ...input.provenance,
267
+ updatedAt,
268
+ },
269
+ };
270
+ pool.updatedAt = updatedAt;
271
+ pool.providers[input.provider] = record;
272
+ writeProviderCredentialPool(homeDir, pool);
273
+ (0, runtime_1.emitNervesEvent)({
274
+ component: "config/identity",
275
+ event: "config.provider_credential_upserted",
276
+ message: "upserted provider credential record",
277
+ meta: { provider: input.provider, revision: record.revision, source: record.provenance.source },
278
+ });
279
+ return record;
280
+ }
281
+ function redactProviderCredentialPool(pool) {
282
+ const validated = validateProviderCredentialPool(pool);
283
+ const providers = {};
284
+ for (const [provider, record] of Object.entries(validated.providers)) {
285
+ const redactedCredentials = {};
286
+ for (const key of Object.keys(record.credentials)) {
287
+ redactedCredentials[key] = "[redacted]";
288
+ }
289
+ providers[provider] = {
290
+ ...record,
291
+ credentials: redactedCredentials,
292
+ config: { ...record.config },
293
+ provenance: { ...record.provenance },
294
+ };
295
+ }
296
+ return {
297
+ ...validated,
298
+ providers,
299
+ };
300
+ }
301
+ function summarizeProviderCredentialPool(pool) {
302
+ const validated = validateProviderCredentialPool(pool);
303
+ return {
304
+ providers: Object.entries(validated.providers).map(([, record]) => ({
305
+ provider: record.provider,
306
+ revision: record.revision,
307
+ source: record.provenance.source,
308
+ contributedByAgent: record.provenance.contributedByAgent,
309
+ updatedAt: record.updatedAt,
310
+ credentialFields: Object.keys(record.credentials).sort(),
311
+ configFields: Object.keys(record.config).sort(),
312
+ })),
313
+ };
314
+ }
315
+ function readLegacyAgentProviderCredentials(input) {
316
+ const homeDir = input.homeDir ?? os.homedir();
317
+ const secretsPath = path.join(homeDir, ".agentsecrets", input.agentName, "secrets.json");
318
+ let parsed;
319
+ try {
320
+ parsed = JSON.parse(fs.readFileSync(secretsPath, "utf-8"));
321
+ }
322
+ catch (error) {
323
+ if (error.code === "ENOENT") {
324
+ return [];
325
+ }
326
+ throw new Error(`Failed to read legacy provider credentials for ${input.agentName}: ${String(error)}`);
327
+ }
328
+ if (!isRecord(parsed) || !isRecord(parsed.providers)) {
329
+ return [];
330
+ }
331
+ const candidates = [];
332
+ for (const [providerKey, rawProviderConfig] of Object.entries(parsed.providers)) {
333
+ if (!isAgentProvider(providerKey) || !isRecord(rawProviderConfig)) {
334
+ continue;
335
+ }
336
+ if (!legacyProviderHasCredentialData(providerKey, rawProviderConfig)) {
337
+ continue;
338
+ }
339
+ const { credentials, config } = splitLegacyProviderConfig(providerKey, rawProviderConfig);
340
+ candidates.push({
341
+ provider: providerKey,
342
+ credentials,
343
+ config,
344
+ provenance: {
345
+ source: "legacy-agent-secrets",
346
+ contributedByAgent: input.agentName,
347
+ },
348
+ });
349
+ }
350
+ (0, runtime_1.emitNervesEvent)({
351
+ component: "config/identity",
352
+ event: "config.provider_credential_legacy_read",
353
+ message: "read legacy provider credential candidates",
354
+ meta: { agentName: input.agentName, providerCount: candidates.length },
355
+ });
356
+ return candidates;
357
+ }
358
+ function migrateLegacyAgentProviderCredentials(input) {
359
+ const homeDir = input.homeDir ?? os.homedir();
360
+ const migrated = [];
361
+ for (const agentName of input.agentNames) {
362
+ const candidates = readLegacyAgentProviderCredentials({ homeDir, agentName });
363
+ for (const candidate of candidates) {
364
+ const record = upsertProviderCredential({
365
+ homeDir,
366
+ provider: candidate.provider,
367
+ credentials: candidate.credentials,
368
+ config: candidate.config,
369
+ provenance: candidate.provenance,
370
+ now: input.now,
371
+ makeRevision: input.makeRevision,
372
+ });
373
+ migrated.push({ agentName, provider: candidate.provider, revision: record.revision });
374
+ }
375
+ }
376
+ (0, runtime_1.emitNervesEvent)({
377
+ component: "config/identity",
378
+ event: "config.provider_credential_legacy_migrated",
379
+ message: "migrated legacy provider credentials",
380
+ meta: { migratedCount: migrated.length },
381
+ });
382
+ return {
383
+ poolPath: getProviderCredentialPoolPath(homeDir),
384
+ migrated,
385
+ };
386
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.344",
3
+ "version": "0.1.0-alpha.345",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",