@agentuity/runtime 1.0.30 → 1.0.32
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/_metadata.js +9 -1
- package/dist/_metadata.js.map +1 -1
- package/dist/_process-protection.js +1 -1
- package/dist/_process-protection.js.map +1 -1
- package/dist/_tokens.js +1 -1
- package/dist/_tokens.js.map +1 -1
- package/dist/dev-patches/aisdk.js +9 -1
- package/dist/dev-patches/aisdk.js.map +1 -1
- package/dist/dev-patches/otel-llm.js +9 -1
- package/dist/dev-patches/otel-llm.js.map +1 -1
- package/dist/eval.d.ts +2 -2
- package/dist/eval.d.ts.map +1 -1
- package/dist/eval.js.map +1 -1
- package/dist/handlers/cron.js +1 -1
- package/dist/handlers/cron.js.map +1 -1
- package/dist/middleware.js +1 -1
- package/dist/middleware.js.map +1 -1
- package/dist/otel/logger.js +8 -8
- package/dist/otel/logger.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +32 -33
- package/dist/router.js.map +1 -1
- package/dist/services/local/queue.d.ts +3 -1
- package/dist/services/local/queue.d.ts.map +1 -1
- package/dist/services/local/queue.js +10 -0
- package/dist/services/local/queue.js.map +1 -1
- package/dist/services/local/task.d.ts +2 -1
- package/dist/services/local/task.d.ts.map +1 -1
- package/dist/services/local/task.js +91 -0
- package/dist/services/local/task.js.map +1 -1
- package/dist/signature.js +1 -1
- package/dist/signature.js.map +1 -1
- package/package.json +7 -7
- package/src/_process-protection.ts +1 -1
- package/src/_tokens.ts +1 -1
- package/src/eval.ts +2 -3
- package/src/handlers/cron.ts +1 -1
- package/src/middleware.ts +1 -1
- package/src/otel/logger.ts +8 -8
- package/src/router.ts +33 -34
- package/src/services/local/queue.ts +19 -1
- package/src/services/local/task.ts +123 -0
- package/src/signature.ts +1 -1
package/src/router.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { type Context, Hono, type
|
|
2
|
+
import { type Context, Hono, type Env as HonoEnv, type Schema } from 'hono';
|
|
3
|
+
import { loadBuildMetadata } from './_metadata';
|
|
3
4
|
import { returnResponse } from './_util';
|
|
4
5
|
import type { Env } from './app';
|
|
5
|
-
import { loadBuildMetadata } from './_metadata';
|
|
6
6
|
|
|
7
7
|
// Re-export both Env types
|
|
8
8
|
export type { Env };
|
|
@@ -134,7 +134,6 @@ declare module 'hono' {
|
|
|
134
134
|
export const createRouter = <E extends Env = Env, S extends Schema = Schema>(): Hono<E, S> => {
|
|
135
135
|
const router = new Hono<E, S>();
|
|
136
136
|
// tslint:disable-next-line:no-any no-unused-variable
|
|
137
|
-
// biome-ignore lint:no-any
|
|
138
137
|
const _router = router as any;
|
|
139
138
|
|
|
140
139
|
for (const method of ['get', 'put', 'post', 'delete', 'options', 'patch']) {
|
|
@@ -211,68 +210,68 @@ export const createRouter = <E extends Env = Env, S extends Schema = Schema>():
|
|
|
211
210
|
// Deprecated stubs that throw errors with migration instructions
|
|
212
211
|
_router.websocket = (path: string, ..._args: any[]) => {
|
|
213
212
|
throw new Error(
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
'router.websocket() is deprecated and has been removed.\n\n' +
|
|
214
|
+
'Migration: Use the websocket middleware instead:\n\n' +
|
|
216
215
|
` import { createRouter, websocket } from '@agentuity/runtime';\n\n` +
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
' const router = createRouter();\n\n' +
|
|
217
|
+
' // Before (deprecated):\n' +
|
|
219
218
|
` // router.websocket('${path}', (c) => (ws) => { ... });\n\n` +
|
|
220
|
-
|
|
219
|
+
' // After:\n' +
|
|
221
220
|
` router.get('${path}', websocket((c, ws) => {\n` +
|
|
222
|
-
|
|
221
|
+
' ws.onMessage((event) => {\n' +
|
|
223
222
|
` ws.send('Echo: ' + event.data);\n` +
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
' });\n' +
|
|
224
|
+
' }));'
|
|
226
225
|
);
|
|
227
226
|
};
|
|
228
227
|
|
|
229
228
|
_router.sse = (path: string, ..._args: any[]) => {
|
|
230
229
|
throw new Error(
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
'router.sse() is deprecated and has been removed.\n\n' +
|
|
231
|
+
'Migration: Use the sse middleware instead:\n\n' +
|
|
233
232
|
` import { createRouter, sse } from '@agentuity/runtime';\n\n` +
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
' const router = createRouter();\n\n' +
|
|
234
|
+
' // Before (deprecated):\n' +
|
|
236
235
|
` // router.sse('${path}', (c) => async (stream) => { ... });\n\n` +
|
|
237
|
-
|
|
236
|
+
' // After:\n' +
|
|
238
237
|
` router.get('${path}', sse((c, stream) => {\n` +
|
|
239
238
|
` stream.writeSSE({ data: 'Hello', event: 'message' });\n` +
|
|
240
|
-
|
|
239
|
+
' }));'
|
|
241
240
|
);
|
|
242
241
|
};
|
|
243
242
|
|
|
244
243
|
_router.stream = (path: string, ..._args: any[]) => {
|
|
245
244
|
throw new Error(
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
'router.stream() is deprecated and has been removed.\n\n' +
|
|
246
|
+
'Migration: Use the stream middleware instead:\n\n' +
|
|
248
247
|
` import { createRouter, stream } from '@agentuity/runtime';\n\n` +
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
' const router = createRouter();\n\n' +
|
|
249
|
+
' // Before (deprecated):\n' +
|
|
251
250
|
` // router.stream('${path}', (c) => new ReadableStream({ ... }));\n\n` +
|
|
252
|
-
|
|
251
|
+
' // After:\n' +
|
|
253
252
|
` router.post('${path}', stream((c) => {\n` +
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
' return new ReadableStream({\n' +
|
|
254
|
+
' start(controller) {\n' +
|
|
256
255
|
` controller.enqueue('data\\n');\n` +
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
' controller.close();\n' +
|
|
257
|
+
' }\n' +
|
|
258
|
+
' });\n' +
|
|
259
|
+
' }));'
|
|
261
260
|
);
|
|
262
261
|
};
|
|
263
262
|
|
|
264
263
|
_router.cron = (schedule: string, ..._args: any[]) => {
|
|
265
264
|
throw new Error(
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
'router.cron() is deprecated and has been removed.\n\n' +
|
|
266
|
+
'Migration: Use the cron middleware instead:\n\n' +
|
|
268
267
|
` import { createRouter, cron } from '@agentuity/runtime';\n\n` +
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
' const router = createRouter();\n\n' +
|
|
269
|
+
' // Before (deprecated):\n' +
|
|
271
270
|
` // router.cron('${schedule}', (c) => { ... });\n\n` +
|
|
272
|
-
|
|
271
|
+
' // After:\n' +
|
|
273
272
|
` router.post('/your-cron-path', cron('${schedule}', (c) => {\n` +
|
|
274
273
|
` return { status: 'complete' };\n` +
|
|
275
|
-
|
|
274
|
+
' }));'
|
|
276
275
|
);
|
|
277
276
|
};
|
|
278
277
|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Database } from 'bun:sqlite';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
QueueService,
|
|
4
|
+
QueuePublishParams,
|
|
5
|
+
QueuePublishResult,
|
|
6
|
+
QueueCreateParams,
|
|
7
|
+
QueueCreateResult,
|
|
8
|
+
} from '@agentuity/core';
|
|
3
9
|
|
|
4
10
|
export class LocalQueueStorage implements QueueService {
|
|
5
11
|
#db: Database;
|
|
@@ -124,4 +130,16 @@ export class LocalQueueStorage implements QueueService {
|
|
|
124
130
|
|
|
125
131
|
return publishInTransaction.immediate();
|
|
126
132
|
}
|
|
133
|
+
|
|
134
|
+
async createQueue(queueName: string, params?: QueueCreateParams): Promise<QueueCreateResult> {
|
|
135
|
+
console.debug(`[local] createQueue: ${queueName}`);
|
|
136
|
+
return {
|
|
137
|
+
name: queueName,
|
|
138
|
+
queueType: params?.queueType ?? 'worker',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async deleteQueue(_queueName: string): Promise<void> {
|
|
143
|
+
// No-op in local mode — queues don't need provisioning locally
|
|
144
|
+
}
|
|
127
145
|
}
|
|
@@ -10,6 +10,8 @@ import type {
|
|
|
10
10
|
UpdateTaskParams,
|
|
11
11
|
ListTasksParams,
|
|
12
12
|
ListTasksResult,
|
|
13
|
+
BatchDeleteTasksParams,
|
|
14
|
+
BatchDeleteTasksResult,
|
|
13
15
|
TaskChangelogResult,
|
|
14
16
|
Comment,
|
|
15
17
|
Tag,
|
|
@@ -120,6 +122,32 @@ const SORT_FIELDS: Record<string, string> = {
|
|
|
120
122
|
closed_date: 'closed_date',
|
|
121
123
|
};
|
|
122
124
|
|
|
125
|
+
const DURATION_UNITS: Record<string, number> = {
|
|
126
|
+
m: 60 * 1000,
|
|
127
|
+
h: 60 * 60 * 1000,
|
|
128
|
+
d: 24 * 60 * 60 * 1000,
|
|
129
|
+
w: 7 * 24 * 60 * 60 * 1000,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const InvalidDurationError = StructuredError(
|
|
133
|
+
'InvalidDurationError',
|
|
134
|
+
'Invalid duration format: use a number followed by m (minutes), h (hours), d (days), or w (weeks)'
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
function parseDurationMs(duration: string): number {
|
|
138
|
+
const match = duration.match(/^(\d+)([mhdw])$/);
|
|
139
|
+
if (!match) {
|
|
140
|
+
throw new InvalidDurationError();
|
|
141
|
+
}
|
|
142
|
+
const value = parseInt(match[1]!, 10);
|
|
143
|
+
const unit = match[2]!;
|
|
144
|
+
const ms = DURATION_UNITS[unit];
|
|
145
|
+
if (!ms) {
|
|
146
|
+
throw new InvalidDurationError();
|
|
147
|
+
}
|
|
148
|
+
return value * ms;
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
function generateTaskId(): string {
|
|
124
152
|
return `task_${crypto.randomUUID().replace(/-/g, '').slice(0, 24)}`;
|
|
125
153
|
}
|
|
@@ -716,6 +744,101 @@ export class LocalTaskStorage implements TaskStorage {
|
|
|
716
744
|
return updated!;
|
|
717
745
|
}
|
|
718
746
|
|
|
747
|
+
async batchDelete(params: BatchDeleteTasksParams): Promise<BatchDeleteTasksResult> {
|
|
748
|
+
const conditions: string[] = ['project_path = ?', 'deleted = 0'];
|
|
749
|
+
const args: (string | number)[] = [this.#projectPath];
|
|
750
|
+
|
|
751
|
+
if (params.status) {
|
|
752
|
+
conditions.push('status = ?');
|
|
753
|
+
args.push(params.status);
|
|
754
|
+
}
|
|
755
|
+
if (params.type) {
|
|
756
|
+
conditions.push('type = ?');
|
|
757
|
+
args.push(params.type);
|
|
758
|
+
}
|
|
759
|
+
if (params.priority) {
|
|
760
|
+
conditions.push('priority = ?');
|
|
761
|
+
args.push(params.priority);
|
|
762
|
+
}
|
|
763
|
+
if (params.parent_id) {
|
|
764
|
+
conditions.push('parent_id = ?');
|
|
765
|
+
args.push(params.parent_id);
|
|
766
|
+
}
|
|
767
|
+
if (params.created_id) {
|
|
768
|
+
conditions.push('created_id = ?');
|
|
769
|
+
args.push(params.created_id);
|
|
770
|
+
}
|
|
771
|
+
if (params.older_than) {
|
|
772
|
+
const ms = parseDurationMs(params.older_than);
|
|
773
|
+
const cutoff = Date.now() - ms;
|
|
774
|
+
conditions.push('created_at < ?');
|
|
775
|
+
args.push(cutoff);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Require at least one filter beyond project_path + deleted
|
|
779
|
+
if (conditions.length < 3) {
|
|
780
|
+
const BatchDeleteFilterRequiredError = StructuredError(
|
|
781
|
+
'BatchDeleteFilterRequiredError',
|
|
782
|
+
'At least one filter is required for batch delete'
|
|
783
|
+
);
|
|
784
|
+
throw new BatchDeleteFilterRequiredError();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (params.limit !== undefined && (!Number.isInteger(params.limit) || params.limit <= 0)) {
|
|
788
|
+
const InvalidBatchDeleteLimitError = StructuredError(
|
|
789
|
+
'InvalidBatchDeleteLimitError',
|
|
790
|
+
'Batch delete limit must be a positive integer'
|
|
791
|
+
);
|
|
792
|
+
throw new InvalidBatchDeleteLimitError();
|
|
793
|
+
}
|
|
794
|
+
const limit = Math.min(params.limit ?? 50, 200);
|
|
795
|
+
|
|
796
|
+
const whereClause = conditions.join(' AND ');
|
|
797
|
+
const selectQuery = `SELECT id, title FROM task_storage WHERE ${whereClause} ORDER BY created_at ASC LIMIT ?`;
|
|
798
|
+
const selectStmt = this.#db.prepare(selectQuery);
|
|
799
|
+
const rows = selectStmt.all(...args, limit) as Array<{ id: string; title: string }>;
|
|
800
|
+
|
|
801
|
+
if (rows.length === 0) {
|
|
802
|
+
return { deleted: [], count: 0 };
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const timestamp = now();
|
|
806
|
+
const ids = rows.map((r) => r.id);
|
|
807
|
+
const placeholders = ids.map(() => '?').join(', ');
|
|
808
|
+
|
|
809
|
+
const txn = this.#db.transaction(() => {
|
|
810
|
+
const updateStmt = this.#db.prepare(`
|
|
811
|
+
UPDATE task_storage
|
|
812
|
+
SET status = 'closed', deleted = 1, closed_date = COALESCE(closed_date, ?), updated_at = ?
|
|
813
|
+
WHERE project_path = ? AND id IN (${placeholders})
|
|
814
|
+
`);
|
|
815
|
+
updateStmt.run(new Date(timestamp).toISOString(), timestamp, this.#projectPath, ...ids);
|
|
816
|
+
|
|
817
|
+
const changelogStmt = this.#db.prepare(`
|
|
818
|
+
INSERT INTO task_changelog_storage (
|
|
819
|
+
project_path, id, task_id, field, old_value, new_value, created_at
|
|
820
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
821
|
+
`);
|
|
822
|
+
for (const row of rows) {
|
|
823
|
+
changelogStmt.run(
|
|
824
|
+
this.#projectPath,
|
|
825
|
+
generateChangelogId(),
|
|
826
|
+
row.id,
|
|
827
|
+
'deleted',
|
|
828
|
+
'false',
|
|
829
|
+
'true',
|
|
830
|
+
timestamp
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
txn();
|
|
835
|
+
|
|
836
|
+
return {
|
|
837
|
+
deleted: rows.map((r) => ({ id: r.id, title: r.title })),
|
|
838
|
+
count: rows.length,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
719
842
|
async createComment(taskId: string, body: string, userId: string): Promise<Comment> {
|
|
720
843
|
const trimmedBody = body?.trim();
|
|
721
844
|
if (!trimmedBody) {
|
package/src/signature.ts
CHANGED
|
@@ -48,7 +48,7 @@ export async function verifySignature(
|
|
|
48
48
|
// Verify timestamp is within acceptable range (prevent replay attacks)
|
|
49
49
|
const ts = parseInt(timestamp, 10);
|
|
50
50
|
const now = Math.floor(Date.now() / 1000);
|
|
51
|
-
if (isNaN(ts) || Math.abs(now - ts) > MAX_SIGNATURE_AGE_SECONDS) {
|
|
51
|
+
if (Number.isNaN(ts) || Math.abs(now - ts) > MAX_SIGNATURE_AGE_SECONDS) {
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
54
54
|
|