@ouro.bot/cli 0.1.0-alpha.474 → 0.1.0-alpha.476

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,22 @@
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.476",
6
+ "changes": [
7
+ "`ouro mail import-mbox` now works against hosted Mailroom config by reading public registry coordinates from the owning agent vault instead of requiring local-only registry and store paths.",
8
+ "Delegated HEY archive imports now stream large local MBOX files into encrypted Mailroom storage with per-message dedupe, so multi-gigabyte hosted backfills can resume safely after interruption.",
9
+ "Agent Mail docs now teach linked HEY accounts as separate export and forwarding feeders even under one browser login, while keeping one delegated-source provenance lens for the agent."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.475",
14
+ "changes": [
15
+ "Native mail autonomy now counts recently submitted provider-backed sends against the rate window immediately, so Azure Communication Services deliveries cannot slip past the autonomous send limit while delivery events are still pending.",
16
+ "Mail autonomy coverage now locks the submitted-provider case explicitly, preventing back-to-back autonomous sends from bypassing policy before provider reconciliation arrives.",
17
+ "The `ouro.bot` wrapper stays version-synced for the mail autonomy rate-limit repair release."
18
+ ]
19
+ },
4
20
  {
5
21
  "version": "0.1.0-alpha.474",
6
22
  "changes": [
@@ -80,8 +80,8 @@ const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
80
80
  const update_checker_1 = require("../versioning/update-checker");
81
81
  const sync_1 = require("../sync");
82
82
  const core_1 = require("../../mailroom/core");
83
- const file_store_1 = require("../../mailroom/file-store");
84
83
  const mbox_import_1 = require("../../mailroom/mbox-import");
84
+ const reader_1 = require("../../mailroom/reader");
85
85
  const cli_parse_1 = require("./cli-parse");
86
86
  const cli_parse_2 = require("./cli-parse");
87
87
  const cli_help_1 = require("./cli-help");
@@ -3409,27 +3409,42 @@ async function executeMailImportMbox(command, deps) {
3409
3409
  progress.end();
3410
3410
  throw new Error(`cannot read Mailroom config from ${runtime.itemPath}: ${runtime.error}`);
3411
3411
  }
3412
- const mailroom = runtime.config.mailroom;
3413
- if (!mailroom || typeof mailroom !== "object" || Array.isArray(mailroom)) {
3412
+ const mailroom = (0, reader_1.parseMailroomConfig)(runtime.config.mailroom);
3413
+ if (!mailroom) {
3414
3414
  progress.end();
3415
3415
  throw new Error(`missing mailroom config for ${command.agent}; agent-runnable repair: 'ouro connect mail --agent ${command.agent}'`);
3416
3416
  }
3417
- const registryPath = stringField(mailroom, "registryPath");
3418
- const storePath = stringField(mailroom, "storePath");
3419
- if (!registryPath || !storePath) {
3417
+ const hosted = Boolean(mailroom.azureAccountUrl);
3418
+ if (hosted) {
3419
+ const registryAzureAccountUrl = mailroom.registryAzureAccountUrl?.trim() ?? "";
3420
+ const registryBlob = mailroom.registryBlob?.trim() ?? "";
3421
+ if (!registryAzureAccountUrl || !registryBlob) {
3422
+ progress.end();
3423
+ throw new Error(`mailroom config for ${command.agent} is missing hosted registry coordinates; agent-runnable repair: 'ouro connect mail --agent ${command.agent}'`);
3424
+ }
3425
+ }
3426
+ else {
3427
+ const registryPath = mailroom.registryPath?.trim() ?? "";
3428
+ const storePath = mailroom.storePath?.trim() ?? "";
3429
+ if (!registryPath || !storePath) {
3430
+ progress.end();
3431
+ throw new Error(`mailroom config for ${command.agent} is missing registryPath/storePath; agent-runnable repair: 'ouro connect mail --agent ${command.agent}'`);
3432
+ }
3433
+ }
3434
+ const resolved = (0, reader_1.resolveMailroomReader)(command.agent);
3435
+ if (!resolved.ok) {
3420
3436
  progress.end();
3421
- throw new Error(`mailroom config for ${command.agent} is missing registryPath/storePath; agent-runnable repair: 'ouro connect mail --agent ${command.agent}'`);
3437
+ throw new Error(resolved.error);
3422
3438
  }
3423
3439
  progress.updateDetail("reading registry and MBOX");
3424
- const registry = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
3440
+ const registry = await (0, reader_1.readMailroomRegistry)(resolved.config);
3425
3441
  const filePath = path.resolve(command.filePath);
3426
- const rawMbox = fs.readFileSync(filePath);
3427
3442
  progress.updateDetail("importing delegated mail");
3428
- const result = await (0, mbox_import_1.importMboxToStore)({
3443
+ const result = await (0, mbox_import_1.importMboxFileToStore)({
3429
3444
  registry,
3430
- store: new file_store_1.FileMailroomStore({ rootDir: storePath }),
3445
+ store: resolved.store,
3431
3446
  agentId: command.agent,
3432
- rawMbox,
3447
+ filePath,
3433
3448
  ownerEmail: command.ownerEmail,
3434
3449
  source: command.source,
3435
3450
  });
@@ -73,9 +73,9 @@ function isRecipientAllowed(policy, recipient) {
73
73
  return policy.allowedRecipients.includes(recipient) || policy.allowedDomains.includes(recipientDomain(recipient));
74
74
  }
75
75
  function autonomousSentAt(record) {
76
- if (record.status !== "sent" || record.sendMode !== "autonomous")
76
+ if (record.sendMode !== "autonomous")
77
77
  return null;
78
- return record.sentAt ?? record.updatedAt;
78
+ return record.sentAt ?? record.submittedAt ?? record.acceptedAt ?? record.deliveredAt ?? record.failedAt ?? record.updatedAt;
79
79
  }
80
80
  function countRecentAutonomousSends(input) {
81
81
  const startsAt = input.nowMs - input.windowMs;
@@ -1,8 +1,42 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.splitMboxMessages = splitMboxMessages;
4
37
  exports.importMboxToStore = importMboxToStore;
5
- const mailparser_1 = require("mailparser");
38
+ exports.importMboxFileToStore = importMboxFileToStore;
39
+ const fs = __importStar(require("node:fs"));
6
40
  const runtime_1 = require("../nerves/runtime");
7
41
  const core_1 = require("./core");
8
42
  function splitMboxMessages(rawMbox) {
@@ -28,6 +62,65 @@ function splitMboxMessages(rawMbox) {
28
62
  });
29
63
  return messages;
30
64
  }
65
+ function trimTrailingLineEnding(message) {
66
+ if (message.length >= 2 && message[message.length - 2] === 0x0d && message[message.length - 1] === 0x0a) {
67
+ return message.subarray(0, -2);
68
+ }
69
+ if (message.length >= 1 && message[message.length - 1] === 0x0a) {
70
+ return message.subarray(0, -1);
71
+ }
72
+ return message;
73
+ }
74
+ function isSeparatorLine(line) {
75
+ return line.length >= 5 &&
76
+ line[0] === 0x46 &&
77
+ line[1] === 0x72 &&
78
+ line[2] === 0x6f &&
79
+ line[3] === 0x6d &&
80
+ line[4] === 0x20;
81
+ }
82
+ async function* streamMboxMessagesFromFile(filePath) {
83
+ const stream = fs.createReadStream(filePath);
84
+ let pending = Buffer.alloc(0);
85
+ let currentParts = [];
86
+ const flushCurrent = () => {
87
+ if (currentParts.length === 0)
88
+ return null;
89
+ const message = trimTrailingLineEnding(Buffer.concat(currentParts));
90
+ currentParts = [];
91
+ return message;
92
+ };
93
+ for await (const chunk of stream) {
94
+ pending = pending.length > 0 ? Buffer.concat([pending, chunk]) : Buffer.from(chunk);
95
+ let newlineIndex = pending.indexOf(0x0a);
96
+ while (newlineIndex >= 0) {
97
+ const line = pending.subarray(0, newlineIndex + 1);
98
+ pending = pending.subarray(newlineIndex + 1);
99
+ if (isSeparatorLine(line)) {
100
+ const message = flushCurrent();
101
+ if (message && message.length > 0)
102
+ yield message;
103
+ }
104
+ else {
105
+ currentParts.push(Buffer.from(line));
106
+ }
107
+ newlineIndex = pending.indexOf(0x0a);
108
+ }
109
+ }
110
+ if (pending.length > 0) {
111
+ if (isSeparatorLine(pending)) {
112
+ const message = flushCurrent();
113
+ if (message && message.length > 0)
114
+ yield message;
115
+ }
116
+ else {
117
+ currentParts.push(Buffer.from(pending));
118
+ }
119
+ }
120
+ const finalMessage = flushCurrent();
121
+ if (finalMessage && finalMessage.length > 0)
122
+ yield finalMessage;
123
+ }
31
124
  function findSourceGrant(input) {
32
125
  const ownerEmail = input.ownerEmail ? (0, core_1.normalizeMailAddress)(input.ownerEmail) : undefined;
33
126
  const source = input.source?.trim().toLowerCase();
@@ -48,9 +141,9 @@ function findSourceGrant(input) {
48
141
  }
49
142
  return grants[0];
50
143
  }
51
- async function parseMboxMessage(rawMessage, grant) {
52
- const parsed = await (0, mailparser_1.simpleParser)(rawMessage);
53
- const mailFrom = parsed.from?.value?.[0]?.address;
144
+ function parseMboxMessage(rawMessage, grant) {
145
+ const mailFrom = extractHeaderAddress(rawMessage, "from");
146
+ const messageDate = extractHeaderDate(rawMessage, "date");
54
147
  return {
55
148
  rawMessage,
56
149
  envelope: {
@@ -58,7 +151,7 @@ async function parseMboxMessage(rawMessage, grant) {
58
151
  rcptTo: [(0, core_1.normalizeMailAddress)(grant.aliasAddress)],
59
152
  remoteAddress: "mbox-import",
60
153
  },
61
- ...(parsed.date ? { messageDate: parsed.date } : {}),
154
+ ...(messageDate ? { messageDate } : {}),
62
155
  };
63
156
  }
64
157
  function latestMessageDate(messages) {
@@ -69,6 +162,52 @@ function latestMessageDate(messages) {
69
162
  return null;
70
163
  return new Date(Math.max(...timestamps)).toISOString();
71
164
  }
165
+ function readHeaderBlock(rawMessage) {
166
+ const crlfBoundary = rawMessage.indexOf("\r\n\r\n");
167
+ if (crlfBoundary >= 0) {
168
+ return rawMessage.subarray(0, crlfBoundary).toString("utf-8");
169
+ }
170
+ const lfBoundary = rawMessage.indexOf("\n\n");
171
+ if (lfBoundary >= 0) {
172
+ return rawMessage.subarray(0, lfBoundary).toString("utf-8");
173
+ }
174
+ return rawMessage.toString("utf-8");
175
+ }
176
+ function extractHeaderValue(rawMessage, headerName) {
177
+ const target = `${headerName.toLowerCase()}:`;
178
+ let currentHeader = "";
179
+ const unfoldedHeaders = [];
180
+ for (const line of readHeaderBlock(rawMessage).split(/\r?\n/)) {
181
+ if (/^[ \t]/.test(line) && currentHeader) {
182
+ currentHeader += ` ${line.trim()}`;
183
+ continue;
184
+ }
185
+ if (currentHeader)
186
+ unfoldedHeaders.push(currentHeader);
187
+ currentHeader = line;
188
+ }
189
+ if (currentHeader)
190
+ unfoldedHeaders.push(currentHeader);
191
+ const match = unfoldedHeaders.find((line) => line.toLowerCase().startsWith(target));
192
+ return match ? match.slice(target.length).trim() : "";
193
+ }
194
+ function extractHeaderAddress(rawMessage, headerName) {
195
+ const value = extractHeaderValue(rawMessage, headerName);
196
+ if (!value)
197
+ return "";
198
+ const bracketed = value.match(/<([^>]+)>/);
199
+ if (bracketed?.[1])
200
+ return bracketed[1];
201
+ const plain = value.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
202
+ return plain?.[0] ?? "";
203
+ }
204
+ function extractHeaderDate(rawMessage, headerName) {
205
+ const value = extractHeaderValue(rawMessage, headerName);
206
+ if (!value)
207
+ return undefined;
208
+ const parsed = new Date(value);
209
+ return Number.isFinite(parsed.getTime()) ? parsed : undefined;
210
+ }
72
211
  function historicalImportClassification(resolvedPlacement, sourceGrant) {
73
212
  return {
74
213
  placement: resolvedPlacement,
@@ -76,61 +215,124 @@ function historicalImportClassification(resolvedPlacement, sourceGrant) {
76
215
  trustReason: `delegated source grant ${sourceGrant.source} historical mbox import`,
77
216
  };
78
217
  }
79
- async function importMboxToStore(input) {
80
- const agentId = input.agentId.toLowerCase();
81
- const sourceGrant = findSourceGrant({
82
- registry: input.registry,
83
- agentId,
84
- ownerEmail: input.ownerEmail,
85
- source: input.source,
86
- });
87
- const resolved = (0, core_1.resolveMailAddress)(input.registry, sourceGrant.aliasAddress);
88
- /* v8 ignore start -- findSourceGrant and resolveMailAddress share the same registry; this is a corruption guard. @preserve */
89
- if (!resolved) {
90
- throw new Error(`Source grant alias ${sourceGrant.aliasAddress} is not resolvable`);
91
- }
92
- /* v8 ignore stop */
218
+ async function importParsedMessagesToStore(input) {
93
219
  let imported = 0;
94
220
  let duplicates = 0;
95
221
  const messages = [];
96
- const rawMessages = splitMboxMessages(input.rawMbox);
97
- const parsedMessages = await Promise.all(rawMessages.map((rawMessage) => parseMboxMessage(rawMessage, sourceGrant)));
222
+ let scanned = 0;
98
223
  const importedAt = (input.importedAt ?? new Date()).toISOString();
99
- const sourceFreshThrough = latestMessageDate(parsedMessages);
100
- for (const parsedMessage of parsedMessages) {
101
- const result = await input.store.putRawMessage({
102
- resolved,
103
- envelope: parsedMessage.envelope,
104
- rawMime: parsedMessage.rawMessage,
105
- receivedAt: parsedMessage.messageDate ?? input.importedAt,
106
- ingest: {
107
- schemaVersion: 1,
108
- kind: "mbox-import",
109
- importedAt,
110
- sourceFreshThrough,
111
- attentionSuppressed: true,
112
- },
113
- classification: historicalImportClassification(resolved.defaultPlacement, sourceGrant),
224
+ const sourceFreshThrough = input.sourceFreshThrough ?? null;
225
+ const maxConcurrency = Math.max(1, input.maxConcurrency ?? 1);
226
+ const inFlight = new Set();
227
+ const enqueue = (parsedMessage) => {
228
+ const task = (async () => {
229
+ const result = await input.store.putRawMessage({
230
+ resolved: input.resolved,
231
+ envelope: parsedMessage.envelope,
232
+ rawMime: parsedMessage.rawMessage,
233
+ receivedAt: parsedMessage.messageDate ?? input.importedAt,
234
+ ingest: {
235
+ schemaVersion: 1,
236
+ kind: "mbox-import",
237
+ importedAt,
238
+ sourceFreshThrough,
239
+ attentionSuppressed: true,
240
+ },
241
+ classification: historicalImportClassification(input.resolved.defaultPlacement, input.sourceGrant),
242
+ });
243
+ if (input.collectMessages !== false)
244
+ messages.push(result.message);
245
+ if (result.created)
246
+ imported += 1;
247
+ else
248
+ duplicates += 1;
249
+ })().finally(() => {
250
+ inFlight.delete(task);
114
251
  });
115
- messages.push(result.message);
116
- if (result.created)
117
- imported += 1;
118
- else
119
- duplicates += 1;
252
+ inFlight.add(task);
253
+ return task;
254
+ };
255
+ for await (const parsedMessage of input.parsedMessages) {
256
+ scanned += 1;
257
+ enqueue(parsedMessage);
258
+ if (inFlight.size >= maxConcurrency) {
259
+ await Promise.race(inFlight);
260
+ }
120
261
  }
262
+ await Promise.all(inFlight);
121
263
  (0, runtime_1.emitNervesEvent)({
122
264
  component: "senses",
123
265
  event: "senses.mail_mbox_imported",
124
266
  message: "mbox mail imported",
125
- meta: { agentId, scanned: rawMessages.length, imported, duplicates, grantId: sourceGrant.grantId },
267
+ meta: { agentId: input.sourceGrant.agentId.toLowerCase(), scanned, imported, duplicates, grantId: input.sourceGrant.grantId },
126
268
  });
127
269
  return {
128
- agentId,
129
- sourceGrant,
130
- scanned: rawMessages.length,
270
+ agentId: input.sourceGrant.agentId.toLowerCase(),
271
+ sourceGrant: input.sourceGrant,
272
+ scanned,
131
273
  imported,
132
274
  duplicates,
133
275
  sourceFreshThrough,
134
276
  messages,
135
277
  };
136
278
  }
279
+ async function scanMboxFileFreshness(filePath, sourceGrant) {
280
+ let latestTimestamp = Number.NEGATIVE_INFINITY;
281
+ for await (const rawMessage of streamMboxMessagesFromFile(filePath)) {
282
+ const parsedMessage = parseMboxMessage(rawMessage, sourceGrant);
283
+ if (parsedMessage.messageDate) {
284
+ latestTimestamp = Math.max(latestTimestamp, parsedMessage.messageDate.getTime());
285
+ }
286
+ }
287
+ return Number.isFinite(latestTimestamp) ? new Date(latestTimestamp).toISOString() : null;
288
+ }
289
+ function resolveImportTarget(input) {
290
+ const agentId = input.agentId.toLowerCase();
291
+ const sourceGrant = findSourceGrant({
292
+ registry: input.registry,
293
+ agentId,
294
+ ownerEmail: input.ownerEmail,
295
+ source: input.source,
296
+ });
297
+ const resolved = (0, core_1.resolveMailAddress)(input.registry, sourceGrant.aliasAddress);
298
+ /* v8 ignore start -- findSourceGrant and resolveMailAddress share the same registry; this is a corruption guard. @preserve */
299
+ if (!resolved) {
300
+ throw new Error(`Source grant alias ${sourceGrant.aliasAddress} is not resolvable`);
301
+ }
302
+ /* v8 ignore stop */
303
+ return { agentId, sourceGrant, resolved };
304
+ }
305
+ async function importMboxToStore(input) {
306
+ const target = resolveImportTarget(input);
307
+ const rawMessages = splitMboxMessages(input.rawMbox);
308
+ const parsedMessages = rawMessages.map((rawMessage) => parseMboxMessage(rawMessage, target.sourceGrant));
309
+ const sourceFreshThrough = latestMessageDate(parsedMessages);
310
+ return importParsedMessagesToStore({
311
+ sourceGrant: target.sourceGrant,
312
+ resolved: target.resolved,
313
+ store: input.store,
314
+ parsedMessages,
315
+ importedAt: input.importedAt,
316
+ collectMessages: true,
317
+ sourceFreshThrough,
318
+ });
319
+ }
320
+ async function importMboxFileToStore(input) {
321
+ const target = resolveImportTarget(input);
322
+ const sourceFreshThrough = await scanMboxFileFreshness(input.filePath, target.sourceGrant);
323
+ async function* parsedMessages() {
324
+ for await (const rawMessage of streamMboxMessagesFromFile(input.filePath)) {
325
+ yield parseMboxMessage(rawMessage, target.sourceGrant);
326
+ }
327
+ }
328
+ return importParsedMessagesToStore({
329
+ sourceGrant: target.sourceGrant,
330
+ resolved: target.resolved,
331
+ store: input.store,
332
+ parsedMessages: parsedMessages(),
333
+ importedAt: input.importedAt,
334
+ collectMessages: false,
335
+ sourceFreshThrough,
336
+ maxConcurrency: 8,
337
+ });
338
+ }
@@ -34,7 +34,9 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.parseMailroomConfig = parseMailroomConfig;
37
+ exports.readMailroomRegistry = readMailroomRegistry;
37
38
  exports.resolveMailroomReader = resolveMailroomReader;
39
+ const fs = __importStar(require("node:fs"));
38
40
  const path = __importStar(require("node:path"));
39
41
  const storage_blob_1 = require("@azure/storage-blob");
40
42
  const identity_1 = require("@azure/identity");
@@ -71,6 +73,9 @@ function parseMailroomConfig(value) {
71
73
  const storePath = textField(value, "storePath");
72
74
  const azureAccountUrl = textField(value, "azureAccountUrl");
73
75
  const azureContainer = textField(value, "azureContainer");
76
+ const registryAzureAccountUrl = textField(value, "registryAzureAccountUrl");
77
+ const registryContainer = textField(value, "registryContainer");
78
+ const registryBlob = textField(value, "registryBlob");
74
79
  const azureManagedIdentityClientId = textField(value, "azureManagedIdentityClientId");
75
80
  const smtpPort = numberField(value, "smtpPort");
76
81
  const httpPort = numberField(value, "httpPort");
@@ -86,6 +91,9 @@ function parseMailroomConfig(value) {
86
91
  ...(storePath ? { storePath } : {}),
87
92
  ...(azureAccountUrl ? { azureAccountUrl } : {}),
88
93
  ...(azureContainer ? { azureContainer } : {}),
94
+ ...(registryAzureAccountUrl ? { registryAzureAccountUrl } : {}),
95
+ ...(registryContainer ? { registryContainer } : {}),
96
+ ...(registryBlob ? { registryBlob } : {}),
89
97
  ...(azureManagedIdentityClientId ? { azureManagedIdentityClientId } : {}),
90
98
  ...(smtpPort !== undefined ? { smtpPort } : {}),
91
99
  ...(httpPort !== undefined ? { httpPort } : {}),
@@ -96,15 +104,17 @@ function parseMailroomConfig(value) {
96
104
  privateKeys,
97
105
  };
98
106
  }
107
+ function createBlobCredential(config) {
108
+ return config.azureManagedIdentityClientId
109
+ ? new identity_1.DefaultAzureCredential({ managedIdentityClientId: config.azureManagedIdentityClientId })
110
+ : new identity_1.DefaultAzureCredential();
111
+ }
99
112
  function createMailroomStore(config, agentName) {
100
113
  if (config.azureAccountUrl) {
101
- const credential = config.azureManagedIdentityClientId
102
- ? new identity_1.DefaultAzureCredential({ managedIdentityClientId: config.azureManagedIdentityClientId })
103
- : new identity_1.DefaultAzureCredential();
104
114
  const containerName = config.azureContainer ?? "mailroom";
105
115
  return {
106
116
  store: new blob_store_1.AzureBlobMailroomStore({
107
- serviceClient: new storage_blob_1.BlobServiceClient(config.azureAccountUrl, credential),
117
+ serviceClient: new storage_blob_1.BlobServiceClient(config.azureAccountUrl, createBlobCredential(config)),
108
118
  containerName,
109
119
  }),
110
120
  storeKind: "azure-blob",
@@ -118,6 +128,23 @@ function createMailroomStore(config, agentName) {
118
128
  storeLabel: storePath,
119
129
  };
120
130
  }
131
+ async function readMailroomRegistry(config) {
132
+ if (config.registryPath) {
133
+ return JSON.parse(fs.readFileSync(config.registryPath, "utf-8"));
134
+ }
135
+ const registryAzureAccountUrl = config.registryAzureAccountUrl ?? config.azureAccountUrl;
136
+ const registryContainer = config.registryContainer ?? config.azureContainer ?? "mailroom";
137
+ const registryBlob = config.registryBlob;
138
+ if (!registryAzureAccountUrl || !registryBlob) {
139
+ throw new Error("mailroom config is missing registryPath or hosted registry coordinates");
140
+ }
141
+ const serviceClient = new storage_blob_1.BlobServiceClient(registryAzureAccountUrl, createBlobCredential(config));
142
+ const blobClient = serviceClient.getContainerClient(registryContainer).getBlockBlobClient(registryBlob);
143
+ if (!await blobClient.exists()) {
144
+ throw new Error(`mailroom registry blob not found: ${registryAzureAccountUrl}/${registryContainer}/${registryBlob}`);
145
+ }
146
+ return JSON.parse((await blobClient.downloadToBuffer()).toString("utf-8"));
147
+ }
121
148
  function resolveMailroomReader(agentName = (0, identity_2.getAgentName)()) {
122
149
  const runtime = (0, runtime_credentials_1.readRuntimeCredentialConfig)(agentName);
123
150
  if (!runtime.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.474",
3
+ "version": "0.1.0-alpha.476",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",