@ouro.bot/cli 0.1.0-alpha.465 → 0.1.0-alpha.467

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.
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PORKBUN_OPS_COMPATIBILITY_ALIAS = exports.PORKBUN_OPS_CREDENTIAL_PREFIX = void 0;
4
+ exports.isVaultItemTemplate = isVaultItemTemplate;
5
+ exports.normalizeVaultItemName = normalizeVaultItemName;
6
+ exports.normalizeVaultItemFieldName = normalizeVaultItemFieldName;
7
+ exports.vaultItemTemplateSecretFields = vaultItemTemplateSecretFields;
8
+ exports.normalizePorkbunOpsAccount = normalizePorkbunOpsAccount;
9
+ exports.porkbunOpsCredentialItemName = porkbunOpsCredentialItemName;
10
+ exports.porkbunOpsAccountFromItemName = porkbunOpsAccountFromItemName;
11
+ exports.requireVaultItemSecret = requireVaultItemSecret;
12
+ exports.PORKBUN_OPS_CREDENTIAL_PREFIX = "ops/registrars/porkbun/accounts";
13
+ exports.PORKBUN_OPS_COMPATIBILITY_ALIAS = "vault ops porkbun";
14
+ const VAULT_ITEM_NAME_FORBIDDEN = /[\r\n\t]/;
15
+ const VAULT_ITEM_FIELD_FORBIDDEN = /[\r\n\t=]/;
16
+ const PORKBUN_OPS_ACCOUNT_FORBIDDEN = /[\/\r\n\t]/;
17
+ function isVaultItemTemplate(value) {
18
+ return value === "porkbun-api";
19
+ }
20
+ function normalizeVaultItemName(item) {
21
+ const normalized = item?.trim() ?? "";
22
+ if (!normalized || VAULT_ITEM_NAME_FORBIDDEN.test(normalized) || normalized.startsWith("/") || normalized.endsWith("/")) {
23
+ throw new Error("Vault item name/path must be non-empty, relative, and free of control characters.");
24
+ }
25
+ return normalized;
26
+ }
27
+ function normalizeVaultItemFieldName(field) {
28
+ const normalized = field?.trim() ?? "";
29
+ if (!normalized || VAULT_ITEM_FIELD_FORBIDDEN.test(normalized)) {
30
+ throw new Error("Vault item field names must be non-empty and free of control characters or '='.");
31
+ }
32
+ return normalized;
33
+ }
34
+ function vaultItemTemplateSecretFields(_template) {
35
+ return ["apiKey", "secretApiKey"];
36
+ }
37
+ function normalizePorkbunOpsAccount(account) {
38
+ const normalized = account?.trim() ?? "";
39
+ if (!normalized || PORKBUN_OPS_ACCOUNT_FORBIDDEN.test(normalized)) {
40
+ throw new Error("Porkbun account must be a non-empty account label without slashes or control characters.");
41
+ }
42
+ return normalized;
43
+ }
44
+ function porkbunOpsCredentialItemName(account) {
45
+ return `${exports.PORKBUN_OPS_CREDENTIAL_PREFIX}/${normalizePorkbunOpsAccount(account)}`;
46
+ }
47
+ function porkbunOpsAccountFromItemName(itemName) {
48
+ const prefix = `${exports.PORKBUN_OPS_CREDENTIAL_PREFIX}/`;
49
+ return itemName.startsWith(prefix) ? itemName.slice(prefix.length) : undefined;
50
+ }
51
+ function requireVaultItemSecret(value, label) {
52
+ const trimmed = value.trim();
53
+ if (!trimmed)
54
+ throw new Error(`${label} cannot be blank`);
55
+ return trimmed;
56
+ }
@@ -5,6 +5,7 @@ exports.readMailMessageView = readMailMessageView;
5
5
  const runtime_1 = require("../../../nerves/runtime");
6
6
  const file_store_1 = require("../../../mailroom/file-store");
7
7
  const reader_1 = require("../../../mailroom/reader");
8
+ const core_1 = require("../../../mailroom/core");
8
9
  const OUTLOOK_MAIL_LIST_LIMIT = 50;
