@ouro.bot/cli 0.1.0-alpha.450 → 0.1.0-alpha.452

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readSelfFixView = exports.readOutlookContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readHabitView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readSessionTranscript = exports.readSessionInventory = exports.readOutlookMachineState = exports.readOutlookAgentState = exports.readObligationSummary = void 0;
3
+ exports.readSelfFixView = exports.readOutlookContinuity = exports.readOrientationView = exports.readObligationDetailView = exports.readNoteDecisionView = exports.readChangesView = exports.readNeedsMeView = exports.readNotesView = exports.readLogView = exports.readHabitView = exports.readFriendView = exports.readDeskPrefs = exports.readDaemonHealthDeep = exports.readCodingDeep = exports.readBridgeInventory = exports.readAttentionView = exports.readMailView = exports.readMailMessageView = exports.readSessionTranscript = exports.readSessionInventory = exports.readOutlookMachineState = exports.readOutlookAgentState = exports.readObligationSummary = void 0;
4
4
  var agent_machine_1 = require("./readers/agent-machine");
5
5
  Object.defineProperty(exports, "readObligationSummary", { enumerable: true, get: function () { return agent_machine_1.readObligationSummary; } });
6
6
  Object.defineProperty(exports, "readOutlookAgentState", { enumerable: true, get: function () { return agent_machine_1.readOutlookAgentState; } });
