@mindstudio-ai/agent 0.1.107 → 0.1.108
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/dist/cli.js +287 -56
- package/dist/index.d.ts +252 -1
- package/dist/index.js +276 -43
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +287 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -102,9 +102,12 @@ function runWithContext(ctx, fn) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/db-ws.ts
|
|
105
|
-
var
|
|
105
|
+
var DB_WS_SUBPROTOCOL = "ms-db-v2";
|
|
106
|
+
var WS_ACK_TIMEOUT_MS = 2e4;
|
|
107
|
+
var WS_IDLE_TIMEOUT_MS = 3e5;
|
|
106
108
|
var WS_OPEN_TIMEOUT_MS = 5e3;
|
|
107
109
|
var WS_OPEN = 1;
|
|
110
|
+
var WS_CHUNK_CHARS = 256 * 1024;
|
|
108
111
|
var WS_MAX_FRAME_CHARS = 9e5;
|
|
109
112
|
var DbWsTransportError = class extends Error {
|
|
110
113
|
constructor(message, sent = false) {
|
|
@@ -117,13 +120,86 @@ function isReadOnlySql(sql) {
|
|
|
117
120
|
const head = sql.trimStart().slice(0, 8).toUpperCase();
|
|
118
121
|
return head.startsWith("SELECT") || head.startsWith("PRAGMA") || head.startsWith("EXPLAIN");
|
|
119
122
|
}
|
|
120
|
-
var
|
|
123
|
+
var conn = null;
|
|
121
124
|
var opening = null;
|
|
122
125
|
var idCounter = 0;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
function clearPendingTimer(p) {
|
|
127
|
+
if (p.timer !== null) {
|
|
128
|
+
clearTimeout(p.timer);
|
|
129
|
+
p.timer = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function forget(self, id) {
|
|
133
|
+
self.pending.delete(id);
|
|
134
|
+
self.inbound.delete(id);
|
|
135
|
+
if (self.retired && self.pending.size === 0) {
|
|
136
|
+
try {
|
|
137
|
+
self.ws.close();
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function retire(self, reason) {
|
|
143
|
+
if (self.retired) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
self.retired = true;
|
|
147
|
+
if (conn === self) {
|
|
148
|
+
conn = null;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
console.warn(
|
|
152
|
+
`[mindstudio] db: retiring WebSocket after ${reason}; next query reconnects.`
|
|
153
|
+
);
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
if (self.pending.size === 0) {
|
|
157
|
+
try {
|
|
158
|
+
self.ws.close();
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function armIdleTimer(self, id, p) {
|
|
164
|
+
clearPendingTimer(p);
|
|
165
|
+
p.timer = setTimeout(() => {
|
|
166
|
+
forget(self, id);
|
|
167
|
+
retire(self, "a reply went silent");
|
|
168
|
+
p.reject(new DbWsTransportError("ws reply went silent", true));
|
|
169
|
+
}, WS_IDLE_TIMEOUT_MS);
|
|
170
|
+
}
|
|
171
|
+
function deliver(self, msg) {
|
|
172
|
+
const p = self.pending.get(msg.id);
|
|
173
|
+
if (!p) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
clearPendingTimer(p);
|
|
177
|
+
forget(self, msg.id);
|
|
178
|
+
if (msg.error) {
|
|
179
|
+
p.reject(
|
|
180
|
+
new MindStudioError(
|
|
181
|
+
`[db] ${msg.error.message || "Database query failed"}`,
|
|
182
|
+
msg.error.code || "db_query_error",
|
|
183
|
+
400,
|
|
184
|
+
msg.error
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
p.resolve(msg.results);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function failTransport(self, id, err) {
|
|
192
|
+
const p = self.pending.get(id);
|
|
193
|
+
if (!p) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
clearPendingTimer(p);
|
|
197
|
+
forget(self, id);
|
|
198
|
+
p.reject(err);
|
|
199
|
+
}
|
|
200
|
+
function getOrOpen(url, token) {
|
|
201
|
+
if (conn && !conn.retired && conn.ws.readyState === WS_OPEN) {
|
|
202
|
+
return Promise.resolve(conn);
|
|
127
203
|
}
|
|
128
204
|
if (opening) {
|
|
129
205
|
return opening;
|
|
@@ -136,17 +212,22 @@ function getOrOpen(url) {
|
|
|
136
212
|
}
|
|
137
213
|
opening = new Promise((resolve, reject) => {
|
|
138
214
|
let ws;
|
|
215
|
+
let self = null;
|
|
139
216
|
const fail = (err) => {
|
|
140
217
|
clearTimeout(openTimer);
|
|
141
|
-
if (
|
|
142
|
-
|
|
218
|
+
if (self) {
|
|
219
|
+
self.retired = true;
|
|
220
|
+
if (conn === self) {
|
|
221
|
+
conn = null;
|
|
222
|
+
}
|
|
223
|
+
for (const [, p] of self.pending) {
|
|
224
|
+
clearPendingTimer(p);
|
|
225
|
+
p.reject(new DbWsTransportError(err.message, true));
|
|
226
|
+
}
|
|
227
|
+
self.pending.clear();
|
|
228
|
+
self.inbound.clear();
|
|
143
229
|
}
|
|
144
230
|
opening = null;
|
|
145
|
-
for (const [, p] of pending) {
|
|
146
|
-
clearTimeout(p.timer);
|
|
147
|
-
p.reject(new DbWsTransportError(err.message, true));
|
|
148
|
-
}
|
|
149
|
-
pending.clear();
|
|
150
231
|
reject(err);
|
|
151
232
|
};
|
|
152
233
|
const openTimer = setTimeout(() => {
|
|
@@ -157,16 +238,26 @@ function getOrOpen(url) {
|
|
|
157
238
|
fail(new DbWsTransportError("ws open timed out"));
|
|
158
239
|
}, WS_OPEN_TIMEOUT_MS);
|
|
159
240
|
try {
|
|
160
|
-
ws = new WebSocketCtor(url);
|
|
241
|
+
ws = new WebSocketCtor(url, ["auth", token, DB_WS_SUBPROTOCOL]);
|
|
161
242
|
} catch (err) {
|
|
162
243
|
fail(new DbWsTransportError(err?.message || "ws construct failed"));
|
|
163
244
|
return;
|
|
164
245
|
}
|
|
246
|
+
self = {
|
|
247
|
+
ws,
|
|
248
|
+
pending: /* @__PURE__ */ new Map(),
|
|
249
|
+
chunked: false,
|
|
250
|
+
inbound: /* @__PURE__ */ new Map(),
|
|
251
|
+
acksSeen: false,
|
|
252
|
+
retired: false
|
|
253
|
+
};
|
|
254
|
+
const c = self;
|
|
165
255
|
ws.onopen = () => {
|
|
166
256
|
clearTimeout(openTimer);
|
|
167
|
-
|
|
257
|
+
c.chunked = ws.protocol === DB_WS_SUBPROTOCOL;
|
|
258
|
+
conn = c;
|
|
168
259
|
opening = null;
|
|
169
|
-
resolve(
|
|
260
|
+
resolve(c);
|
|
170
261
|
};
|
|
171
262
|
ws.onmessage = (ev) => {
|
|
172
263
|
let msg;
|
|
@@ -177,26 +268,54 @@ function getOrOpen(url) {
|
|
|
177
268
|
} catch {
|
|
178
269
|
return;
|
|
179
270
|
}
|
|
180
|
-
if (!msg || msg.
|
|
271
|
+
if (!msg || msg.id == null) {
|
|
181
272
|
return;
|
|
182
273
|
}
|
|
183
|
-
|
|
184
|
-
|
|
274
|
+
if (msg.t === "db-ack") {
|
|
275
|
+
c.acksSeen = true;
|
|
276
|
+
const p = c.pending.get(msg.id);
|
|
277
|
+
if (p && !p.acked) {
|
|
278
|
+
p.acked = true;
|
|
279
|
+
armIdleTimer(c, msg.id, p);
|
|
280
|
+
}
|
|
185
281
|
return;
|
|
186
282
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
283
|
+
if (msg.t === "db-chunk") {
|
|
284
|
+
const p = c.pending.get(msg.id);
|
|
285
|
+
if (!p || typeof msg.data !== "string") {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const parts = c.inbound.get(msg.id);
|
|
289
|
+
if (parts) {
|
|
290
|
+
parts.push(msg.data);
|
|
291
|
+
} else {
|
|
292
|
+
c.inbound.set(msg.id, [msg.data]);
|
|
293
|
+
}
|
|
294
|
+
armIdleTimer(c, msg.id, p);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (msg.t === "db-end") {
|
|
298
|
+
const parts = c.inbound.get(msg.id);
|
|
299
|
+
if (!parts) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
c.inbound.delete(msg.id);
|
|
303
|
+
let assembled;
|
|
304
|
+
try {
|
|
305
|
+
assembled = JSON.parse(parts.join(""));
|
|
306
|
+
} catch {
|
|
307
|
+
failTransport(
|
|
308
|
+
c,
|
|
309
|
+
msg.id,
|
|
310
|
+
new DbWsTransportError("chunked reply did not parse", true)
|
|
311
|
+
);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
deliver(c, assembled);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (msg.t === "db-result") {
|
|
318
|
+
deliver(c, msg);
|
|
200
319
|
}
|
|
201
320
|
};
|
|
202
321
|
ws.onerror = () => fail(new DbWsTransportError("ws error"));
|
|
@@ -204,28 +323,59 @@ function getOrOpen(url) {
|
|
|
204
323
|
});
|
|
205
324
|
return opening;
|
|
206
325
|
}
|
|
326
|
+
function* outboundFrames(id, json) {
|
|
327
|
+
if (json.length <= WS_CHUNK_CHARS) {
|
|
328
|
+
yield json;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
let seq = 0;
|
|
332
|
+
for (let offset = 0; offset < json.length; offset += WS_CHUNK_CHARS) {
|
|
333
|
+
yield JSON.stringify({
|
|
334
|
+
t: "db-chunk",
|
|
335
|
+
id,
|
|
336
|
+
seq: seq++,
|
|
337
|
+
data: json.slice(offset, offset + WS_CHUNK_CHARS)
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
yield JSON.stringify({ t: "db-end", id });
|
|
341
|
+
}
|
|
207
342
|
async function executeDbBatchOverWs(url, token, databaseId, queries) {
|
|
208
343
|
const id = ++idCounter;
|
|
209
344
|
const frame = JSON.stringify({ t: "db", id, token, databaseId, queries });
|
|
210
|
-
|
|
345
|
+
const self = await getOrOpen(url, token);
|
|
346
|
+
if (!self.chunked && frame.length > WS_MAX_FRAME_CHARS) {
|
|
211
347
|
throw new DbWsTransportError("payload too large for ws");
|
|
212
348
|
}
|
|
213
|
-
const ws = await getOrOpen(url);
|
|
214
349
|
return await new Promise((resolve, reject) => {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
350
|
+
const pending = { resolve, reject, timer: null, acked: false };
|
|
351
|
+
if (self.acksSeen) {
|
|
352
|
+
pending.timer = setTimeout(() => {
|
|
353
|
+
forget(self, id);
|
|
354
|
+
retire(self, "a request went unacknowledged");
|
|
355
|
+
reject(new DbWsTransportError("ws ack timed out", true));
|
|
356
|
+
}, WS_ACK_TIMEOUT_MS);
|
|
357
|
+
} else {
|
|
358
|
+
armIdleTimer(self, id, pending);
|
|
359
|
+
}
|
|
360
|
+
self.pending.set(id, pending);
|
|
361
|
+
let started = false;
|
|
362
|
+
let complete = false;
|
|
220
363
|
try {
|
|
221
|
-
|
|
364
|
+
for (const out of outboundFrames(id, frame)) {
|
|
365
|
+
self.ws.send(out);
|
|
366
|
+
started = true;
|
|
367
|
+
}
|
|
368
|
+
complete = true;
|
|
222
369
|
} catch (err) {
|
|
223
|
-
|
|
224
|
-
|
|
370
|
+
clearPendingTimer(pending);
|
|
371
|
+
forget(self, id);
|
|
372
|
+
if (started) {
|
|
373
|
+
retire(self, "a partial request upload failed");
|
|
374
|
+
}
|
|
225
375
|
reject(
|
|
226
376
|
new DbWsTransportError(
|
|
227
377
|
`ws send failed: ${err?.message || "unknown"}`,
|
|
228
|
-
|
|
378
|
+
complete
|
|
229
379
|
)
|
|
230
380
|
);
|
|
231
381
|
}
|
|
@@ -2814,6 +2964,50 @@ function createAnalytics(call) {
|
|
|
2814
2964
|
};
|
|
2815
2965
|
}
|
|
2816
2966
|
|
|
2967
|
+
// src/email/index.ts
|
|
2968
|
+
var iso = (v) => v === void 0 ? void 0 : v instanceof Date ? v.toISOString() : v;
|
|
2969
|
+
function createEmail(call) {
|
|
2970
|
+
return {
|
|
2971
|
+
stats(options) {
|
|
2972
|
+
return call("stats", {
|
|
2973
|
+
start: iso(options?.start),
|
|
2974
|
+
end: iso(options?.end),
|
|
2975
|
+
buckets: options?.buckets
|
|
2976
|
+
});
|
|
2977
|
+
},
|
|
2978
|
+
messages(options) {
|
|
2979
|
+
return call("messages", {
|
|
2980
|
+
...options,
|
|
2981
|
+
start: iso(options?.start),
|
|
2982
|
+
end: iso(options?.end)
|
|
2983
|
+
});
|
|
2984
|
+
},
|
|
2985
|
+
message(messageId) {
|
|
2986
|
+
return call("message", { messageId });
|
|
2987
|
+
},
|
|
2988
|
+
batches(options) {
|
|
2989
|
+
return call("batches", {
|
|
2990
|
+
start: iso(options?.start),
|
|
2991
|
+
end: iso(options?.end),
|
|
2992
|
+
limit: options?.limit,
|
|
2993
|
+
offset: options?.offset
|
|
2994
|
+
});
|
|
2995
|
+
},
|
|
2996
|
+
batch(batchId) {
|
|
2997
|
+
return call("batch", { batchId });
|
|
2998
|
+
},
|
|
2999
|
+
suppressions(options) {
|
|
3000
|
+
return call("suppressions", options ?? {});
|
|
3001
|
+
},
|
|
3002
|
+
suppress(email2) {
|
|
3003
|
+
return call("suppress", { email: email2 });
|
|
3004
|
+
},
|
|
3005
|
+
unsuppress(email2) {
|
|
3006
|
+
return call("unsuppress", { email: email2 });
|
|
3007
|
+
}
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
|
|
2817
3011
|
// src/task/index.ts
|
|
2818
3012
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2819
3013
|
|
|
@@ -5526,6 +5720,7 @@ var MindStudioAgent = class {
|
|
|
5526
5720
|
_dataSources;
|
|
5527
5721
|
_voice;
|
|
5528
5722
|
_analytics;
|
|
5723
|
+
_email;
|
|
5529
5724
|
/** @internal Auth type — 'internal' for CALLBACK_TOKEN (managed mode), 'apiKey' otherwise. */
|
|
5530
5725
|
_authType;
|
|
5531
5726
|
/** @internal Usage source sent on step executions (from MINDSTUDIO_REQUEST_SOURCE).
|
|
@@ -6570,6 +6765,24 @@ var MindStudioAgent = class {
|
|
|
6570
6765
|
this._analyticsRequest.bind(this)
|
|
6571
6766
|
);
|
|
6572
6767
|
}
|
|
6768
|
+
/**
|
|
6769
|
+
* Outbound email: the per-recipient delivery log, blast stats, and this app's
|
|
6770
|
+
* unsubscribe list. Sending is the `sendEmail` action, not this namespace.
|
|
6771
|
+
*
|
|
6772
|
+
* @example
|
|
6773
|
+
* ```ts
|
|
6774
|
+
* import { email } from '@mindstudio-ai/agent';
|
|
6775
|
+
*
|
|
6776
|
+
* // A support lookup: did this person get their mail?
|
|
6777
|
+
* const { messages } = await email.messages({ recipient: 'a@b.com' });
|
|
6778
|
+
*
|
|
6779
|
+
* // How did the August newsletter do?
|
|
6780
|
+
* const blast = await email.batch('newsletter-2026-08');
|
|
6781
|
+
* ```
|
|
6782
|
+
*/
|
|
6783
|
+
get email() {
|
|
6784
|
+
return this._email ??= createEmail(this._emailRequest.bind(this));
|
|
6785
|
+
}
|
|
6573
6786
|
/**
|
|
6574
6787
|
* Jewel surfaces: arrival-shaped triggers (`propose`) and the app-native
|
|
6575
6788
|
* approval queue (`queue.list` / `queue.resolve`). See {@link JewelsApi}.
|
|
@@ -6708,6 +6921,18 @@ var MindStudioAgent = class {
|
|
|
6708
6921
|
* of surfacing as empty data. Deliberately single-shot and scoped to this
|
|
6709
6922
|
* namespace — the other brokered transports keep their semantics.
|
|
6710
6923
|
*/
|
|
6924
|
+
/**
|
|
6925
|
+
* Brokered email transport. No 429 retry, unlike analytics: this namespace
|
|
6926
|
+
* carries writes (suppress / unsuppress), and silently retrying a mutation is a
|
|
6927
|
+
* different risk from retrying an idempotent read. A caller that wants one can
|
|
6928
|
+
* retry a read itself.
|
|
6929
|
+
*/
|
|
6930
|
+
async _emailRequest(op, body) {
|
|
6931
|
+
return this._brokeredRequest("email", op, body, {
|
|
6932
|
+
fallbackMessage: "Email API call failed",
|
|
6933
|
+
fallbackCode: "email_error"
|
|
6934
|
+
});
|
|
6935
|
+
}
|
|
6711
6936
|
async _analyticsRequest(op, body) {
|
|
6712
6937
|
const call = () => this._brokeredRequest("analytics", op, body, {
|
|
6713
6938
|
fallbackMessage: "Analytics read failed",
|
|
@@ -7612,6 +7837,13 @@ var analytics = new Proxy({}, {
|
|
|
7612
7837
|
return typeof value === "function" ? value.bind(target) : value;
|
|
7613
7838
|
}
|
|
7614
7839
|
});
|
|
7840
|
+
var email = new Proxy({}, {
|
|
7841
|
+
get(_, prop) {
|
|
7842
|
+
const target = mindstudio.email;
|
|
7843
|
+
const value = Reflect.get(target, prop, target);
|
|
7844
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
7845
|
+
}
|
|
7846
|
+
});
|
|
7615
7847
|
var session = new Proxy(
|
|
7616
7848
|
{},
|
|
7617
7849
|
{
|
|
@@ -7640,6 +7872,7 @@ export {
|
|
|
7640
7872
|
db,
|
|
7641
7873
|
index_default as default,
|
|
7642
7874
|
defineJewel,
|
|
7875
|
+
email,
|
|
7643
7876
|
files,
|
|
7644
7877
|
getRequestContext,
|
|
7645
7878
|
mindstudio,
|