9
10
  const OUTLOOK_MAIL_COUNT_LIMIT = 500;
10
11
  const OUTLOOK_MAIL_BODY_LIMIT = 12_000;
@@ -92,13 +93,27 @@ function buildFolders(messages, outbound) {
92
93
  { id: "native", label: "Native", count: messages.filter((message) => message.compartmentKind === "native").length },
93
94
  ];
94
95
  const sourceCounts = new Map();
96
+ const sourceOwnerCounts = new Map();
95
97
  for (const message of messages) {
96
98
  if (!message.source)
97
99
  continue;
98
100
  sourceCounts.set(message.source, (sourceCounts.get(message.source) ?? 0) + 1);
101
+ const owner = message.ownerEmail ?? "";
102
+ const ownerCounts = sourceOwnerCounts.get(message.source) ?? new Map();
103
+ ownerCounts.set(owner, (ownerCounts.get(owner) ?? 0) + 1);
104
+ sourceOwnerCounts.set(message.source, ownerCounts);
99
105
  }
100
106
  for (const [source, count] of [...sourceCounts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
101
- folders.push({ id: `source:${source}`, label: source.toUpperCase(), count });
107
+ const ownerCounts = sourceOwnerCounts.get(source);
108
+ if (!ownerCounts || ownerCounts.size <= 1) {
109
+ folders.push({ id: `source:${source}`, label: source.toUpperCase(), count });
110
+ continue;
111
+ }
112
+ for (const [owner, ownerCount] of [...ownerCounts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
113
+ const ownerLabel = owner || "unknown owner";
114
+ const ownerId = owner || "unknown-owner";
115
+ folders.push({ id: `source:${source}:${ownerId}`, label: `${source.toUpperCase()} / ${ownerLabel}`, count: ownerCount });
116
+ }
102
117
  }
103
118
  return folders;
104
119
  }
@@ -123,6 +138,10 @@ function outboundRecord(record) {
123
138
  return {
124
139
  id: record.id,
125
140
  status: record.status,
141
+ mailboxRole: record.mailboxRole ?? "agent-native-mailbox",
142
+ sendAuthority: record.sendAuthority ?? "agent-native",
143
+ ownerEmail: record.ownerEmail ?? null,
144
+ source: record.source ?? null,
126
145
  from: record.from,
127
146
  to: record.to,
128
147
  cc: record.cc,
@@ -152,9 +171,22 @@ function accessEntries(entries) {
152
171
  threadId: entry.threadId ?? null,
153
172
  tool: entry.tool,
154
173
  reason: entry.reason,
174
+ mailboxRole: entry.mailboxRole ?? null,
175
+ compartmentKind: entry.compartmentKind ?? null,
176
+ ownerEmail: entry.ownerEmail ?? null,
177
+ source: entry.source ?? null,
155
178
  accessedAt: entry.accessedAt,
156
179
  }));
157
180
  }
