runline 0.18.0 → 0.19.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/core/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
3
|
import { Worker } from "node:worker_threads";
|
|
4
4
|
import { applyEnvOverrides, updateConnectionConfig } from "../config/loader.js";
|
|
5
5
|
import { formatValidationError, helpInputs, isTypedInputSchema, validateTypedInput, } from "../plugin/schema.js";
|
|
@@ -385,14 +385,12 @@ function formatError(cause) {
|
|
|
385
385
|
}
|
|
386
386
|
return String(cause);
|
|
387
387
|
}
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
// See vendor/README.md for the upgrade procedure.
|
|
395
|
-
const minisearchSource = readFileSync(new URL("../../vendor/minisearch.umd.js", import.meta.url), "utf8");
|
|
388
|
+
// ferrosearch is a Rust native addon, so unlike the old minisearch UMD it
|
|
389
|
+
// cannot be inlined into the worker source as text. Resolve its entry point
|
|
390
|
+
// host-side and require it by absolute path inside the worker: eval'd worker
|
|
391
|
+
// code gets a cwd-relative `require`, so a bare specifier would resolve
|
|
392
|
+
// against the caller's cwd instead of runline's own node_modules.
|
|
393
|
+
const ferrosearchPath = createRequire(import.meta.url).resolve("@shift-labs/ferrosearch");
|
|
396
394
|
function buildHelpData(plugins) {
|
|
397
395
|
const data = {};
|
|
398
396
|
for (const p of plugins) {
|
|
@@ -432,7 +430,7 @@ function buildWorkerSource(pluginNames = [], helpData = {}) {
|
|
|
432
430
|
"console",
|
|
433
431
|
"fetch",
|
|
434
432
|
"require",
|
|
435
|
-
"
|
|
433
|
+
"FerroSearch",
|
|
436
434
|
];
|
|
437
435
|
const wrapped = `"use strict";
|
|
438
436
|
const { parentPort: __port } = require("node:worker_threads");
|
|
@@ -522,14 +520,11 @@ const __fmtErr = (e) => {
|
|
|
522
520
|
// structured-clone-safe and preserves JSON value semantics.
|
|
523
521
|
const __toJson = (v) => v === undefined ? undefined : JSON.parse(JSON.stringify(v));
|
|
524
522
|
|
|
525
|
-
//
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
${minisearchSource}
|
|
531
|
-
return module.exports;
|
|
532
|
-
})();
|
|
523
|
+
// ferrosearch native addon, resolved to an absolute path by the host. Its
|
|
524
|
+
// index memory lives outside the V8 heap, which is the point — minisearch's
|
|
525
|
+
// JS index counted against maxOldGenerationSizeMb and grew worker RSS — but
|
|
526
|
+
// also means an index built by agent code is invisible to that limit.
|
|
527
|
+
const { FerroSearch } = require(${JSON.stringify(ferrosearchPath)});
|
|
533
528
|
|
|
534
529
|
const __help = ${JSON.stringify(helpData)};
|
|
535
530
|
|
|
@@ -563,7 +558,7 @@ const __formatSignature = (plugin, entry) => {
|
|
|
563
558
|
return plugin + '.' + entry.action + (fields ? '({ ' + fields + ' })' : '()');
|
|
564
559
|
};
|
|
565
560
|
|
|
566
|
-
// Build a
|
|
561
|
+
// Build a FerroSearch index over every action path. Indexed at worker
|
|
567
562
|
// startup, queried by actions.find().
|
|
568
563
|
const __search = (() => {
|
|
569
564
|
const docs = [];
|
|
@@ -577,7 +572,7 @@ const __search = (() => {
|
|
|
577
572
|
description: entry.description || '',
|
|
578
573
|
});
|
|
579
574
|
}
|
|
580
|
-
const
|
|
575
|
+
const index = new FerroSearch({
|
|
581
576
|
fields: ['path', 'plugin', 'action', 'description'],
|
|
582
577
|
storeFields: ['path', 'description'],
|
|
583
578
|
searchOptions: {
|
|
@@ -586,8 +581,8 @@ const __search = (() => {
|
|
|
586
581
|
boost: { path: 3, action: 2, plugin: 2 },
|
|
587
582
|
},
|
|
588
583
|
});
|
|
589
|
-
|
|
590
|
-
return
|
|
584
|
+
index.addAll(docs);
|
|
585
|
+
return index;
|
|
591
586
|
})();
|
|
592
587
|
|
|
593
588
|
const __actionsApi = {
|
|
@@ -813,7 +808,7 @@ const __injectValues = __injectNames.map((n) => {
|
|
|
813
808
|
case "console": return console;
|
|
814
809
|
case "fetch": return fetch;
|
|
815
810
|
case "require": return __bodyRequire;
|
|
816
|
-
case "
|
|
811
|
+
case "FerroSearch": return FerroSearch;
|
|
817
812
|
default: return __makeProxy([n]);
|
|
818
813
|
}
|
|
819
814
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as t from "typebox";
|
|
2
2
|
import { registerIssueViewActions } from "./issue-views.js";
|
|
3
3
|
import { registerIssueActions } from "./issues.js";
|
|
4
|
+
import { registerObjectActions } from "./objects.js";
|
|
4
5
|
import { registerPageActions } from "./pages.js";
|
|
5
6
|
import { registerProjectActions } from "./projects.js";
|
|
6
7
|
import { registerTranscriptionActions } from "./transcription.js";
|
|
@@ -8,18 +9,17 @@ export default function shiftLabs(rl) {
|
|
|
8
9
|
rl.setName("shiftLabs");
|
|
9
10
|
rl.setVersion("0.1.0");
|
|
10
11
|
rl.setConnectionSchema(t.Object({
|
|
12
|
+
// The API key is the tenant authority for every action; the cloud
|
|
13
|
+
// derives the organization from it (SHFT-852), so no org ID is needed.
|
|
11
14
|
apiKey: t.String({
|
|
12
15
|
description: "Shift Labs API key",
|
|
13
16
|
env: "SHIFT_LABS_API_KEY",
|
|
14
17
|
}),
|
|
15
|
-
organizationId: t.Optional(t.String({
|
|
16
|
-
description: "Shift Labs organization ID (required for transcription actions)",
|
|
17
|
-
env: "SHIFT_LABS_ORG_ID",
|
|
18
|
-
})),
|
|
19
18
|
}));
|
|
20
19
|
registerProjectActions(rl);
|
|
21
20
|
registerIssueActions(rl);
|
|
22
21
|
registerIssueViewActions(rl);
|
|
23
22
|
registerPageActions(rl);
|
|
24
23
|
registerTranscriptionActions(rl);
|
|
24
|
+
registerObjectActions(rl);
|
|
25
25
|
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { openAsBlob } from "node:fs";
|
|
2
|
+
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, extname } from "node:path";
|
|
4
|
+
import * as t from "typebox";
|
|
5
|
+
import { enumSchema, pathSegment, request } from "./shared.js";
|
|
6
|
+
const OBJECTS_BASE = "/v1/services/objects";
|
|
7
|
+
export const OBJECT_STATUS = ["pending_upload", "ready", "archived"];
|
|
8
|
+
export const OBJECT_LINK_TARGET = [
|
|
9
|
+
"session",
|
|
10
|
+
"artifact",
|
|
11
|
+
"eval_case",
|
|
12
|
+
"db_record",
|
|
13
|
+
];
|
|
14
|
+
/** Mirrors the public service contract's 5 GiB object ceiling. */
|
|
15
|
+
const MAX_OBJECT_BYTES = 5 * 1024 * 1024 * 1024;
|
|
16
|
+
const STRICT_OBJECT = { additionalProperties: false };
|
|
17
|
+
const Id = t.String({ minLength: 1, pattern: "\\S" });
|
|
18
|
+
const MEDIA_TYPES = {
|
|
19
|
+
csv: "text/csv",
|
|
20
|
+
doc: "application/msword",
|
|
21
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
22
|
+
gif: "image/gif",
|
|
23
|
+
html: "text/html",
|
|
24
|
+
jpeg: "image/jpeg",
|
|
25
|
+
jpg: "image/jpeg",
|
|
26
|
+
json: "application/json",
|
|
27
|
+
md: "text/markdown",
|
|
28
|
+
mp3: "audio/mpeg",
|
|
29
|
+
mp4: "video/mp4",
|
|
30
|
+
pdf: "application/pdf",
|
|
31
|
+
png: "image/png",
|
|
32
|
+
txt: "text/plain",
|
|
33
|
+
wav: "audio/wav",
|
|
34
|
+
webp: "image/webp",
|
|
35
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
36
|
+
zip: "application/zip",
|
|
37
|
+
};
|
|
38
|
+
function mediaTypeFor(path, override) {
|
|
39
|
+
if (override)
|
|
40
|
+
return override;
|
|
41
|
+
return (MEDIA_TYPES[extname(path).slice(1).toLowerCase()] ??
|
|
42
|
+
"application/octet-stream");
|
|
43
|
+
}
|
|
44
|
+
async function fileBody(path, sizeBytes) {
|
|
45
|
+
try {
|
|
46
|
+
return await openAsBlob(path);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Runtimes without fs.openAsBlob fall back to buffering; refuse to
|
|
50
|
+
// buffer files that would strain the host process.
|
|
51
|
+
if (sizeBytes > 512 * 1024 * 1024) {
|
|
52
|
+
throw new Error("This runtime cannot stream uploads and the file is too large to buffer.");
|
|
53
|
+
}
|
|
54
|
+
return await readFile(path);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function linkSchema() {
|
|
58
|
+
return t.Object({
|
|
59
|
+
targetType: enumSchema("Link target type", OBJECT_LINK_TARGET),
|
|
60
|
+
targetId: t.String({
|
|
61
|
+
minLength: 1,
|
|
62
|
+
maxLength: 500,
|
|
63
|
+
pattern: "\\S",
|
|
64
|
+
description: "Target ID; db_record targets use source_system/table/row style ids, e.g. R2M_Sys/GoodsReceiptLog/42",
|
|
65
|
+
}),
|
|
66
|
+
role: t.Optional(t.String({
|
|
67
|
+
minLength: 1,
|
|
68
|
+
maxLength: 200,
|
|
69
|
+
pattern: "\\S",
|
|
70
|
+
description: "Link role, e.g. source",
|
|
71
|
+
})),
|
|
72
|
+
}, STRICT_OBJECT);
|
|
73
|
+
}
|
|
74
|
+
const provenanceFields = {
|
|
75
|
+
workspaceId: t.Optional(Id),
|
|
76
|
+
sessionId: t.Optional(Id),
|
|
77
|
+
artifactId: t.Optional(Id),
|
|
78
|
+
};
|
|
79
|
+
export function registerObjectActions(rl) {
|
|
80
|
+
rl.registerAction("object.upload", {
|
|
81
|
+
access: "write",
|
|
82
|
+
description: "Persist a local binary file (image, PDF, document, export) in the " +
|
|
83
|
+
"organization's durable object bucket. Uploads through a short-lived " +
|
|
84
|
+
"signed grant and returns the ready object with its stable ID — use " +
|
|
85
|
+
"that ID as a durable sourceObjectId in DB rows and artifact links.",
|
|
86
|
+
inputSchema: t.Object({
|
|
87
|
+
path: t.String({
|
|
88
|
+
minLength: 1,
|
|
89
|
+
pattern: "\\S",
|
|
90
|
+
description: "Host path of the file to store",
|
|
91
|
+
}),
|
|
92
|
+
contentType: t.Optional(t.String({
|
|
93
|
+
minLength: 1,
|
|
94
|
+
maxLength: 200,
|
|
95
|
+
pattern: "\\S",
|
|
96
|
+
description: "Explicit MIME type; inferred from the extension when omitted",
|
|
97
|
+
})),
|
|
98
|
+
filename: t.Optional(t.String({
|
|
99
|
+
minLength: 1,
|
|
100
|
+
maxLength: 500,
|
|
101
|
+
pattern: "\\S",
|
|
102
|
+
description: "Original filename; defaults to the path's basename",
|
|
103
|
+
})),
|
|
104
|
+
metadata: t.Optional(t.Record(t.String(), t.Unknown(), {
|
|
105
|
+
description: "Small caller-defined metadata object",
|
|
106
|
+
})),
|
|
107
|
+
...provenanceFields,
|
|
108
|
+
}, STRICT_OBJECT),
|
|
109
|
+
async execute(input, ctx) {
|
|
110
|
+
const fields = input;
|
|
111
|
+
const contentType = mediaTypeFor(fields.path, fields.contentType);
|
|
112
|
+
const file = await stat(fields.path);
|
|
113
|
+
if (!file.isFile())
|
|
114
|
+
throw new Error(`${fields.path} is not a file`);
|
|
115
|
+
if (file.size === 0)
|
|
116
|
+
throw new Error("File is empty");
|
|
117
|
+
if (file.size > MAX_OBJECT_BYTES) {
|
|
118
|
+
throw new Error(`File is larger than the ${MAX_OBJECT_BYTES}-byte service limit`);
|
|
119
|
+
}
|
|
120
|
+
const created = await request(ctx, `${OBJECTS_BASE}/objects`, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
// The API key is the tenant authority; no organizationId is sent.
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
contentType,
|
|
125
|
+
sizeBytes: file.size,
|
|
126
|
+
filename: fields.filename ?? basename(fields.path),
|
|
127
|
+
workspaceId: fields.workspaceId,
|
|
128
|
+
sessionId: fields.sessionId,
|
|
129
|
+
artifactId: fields.artifactId,
|
|
130
|
+
metadata: fields.metadata,
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
const uploadHeaders = new Headers(created.upload.headers);
|
|
134
|
+
if (!uploadHeaders.has("content-type")) {
|
|
135
|
+
uploadHeaders.set("content-type", contentType);
|
|
136
|
+
}
|
|
137
|
+
const upload = await fetch(created.upload.url, {
|
|
138
|
+
method: created.upload.method,
|
|
139
|
+
headers: uploadHeaders,
|
|
140
|
+
body: await fileBody(fields.path, file.size),
|
|
141
|
+
});
|
|
142
|
+
if (!upload.ok) {
|
|
143
|
+
throw new Error(`Object upload failed: HTTP ${upload.status}`);
|
|
144
|
+
}
|
|
145
|
+
return request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(created.object.id)}/complete`, { method: "POST" });
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
rl.registerAction("object.list", {
|
|
149
|
+
access: "read",
|
|
150
|
+
description: "List stored objects, newest first. Archived objects appear only " +
|
|
151
|
+
"when filtered for by status.",
|
|
152
|
+
inputSchema: t.Object({
|
|
153
|
+
status: t.Optional(enumSchema("Object status", OBJECT_STATUS)),
|
|
154
|
+
contentType: t.Optional(t.String({ minLength: 1, maxLength: 200, pattern: "\\S" })),
|
|
155
|
+
...provenanceFields,
|
|
156
|
+
}, STRICT_OBJECT),
|
|
157
|
+
async execute(input, ctx) {
|
|
158
|
+
const params = new URLSearchParams();
|
|
159
|
+
for (const [key, value] of Object.entries(input)) {
|
|
160
|
+
if (value !== undefined)
|
|
161
|
+
params.set(key, String(value));
|
|
162
|
+
}
|
|
163
|
+
const query = params.toString();
|
|
164
|
+
const body = await request(ctx, `${OBJECTS_BASE}/objects${query ? `?${query}` : ""}`);
|
|
165
|
+
return body.objects;
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
rl.registerAction("object.get", {
|
|
169
|
+
access: "read",
|
|
170
|
+
description: "Get a stored object's metadata by ID.",
|
|
171
|
+
inputSchema: t.Object({ id: Id }, STRICT_OBJECT),
|
|
172
|
+
async execute(input, ctx) {
|
|
173
|
+
const { id } = input;
|
|
174
|
+
return request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(id)}`);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
rl.registerAction("object.download", {
|
|
178
|
+
access: "read",
|
|
179
|
+
description: "Mint a short-lived signed download URL for a ready object. Set " +
|
|
180
|
+
"savePath to also download the bytes to a host file.",
|
|
181
|
+
inputSchema: t.Object({
|
|
182
|
+
id: Id,
|
|
183
|
+
savePath: t.Optional(t.String({
|
|
184
|
+
minLength: 1,
|
|
185
|
+
pattern: "\\S",
|
|
186
|
+
description: "Host path to write the downloaded bytes to",
|
|
187
|
+
})),
|
|
188
|
+
}, STRICT_OBJECT),
|
|
189
|
+
async execute(input, ctx) {
|
|
190
|
+
const fields = input;
|
|
191
|
+
const grant = await request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(fields.id)}/download`, {
|
|
192
|
+
method: "POST",
|
|
193
|
+
});
|
|
194
|
+
if (!fields.savePath)
|
|
195
|
+
return grant;
|
|
196
|
+
const response = await fetch(grant.url, { method: grant.method });
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new Error(`Object download failed: HTTP ${response.status}`);
|
|
199
|
+
}
|
|
200
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
201
|
+
await writeFile(fields.savePath, bytes);
|
|
202
|
+
return { savedTo: fields.savePath, sizeBytes: bytes.byteLength };
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
rl.registerAction("object.attach", {
|
|
206
|
+
access: "write",
|
|
207
|
+
description: "Link an object to what produced or consumed it: a session, " +
|
|
208
|
+
"artifact, eval case, or customer DB record. Links are append-only " +
|
|
209
|
+
"provenance; duplicates are rejected.",
|
|
210
|
+
inputSchema: t.Object({
|
|
211
|
+
id: Id,
|
|
212
|
+
links: t.Array(linkSchema(), { minItems: 1, maxItems: 20 }),
|
|
213
|
+
}, STRICT_OBJECT),
|
|
214
|
+
async execute(input, ctx) {
|
|
215
|
+
const fields = input;
|
|
216
|
+
const body = await request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(fields.id)}/links`, { method: "POST", body: JSON.stringify({ links: fields.links }) });
|
|
217
|
+
return body.links;
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
rl.registerAction("object.links", {
|
|
221
|
+
access: "read",
|
|
222
|
+
description: "List an object's provenance links.",
|
|
223
|
+
inputSchema: t.Object({ id: Id }, STRICT_OBJECT),
|
|
224
|
+
async execute(input, ctx) {
|
|
225
|
+
const { id } = input;
|
|
226
|
+
const body = await request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(id)}/links`);
|
|
227
|
+
return body.links;
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
rl.registerAction("object.archive", {
|
|
231
|
+
access: "write",
|
|
232
|
+
description: "Archive a stored object (idempotent). Archived objects drop out of " +
|
|
233
|
+
"default listings and refuse download grants; nothing is deleted.",
|
|
234
|
+
inputSchema: t.Object({ id: Id }, STRICT_OBJECT),
|
|
235
|
+
async execute(input, ctx) {
|
|
236
|
+
const { id } = input;
|
|
237
|
+
return request(ctx, `${OBJECTS_BASE}/objects/${pathSegment(id)}/archive`, { method: "POST" });
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
rl.registerAction("object.bucket", {
|
|
241
|
+
access: "read",
|
|
242
|
+
description: "Get the organization's durable object bucket, provisioning it on " +
|
|
243
|
+
"first use.",
|
|
244
|
+
inputSchema: t.Object({}, STRICT_OBJECT),
|
|
245
|
+
async execute(_input, ctx) {
|
|
246
|
+
return request(ctx, `${OBJECTS_BASE}/bucket`);
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
@@ -25,13 +25,6 @@ const MAX_MEDIA_BYTES = 5 * 1024 * 1024 * 1024;
|
|
|
25
25
|
const MAX_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
|
|
26
26
|
const STRICT_OBJECT = { additionalProperties: false };
|
|
27
27
|
const Id = t.String({ minLength: 1, pattern: "\\S" });
|
|
28
|
-
function organizationId(ctx) {
|
|
29
|
-
const value = ctx.connection.config.organizationId;
|
|
30
|
-
if (typeof value !== "string" || !value) {
|
|
31
|
-
throw new Error("SHIFT_LABS_ORG_ID is not set. Transcription actions need the organization ID that owns the API key.");
|
|
32
|
-
}
|
|
33
|
-
return value;
|
|
34
|
-
}
|
|
35
28
|
function mediaTypeFor(path) {
|
|
36
29
|
const mediaType = MEDIA_TYPES[extname(path).slice(1).toLowerCase()];
|
|
37
30
|
if (!mediaType) {
|
|
@@ -83,7 +76,6 @@ export function registerTranscriptionActions(rl) {
|
|
|
83
76
|
}, STRICT_OBJECT),
|
|
84
77
|
async execute(input, ctx) {
|
|
85
78
|
const fields = input;
|
|
86
|
-
const org = organizationId(ctx);
|
|
87
79
|
const contentType = mediaTypeFor(fields.path);
|
|
88
80
|
const file = await stat(fields.path);
|
|
89
81
|
if (!file.isFile())
|
|
@@ -95,8 +87,8 @@ export function registerTranscriptionActions(rl) {
|
|
|
95
87
|
}
|
|
96
88
|
const created = await request(ctx, "/v1/services/transcription/assets", {
|
|
97
89
|
method: "POST",
|
|
90
|
+
// The API key is the tenant authority; no organizationId is sent.
|
|
98
91
|
body: JSON.stringify({
|
|
99
|
-
organizationId: org,
|
|
100
92
|
contentType,
|
|
101
93
|
sizeBytes: file.size,
|
|
102
94
|
}),
|
|
@@ -117,7 +109,6 @@ export function registerTranscriptionActions(rl) {
|
|
|
117
109
|
const job = await request(ctx, "/v1/services/transcription/jobs", {
|
|
118
110
|
method: "POST",
|
|
119
111
|
body: JSON.stringify({
|
|
120
|
-
organizationId: org,
|
|
121
112
|
assetId: created.asset.id,
|
|
122
113
|
language: fields.language,
|
|
123
114
|
diarize: fields.diarize,
|
|
@@ -135,7 +126,7 @@ export function registerTranscriptionActions(rl) {
|
|
|
135
126
|
description: "List transcription jobs for the API key's organization, newest first.",
|
|
136
127
|
inputSchema: t.Object({}, STRICT_OBJECT),
|
|
137
128
|
async execute(_input, ctx) {
|
|
138
|
-
const body = await request(ctx,
|
|
129
|
+
const body = await request(ctx, "/v1/services/transcription/jobs");
|
|
139
130
|
return body.jobs;
|
|
140
131
|
},
|
|
141
132
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Code mode for agents — turn any API or command into a callable action",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
"runline": "./dist/main.js"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist"
|
|
29
|
-
"vendor"
|
|
28
|
+
"dist"
|
|
30
29
|
],
|
|
31
30
|
"scripts": {
|
|
32
31
|
"build": "tsc && bun --filter runline-plugins build && rm -rf dist/plugins && cp -R ../runline-plugins/dist dist/plugins",
|
|
@@ -59,7 +58,6 @@
|
|
|
59
58
|
"@types/bun": "^1.2.17",
|
|
60
59
|
"@types/node": "^25.5.0",
|
|
61
60
|
"@types/proper-lockfile": "^4.1.4",
|
|
62
|
-
"minisearch": "^7.2.0",
|
|
63
61
|
"typescript": "^5.8.0"
|
|
64
62
|
},
|
|
65
63
|
"peerDependencies": {
|
|
@@ -91,6 +89,7 @@
|
|
|
91
89
|
}
|
|
92
90
|
},
|
|
93
91
|
"dependencies": {
|
|
92
|
+
"@shift-labs/ferrosearch": "^0.1.0",
|
|
94
93
|
"chalk": "^5.6.2",
|
|
95
94
|
"commander": "^14.0.3",
|
|
96
95
|
"jiti": "^2.7.0",
|
package/vendor/README.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# vendor
|
|
2
|
-
|
|
3
|
-
Third-party bundles inlined into the QuickJS sandbox at runtime.
|
|
4
|
-
|
|
5
|
-
These files are committed deliberately: the sandbox eval'd source is part of runline's behavior, and PR diffs should show exactly what changes when a bundle is bumped.
|
|
6
|
-
|
|
7
|
-
## minisearch.umd.js
|
|
8
|
-
|
|
9
|
-
UMD build of [minisearch](https://github.com/lucaong/minisearch). Read by `src/core/engine.ts` and inlined into the sandbox so `MiniSearch` is available as a global to agent code (powers `actions.find()`).
|
|
10
|
-
|
|
11
|
-
To upgrade:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm i -D minisearch@latest
|
|
15
|
-
cp node_modules/minisearch/dist/umd/index.js vendor/minisearch.umd.js
|
|
16
|
-
git commit -am "vendor: bump minisearch"
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The `minisearch` devDependency exists only as a convenience for that `cp` — nothing imports it at runtime.
|