@@ -8,6 +8,9 @@ Object.defineProperty(exports, "readOutlookMachineState", { enumerable: true, ge
8
8
  var sessions_1 = require("./readers/sessions");
9
9
  Object.defineProperty(exports, "readSessionInventory", { enumerable: true, get: function () { return sessions_1.readSessionInventory; } });
10
10
  Object.defineProperty(exports, "readSessionTranscript", { enumerable: true, get: function () { return sessions_1.readSessionTranscript; } });
11
+ var mail_1 = require("./readers/mail");
12
+ Object.defineProperty(exports, "readMailMessageView", { enumerable: true, get: function () { return mail_1.readMailMessageView; } });
13
+ Object.defineProperty(exports, "readMailView", { enumerable: true, get: function () { return mail_1.readMailView; } });
11
14
  var runtime_readers_1 = require("./readers/runtime-readers");
12
15
  Object.defineProperty(exports, "readAttentionView", { enumerable: true, get: function () { return runtime_readers_1.readAttentionView; } });
13
16
  Object.defineProperty(exports, "readBridgeInventory", { enumerable: true, get: function () { return runtime_readers_1.readBridgeInventory; } });
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readMailView = readMailView;
4
+ exports.readMailMessageView = readMailMessageView;
5
+ const runtime_1 = require("../../../nerves/runtime");
6
+ const file_store_1 = require("../../../mailroom/file-store");
7
+ const reader_1 = require("../../../mailroom/reader");
8
+ const OUTLOOK_MAIL_LIST_LIMIT = 50;
9
+ const OUTLOOK_MAIL_COUNT_LIMIT = 500;
10
+ const OUTLOOK_MAIL_BODY_LIMIT = 12_000;
11
+ function emptyFolders() {
12
+ return [
13
+ { id: "imbox", label: "Imbox", count: 0 },
14
+ { id: "screener", label: "Screener", count: 0 },
15
+ { id: "delegated", label: "Delegated", count: 0 },
16
+ { id: "native", label: "Native", count: 0 },
17
+ ];
18
+ }
19
+ function unavailableMailView(agentName, status, error) {
20
+ return {
21
+ status,
22
+ agentName,
23
+ mailboxAddress: null,
24
+ generatedAt: new Date().toISOString(),
25
+ store: null,
26
+ folders: emptyFolders(),
27
+ messages: [],
28
+ accessLog: [],
29
+ error,
30
+ };
31
+ }
32
+ function unavailableMessageView(agentName, status, error) {
33
+ return {
34
+ status,
35
+ agentName,
36
+ mailboxAddress: null,
37
+ generatedAt: new Date().toISOString(),
38
+ message: null,
39
+ accessLog: [],
40
+ error,
41
+ };
42
+ }
43
+ function mailSummary(message) {
44
+ return {
45
+ id: message.id,
46
+ subject: message.private.subject,
47
+ from: message.private.from,
48
+ to: message.private.to,
49
+ cc: message.private.cc,
50
+ date: message.private.date ?? null,
51
+ receivedAt: message.receivedAt,
52
+ snippet: message.private.snippet,
53
+ placement: message.placement,
54
+ compartmentKind: message.compartmentKind,
55
+ ownerEmail: message.ownerEmail ?? null,
56
+ source: message.source ?? null,
57
+ recipient: message.recipient,
58
+ attachmentCount: message.private.attachments.length,
59
+ untrustedContentWarning: message.private.untrustedContentWarning,
60
+ };
61
+ }
62
+ function buildFolders(messages) {
63
+ const folders = [
64
+ { id: "imbox", label: "Imbox", count: messages.filter((message) => message.placement === "imbox").length },
65
+ { id: "screener", label: "Screener", count: messages.filter((message) => message.placement === "screener").length },
66
+ { id: "delegated", label: "Delegated", count: messages.filter((message) => message.compartmentKind === "delegated").length },
67
+ { id: "native", label: "Native", count: messages.filter((message) => message.compartmentKind === "native").length },
68
+ ];
69
+ const sourceCounts = new Map();
70
+ for (const message of messages) {
71
+ if (!message.source)
72
+ continue;
73
+ sourceCounts.set(message.source, (sourceCounts.get(message.source) ?? 0) + 1);
74
+ }
75
+ for (const [source, count] of [...sourceCounts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
76
+ folders.push({ id: `source:${source}`, label: source.toUpperCase(), count });
77
+ }
78
+ return folders;
79
+ }
80
+ function accessEntries(entries) {
81
+ return entries
82
+ .slice()
83
+ .sort((left, right) => right.accessedAt.localeCompare(left.accessedAt))
84
+ .slice(0, 20)
85
+ .map((entry) => ({
86
+ id: entry.id,
87
+ messageId: entry.messageId ?? null,
88
+ threadId: entry.threadId ?? null,
89
+ tool: entry.tool,
90
+ reason: entry.reason,
91
+ accessedAt: entry.accessedAt,
92
+ }));
93
+ }
94
+ function emitMailRead(agentName, mode, status) {
95
+ (0, runtime_1.emitNervesEvent)({
96
+ component: "heart",
97
+ event: "heart.outlook_mail_read",
98
+ message: "reading Outlook mail surface",
99
+ meta: { agentName, mode, status },
100
+ });
101
+ }
102
+ function statusFromReaderFailure(reason) {
103
+ return reason;
104
+ }
105
+ async function readMailView(agentName) {
106
+ const resolved = (0, reader_1.resolveMailroomReader)(agentName);
107
+ if (!resolved.ok) {
108
+ const status = statusFromReaderFailure(resolved.reason);
109
+ emitMailRead(agentName, "list", status);
110
+ return unavailableMailView(agentName, status, resolved.error);
111
+ }
112
+ try {
113
+ const stored = await resolved.store.listMessages({ agentId: agentName, limit: OUTLOOK_MAIL_COUNT_LIMIT });
114
+ const decrypted = (0, file_store_1.decryptMessages)(stored, resolved.config.privateKeys);
115
+ const summaries = decrypted.map(mailSummary);
116
+ await resolved.store.recordAccess({
117
+ agentId: agentName,
118
+ tool: "outlook_mail_list",
119
+ reason: "outlook read-only mailbox",
120
+ });
121
+ const accessLog = accessEntries(await resolved.store.listAccessLog(agentName));
122
+ emitMailRead(agentName, "list", "ready");
123
+ return {
124
+ status: "ready",
125
+ agentName,
126
+ mailboxAddress: resolved.config.mailboxAddress,
127
+ generatedAt: new Date().toISOString(),
128
+ store: {
129
+ kind: resolved.storeKind,
130
+ label: resolved.storeLabel,
131
+ },
132
+ folders: buildFolders(summaries),
133
+ messages: summaries.slice(0, OUTLOOK_MAIL_LIST_LIMIT),
134
+ accessLog,
135
+ error: null,
136
+ };
137
+ }
138
+ catch (error) {
139
+ const message = error instanceof Error ? error.message : String(error);
140
+ emitMailRead(agentName, "list", "error");
141
+ return unavailableMailView(agentName, "error", message);
142
+ }
143
+ }
144
+ async function readMailMessageView(agentName, messageId) {
145
+ const resolved = (0, reader_1.resolveMailroomReader)(agentName);
146
+ if (!resolved.ok) {
147
+ const status = statusFromReaderFailure(resolved.reason);
148
+ emitMailRead(agentName, "message", status);
149
+ return unavailableMessageView(agentName, status, resolved.error);
150
+ }
151
+ try {
152
+ const stored = await resolved.store.getMessage(messageId);
153
+ if (!stored || stored.agentId !== agentName) {
154
+ emitMailRead(agentName, "message", "not-found");
155
+ return {
156
+ status: "not-found",
157
+ agentName,
158
+ mailboxAddress: resolved.config.mailboxAddress,
159
+ generatedAt: new Date().toISOString(),
160
+ message: null,
161
+ accessLog: accessEntries(await resolved.store.listAccessLog(agentName)),
162
+ error: `No visible mail message found for ${messageId}.`,
163
+ };
164
+ }
165
+ const decrypted = (0, file_store_1.decryptMessages)([stored], resolved.config.privateKeys)[0];
166
+ const access = await resolved.store.recordAccess({
167
+ agentId: agentName,
168
+ messageId,
169
+ tool: "outlook_mail_message",
170
+ reason: "outlook read-only message body",
171
+ });
172
+ const body = decrypted.private.text.length > OUTLOOK_MAIL_BODY_LIMIT
173
+ ? decrypted.private.text.slice(0, OUTLOOK_MAIL_BODY_LIMIT)
174
+ : decrypted.private.text;
175
+ const detail = {
176
+ ...mailSummary(decrypted),
177
+ text: body,
178
+ htmlAvailable: typeof decrypted.private.html === "string" && decrypted.private.html.length > 0,
179
+ bodyTruncated: decrypted.private.text.length > OUTLOOK_MAIL_BODY_LIMIT,
180
+ attachments: decrypted.private.attachments,
181
+ access: {
182
+ tool: access.tool,
183
+ reason: access.reason,
184
+ accessedAt: access.accessedAt,
185
+ },
186
+ };
187
+ emitMailRead(agentName, "message", "ready");
188
+ return {
189
+ status: "ready",
190
+ agentName,
191
+ mailboxAddress: resolved.config.mailboxAddress,
192
+ generatedAt: new Date().toISOString(),
193
+ message: detail,
194
+ accessLog: accessEntries(await resolved.store.listAccessLog(agentName)),
195
+ error: null,
196
+ };
197
+ }
198
+ catch (error) {
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ emitMailRead(agentName, "message", "error");
201
+ return unavailableMessageView(agentName, "error", message);
202
+ }
203
+ }
@@ -7,12 +7,16 @@ const SENSES = [
7
7
  { sense: "cli", label: "CLI", daemonManaged: false },
8
8
  { sense: "teams", label: "Teams", daemonManaged: true },
9
9
  { sense: "bluebubbles", label: "BlueBubbles", daemonManaged: true },
10
+ { sense: "mail", label: "Mail", daemonManaged: true },
10
11
  ];
11
12
  function configuredSenses(senses) {
12
- return senses ?? {
13
- cli: { ...identity_1.DEFAULT_AGENT_SENSES.cli },
14
- teams: { ...identity_1.DEFAULT_AGENT_SENSES.teams },
15
- bluebubbles: { ...identity_1.DEFAULT_AGENT_SENSES.bluebubbles },
13
+ const configured = senses ?? {};
14
+ return {
15
+ ...configured,
16
+ cli: configured.cli ?? { ...identity_1.DEFAULT_AGENT_SENSES.cli },
17
+ teams: configured.teams ?? { ...identity_1.DEFAULT_AGENT_SENSES.teams },
18
+ bluebubbles: configured.bluebubbles ?? { ...identity_1.DEFAULT_AGENT_SENSES.bluebubbles },
19
+ mail: configured.mail ?? { ...identity_1.DEFAULT_AGENT_SENSES.mail },
16
20
  };
17
21
  }
18
22
  function resolveStatus(enabled, daemonManaged, runtimeInfo) {
@@ -141,18 +141,24 @@ function hasTextField(record, key) {
141
141
  }
142
142
  function readSenseStatusLines() {
143
143
  const config = (0, identity_1.loadAgentConfig)();
144
- const senses = config.senses ?? {
145
- cli: { enabled: true },
146
- teams: { enabled: false },
147
- bluebubbles: { enabled: false },
144
+ const configuredSenses = config.senses ?? {};
145
+ const senses = {
146
+ ...configuredSenses,
147
+ cli: configuredSenses.cli ?? { enabled: true },
148
+ teams: configuredSenses.teams ?? { enabled: false },
149
+ bluebubbles: configuredSenses.bluebubbles ?? { enabled: false },
150
+ mail: configuredSenses.mail ?? { enabled: false },
148
151
  };
149
152
  const payload = (0, config_1.loadConfig)();
150
153
  const teams = payload.teams;
151
154
  const bluebubbles = payload.bluebubbles;
155
+ const mailroom = payload.mailroom;
156
+ const privateKeys = mailroom?.privateKeys;
152
157
  const configured = {
153
158
  cli: true,
154
159
  teams: hasTextField(teams, "clientId") && hasTextField(teams, "clientSecret") && hasTextField(teams, "tenantId"),
155
160
  bluebubbles: hasTextField(bluebubbles, "serverUrl") && hasTextField(bluebubbles, "password"),
161
+ mail: hasTextField(mailroom, "mailboxAddress") && !!privateKeys && typeof privateKeys === "object" && !Array.isArray(privateKeys),
156
162
  };
157
163
  const rows = [
158
164
  { label: "CLI", status: "interactive" },
@@ -164,6 +170,10 @@ function readSenseStatusLines() {
164
170
  label: "BlueBubbles",
165
171
  status: !senses.bluebubbles.enabled ? "disabled" : configured.bluebubbles ? "ready" : "not_attached",
166
172
  },
173
+ {
174
+ label: "Mail",
175
+ status: !senses.mail.enabled ? "disabled" : configured.mail ? "ready" : "needs_config",
176
+ },
167
177
  ];
168
178
  return rows.map((row) => `- ${row.label}: ${row.status}`);
169
179
  }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AzureBlobMailroomStore = void 0;
4
+ exports.decryptBlobMessages = decryptBlobMessages;
5
+ const runtime_1 = require("../nerves/runtime");
6
+ const core_1 = require("./core");
7
+ function compareNewestFirst(left, right) {
8
+ return Date.parse(right.receivedAt) - Date.parse(left.receivedAt);
9
+ }
10
+ function blobText(value) {
11
+ return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, "utf-8");
12
+ }
13
+ async function downloadJson(blob) {
14
+ if (!await blob.exists())
15
+ return null;
16
+ return JSON.parse((await blob.downloadToBuffer()).toString("utf-8"));
17
+ }
18
+ class AzureBlobMailroomStore {
19
+ serviceClient;
20
+ containerName;
21
+ containerReady = null;
22
+ constructor(options) {
23
+ this.serviceClient = options.serviceClient;
24
+ this.containerName = options.containerName;
25
+ (0, runtime_1.emitNervesEvent)({
26
+ component: "senses",
27
+ event: "senses.mail_blob_store_init",
28
+ message: "azure blob mailroom store initialized",
29
+ meta: { containerName: this.containerName },
30
+ });
31
+ }
32
+ get container() {
33
+ return this.serviceClient.getContainerClient(this.containerName);
34
+ }
35
+ async ensureContainer() {
36
+ if (!this.containerReady) {
37
+ this.containerReady = this.container.createIfNotExists().then(() => undefined);
38
+ }
39
+ await this.containerReady;
40
+ }
41
+ messageBlob(id) {
42
+ return this.container.getBlockBlobClient(`messages/${id}.json`);
43
+ }
44
+ rawBlob(objectName) {
45
+ return this.container.getBlockBlobClient(objectName);
46
+ }
47
+ accessLogBlob(agentId) {
48
+ return this.container.getBlockBlobClient(`access-log/${agentId}.jsonl`);
49
+ }
50
+ async putRawMessage(input) {
51
+ await this.ensureContainer();
52
+ const { message, rawPayload } = await (0, core_1.buildStoredMailMessage)(input);
53
+ const existing = await downloadJson(this.messageBlob(message.id));
54
+ if (existing) {
55
+ (0, runtime_1.emitNervesEvent)({
56
+ component: "senses",
57
+ event: "senses.mail_blob_store_dedupe",
58
+ message: "azure blob mailroom store deduped existing message",
59
+ meta: { id: message.id, agentId: message.agentId },
60
+ });
61
+ return { created: false, message: existing };
62
+ }
63
+ await this.rawBlob(message.rawObject).uploadData(blobText(rawPayload));
64
+ await this.messageBlob(message.id).uploadData(blobText(message));
65
+ (0, runtime_1.emitNervesEvent)({
66
+ component: "senses",
67
+ event: "senses.mail_blob_store_message_written",
68
+ message: "azure blob mailroom store wrote message",
69
+ meta: { id: message.id, agentId: message.agentId },
70
+ });
71
+ return { created: true, message };
72
+ }
73
+ async getMessage(id) {
74
+ await this.ensureContainer();
75
+ const message = await downloadJson(this.messageBlob(id));
76
+ (0, runtime_1.emitNervesEvent)({
77
+ component: "senses",
78
+ event: "senses.mail_blob_store_message_read",
79
+ message: "azure blob mailroom store read message",
80
+ meta: { id, found: message !== null },
81
+ });
82
+ return message;
83
+ }
84
+ async listMessages(filters) {
85
+ await this.ensureContainer();
86
+ const messages = [];
87
+ for await (const item of this.container.listBlobsFlat({ prefix: "messages/" })) {
88
+ const message = await downloadJson(this.container.getBlockBlobClient(item.name));
89
+ if (message)
90
+ messages.push(message);
91
+ }
92
+ const filtered = messages
93
+ .filter((message) => message.agentId === filters.agentId)
94
+ .filter((message) => filters.placement ? message.placement === filters.placement : true)
95
+ .filter((message) => filters.compartmentKind ? message.compartmentKind === filters.compartmentKind : true)
96
+ .filter((message) => filters.source ? message.source === filters.source : true)
97
+ .sort(compareNewestFirst)
98
+ .slice(0, filters.limit ?? 20);
99
+ (0, runtime_1.emitNervesEvent)({
100
+ component: "senses",
101
+ event: "senses.mail_blob_store_messages_listed",
102
+ message: "azure blob mailroom store listed messages",
103
+ meta: { agentId: filters.agentId, count: filtered.length },
104
+ });
105
+ return filtered;
106
+ }
107
+ async readRawPayload(objectName) {
108
+ await this.ensureContainer();
109
+ const payload = await downloadJson(this.rawBlob(objectName));
110
+ (0, runtime_1.emitNervesEvent)({
111
+ component: "senses",
112
+ event: "senses.mail_blob_store_raw_read",
113
+ message: "azure blob mailroom store read raw payload",
114
+ meta: { objectName, found: payload !== null },
115
+ });
116
+ return payload;
117
+ }
118
+ async recordAccess(entry) {
119
+ await this.ensureContainer();
120
+ const complete = {
121
+ ...entry,
122
+ id: `access_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
123
+ accessedAt: new Date().toISOString(),
124
+ };
125
+ const blob = this.accessLogBlob(entry.agentId);
126
+ const existing = await downloadJson(blob).catch(() => null);
127
+ const entries = Array.isArray(existing) ? existing : [];
128
+ entries.push(complete);
129
+ await blob.uploadData(blobText(entries));
130
+ (0, runtime_1.emitNervesEvent)({
131
+ component: "senses",
132
+ event: "senses.mail_blob_access_recorded",
133
+ message: "azure blob mail access recorded",
134
+ meta: { agentId: entry.agentId, messageId: entry.messageId ?? null, tool: entry.tool },
135
+ });
136
+ return complete;
137
+ }
138
+ async listAccessLog(agentId) {
139
+ await this.ensureContainer();
140
+ const entries = await downloadJson(this.accessLogBlob(agentId));
141
+ const safeEntries = Array.isArray(entries) ? entries : [];
142
+ (0, runtime_1.emitNervesEvent)({
143
+ component: "senses",
144
+ event: "senses.mail_blob_access_log_listed",
145
+ message: "azure blob mail access log listed",
146
+ meta: { agentId, count: safeEntries.length },
147
+ });
148
+ return safeEntries;
149
+ }
150
+ }
151
+ exports.AzureBlobMailroomStore = AzureBlobMailroomStore;
152
+ function decryptBlobMessages(messages, privateKeys) {
153
+ return messages.map((message) => (0, core_1.decryptStoredMailMessage)(message, privateKeys));
154
+ }