@sentry/junior 0.74.1 → 0.75.0
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/agent-hooks-2HEB4C3Q.js +33 -0
- package/dist/api-reference.d.ts +1 -1
- package/dist/app.js +5211 -5316
- package/dist/build/copy-build-content.d.ts +1 -1
- package/dist/chat/agent-dispatch/context.d.ts +2 -3
- package/dist/chat/agent-dispatch/types.d.ts +2 -1
- package/dist/chat/config.d.ts +2 -0
- package/dist/chat/conversations/configured.d.ts +2 -0
- package/dist/chat/credentials/subject.d.ts +3 -3
- package/dist/chat/plugins/agent-hooks.d.ts +13 -13
- package/dist/chat/plugins/credential-hooks.d.ts +6 -6
- package/dist/chat/plugins/db.d.ts +31 -0
- package/dist/chat/plugins/logging.d.ts +2 -2
- package/dist/chat/plugins/package-discovery.d.ts +2 -1
- package/dist/chat/plugins/registry.d.ts +4 -0
- package/dist/chat/plugins/state.d.ts +3 -5
- package/dist/chat/plugins/types.d.ts +1 -0
- package/dist/chat/plugins/validation.d.ts +5 -0
- package/dist/chat/prompt.d.ts +11 -1
- package/dist/chat/respond.d.ts +10 -1
- package/dist/chat/runtime/slack-runtime.d.ts +6 -1
- package/dist/chat/sandbox/egress-credentials.d.ts +8 -8
- package/dist/chat/sandbox/sandbox.d.ts +2 -2
- package/dist/chat/sql/db.d.ts +3 -0
- package/dist/chat/sql/executor.d.ts +7 -0
- package/dist/chat/sql/neon.d.ts +2 -4
- package/dist/chat/sql/postgres.d.ts +6 -0
- package/dist/chat/task-execution/state.d.ts +7 -2
- package/dist/chat/task-execution/worker.d.ts +1 -1
- package/dist/chat/tools/agent-tools.d.ts +2 -2
- package/dist/chat/tools/types.d.ts +3 -0
- package/dist/{chunk-7Q5YOUUT.js → chunk-2RWFUS5F.js} +47 -10
- package/dist/{chunk-YRDS7VKO.js → chunk-62FUNJYS.js} +3 -54
- package/dist/{chunk-M4FLLXXD.js → chunk-74HO27II.js} +1 -1
- package/dist/chunk-BNJIEFQC.js +115 -0
- package/dist/{chunk-YOHFWWBV.js → chunk-C3AM4Z4J.js} +1 -103
- package/dist/chunk-D7NFH5GD.js +570 -0
- package/dist/chunk-EE6PJWY4.js +130 -0
- package/dist/{chunk-CYUI7JU5.js → chunk-EIYL7I4S.js} +1 -1
- package/dist/{chunk-GM7HTXYC.js → chunk-FCZO7LAR.js} +13 -2
- package/dist/{chunk-2LUZA3LY.js → chunk-JEELK46E.js} +5 -5
- package/dist/chunk-MCMROINU.js +12 -0
- package/dist/chunk-NPVUAXUE.js +694 -0
- package/dist/{chunk-OR6NQJ5E.js → chunk-OJODNL2P.js} +3 -3
- package/dist/{chunk-3BYAPS6B.js → chunk-OK4KKR7B.js} +1 -11
- package/dist/chunk-OZSPLAQ4.js +71 -0
- package/dist/{chunk-KVZL5NZS.js → chunk-Q3XNY442.js} +17 -7
- package/dist/{chunk-SQGMG7OD.js → chunk-TQ74BATR.js} +100 -58
- package/dist/{chunk-JL2SLRAT.js → chunk-UJ7OTHPO.js} +76 -312
- package/dist/{chunk-HYHKTFG2.js → chunk-VNTLUFTY.js} +80 -843
- package/dist/chunk-WBZ4M5N5.js +59 -0
- package/dist/{chunk-6UP2Z2RZ.js → chunk-XJHDZUGD.js} +7 -7
- package/dist/chunk-Y2CM7HXH.js +111 -0
- package/dist/{chunk-F6HWCPOC.js → chunk-ZNNTSPNF.js} +1 -1
- package/dist/cli/chat.js +52 -2
- package/dist/cli/check.js +6 -5
- package/dist/cli/snapshot-warmup.js +10 -9
- package/dist/cli/upgrade.js +256 -16
- package/dist/db-A3ILH67H.js +20 -0
- package/dist/handlers/sandbox-egress-route.d.ts +4 -0
- package/dist/handlers/slack-webhook.d.ts +4 -0
- package/dist/handlers/webhooks.d.ts +6 -13
- package/dist/nitro.js +34 -89
- package/dist/plugin-module.d.ts +21 -0
- package/dist/plugins-OMJKLRJ2.js +13 -0
- package/dist/plugins.d.ts +6 -4
- package/dist/registry-NLZFIW23.js +46 -0
- package/dist/reporting/conversations.d.ts +3 -3
- package/dist/reporting.d.ts +6 -5
- package/dist/reporting.js +23 -17
- package/dist/{runner-27NP2TEO.js → runner-LUQZ5G67.js} +18 -13
- package/dist/validation-VMCPP3YO.js +15 -0
- package/package.json +11 -9
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getChatConfig
|
|
3
|
+
} from "./chunk-FCZO7LAR.js";
|
|
4
|
+
|
|
5
|
+
// src/chat/plugins/db.ts
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
// src/chat/sql/neon.ts
|
|
12
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
13
|
+
import {
|
|
14
|
+
Pool
|
|
15
|
+
} from "@neondatabase/serverless";
|
|
16
|
+
import { drizzle } from "drizzle-orm/neon-serverless";
|
|
17
|
+
|
|
18
|
+
// src/chat/conversations/sql/schema/conversations.ts
|
|
19
|
+
import { sql } from "drizzle-orm";
|
|
20
|
+
import { index as index3, integer, jsonb as jsonb3, pgTable as pgTable3, text as text3 } from "drizzle-orm/pg-core";
|
|
21
|
+
|
|
22
|
+
// src/chat/conversations/sql/schema/destinations.ts
|
|
23
|
+
import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
|
|
24
|
+
|
|
25
|
+
// src/chat/conversations/sql/schema/timestamps.ts
|
|
26
|
+
import { timestamp } from "drizzle-orm/pg-core";
|
|
27
|
+
var timestamptz = (name) => timestamp(name, { withTimezone: true });
|
|
28
|
+
|
|
29
|
+
// src/chat/conversations/sql/schema/destinations.ts
|
|
30
|
+
var juniorDestinations = pgTable(
|
|
31
|
+
"junior_destinations",
|
|
32
|
+
{
|
|
33
|
+
id: text("id").primaryKey(),
|
|
34
|
+
provider: text("provider").notNull(),
|
|
35
|
+
providerTenantId: text("provider_tenant_id").notNull().default(""),
|
|
36
|
+
providerDestinationId: text("provider_destination_id").notNull(),
|
|
37
|
+
kind: text("kind").$type().notNull(),
|
|
38
|
+
parentDestinationId: text("parent_destination_id"),
|
|
39
|
+
displayName: text("display_name"),
|
|
40
|
+
visibility: text("visibility").$type().notNull().default("unknown"),
|
|
41
|
+
metadata: jsonb("metadata_json"),
|
|
42
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
43
|
+
updatedAt: timestamptz("updated_at").notNull()
|
|
44
|
+
},
|
|
45
|
+
(table) => [
|
|
46
|
+
uniqueIndex("junior_destinations_provider_destination_uidx").on(
|
|
47
|
+
table.provider,
|
|
48
|
+
table.providerTenantId,
|
|
49
|
+
table.providerDestinationId
|
|
50
|
+
),
|
|
51
|
+
index("junior_destinations_provider_kind_idx").on(
|
|
52
|
+
table.provider,
|
|
53
|
+
table.kind
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// src/chat/conversations/sql/schema/identities.ts
|
|
59
|
+
import { index as index2, jsonb as jsonb2, pgTable as pgTable2, text as text2, uniqueIndex as uniqueIndex2 } from "drizzle-orm/pg-core";
|
|
60
|
+
var juniorIdentities = pgTable2(
|
|
61
|
+
"junior_identities",
|
|
62
|
+
{
|
|
63
|
+
id: text2("id").primaryKey(),
|
|
64
|
+
kind: text2("kind").$type().notNull(),
|
|
65
|
+
provider: text2("provider").notNull(),
|
|
66
|
+
providerTenantId: text2("provider_tenant_id").notNull().default(""),
|
|
67
|
+
providerSubjectId: text2("provider_subject_id").notNull(),
|
|
68
|
+
displayName: text2("display_name"),
|
|
69
|
+
handle: text2("handle"),
|
|
70
|
+
email: text2("email"),
|
|
71
|
+
avatarUrl: text2("avatar_url"),
|
|
72
|
+
metadata: jsonb2("metadata_json"),
|
|
73
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
74
|
+
updatedAt: timestamptz("updated_at").notNull()
|
|
75
|
+
},
|
|
76
|
+
(table) => [
|
|
77
|
+
uniqueIndex2("junior_identities_provider_subject_uidx").on(
|
|
78
|
+
table.provider,
|
|
79
|
+
table.providerTenantId,
|
|
80
|
+
table.providerSubjectId
|
|
81
|
+
),
|
|
82
|
+
index2("junior_identities_kind_provider_idx").on(table.kind, table.provider)
|
|
83
|
+
]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// src/chat/conversations/sql/schema/conversations.ts
|
|
87
|
+
var juniorConversations = pgTable3(
|
|
88
|
+
"junior_conversations",
|
|
89
|
+
{
|
|
90
|
+
conversationId: text3("conversation_id").primaryKey(),
|
|
91
|
+
schemaVersion: integer("schema_version").notNull().default(1),
|
|
92
|
+
source: text3("source").$type(),
|
|
93
|
+
originType: text3("origin_type"),
|
|
94
|
+
originId: text3("origin_id"),
|
|
95
|
+
originRunId: text3("origin_run_id"),
|
|
96
|
+
destinationId: text3("destination_id").references(
|
|
97
|
+
() => juniorDestinations.id
|
|
98
|
+
),
|
|
99
|
+
destination: jsonb3("destination_json").$type(),
|
|
100
|
+
actorIdentityId: text3("actor_identity_id").references(
|
|
101
|
+
() => juniorIdentities.id
|
|
102
|
+
),
|
|
103
|
+
requesterIdentityId: text3("requester_identity_id").references(
|
|
104
|
+
() => juniorIdentities.id
|
|
105
|
+
),
|
|
106
|
+
creatorIdentityId: text3("creator_identity_id").references(
|
|
107
|
+
() => juniorIdentities.id
|
|
108
|
+
),
|
|
109
|
+
credentialSubjectIdentityId: text3(
|
|
110
|
+
"credential_subject_identity_id"
|
|
111
|
+
).references(() => juniorIdentities.id),
|
|
112
|
+
requester: jsonb3("requester_json").$type(),
|
|
113
|
+
channelName: text3("channel_name"),
|
|
114
|
+
title: text3("title"),
|
|
115
|
+
createdAt: timestamptz("created_at").notNull(),
|
|
116
|
+
lastActivityAt: timestamptz("last_activity_at").notNull(),
|
|
117
|
+
updatedAt: timestamptz("updated_at").notNull(),
|
|
118
|
+
executionUpdatedAt: timestamptz("execution_updated_at"),
|
|
119
|
+
executionStatus: text3("execution_status").$type().notNull(),
|
|
120
|
+
runId: text3("run_id"),
|
|
121
|
+
lastCheckpointAt: timestamptz("last_checkpoint_at"),
|
|
122
|
+
lastEnqueuedAt: timestamptz("last_enqueued_at")
|
|
123
|
+
},
|
|
124
|
+
(table) => [
|
|
125
|
+
index3("junior_conversations_last_activity_idx").on(
|
|
126
|
+
table.lastActivityAt.desc(),
|
|
127
|
+
table.conversationId
|
|
128
|
+
),
|
|
129
|
+
index3("junior_conversations_active_idx").using(
|
|
130
|
+
"btree",
|
|
131
|
+
sql`coalesce(${table.executionUpdatedAt}, ${table.updatedAt})`,
|
|
132
|
+
table.conversationId
|
|
133
|
+
).where(sql`${table.executionStatus} <> 'idle'`),
|
|
134
|
+
index3("junior_conversations_destination_activity_idx").on(
|
|
135
|
+
table.destinationId,
|
|
136
|
+
table.lastActivityAt.desc()
|
|
137
|
+
),
|
|
138
|
+
index3("junior_conversations_actor_activity_idx").on(
|
|
139
|
+
table.actorIdentityId,
|
|
140
|
+
table.lastActivityAt.desc()
|
|
141
|
+
),
|
|
142
|
+
index3("junior_conversations_requester_activity_idx").on(
|
|
143
|
+
table.requesterIdentityId,
|
|
144
|
+
table.lastActivityAt.desc()
|
|
145
|
+
),
|
|
146
|
+
index3("junior_conversations_origin_idx").on(
|
|
147
|
+
table.originType,
|
|
148
|
+
table.originId,
|
|
149
|
+
table.lastActivityAt.desc()
|
|
150
|
+
)
|
|
151
|
+
]
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// src/chat/conversations/sql/schema/migrations.ts
|
|
155
|
+
import { pgTable as pgTable4, text as text4 } from "drizzle-orm/pg-core";
|
|
156
|
+
var juniorSchemaMigrations = pgTable4("junior_schema_migrations", {
|
|
157
|
+
id: text4("id").primaryKey(),
|
|
158
|
+
checksum: text4("checksum").notNull(),
|
|
159
|
+
appliedAt: timestamptz("applied_at").notNull().defaultNow()
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// src/chat/conversations/sql/schema.ts
|
|
163
|
+
var schema = {
|
|
164
|
+
juniorConversations,
|
|
165
|
+
juniorDestinations,
|
|
166
|
+
juniorIdentities,
|
|
167
|
+
juniorSchemaMigrations
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/chat/sql/schema.ts
|
|
171
|
+
var juniorSqlSchema = {
|
|
172
|
+
...schema
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/chat/sql/neon.ts
|
|
176
|
+
var NeonExecutor = class {
|
|
177
|
+
constructor(pool) {
|
|
178
|
+
this.pool = pool;
|
|
179
|
+
}
|
|
180
|
+
pool;
|
|
181
|
+
transactionClient = new AsyncLocalStorage();
|
|
182
|
+
savepointId = 0;
|
|
183
|
+
db() {
|
|
184
|
+
return drizzle(this.queryClient(), {
|
|
185
|
+
schema: juniorSqlSchema
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async execute(statement, params = []) {
|
|
189
|
+
await this.queryClient().query(statement, [...params]);
|
|
190
|
+
}
|
|
191
|
+
async query(statement, params = []) {
|
|
192
|
+
const result = await this.queryClient().query(statement, [
|
|
193
|
+
...params
|
|
194
|
+
]);
|
|
195
|
+
return result.rows;
|
|
196
|
+
}
|
|
197
|
+
async transaction(callback) {
|
|
198
|
+
const existingClient = this.transactionClient.getStore();
|
|
199
|
+
if (existingClient) {
|
|
200
|
+
const savepoint = `junior_savepoint_${++this.savepointId}`;
|
|
201
|
+
await existingClient.query(`SAVEPOINT ${savepoint}`);
|
|
202
|
+
try {
|
|
203
|
+
const result = await callback();
|
|
204
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
205
|
+
return result;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
await existingClient.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
208
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const client = await this.pool.connect();
|
|
213
|
+
try {
|
|
214
|
+
await client.query("BEGIN");
|
|
215
|
+
const result = await this.transactionClient.run(client, callback);
|
|
216
|
+
await client.query("COMMIT");
|
|
217
|
+
return result;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
await client.query("ROLLBACK");
|
|
220
|
+
throw error;
|
|
221
|
+
} finally {
|
|
222
|
+
client.release();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async withLock(lockName, callback) {
|
|
226
|
+
if (!lockName) {
|
|
227
|
+
throw new Error("SQL lock name is required");
|
|
228
|
+
}
|
|
229
|
+
const existingClient = this.transactionClient.getStore();
|
|
230
|
+
if (existingClient) {
|
|
231
|
+
await existingClient.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
232
|
+
lockName
|
|
233
|
+
]);
|
|
234
|
+
return await callback();
|
|
235
|
+
}
|
|
236
|
+
const client = await this.pool.connect();
|
|
237
|
+
try {
|
|
238
|
+
await client.query("BEGIN");
|
|
239
|
+
return await this.transactionClient.run(client, async () => {
|
|
240
|
+
try {
|
|
241
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
242
|
+
lockName
|
|
243
|
+
]);
|
|
244
|
+
const result = await callback();
|
|
245
|
+
await client.query("COMMIT");
|
|
246
|
+
return result;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
await client.query("ROLLBACK");
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
} finally {
|
|
253
|
+
client.release();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async close() {
|
|
257
|
+
await this.pool.end();
|
|
258
|
+
}
|
|
259
|
+
queryClient() {
|
|
260
|
+
return this.transactionClient.getStore() ?? this.pool;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
function createNeonJuniorSqlExecutor(args) {
|
|
264
|
+
return new NeonExecutor(
|
|
265
|
+
new Pool({
|
|
266
|
+
connectionString: args.connectionString,
|
|
267
|
+
max: 3
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/chat/sql/postgres.ts
|
|
273
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
274
|
+
import pg from "pg";
|
|
275
|
+
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
276
|
+
var { Pool: Pool2 } = pg;
|
|
277
|
+
var PostgresExecutor = class {
|
|
278
|
+
constructor(pool) {
|
|
279
|
+
this.pool = pool;
|
|
280
|
+
}
|
|
281
|
+
pool;
|
|
282
|
+
transactionClient = new AsyncLocalStorage2();
|
|
283
|
+
savepointId = 0;
|
|
284
|
+
db() {
|
|
285
|
+
return drizzle2(this.queryClient(), {
|
|
286
|
+
schema: juniorSqlSchema
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
async execute(statement, params = []) {
|
|
290
|
+
await this.queryClient().query(statement, [...params]);
|
|
291
|
+
}
|
|
292
|
+
async query(statement, params = []) {
|
|
293
|
+
const result = await this.queryClient().query(statement, [
|
|
294
|
+
...params
|
|
295
|
+
]);
|
|
296
|
+
return result.rows;
|
|
297
|
+
}
|
|
298
|
+
async transaction(callback) {
|
|
299
|
+
const existingClient = this.transactionClient.getStore();
|
|
300
|
+
if (existingClient) {
|
|
301
|
+
const savepoint = `junior_savepoint_${++this.savepointId}`;
|
|
302
|
+
await existingClient.query(`SAVEPOINT ${savepoint}`);
|
|
303
|
+
try {
|
|
304
|
+
const result = await callback();
|
|
305
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
306
|
+
return result;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
await existingClient.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
|
|
309
|
+
await existingClient.query(`RELEASE SAVEPOINT ${savepoint}`);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const client = await this.pool.connect();
|
|
314
|
+
try {
|
|
315
|
+
await client.query("BEGIN");
|
|
316
|
+
const result = await this.transactionClient.run(client, callback);
|
|
317
|
+
await client.query("COMMIT");
|
|
318
|
+
return result;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
await client.query("ROLLBACK");
|
|
321
|
+
throw error;
|
|
322
|
+
} finally {
|
|
323
|
+
client.release();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async withLock(lockName, callback) {
|
|
327
|
+
if (!lockName) {
|
|
328
|
+
throw new Error("SQL lock name is required");
|
|
329
|
+
}
|
|
330
|
+
const existingClient = this.transactionClient.getStore();
|
|
331
|
+
if (existingClient) {
|
|
332
|
+
await existingClient.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
333
|
+
lockName
|
|
334
|
+
]);
|
|
335
|
+
return await callback();
|
|
336
|
+
}
|
|
337
|
+
const client = await this.pool.connect();
|
|
338
|
+
try {
|
|
339
|
+
await client.query("BEGIN");
|
|
340
|
+
return await this.transactionClient.run(client, async () => {
|
|
341
|
+
try {
|
|
342
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1))", [
|
|
343
|
+
lockName
|
|
344
|
+
]);
|
|
345
|
+
const result = await callback();
|
|
346
|
+
await client.query("COMMIT");
|
|
347
|
+
return result;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
await client.query("ROLLBACK");
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
} finally {
|
|
354
|
+
client.release();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async close() {
|
|
358
|
+
await this.pool.end();
|
|
359
|
+
}
|
|
360
|
+
queryClient() {
|
|
361
|
+
return this.transactionClient.getStore() ?? this.pool;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
function createPostgresJuniorSqlExecutor(args) {
|
|
365
|
+
return new PostgresExecutor(
|
|
366
|
+
new Pool2({
|
|
367
|
+
application_name: args.applicationName,
|
|
368
|
+
connectionString: args.connectionString,
|
|
369
|
+
max: 3
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/chat/sql/executor.ts
|
|
375
|
+
function createJuniorSqlExecutor(args) {
|
|
376
|
+
if (args.driver === "postgres") {
|
|
377
|
+
return createPostgresJuniorSqlExecutor(args);
|
|
378
|
+
}
|
|
379
|
+
return createNeonJuniorSqlExecutor(args);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/chat/plugins/db.ts
|
|
383
|
+
var PLUGIN_SCHEMA_LOCK_NAME = "junior_plugin_schema";
|
|
384
|
+
var MIGRATION_FILENAME_RE = /^[0-9]{4}_[a-z0-9_]+\.sql$/;
|
|
385
|
+
var migrationRecordSchema = z.object({
|
|
386
|
+
id: z.string().min(1),
|
|
387
|
+
checksum: z.string().min(1)
|
|
388
|
+
}).strict();
|
|
389
|
+
var configuredPluginDb;
|
|
390
|
+
function checksumSql(sql2) {
|
|
391
|
+
return createHash("sha256").update(sql2).digest("hex");
|
|
392
|
+
}
|
|
393
|
+
function parseStoredMigrationRecord(value) {
|
|
394
|
+
return migrationRecordSchema.parse(value);
|
|
395
|
+
}
|
|
396
|
+
function assertMigrationFilename(filename) {
|
|
397
|
+
if (!filename || filename !== path.basename(filename) || !MIGRATION_FILENAME_RE.test(filename)) {
|
|
398
|
+
throw new Error(`Plugin migration filename "${filename}" is invalid`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function assertUniqueMigrationIds(migrations) {
|
|
402
|
+
const seen = /* @__PURE__ */ new Set();
|
|
403
|
+
for (const migration of migrations) {
|
|
404
|
+
if (seen.has(migration.id)) {
|
|
405
|
+
throw new Error(`Duplicate plugin migration id ${migration.id}`);
|
|
406
|
+
}
|
|
407
|
+
seen.add(migration.id);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function migrationId(pluginName, filename) {
|
|
411
|
+
return `plugin:${pluginName}/${filename}`;
|
|
412
|
+
}
|
|
413
|
+
function createMigrationTableSql() {
|
|
414
|
+
return `
|
|
415
|
+
CREATE TABLE IF NOT EXISTS junior_schema_migrations (
|
|
416
|
+
id TEXT PRIMARY KEY,
|
|
417
|
+
checksum TEXT NOT NULL,
|
|
418
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
419
|
+
)
|
|
420
|
+
`;
|
|
421
|
+
}
|
|
422
|
+
function createPluginDb(executor) {
|
|
423
|
+
const db = executor.db();
|
|
424
|
+
const pluginDb = {
|
|
425
|
+
delete: db.delete.bind(db),
|
|
426
|
+
execute: (statement, params) => executor.execute(statement, params),
|
|
427
|
+
insert: db.insert.bind(db),
|
|
428
|
+
query: (statement, params) => executor.query(statement, params),
|
|
429
|
+
select: db.select.bind(db),
|
|
430
|
+
transaction: async (callback) => await executor.transaction(
|
|
431
|
+
async () => await callback(createPluginDb(executor))
|
|
432
|
+
),
|
|
433
|
+
update: db.update.bind(db)
|
|
434
|
+
};
|
|
435
|
+
return pluginDb;
|
|
436
|
+
}
|
|
437
|
+
function getConfiguredPluginDb() {
|
|
438
|
+
const databaseUrl = getChatConfig().sql.databaseUrl;
|
|
439
|
+
const driver = getChatConfig().sql.driver;
|
|
440
|
+
if (!databaseUrl) {
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
if (configuredPluginDb?.databaseUrl !== databaseUrl || configuredPluginDb.driver !== driver) {
|
|
444
|
+
void configuredPluginDb?.executor.close().catch(() => void 0);
|
|
445
|
+
const executor = createJuniorSqlExecutor({
|
|
446
|
+
connectionString: databaseUrl,
|
|
447
|
+
driver
|
|
448
|
+
});
|
|
449
|
+
configuredPluginDb = {
|
|
450
|
+
databaseUrl,
|
|
451
|
+
driver,
|
|
452
|
+
executor,
|
|
453
|
+
db: createPluginDb(executor)
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return configuredPluginDb.db;
|
|
457
|
+
}
|
|
458
|
+
async function closeConfiguredPluginDb() {
|
|
459
|
+
const current = configuredPluginDb;
|
|
460
|
+
configuredPluginDb = void 0;
|
|
461
|
+
await current?.executor.close();
|
|
462
|
+
}
|
|
463
|
+
async function listAppliedMigrations(executor) {
|
|
464
|
+
const rows = await executor.query(
|
|
465
|
+
"SELECT id, checksum FROM junior_schema_migrations ORDER BY id ASC"
|
|
466
|
+
);
|
|
467
|
+
const records = /* @__PURE__ */ new Map();
|
|
468
|
+
for (const row of rows) {
|
|
469
|
+
const record = parseStoredMigrationRecord(row);
|
|
470
|
+
records.set(record.id, record);
|
|
471
|
+
}
|
|
472
|
+
return records;
|
|
473
|
+
}
|
|
474
|
+
async function applyPluginMigration(executor, migration) {
|
|
475
|
+
await executor.transaction(async () => {
|
|
476
|
+
await executor.execute(migration.sql);
|
|
477
|
+
await executor.execute(
|
|
478
|
+
"INSERT INTO junior_schema_migrations (id, checksum) VALUES ($1, $2)",
|
|
479
|
+
[migration.id, migration.checksum]
|
|
480
|
+
);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function createPluginDbForExecutor(executor) {
|
|
484
|
+
return createPluginDb(executor);
|
|
485
|
+
}
|
|
486
|
+
function getPluginDbForRegistration(registration) {
|
|
487
|
+
if (!registration.database) {
|
|
488
|
+
return void 0;
|
|
489
|
+
}
|
|
490
|
+
return getConfiguredPluginDb();
|
|
491
|
+
}
|
|
492
|
+
function validatePluginDatabaseRequirements(registrations) {
|
|
493
|
+
if (getChatConfig().sql.databaseUrl) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const databasePlugins = registrations.filter((registration) => registration.database).map((registration) => registration.manifest.name);
|
|
497
|
+
if (databasePlugins.length > 0) {
|
|
498
|
+
throw new Error(
|
|
499
|
+
`Plugin database access requires JUNIOR_DATABASE_URL or DATABASE_URL for: ${databasePlugins.join(", ")}`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
function readPluginMigrations(root) {
|
|
504
|
+
const migrationsDir = root.dir;
|
|
505
|
+
let stat;
|
|
506
|
+
try {
|
|
507
|
+
stat = statSync(migrationsDir);
|
|
508
|
+
} catch {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
if (!stat.isDirectory()) {
|
|
512
|
+
throw new Error(
|
|
513
|
+
`Plugin "${root.pluginName}" migrations path is not a directory`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
return readdirSync(migrationsDir).filter((filename) => filename.endsWith(".sql")).sort((left, right) => left.localeCompare(right)).map((filename) => {
|
|
517
|
+
assertMigrationFilename(filename);
|
|
518
|
+
const sql2 = readFileSync(path.join(migrationsDir, filename), "utf8");
|
|
519
|
+
if (!sql2.trim()) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Plugin "${root.pluginName}" migration "${filename}" is empty`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
checksum: checksumSql(sql2),
|
|
526
|
+
filename,
|
|
527
|
+
id: migrationId(root.pluginName, filename),
|
|
528
|
+
pluginName: root.pluginName,
|
|
529
|
+
sql: sql2
|
|
530
|
+
};
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
async function migratePluginSchemas(executor, migrations) {
|
|
534
|
+
assertUniqueMigrationIds(migrations);
|
|
535
|
+
const result = {
|
|
536
|
+
existing: 0,
|
|
537
|
+
migrated: 0,
|
|
538
|
+
scanned: migrations.length
|
|
539
|
+
};
|
|
540
|
+
await executor.withLock(PLUGIN_SCHEMA_LOCK_NAME, async () => {
|
|
541
|
+
await executor.execute(createMigrationTableSql());
|
|
542
|
+
const applied = await listAppliedMigrations(executor);
|
|
543
|
+
for (const migration of migrations) {
|
|
544
|
+
const existing = applied.get(migration.id);
|
|
545
|
+
if (existing) {
|
|
546
|
+
if (existing.checksum !== migration.checksum) {
|
|
547
|
+
throw new Error(`Plugin migration ${migration.id} checksum changed`);
|
|
548
|
+
}
|
|
549
|
+
result.existing++;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
await applyPluginMigration(executor, migration);
|
|
553
|
+
result.migrated++;
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
return result;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export {
|
|
560
|
+
juniorDestinations,
|
|
561
|
+
juniorIdentities,
|
|
562
|
+
juniorConversations,
|
|
563
|
+
createJuniorSqlExecutor,
|
|
564
|
+
closeConfiguredPluginDb,
|
|
565
|
+
createPluginDbForExecutor,
|
|
566
|
+
getPluginDbForRegistration,
|
|
567
|
+
validatePluginDatabaseRequirements,
|
|
568
|
+
readPluginMigrations,
|
|
569
|
+
migratePluginSchemas
|
|
570
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// src/plugins.ts
|
|
2
|
+
function cloneManifests(manifests) {
|
|
3
|
+
return manifests ? structuredClone(manifests) : void 0;
|
|
4
|
+
}
|
|
5
|
+
function cloneInlineManifests(registrations) {
|
|
6
|
+
const inlineManifests = registrations.flatMap(
|
|
7
|
+
(plugin) => plugin.manifest ? [
|
|
8
|
+
{
|
|
9
|
+
manifest: {
|
|
10
|
+
...structuredClone(plugin.manifest),
|
|
11
|
+
capabilities: plugin.manifest.capabilities?.map(
|
|
12
|
+
(capability) => capability.includes(".") ? capability : `${plugin.manifest.name}.${capability}`
|
|
13
|
+
) ?? [],
|
|
14
|
+
configKeys: plugin.manifest.configKeys?.map(
|
|
15
|
+
(key) => key.includes(".") ? key : `${plugin.manifest.name}.${key}`
|
|
16
|
+
) ?? [],
|
|
17
|
+
...plugin.manifest.target ? {
|
|
18
|
+
target: {
|
|
19
|
+
...plugin.manifest.target,
|
|
20
|
+
configKey: plugin.manifest.target.configKey.includes(".") ? plugin.manifest.target.configKey : `${plugin.manifest.name}.${plugin.manifest.target.configKey}`
|
|
21
|
+
}
|
|
22
|
+
} : {}
|
|
23
|
+
},
|
|
24
|
+
...plugin.packageName ? { packageName: plugin.packageName } : {}
|
|
25
|
+
}
|
|
26
|
+
] : []
|
|
27
|
+
);
|
|
28
|
+
return inlineManifests.length > 0 ? inlineManifests : void 0;
|
|
29
|
+
}
|
|
30
|
+
function assertUniquePluginNames(registrations) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
for (const plugin of registrations) {
|
|
33
|
+
const name = plugin.manifest.name;
|
|
34
|
+
if (seen.has(name)) {
|
|
35
|
+
throw new Error(`Duplicate plugin registration name "${name}"`);
|
|
36
|
+
}
|
|
37
|
+
seen.add(name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function assertUniquePackageNames(packageNames) {
|
|
41
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42
|
+
for (const packageName of packageNames) {
|
|
43
|
+
if (seen.has(packageName)) {
|
|
44
|
+
throw new Error(`Duplicate plugin package name "${packageName}"`);
|
|
45
|
+
}
|
|
46
|
+
seen.add(packageName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function normalizePluginInput(input) {
|
|
50
|
+
if (typeof input === "string") {
|
|
51
|
+
return { packageName: input };
|
|
52
|
+
}
|
|
53
|
+
return { registration: input };
|
|
54
|
+
}
|
|
55
|
+
function defineJuniorPlugins(inputs, options = {}) {
|
|
56
|
+
const normalized = inputs.map(normalizePluginInput);
|
|
57
|
+
const packageNames = normalized.flatMap(
|
|
58
|
+
(input) => input.packageName ? [input.packageName] : []
|
|
59
|
+
);
|
|
60
|
+
const registrations = normalized.flatMap(
|
|
61
|
+
(input) => input.registration ? [input.registration] : []
|
|
62
|
+
);
|
|
63
|
+
assertUniquePackageNames(packageNames);
|
|
64
|
+
assertUniquePluginNames(registrations);
|
|
65
|
+
const manifests = cloneManifests(options.manifests);
|
|
66
|
+
return {
|
|
67
|
+
packageNames,
|
|
68
|
+
registrations: registrations.map((plugin) => ({ ...plugin })),
|
|
69
|
+
...manifests ? { manifests } : {}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function pluginCatalogConfigFromPluginSet(pluginSet) {
|
|
73
|
+
if (!pluginSet) {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
const packages = [
|
|
77
|
+
.../* @__PURE__ */ new Set([
|
|
78
|
+
...pluginSet.packageNames,
|
|
79
|
+
...pluginSet.registrations.flatMap(
|
|
80
|
+
(plugin) => plugin.packageName ? [plugin.packageName] : []
|
|
81
|
+
)
|
|
82
|
+
])
|
|
83
|
+
];
|
|
84
|
+
const manifests = cloneManifests(pluginSet.manifests);
|
|
85
|
+
const inlineManifests = cloneInlineManifests(pluginSet.registrations);
|
|
86
|
+
if (packages.length === 0 && !manifests && !inlineManifests) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
...inlineManifests ? { inlineManifests } : {},
|
|
91
|
+
...packages.length > 0 ? { packages } : {},
|
|
92
|
+
...manifests ? { manifests } : {}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function readEnvPluginPackages(env = process.env) {
|
|
96
|
+
const value = env.JUNIOR_PLUGIN_PACKAGES;
|
|
97
|
+
if (!value) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(value);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new Error("JUNIOR_PLUGIN_PACKAGES must be valid JSON", {
|
|
105
|
+
cause: error
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string" || !item.trim())) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"JUNIOR_PLUGIN_PACKAGES must be a JSON array of package names"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
function pluginCatalogConfigFromEnv(env = process.env) {
|
|
116
|
+
const packages = readEnvPluginPackages(env);
|
|
117
|
+
return packages ? { packages } : void 0;
|
|
118
|
+
}
|
|
119
|
+
function pluginHookRegistrationsFromPluginSet(pluginSet) {
|
|
120
|
+
return pluginSet?.registrations.filter(
|
|
121
|
+
(plugin) => plugin.database || plugin.hooks
|
|
122
|
+
) ?? [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
defineJuniorPlugins,
|
|
127
|
+
pluginCatalogConfigFromPluginSet,
|
|
128
|
+
pluginCatalogConfigFromEnv,
|
|
129
|
+
pluginHookRegistrationsFromPluginSet
|
|
130
|
+
};
|