181
+ function accessProvenance(message) {
182
+ const provenance = (0, core_1.describeMailProvenance)(message);
183
+ return {
184
+ mailboxRole: provenance.mailboxRole,
185
+ compartmentKind: message.compartmentKind,
186
+ ownerEmail: provenance.ownerEmail,
187
+ source: provenance.source,
188
+ };
189
+ }
158
190
  function emitMailRead(agentName, mode, status) {
159
191
  (0, runtime_1.emitNervesEvent)({
160
192
  component: "heart",
@@ -238,6 +270,7 @@ async function readMailMessageView(agentName, messageId) {
238
270
  messageId,
239
271
  tool: "outlook_mail_message",
240
272
  reason: "outlook read-only message body",
273
+ ...accessProvenance(decrypted),
241
274
  });
242
275
  const body = decrypted.private.text.length > OUTLOOK_MAIL_BODY_LIMIT
243
276
  ? decrypted.private.text.slice(0, OUTLOOK_MAIL_BODY_LIMIT)
@@ -74,6 +74,14 @@ function displaySender(candidate) {
74
74
  }
75
75
  return candidate.senderEmail;
76
76
  }
77
+ function candidateCompartmentKind(candidate) {
78
+ return candidate.ownerEmail || candidate.source ? "delegated" : "native";
79
+ }
80
+ function candidateMailboxRole(candidate) {
81
+ return candidateCompartmentKind(candidate) === "delegated"
82
+ ? "delegated-human-mailbox"
83
+ : "agent-native-mailbox";
84
+ }
77
85
  function renderAttentionContent(candidate) {
78
86
  return [
79
87
  "[Mail Screener]",
@@ -92,6 +100,7 @@ function renderAttentionContent(candidate) {
92
100
  ].filter(Boolean).join("\n");
93
101
  }
94
102
  function queuedSummary(candidate, queuedAt) {
103
+ const compartmentKind = candidateCompartmentKind(candidate);
95
104
  return {
96
105
  candidateId: candidate.id,
97
106
  messageId: candidate.messageId,
@@ -99,6 +108,10 @@ function queuedSummary(candidate, queuedAt) {
99
108
  senderDisplay: candidate.senderDisplay,
100
109
  recipient: candidate.recipient,
101
110
  placement: candidate.placement,
111
+ mailboxRole: candidateMailboxRole(candidate),
112
+ compartmentKind,
113
+ ownerEmail: candidate.ownerEmail ?? null,
114
+ source: candidate.source ?? null,
102
115
  queuedAt,
103
116
  };
104
117
  }
@@ -42,6 +42,7 @@ exports.decryptMailPayload = decryptMailPayload;
42
42
  exports.encryptJsonForMailKey = encryptJsonForMailKey;
43
43
  exports.decryptMailJson = decryptMailJson;
44
44
  exports.resolveMailAddress = resolveMailAddress;
45
+ exports.describeMailProvenance = describeMailProvenance;
45
46
  exports.buildStoredMailMessage = buildStoredMailMessage;
46
47
  exports.decryptStoredMailMessage = decryptStoredMailMessage;
47
48
  exports.provisionMailboxRegistry = provisionMailboxRegistry;
@@ -237,6 +238,32 @@ function resolveMailAddress(registry, address) {
237
238
  defaultPlacement: grant.defaultPlacement,
238
239
  };
239
240
  }
241
+ function describeMailProvenance(message) {
242
+ if (message.compartmentKind === "delegated") {
243
+ const ownerEmail = message.ownerEmail ?? null;
244
+ const source = message.source ?? null;
245
+ const ownerLabel = ownerEmail ?? "unknown owner";
246
+ const sourceLabel = source ?? "unknown source";
247
+ return {
248
+ mailboxRole: "delegated-human-mailbox",
249
+ mailboxLabel: `${ownerLabel} / ${sourceLabel} delegated to ${message.agentId}`,
250
+ agentId: message.agentId,
251
+ ownerEmail,
252
+ source,
253
+ recipient: message.recipient,
254
+ sendAsHumanAllowed: false,
255
+ };
256
+ }
257
+ return {
258
+ mailboxRole: "agent-native-mailbox",
259
+ mailboxLabel: `${message.recipient} (native agent mail)`,
260
+ agentId: message.agentId,
261
+ ownerEmail: null,
262
+ source: null,
263
+ recipient: message.recipient,
264
+ sendAsHumanAllowed: false,
265
+ };
266
+ }
240
267
  function addressList(values) {
241
268
  /* v8 ignore next -- parsedAddressList filters undefined top-level values; this guards malformed address-group entries. @preserve */
242
269
  return (values ?? [])
@@ -96,6 +96,10 @@ async function createMailDraft(input) {
96
96
  id: draftId(),
97
97
  agentId: input.agentId,
98
98
  status: "draft",
99
+ mailboxRole: "agent-native-mailbox",
100
+ sendAuthority: "agent-native",
101
+ ownerEmail: null,
102
+ source: null,
99
103
  from: (0, core_1.normalizeMailAddress)(input.from),
100
104
  to,
101
105
  cc: normalizeList(input.cc ?? []),
@@ -63,7 +63,7 @@ const DISPATCH_EXEMPT_PATTERNS = [
63
63
  "daemon/cli-parse",
64
64
  "daemon/cli-render",
65
65
  "daemon/cli-help",
66
- "daemon/porkbun-ops",
66
+ "daemon/vault-items",
67
67
  // Shared utility modules: pure helpers consumed by modules that own observability.
68
68
  "arc/json-store",
69
69
  "repertoire/api-client",
@@ -72,6 +72,11 @@ function optionalTrimmedText(value, fieldName) {
72
72
  const trimmed = value.trim();
73
73
  return trimmed.length > 0 ? trimmed : undefined;
74
74
  }
75
+ function resolveVaultItemArg(args, legacyFieldName = "domain") {
76
+ if (args.item !== undefined)
77
+ return requireTrimmedText(args.item, "item");
78
+ return requireTrimmedText(args[legacyFieldName], legacyFieldName);
79
+ }
75
80
  function parsePasswordLength(value) {
76
81
  if (value === undefined || value === null || value === "")
77
82
  return DEFAULT_PASSWORD_LENGTH;
@@ -127,13 +132,17 @@ exports.credentialToolDefinitions = [
127
132
  type: "function",
128
133
  function: {
129
134
  name: "credential_get",
130
- description: "Get credential metadata for a domain. Returns username, notes, and creation date. Never returns passwords — the credential gateway handles secret injection internally.",
135
+ description: "Get credential metadata for a vault item name/path. Returns username, notes, and creation date. Never returns passwords — the credential gateway handles secret injection internally.",
131
136
  parameters: {
132
137
  type: "object",
133
138
  properties: {
139
+ item: {
140
+ type: "string",
141
+ description: "Vault item name/path to look up (e.g. 'airbnb.com' or 'ops/porkbun/account')",
142
+ },
134
143
  domain: {
135
144
  type: "string",
136
- description: "Domain to look up (e.g. 'airbnb.com', 'api.openweathermap.org')",
145
+ description: "compatibility alias for item when the vault item name is a service domain",
137
146
  },
138
147
  },
139
148
  required: ["domain"],
@@ -141,17 +150,18 @@ exports.credentialToolDefinitions = [
141
150
  },
142
151
  },
143
152
  handler: async (args) => {
153
+ const itemName = resolveVaultItemArg(args);
144
154
  (0, runtime_1.emitNervesEvent)({
145
155
  component: "repertoire",
146
156
  event: "repertoire.credential_tool_call",
147
157
  message: "credential_get invoked",
148
- meta: { tool: "credential_get", domain: args.domain },
158
+ meta: { tool: "credential_get", domain: itemName, item: itemName },
149
159
  });
150
160
  try {
151
161
  const store = (0, credential_access_1.getCredentialStore)();
152
- const meta = await store.get(args.domain);
162
+ const meta = await store.get(itemName);
153
163
  if (!meta) {
154
- return `No credential found for "${args.domain}".`;
164
+ return `No credential found for "${itemName}".`;
155
165
  }
156
166
  return JSON.stringify(meta, null, 2);
157
167
  }
@@ -220,13 +230,17 @@ exports.credentialToolDefinitions = [
220
230
  type: "function",
221
231
  function: {
222
232
  name: "credential_store",
223
- description: "Store credentials the agent acquired or just used successfully during signup. Prefer credential_generate_password for new passwords, then call this tool once the site accepts the exact password. Stored passwords are never returned later — only metadata is visible.",
233
+ description: "Store credentials in a vault item name/path after the agent acquired or just used them successfully. Prefer credential_generate_password for new passwords, then call this tool once the site accepts the exact password. Stored passwords are never returned later — only metadata is visible.",
224
234
  parameters: {
225
235
  type: "object",
226
236
  properties: {
237
+ item: {
238
+ type: "string",
239
+ description: "Vault item name/path to store under; domains are examples, not the schema",
240
+ },
227
241
  domain: {
228
242
  type: "string",
229
- description: "Domain these credentials are for (e.g. 'airbnb.com')",
243
+ description: "compatibility alias for item when the vault item name is a service domain",
230
244
  },
231
245
  username: {
232
246
  type: "string",
@@ -238,7 +252,7 @@ exports.credentialToolDefinitions = [
238
252
  },
239
253
  notes: {
240
254
  type: "string",
241
- description: "Optional notes about this credential",
255
+ description: "Optional human/agent orientation notes about this credential; not parsed by code",
242
256
  },
243
257
  },
244
258
  required: ["domain", "username", "password"],
@@ -250,14 +264,15 @@ exports.credentialToolDefinitions = [
250
264
  let username = "";
251
265
  let password = "";
252
266
  let notes;
267
+ const itemNameForEvent = typeof args.item === "string" && args.item.trim() ? args.item.trim() : args.domain;
253
268
  (0, runtime_1.emitNervesEvent)({
254
269
  component: "repertoire",
255
270
  event: "repertoire.credential_tool_call",
256
271
  message: "credential_store invoked",
257
- meta: { tool: "credential_store", domain: args.domain },
272
+ meta: { tool: "credential_store", domain: itemNameForEvent, item: itemNameForEvent },
258
273
  });
259
274
  try {
260
- domain = requireTrimmedText(args.domain, "domain");
275
+ domain = resolveVaultItemArg(args);
261
276
  username = requireTrimmedText(args.username, "username");
262
277
  password = requireNonBlankSecret(args.password, "password");
263
278
  notes = optionalTrimmedText(args.notes, "notes");
@@ -281,13 +296,13 @@ exports.credentialToolDefinitions = [
281
296
  type: "function",
282
297
  function: {
283
298
  name: "credential_list",
284
- description: "List stored credential domains. Returns metadata only (domain, username, notes, creation date). Never returns passwords.",
299
+ description: "List stored vault items. Returns metadata only (item/domain name, username, notes, creation date). Never returns passwords.",
285
300
  parameters: {
286
301
  type: "object",
287
302
  properties: {
288
303
  search: {
289
304
  type: "string",
290
- description: "Optional search filter to match against domain names",
305
+ description: "Optional search filter to match against vault item names/paths",
291
306
  },
292
307
  },
293
308
  },
@@ -323,13 +338,17 @@ exports.credentialToolDefinitions = [
323
338
  type: "function",
324
339
  function: {
325
340
  name: "credential_delete",
326
- description: "Delete stored credentials for a domain.",
341
+ description: "Delete stored credentials for a vault item name/path.",
327
342
  parameters: {
328
343
  type: "object",
329
344
  properties: {
345
+ item: {
346
+ type: "string",
347
+ description: "Vault item name/path whose credentials should be deleted",
348
+ },
330
349
  domain: {
331
350
  type: "string",
332
- description: "Domain whose credentials should be deleted",
351
+ description: "compatibility alias for item when the vault item name is a service domain",
333
352
  },
334
353
  },
335
354
  required: ["domain"],
@@ -344,12 +363,13 @@ exports.credentialToolDefinitions = [
344
363
  meta: { tool: "credential_delete", domain: args.domain },
345
364
  });
346
365
  try {
366
+ const itemName = resolveVaultItemArg(args);
347
367
  const store = (0, credential_access_1.getCredentialStore)();
348
- const deleted = await store.delete(args.domain);
368
+ const deleted = await store.delete(itemName);
349
369
  if (deleted) {
350
- return `Credentials for "${args.domain}" deleted.`;
370
+ return `Credentials for "${itemName}" deleted.`;
351
371
  }
352
- return `No credential found for "${args.domain}".`;
372
+ return `No credential found for "${itemName}".`;
353
373
  }
354
374
  catch (err) {
355
375
  /* v8 ignore next -- defensive: store.delete wraps errors @preserve */
@@ -125,10 +125,29 @@ function renderAccessLog(entries) {
125
125
  .reverse()
126
126
  .map((entry) => {
127
127
  const target = entry.messageId ? `message=${entry.messageId}` : entry.threadId ? `thread=${entry.threadId}` : "mailbox";
128
- return `- ${entry.accessedAt} ${entry.tool} ${target} reason="${entry.reason}"`;
128
+ const provenance = renderAccessLogProvenance(entry);
129
+ return `- ${entry.accessedAt} ${entry.tool} ${target}${provenance} reason="${entry.reason}"`;
129
130
  })
130
131
  .join("\n");
131
132
  }
133
+ function renderAccessLogProvenance(entry) {
134
+ if (entry.mailboxRole === "delegated-human-mailbox") {
135
+ return ` delegated human mailbox: ${entry.ownerEmail ?? "unknown owner"} / ${entry.source ?? "unknown source"}`;
136
+ }
137
+ if (entry.mailboxRole === "agent-native-mailbox") {
138
+ return " native agent mailbox";
139
+ }
140
+ return "";
141
+ }
142
+ function accessProvenance(message) {
143
+ const provenance = (0, core_1.describeMailProvenance)(message);
144
+ return {
145
+ mailboxRole: provenance.mailboxRole,
146
+ compartmentKind: message.compartmentKind,
147
+ ownerEmail: provenance.ownerEmail,
148
+ source: provenance.source,
149
+ };
150
+ }
132
151
  function renderSourceGrantStatus(config, agentId) {
133
152
  if (!config.registryPath) {
134
153
  return [
@@ -597,6 +616,7 @@ exports.mailToolDefinitions = [
597
616
  messageId,
598
617
  tool: "mail_thread",
599
618
  reason: args.reason,
619
+ ...accessProvenance(message),
600
620
  });
601
621
  const maxChars = numberArg(args.max_chars, 2000, 200, 6000);
602
622
  const body = decrypted.private.text.length > maxChars
@@ -717,6 +737,7 @@ exports.mailToolDefinitions = [
717
737
  messageId,
718
738
  tool: "mail_decide",
719
739
  reason,
740
+ ...accessProvenance(message),
720
741
  });
721
742
  const senderPolicyLine = persistSenderPolicyForDecision({
722
743
  registryPath: resolved.config.registryPath,
@@ -113,22 +113,28 @@ async function startMailSenseApp(options) {
113
113
  if (!resolved.ok) {
114
114
  throw new Error(resolved.error);
115
115
  }
116
- if (!resolved.config.registryPath) {
116
+ const hostedReaderOnly = resolved.storeKind === "azure-blob" && !resolved.config.registryPath;
117
+ if (!resolved.config.registryPath && !hostedReaderOnly) {
117
118
  throw new Error(`missing mailroom.registryPath for ${options.agentName}; agent-runnable repair: 'ouro connect mail --agent ${options.agentName}'`);
118
119
  }
119
- const registry = readRegistry(resolved.config.registryPath);
120
120
  const host = resolved.config.host ?? "127.0.0.1";
121
- const ingress = (options.startIngress ?? smtp_ingress_1.startMailroomIngress)({
122
- registry,
123
- store: resolved.store,
124
- smtpPort: validPort(resolved.config.smtpPort),
125
- httpPort: validPort(resolved.config.httpPort),
126
- host,
127
- });
128
- await Promise.all([
129
- waitForListening(ingress.smtp),
130
- waitForListening(ingress.health),
131
- ]);
121
+ const ingress = hostedReaderOnly
122
+ ? null
123
+ : (options.startIngress ?? smtp_ingress_1.startMailroomIngress)({
124
+ registry: readRegistry(resolved.config.registryPath),
125
+ store: resolved.store,
126
+ smtpPort: validPort(resolved.config.smtpPort),
127
+ httpPort: validPort(resolved.config.httpPort),
128
+ host,
129
+ });
130
+ if (ingress) {
131
+ await Promise.all([
132
+ waitForListening(ingress.smtp),
133
+ waitForListening(ingress.health),
134
+ ]);
135
+ }
136
+ const activeSmtpPort = () => ingress ? serverPort(ingress.smtp) : null;
137
+ const activeHttpPort = () => ingress ? serverPort(ingress.health) : null;
132
138
  const runtimePath = runtimeStatePath(options.agentName);
133
139
  const attentionPath = attentionStatePath(options.agentName);
134
140
  let lastScanAt = null;
@@ -150,8 +156,8 @@ async function startMailSenseApp(options) {
150
156
  agentName: options.agentName,
151
157
  status: "running",
152
158
  mailboxAddress: resolved.config.mailboxAddress,
153
- smtpPort: serverPort(ingress.smtp),
154
- httpPort: serverPort(ingress.health),
159
+ smtpPort: activeSmtpPort(),
160
+ httpPort: activeHttpPort(),
155
161
  host,
156
162
  storeKind: resolved.storeKind,
157
163
  storeLabel: resolved.storeLabel,
@@ -182,30 +188,32 @@ async function startMailSenseApp(options) {
182
188
  meta: {
183
189
  agentName: options.agentName,
184
190
  mailboxAddress: resolved.config.mailboxAddress,
185
- smtpPort: serverPort(ingress.smtp),
186
- httpPort: serverPort(ingress.health),
191
+ smtpPort: activeSmtpPort(),
192
+ httpPort: activeHttpPort(),
187
193
  intervalMs,
188
194
  },
189
195
  });
190
196
  return {
191
197
  runtimeStatePath: runtimePath,
192
198
  attentionStatePath: attentionPath,
193
- smtpPort: serverPort(ingress.smtp),
194
- httpPort: serverPort(ingress.health),
199
+ smtpPort: activeSmtpPort(),
200
+ httpPort: activeHttpPort(),
195
201
  async stop() {
196
202
  ;
197
203
  (options.clearIntervalFn ?? ((activeTimer) => clearInterval(activeTimer)))(timer);
198
- await Promise.all([
199
- closeServer(ingress.smtp),
200
- closeServer(ingress.health),
201
- ]);
204
+ if (ingress) {
205
+ await Promise.all([
206
+ closeServer(ingress.smtp),
207
+ closeServer(ingress.health),
208
+ ]);
209
+ }
202
210
  writeRuntimeState(runtimePath, {
203
211
  schemaVersion: 1,
204
212
  agentName: options.agentName,
205
213
  status: "stopped",
206
214
  mailboxAddress: resolved.config.mailboxAddress,
207
- smtpPort: serverPort(ingress.smtp),
208
- httpPort: serverPort(ingress.health),
215
+ smtpPort: activeSmtpPort(),
216
+ httpPort: activeHttpPort(),
209
217
  host,
210
218
  storeKind: resolved.storeKind,
211
219
  storeLabel: resolved.storeLabel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.465",
3
+ "version": "0.1.0-alpha.467",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PORKBUN_OPS_CREDENTIAL_PREFIX = exports.PORKBUN_OPS_CREDENTIAL_KIND = void 0;
4
- exports.normalizePorkbunOpsAccount = normalizePorkbunOpsAccount;
5
- exports.porkbunOpsCredentialItemName = porkbunOpsCredentialItemName;
6
- exports.requirePorkbunOpsSecret = requirePorkbunOpsSecret;
7
- exports.PORKBUN_OPS_CREDENTIAL_KIND = "ops-credential/porkbun";
8
- exports.PORKBUN_OPS_CREDENTIAL_PREFIX = "ops/registrars/porkbun/accounts";
9
- const PORKBUN_OPS_ACCOUNT_FORBIDDEN = /[\/\r\n\t]/;
10
- function normalizePorkbunOpsAccount(account) {
11
- const normalized = account?.trim() ?? "";
12
- if (!normalized || PORKBUN_OPS_ACCOUNT_FORBIDDEN.test(normalized)) {
13
- throw new Error("Porkbun account must be a non-empty account label without slashes or control characters.");
14
- }
15
- return normalized;
16
- }
17
- function porkbunOpsCredentialItemName(account) {
18
- return `${exports.PORKBUN_OPS_CREDENTIAL_PREFIX}/${normalizePorkbunOpsAccount(account)}`;
19
- }
20
- function requirePorkbunOpsSecret(value, label) {
21
- const trimmed = value.trim();
22
- if (!trimmed)
23
- throw new Error(`${label} cannot be blank`);
24
- return trimmed;
25
- }