spectrum-ts 0.0.1 → 0.1.1
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/chunk-3TBRO2J7.js +58 -0
- package/dist/chunk-UZ2CXPOD.js +175 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +344 -0
- package/dist/providers/imessage/index.d.ts +82 -0
- package/dist/providers/imessage/index.js +413 -0
- package/dist/providers/terminal/index.d.ts +35 -0
- package/dist/providers/terminal/index.js +61 -0
- package/dist/stream-DGy4geUK.d.ts +8 -0
- package/dist/types-DiKuSemh.d.ts +261 -0
- package/package.json +48 -3
- package/src/index.ts +26 -0
- package/src/platform/define.ts +309 -0
- package/src/platform/types.ts +438 -0
- package/src/providers/imessage/auth.ts +161 -0
- package/src/providers/imessage/index.ts +145 -0
- package/src/providers/imessage/local.ts +55 -0
- package/src/providers/imessage/remote.ts +157 -0
- package/src/providers/imessage/types.ts +31 -0
- package/src/providers/terminal/index.ts +66 -0
- package/src/spectrum.ts +377 -0
- package/src/types/content.ts +85 -0
- package/src/types/message.ts +18 -0
- package/src/types/space.ts +10 -0
- package/src/types/user.ts +4 -0
- package/src/utils/cloud.ts +1 -0
- package/src/utils/stream.ts +71 -0
- package/index.js +0 -1
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mergeStreams,
|
|
3
|
+
stream
|
|
4
|
+
} from "../../chunk-3TBRO2J7.js";
|
|
5
|
+
import {
|
|
6
|
+
definePlatform
|
|
7
|
+
} from "../../chunk-UZ2CXPOD.js";
|
|
8
|
+
|
|
9
|
+
// src/providers/imessage/index.ts
|
|
10
|
+
import { createClient as createClient2, directChat } from "@photon-ai/advanced-imessage";
|
|
11
|
+
import { IMessageSDK as IMessageSDK2 } from "@photon-ai/imessage-kit";
|
|
12
|
+
|
|
13
|
+
// src/providers/imessage/auth.ts
|
|
14
|
+
import {
|
|
15
|
+
createClient
|
|
16
|
+
} from "@photon-ai/advanced-imessage";
|
|
17
|
+
|
|
18
|
+
// src/utils/cloud.ts
|
|
19
|
+
var SPECTRUM_CLOUD_URL = `https://${process.env.SPECTRUM_CLOUD_URL ?? "spectrum-cloud.photon.codes"}`;
|
|
20
|
+
|
|
21
|
+
// src/providers/imessage/auth.ts
|
|
22
|
+
var RENEWAL_RATIO = 0.8;
|
|
23
|
+
var EXPIRY_BUFFER_MS = 3e4;
|
|
24
|
+
var RETRY_DELAY_MS = 3e4;
|
|
25
|
+
var cloudAuthState = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
async function fetchTokens(projectId, projectSecret) {
|
|
27
|
+
const url = `${SPECTRUM_CLOUD_URL}/${projectId}/imessage/tokens`;
|
|
28
|
+
const credentials = btoa(`${projectId}:${projectSecret}`);
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Basic ${credentials}`
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const body = await response.text().catch(() => "");
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Spectrum Cloud authentication failed (${response.status}): ${body || response.statusText}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const json = await response.json();
|
|
42
|
+
if (!json.succeed) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"Spectrum Cloud authentication failed: server returned succeed=false"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return json.data;
|
|
48
|
+
}
|
|
49
|
+
async function createCloudClients(projectId, projectSecret) {
|
|
50
|
+
let tokenData = await fetchTokens(projectId, projectSecret);
|
|
51
|
+
let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
52
|
+
let disposed = false;
|
|
53
|
+
let renewalTimer;
|
|
54
|
+
const scheduleRenewal = () => {
|
|
55
|
+
if (disposed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const ttlMs = tokenData.expiresIn * 1e3;
|
|
59
|
+
const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
|
|
60
|
+
renewalTimer = setTimeout(async () => {
|
|
61
|
+
try {
|
|
62
|
+
tokenData = await fetchTokens(projectId, projectSecret);
|
|
63
|
+
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
64
|
+
scheduleRenewal();
|
|
65
|
+
} catch {
|
|
66
|
+
renewalTimer = setTimeout(() => scheduleRenewal(), RETRY_DELAY_MS);
|
|
67
|
+
renewalTimer?.unref?.();
|
|
68
|
+
}
|
|
69
|
+
}, renewInMs);
|
|
70
|
+
renewalTimer?.unref?.();
|
|
71
|
+
};
|
|
72
|
+
scheduleRenewal();
|
|
73
|
+
const refreshIfNeeded = async () => {
|
|
74
|
+
if (Date.now() < tokenExpiresAt - EXPIRY_BUFFER_MS) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
tokenData = await fetchTokens(projectId, projectSecret);
|
|
78
|
+
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
79
|
+
scheduleRenewal();
|
|
80
|
+
};
|
|
81
|
+
const buildClients = () => {
|
|
82
|
+
if (tokenData.type === "shared") {
|
|
83
|
+
const address = process.env.SPECTRUM_IMESSAGE_ADDRESS ?? "spectrum-imessage.photon.codes:443";
|
|
84
|
+
return [
|
|
85
|
+
createClient({
|
|
86
|
+
address,
|
|
87
|
+
tls: true,
|
|
88
|
+
token: async () => {
|
|
89
|
+
await refreshIfNeeded();
|
|
90
|
+
return tokenData.token;
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
return Object.entries(tokenData.auth).map(
|
|
96
|
+
([instanceId, token]) => createClient({
|
|
97
|
+
address: `${instanceId}.imsg.photon.codes:443`,
|
|
98
|
+
tls: true,
|
|
99
|
+
token: async () => {
|
|
100
|
+
await refreshIfNeeded();
|
|
101
|
+
const data = tokenData;
|
|
102
|
+
return data.auth[instanceId] ?? token;
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
const clients = buildClients();
|
|
108
|
+
cloudAuthState.set(clients, {
|
|
109
|
+
dispose: () => {
|
|
110
|
+
disposed = true;
|
|
111
|
+
if (renewalTimer !== void 0) {
|
|
112
|
+
clearTimeout(renewalTimer);
|
|
113
|
+
renewalTimer = void 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return clients;
|
|
118
|
+
}
|
|
119
|
+
async function disposeCloudAuth(clients) {
|
|
120
|
+
const auth = cloudAuthState.get(clients);
|
|
121
|
+
if (auth) {
|
|
122
|
+
auth.dispose();
|
|
123
|
+
cloudAuthState.delete(clients);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/providers/imessage/local.ts
|
|
128
|
+
import { unlink, writeFile } from "fs/promises";
|
|
129
|
+
import { tmpdir } from "os";
|
|
130
|
+
import { join } from "path";
|
|
131
|
+
var toSpace = (message) => ({
|
|
132
|
+
id: `${message.isGroupChat ? "any;+;" : "any;-;"}${message.chatId}`,
|
|
133
|
+
type: message.isGroupChat ? "group" : "dm"
|
|
134
|
+
});
|
|
135
|
+
var toMessage = (message) => ({
|
|
136
|
+
id: message.guid,
|
|
137
|
+
content: [{ type: "plain_text", text: message.text ?? "" }],
|
|
138
|
+
sender: { id: message.sender ?? "" },
|
|
139
|
+
space: toSpace(message),
|
|
140
|
+
timestamp: message.date ?? /* @__PURE__ */ new Date()
|
|
141
|
+
});
|
|
142
|
+
var messages = (client) => stream((emit) => {
|
|
143
|
+
client.startWatching({
|
|
144
|
+
onMessage: (message) => emit(toMessage(message))
|
|
145
|
+
});
|
|
146
|
+
return () => client.stopWatching();
|
|
147
|
+
});
|
|
148
|
+
var send = async (client, spaceId, content) => {
|
|
149
|
+
switch (content.type) {
|
|
150
|
+
case "plain_text":
|
|
151
|
+
await client.send(spaceId, content.text);
|
|
152
|
+
break;
|
|
153
|
+
case "attachment": {
|
|
154
|
+
const tmp = join(tmpdir(), `spectrum-${Date.now()}-${content.name}`);
|
|
155
|
+
await writeFile(tmp, content.data);
|
|
156
|
+
try {
|
|
157
|
+
await client.send(spaceId, { files: [tmp] });
|
|
158
|
+
} finally {
|
|
159
|
+
await unlink(tmp).catch(() => {
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/providers/imessage/remote.ts
|
|
170
|
+
import {
|
|
171
|
+
chatGuid,
|
|
172
|
+
messageGuid,
|
|
173
|
+
Reaction
|
|
174
|
+
} from "@photon-ai/advanced-imessage";
|
|
175
|
+
var TAPBACK_NAMES = new Set(
|
|
176
|
+
Object.values(Reaction).filter((r) => r !== "emoji" && r !== "sticker")
|
|
177
|
+
);
|
|
178
|
+
var toMessage2 = (event) => ({
|
|
179
|
+
id: event.message.guid,
|
|
180
|
+
content: [{ type: "plain_text", text: event.message.text ?? "" }],
|
|
181
|
+
sender: { id: event.message.sender?.address ?? "" },
|
|
182
|
+
space: {
|
|
183
|
+
id: event.chatGuid,
|
|
184
|
+
type: event.chatGuid.includes(";+;") ? "group" : "dm"
|
|
185
|
+
},
|
|
186
|
+
timestamp: event.timestamp
|
|
187
|
+
});
|
|
188
|
+
var clientStream = (client) => {
|
|
189
|
+
const sub = client.messages.subscribe("message.received");
|
|
190
|
+
return stream((emit, end) => {
|
|
191
|
+
(async () => {
|
|
192
|
+
try {
|
|
193
|
+
for await (const event of sub) {
|
|
194
|
+
emit(toMessage2(event));
|
|
195
|
+
}
|
|
196
|
+
end();
|
|
197
|
+
} catch (e) {
|
|
198
|
+
end(e);
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
return () => sub.close();
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
var messages2 = (clients) => mergeStreams(clients.map(clientStream));
|
|
205
|
+
var startTyping = async (clients, spaceId) => {
|
|
206
|
+
const remote = clients[0];
|
|
207
|
+
if (!remote) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await remote.chats.startTyping(chatGuid(spaceId));
|
|
211
|
+
};
|
|
212
|
+
var stopTyping = async (clients, spaceId) => {
|
|
213
|
+
const remote = clients[0];
|
|
214
|
+
if (!remote) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
await remote.chats.stopTyping(chatGuid(spaceId));
|
|
218
|
+
};
|
|
219
|
+
var send2 = async (clients, spaceId, content) => {
|
|
220
|
+
const remote = clients[0];
|
|
221
|
+
if (!remote) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
switch (content.type) {
|
|
225
|
+
case "plain_text":
|
|
226
|
+
await remote.messages.send(chatGuid(spaceId), content.text);
|
|
227
|
+
break;
|
|
228
|
+
case "attachment": {
|
|
229
|
+
const attachment = await remote.attachments.upload({
|
|
230
|
+
data: content.data,
|
|
231
|
+
fileName: content.name,
|
|
232
|
+
mimeType: content.mimeType
|
|
233
|
+
});
|
|
234
|
+
await remote.messages.send(chatGuid(spaceId), "", {
|
|
235
|
+
attachment: attachment.guid
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
default:
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var replyToMessage = async (clients, spaceId, msgId, content) => {
|
|
244
|
+
const remote = clients[0];
|
|
245
|
+
if (!remote) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const chat = chatGuid(spaceId);
|
|
249
|
+
const replyTo = messageGuid(msgId);
|
|
250
|
+
switch (content.type) {
|
|
251
|
+
case "plain_text":
|
|
252
|
+
await remote.messages.send(chat, content.text, { replyTo });
|
|
253
|
+
break;
|
|
254
|
+
case "attachment": {
|
|
255
|
+
const attachment = await remote.attachments.upload({
|
|
256
|
+
data: content.data,
|
|
257
|
+
fileName: content.name,
|
|
258
|
+
mimeType: content.mimeType
|
|
259
|
+
});
|
|
260
|
+
await remote.messages.send(chat, "", {
|
|
261
|
+
attachment: attachment.guid,
|
|
262
|
+
replyTo
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
default:
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
var reactToMessage = async (clients, spaceId, msgId, reaction) => {
|
|
271
|
+
const remote = clients[0];
|
|
272
|
+
if (!remote) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const chat = chatGuid(spaceId);
|
|
276
|
+
const msg = messageGuid(msgId);
|
|
277
|
+
if (TAPBACK_NAMES.has(reaction)) {
|
|
278
|
+
await remote.messages.react(chat, msg, reaction);
|
|
279
|
+
} else {
|
|
280
|
+
await remote.messages.reactEmoji(chat, msg, reaction);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// src/providers/imessage/types.ts
|
|
285
|
+
import { IMessageSDK } from "@photon-ai/imessage-kit";
|
|
286
|
+
import z from "zod";
|
|
287
|
+
var isLocal = (client) => client instanceof IMessageSDK;
|
|
288
|
+
var clientEntry = z.object({ address: z.string(), token: z.string() });
|
|
289
|
+
var configSchema = z.union([
|
|
290
|
+
z.object({ local: z.literal(true) }),
|
|
291
|
+
z.object({
|
|
292
|
+
local: z.literal(false).optional().default(false),
|
|
293
|
+
clients: clientEntry.or(z.array(clientEntry)).optional()
|
|
294
|
+
})
|
|
295
|
+
]);
|
|
296
|
+
var userSchema = z.object({});
|
|
297
|
+
var spaceSchema = z.object({
|
|
298
|
+
id: z.string(),
|
|
299
|
+
type: z.enum(["dm", "group"])
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// src/providers/imessage/index.ts
|
|
303
|
+
var imessage = definePlatform("iMessage", {
|
|
304
|
+
config: configSchema,
|
|
305
|
+
static: {
|
|
306
|
+
tapbacks: {
|
|
307
|
+
love: "love",
|
|
308
|
+
like: "like",
|
|
309
|
+
dislike: "dislike",
|
|
310
|
+
laugh: "laugh",
|
|
311
|
+
emphasize: "emphasize",
|
|
312
|
+
question: "question"
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
user: {
|
|
316
|
+
resolve: async ({ input }) => ({ id: input.userID })
|
|
317
|
+
},
|
|
318
|
+
space: {
|
|
319
|
+
schema: spaceSchema,
|
|
320
|
+
resolve: async ({ input, client }) => {
|
|
321
|
+
if (isLocal(client)) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
"Space creation is not supported in local mode. Local mode only supports replying to messages."
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (input.users.length === 0) {
|
|
327
|
+
throw new Error("iMessage space creation requires at least one user");
|
|
328
|
+
}
|
|
329
|
+
const addresses = input.users.map((u) => u.id);
|
|
330
|
+
if (input.users.length === 1) {
|
|
331
|
+
return {
|
|
332
|
+
id: directChat(addresses[0] ?? ""),
|
|
333
|
+
type: "dm"
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const remote = client[0];
|
|
337
|
+
if (!remote) {
|
|
338
|
+
throw new Error("No remote iMessage client available");
|
|
339
|
+
}
|
|
340
|
+
const { chat } = await remote.chats.create(addresses);
|
|
341
|
+
return { id: chat.guid, type: "group" };
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
lifecycle: {
|
|
345
|
+
createClient: async ({
|
|
346
|
+
config,
|
|
347
|
+
projectId,
|
|
348
|
+
projectSecret
|
|
349
|
+
}) => {
|
|
350
|
+
if (config.local) {
|
|
351
|
+
return new IMessageSDK2();
|
|
352
|
+
}
|
|
353
|
+
if (config.clients) {
|
|
354
|
+
const entries = Array.isArray(config.clients) ? config.clients : [config.clients];
|
|
355
|
+
return entries.map(
|
|
356
|
+
(e) => createClient2({ address: e.address, tls: true, token: e.token })
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return await createCloudClients(projectId, projectSecret);
|
|
360
|
+
},
|
|
361
|
+
destroyClient: async ({ client }) => {
|
|
362
|
+
if (isLocal(client)) {
|
|
363
|
+
await client.close();
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
await disposeCloudAuth(client);
|
|
367
|
+
await Promise.all(client.map((c) => c.close()));
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
events: {
|
|
371
|
+
messages: ({ client }) => isLocal(client) ? messages(client) : messages2(client)
|
|
372
|
+
},
|
|
373
|
+
actions: {
|
|
374
|
+
send: async ({ space, content, client }) => {
|
|
375
|
+
for (const item of content) {
|
|
376
|
+
if (isLocal(client)) {
|
|
377
|
+
await send(client, space.id, item);
|
|
378
|
+
} else {
|
|
379
|
+
await send2(client, space.id, item);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
startTyping: async ({ space, client }) => {
|
|
384
|
+
if (isLocal(client)) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
await startTyping(client, space.id);
|
|
388
|
+
},
|
|
389
|
+
stopTyping: async ({ space, client }) => {
|
|
390
|
+
if (isLocal(client)) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
await stopTyping(client, space.id);
|
|
394
|
+
},
|
|
395
|
+
reactToMessage: async ({ space, messageId, reaction, client }) => {
|
|
396
|
+
if (isLocal(client)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
await reactToMessage(client, space.id, messageId, reaction);
|
|
400
|
+
},
|
|
401
|
+
replyToMessage: async ({ space, messageId, content, client }) => {
|
|
402
|
+
if (isLocal(client)) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
for (const item of content) {
|
|
406
|
+
await replyToMessage(client, space.id, messageId, item);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
export {
|
|
412
|
+
imessage
|
|
413
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DiKuSemh.js';
|
|
2
|
+
import * as node_readline from 'node:readline';
|
|
3
|
+
import z__default from 'zod';
|
|
4
|
+
import 'hotscript';
|
|
5
|
+
import 'type-fest';
|
|
6
|
+
|
|
7
|
+
declare const terminal: Platform<PlatformDef<"terminal", z__default.ZodObject<{}, z__default.core.$strip>, z__default.ZodType<object, unknown, z__default.core.$ZodTypeInternals<object, unknown>> | undefined, z__default.ZodType<object, unknown, z__default.core.$ZodTypeInternals<object, unknown>> | undefined, z__default.ZodType<object, unknown, z__default.core.$ZodTypeInternals<object, unknown>> | undefined, node_readline.Interface, {
|
|
8
|
+
id: string;
|
|
9
|
+
}, {
|
|
10
|
+
id: string;
|
|
11
|
+
}, undefined, ProviderMessage<{
|
|
12
|
+
id: string;
|
|
13
|
+
}, {
|
|
14
|
+
id: string;
|
|
15
|
+
}, Record<never, never>>, {
|
|
16
|
+
messages({ client }: {
|
|
17
|
+
client: node_readline.Interface;
|
|
18
|
+
config: Record<string, never>;
|
|
19
|
+
}): AsyncGenerator<{
|
|
20
|
+
id: `${string}-${string}-${string}-${string}-${string}`;
|
|
21
|
+
content: {
|
|
22
|
+
type: "plain_text";
|
|
23
|
+
text: string;
|
|
24
|
+
}[];
|
|
25
|
+
sender: {
|
|
26
|
+
id: string;
|
|
27
|
+
};
|
|
28
|
+
space: {
|
|
29
|
+
id: string;
|
|
30
|
+
};
|
|
31
|
+
timestamp: Date;
|
|
32
|
+
}, void, any>;
|
|
33
|
+
}>> & Readonly<Record<never, never>>;
|
|
34
|
+
|
|
35
|
+
export { terminal };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePlatform
|
|
3
|
+
} from "../../chunk-UZ2CXPOD.js";
|
|
4
|
+
|
|
5
|
+
// src/providers/terminal/index.ts
|
|
6
|
+
import { createInterface } from "readline";
|
|
7
|
+
import z from "zod";
|
|
8
|
+
var terminal = definePlatform("terminal", {
|
|
9
|
+
config: z.object({}),
|
|
10
|
+
user: {
|
|
11
|
+
resolve: async ({ input }) => ({
|
|
12
|
+
id: input.userID
|
|
13
|
+
})
|
|
14
|
+
},
|
|
15
|
+
space: {
|
|
16
|
+
resolve: async () => ({
|
|
17
|
+
id: "terminal"
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
lifecycle: {
|
|
21
|
+
createClient: async () => {
|
|
22
|
+
const client = createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout
|
|
25
|
+
});
|
|
26
|
+
client.on("SIGINT", () => {
|
|
27
|
+
client.close();
|
|
28
|
+
process.kill(process.pid, "SIGINT");
|
|
29
|
+
});
|
|
30
|
+
return client;
|
|
31
|
+
},
|
|
32
|
+
destroyClient: async ({ client }) => {
|
|
33
|
+
client.close();
|
|
34
|
+
process.stdin.unref();
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
events: {
|
|
38
|
+
async *messages({ client }) {
|
|
39
|
+
for await (const line of client) {
|
|
40
|
+
yield {
|
|
41
|
+
id: crypto.randomUUID(),
|
|
42
|
+
content: [{ type: "plain_text", text: line }],
|
|
43
|
+
sender: { id: "terminal-user" },
|
|
44
|
+
space: { id: "terminal" },
|
|
45
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
actions: {
|
|
51
|
+
send: async ({ content }) => {
|
|
52
|
+
const outputs = content.filter((c) => c.type === "plain_text").map((c) => c.text);
|
|
53
|
+
for (const output of outputs) {
|
|
54
|
+
console.log(output);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
export {
|
|
60
|
+
terminal
|
|
61
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface ManagedStream<T> extends AsyncIterable<T> {
|
|
2
|
+
close(): Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
type StreamCleanup = void | (() => void | Promise<void>);
|
|
5
|
+
declare function stream<T>(setup: (emit: (value: T) => void, end: (error?: unknown) => void) => StreamCleanup | Promise<StreamCleanup>): ManagedStream<T>;
|
|
6
|
+
declare function mergeStreams<T>(streams: readonly ManagedStream<T>[]): ManagedStream<T>;
|
|
7
|
+
|
|
8
|
+
export { type ManagedStream as M, mergeStreams as m, stream as s };
|