@agentrific/server 0.1.6 → 0.2.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/CHANGELOG.md +6 -0
- package/cli.js +181 -67
- package/cli.js.map +4 -4
- package/index.js +156 -50
- package/index.js.map +4 -4
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agentrific/server
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fb249d8: Add highlighted tool file-change previews, persistent editable message queues and explicit run steering. Add the multi-server transport and credential foundation while preserving existing connection recovery and timeout behaviour.
|
|
8
|
+
|
|
3
9
|
## 0.1.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ var init_package = __esm({
|
|
|
30
30
|
"package.json"() {
|
|
31
31
|
package_default = {
|
|
32
32
|
name: "@agentrific/server",
|
|
33
|
-
version: "0.
|
|
33
|
+
version: "0.2.0",
|
|
34
34
|
description: "Self-hosted control plane for long-running coding agents",
|
|
35
35
|
keywords: [
|
|
36
36
|
"agentrific",
|
|
@@ -67,6 +67,7 @@ var init_package = __esm({
|
|
|
67
67
|
"@fastify/cors": "^11.3.0",
|
|
68
68
|
"@fastify/multipart": "^10.1.2",
|
|
69
69
|
"@inquirer/select": "^5.2.5",
|
|
70
|
+
diff: "^9.0.0",
|
|
70
71
|
effect: "4.0.0-rc.117",
|
|
71
72
|
fastify: "^5.6.2",
|
|
72
73
|
"pino-pretty": "^13.1.3",
|
|
@@ -245,7 +246,7 @@ function createPairingLink(payload) {
|
|
|
245
246
|
link.searchParams.set("data", JSON.stringify(PairingPayloadSchema.parse(payload)));
|
|
246
247
|
return link.toString();
|
|
247
248
|
}
|
|
248
|
-
var cronBounds, AutomationConfigSchema, AutomationStateSchema, SessionStatusSchema, AgentKindSchema, ModelReferenceSchema, ModelIdentitySchema, ModelOptionSchema, ModelCatalogSchema, GitFlowSchema, GitDeliverySchema, DeliveryRequestSchema, RunPreparationSchema, SessionSchema, MAX_ATTACHMENTS, MAX_ATTACHMENT_BYTES, AttachmentSchema, AttachmentIdsSchema, CreateSessionSchema, SendMessageSchema, SessionEventSchema, RecordedEventSchema, EventHistorySchema, ServerEventSchema, ServerSettingsSchema, ConnectionsStatusSchema, UpdateSettingsSchema, ProjectSchema, TaskStatusSchema, TaskSchema, CreateTaskSchema, UpdateTaskSchema, PROTOCOL_VERSION, ServerUpdateStatusSchema, ServerHealthSchema, PairingPayloadSchema, PairingInvitationSchema, PairingRequestSchema, DeviceSchema, PairingResultSchema, HandshakeSchema, HandshakeAckSchema;
|
|
249
|
+
var cronBounds, AutomationConfigSchema, AutomationStateSchema, SessionStatusSchema, AgentKindSchema, ModelReferenceSchema, ModelIdentitySchema, ModelOptionSchema, ModelCatalogSchema, GitFlowSchema, GitDeliverySchema, DeliveryRequestSchema, RunPreparationSchema, SessionSchema, MAX_ATTACHMENTS, MAX_ATTACHMENT_BYTES, AttachmentSchema, AttachmentIdsSchema, CreateSessionSchema, SendMessageSchema, FileChangeSchema, SessionEventSchema, RecordedEventSchema, EventHistorySchema, ServerEventSchema, ServerSettingsSchema, ConnectionsStatusSchema, UpdateSettingsSchema, ProjectSchema, TaskStatusSchema, TaskSchema, CreateTaskSchema, UpdateTaskSchema, PROTOCOL_VERSION, ServerUpdateStatusSchema, ServerHealthSchema, PairingPayloadSchema, PairingInvitationSchema, PairingRequestSchema, DeviceSchema, PairingResultSchema, HandshakeSchema, HandshakeAckSchema;
|
|
249
250
|
var init_src = __esm({
|
|
250
251
|
"../../packages/contracts/src/index.ts"() {
|
|
251
252
|
cronBounds = [
|
|
@@ -381,11 +382,18 @@ var init_src = __esm({
|
|
|
381
382
|
SendMessageSchema = z2.object({
|
|
382
383
|
message: z2.string().trim().max(2e4),
|
|
383
384
|
attachmentIds: AttachmentIdsSchema.optional(),
|
|
384
|
-
clientMessageId: z2.string().min(1).max(120).optional()
|
|
385
|
+
clientMessageId: z2.string().min(1).max(120).optional(),
|
|
386
|
+
mode: z2.enum(["followUp", "steer"]).optional()
|
|
385
387
|
}).refine(
|
|
386
388
|
(input) => input.message.length > 0 || (input.attachmentIds?.length ?? 0) > 0,
|
|
387
389
|
"Write a message or attach a file."
|
|
388
390
|
);
|
|
391
|
+
FileChangeSchema = z2.object({
|
|
392
|
+
path: z2.string(),
|
|
393
|
+
patch: z2.string().optional(),
|
|
394
|
+
content: z2.string().optional(),
|
|
395
|
+
omitted: z2.boolean().optional()
|
|
396
|
+
});
|
|
389
397
|
SessionEventSchema = z2.discriminatedUnion("type", [
|
|
390
398
|
z2.object({ type: z2.literal("session.updated"), session: SessionSchema }),
|
|
391
399
|
z2.object({
|
|
@@ -420,7 +428,8 @@ var init_src = __esm({
|
|
|
420
428
|
sessionId: z2.string(),
|
|
421
429
|
toolCallId: z2.string().optional(),
|
|
422
430
|
tool: z2.string(),
|
|
423
|
-
input: z2.string()
|
|
431
|
+
input: z2.string(),
|
|
432
|
+
filePath: z2.string().optional()
|
|
424
433
|
}),
|
|
425
434
|
z2.object({
|
|
426
435
|
type: z2.literal("tool.completed"),
|
|
@@ -428,6 +437,7 @@ var init_src = __esm({
|
|
|
428
437
|
toolCallId: z2.string().optional(),
|
|
429
438
|
tool: z2.string(),
|
|
430
439
|
output: z2.string(),
|
|
440
|
+
fileChange: FileChangeSchema.optional(),
|
|
431
441
|
isError: z2.boolean().default(false)
|
|
432
442
|
}),
|
|
433
443
|
z2.object({
|
|
@@ -2125,7 +2135,7 @@ var init_runtime = __esm({
|
|
|
2125
2135
|
Effect8.ensuring(Effect8.sync(() => this.working.delete(entry.session.id)))
|
|
2126
2136
|
);
|
|
2127
2137
|
}
|
|
2128
|
-
send(session, message, attachments2 = [], messageId = randomUUID4()) {
|
|
2138
|
+
send(session, message, attachments2 = [], messageId = randomUUID4(), mode = "followUp") {
|
|
2129
2139
|
return this.lock(session.id).withPermit(
|
|
2130
2140
|
Effect8.gen({ self: this }, function* () {
|
|
2131
2141
|
if (this.closed)
|
|
@@ -2135,6 +2145,16 @@ var init_runtime = __esm({
|
|
|
2135
2145
|
if (this.hasMessage(session.id, messageId)) return;
|
|
2136
2146
|
const entry = yield* this.open(session);
|
|
2137
2147
|
yield* this.operation(async () => entry.agent.validateAttachments?.(attachments2));
|
|
2148
|
+
const steering = entry.running && mode === "steer";
|
|
2149
|
+
if (steering) {
|
|
2150
|
+
if (!entry.agent.steer)
|
|
2151
|
+
return yield* Effect8.fail(
|
|
2152
|
+
new AgentError({ message: "This agent does not support steering." })
|
|
2153
|
+
);
|
|
2154
|
+
yield* this.operation(
|
|
2155
|
+
() => entry.agent.steer(gitFlowPrompt(entry.session, message), attachments2)
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2138
2158
|
this.emit({
|
|
2139
2159
|
type: "message.user",
|
|
2140
2160
|
sessionId: session.id,
|
|
@@ -2142,6 +2162,7 @@ var init_runtime = __esm({
|
|
|
2142
2162
|
text: message,
|
|
2143
2163
|
attachments: attachments2
|
|
2144
2164
|
});
|
|
2165
|
+
if (steering) return;
|
|
2145
2166
|
if (entry.running) {
|
|
2146
2167
|
entry.pending.push({ text: message, attachments: attachments2 });
|
|
2147
2168
|
return;
|
|
@@ -3116,6 +3137,74 @@ var init_delivery_tool = __esm({
|
|
|
3116
3137
|
}
|
|
3117
3138
|
});
|
|
3118
3139
|
|
|
3140
|
+
// src/file-change.ts
|
|
3141
|
+
import { createWriteToolDefinition, defineTool as defineTool2 } from "@earendil-works/pi-coding-agent";
|
|
3142
|
+
import { createTwoFilesPatch } from "diff";
|
|
3143
|
+
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
3144
|
+
import { z as z13 } from "zod";
|
|
3145
|
+
function boundFileChange(change) {
|
|
3146
|
+
if ((change.patch?.length ?? 0) + (change.content?.length ?? 0) > previewLimit)
|
|
3147
|
+
return { path: change.path, omitted: true };
|
|
3148
|
+
return change;
|
|
3149
|
+
}
|
|
3150
|
+
function completedFileChange(result, path13) {
|
|
3151
|
+
const parsed = EditResultSchema.safeParse(result);
|
|
3152
|
+
if (!parsed.success) return;
|
|
3153
|
+
const details = parsed.data.details;
|
|
3154
|
+
if (details?.fileChange) return boundFileChange(details.fileChange);
|
|
3155
|
+
if (path13 && details?.patch) return boundFileChange({ path: path13, patch: details.patch });
|
|
3156
|
+
}
|
|
3157
|
+
function fileWriteTool(cwd) {
|
|
3158
|
+
const base = createWriteToolDefinition(cwd);
|
|
3159
|
+
return defineTool2({
|
|
3160
|
+
name: base.name,
|
|
3161
|
+
label: base.label,
|
|
3162
|
+
description: base.description,
|
|
3163
|
+
promptSnippet: base.promptSnippet,
|
|
3164
|
+
promptGuidelines: base.promptGuidelines,
|
|
3165
|
+
parameters: base.parameters,
|
|
3166
|
+
constrainedSampling: base.constrainedSampling,
|
|
3167
|
+
async execute(toolCallId, args, signal, _onUpdate, ctx) {
|
|
3168
|
+
let fileChange;
|
|
3169
|
+
const tool = createWriteToolDefinition(cwd, {
|
|
3170
|
+
operations: {
|
|
3171
|
+
mkdir: async (dir) => {
|
|
3172
|
+
await mkdir5(dir, { recursive: true });
|
|
3173
|
+
},
|
|
3174
|
+
// Pi invokes this inside its per-file mutation queue, so the snapshot
|
|
3175
|
+
// and write cannot race another built-in edit/write on the same file.
|
|
3176
|
+
writeFile: async (path13, content) => {
|
|
3177
|
+
const previous = await readFile4(path13, "utf8").catch((error) => {
|
|
3178
|
+
if (error.code === "ENOENT") return void 0;
|
|
3179
|
+
throw error;
|
|
3180
|
+
});
|
|
3181
|
+
await writeFile3(path13, content, "utf8");
|
|
3182
|
+
fileChange = boundFileChange(
|
|
3183
|
+
previous === void 0 ? { path: args.path, content } : {
|
|
3184
|
+
path: args.path,
|
|
3185
|
+
patch: createTwoFilesPatch(args.path, args.path, previous, content)
|
|
3186
|
+
}
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
});
|
|
3191
|
+
const result = await tool.execute(toolCallId, args, signal, void 0, ctx);
|
|
3192
|
+
return { ...result, details: { fileChange } };
|
|
3193
|
+
}
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
var previewLimit, EditResultSchema;
|
|
3197
|
+
var init_file_change = __esm({
|
|
3198
|
+
"src/file-change.ts"() {
|
|
3199
|
+
"use strict";
|
|
3200
|
+
init_src();
|
|
3201
|
+
previewLimit = 256e3;
|
|
3202
|
+
EditResultSchema = z13.object({
|
|
3203
|
+
details: z13.object({ patch: z13.string().optional(), fileChange: FileChangeSchema.optional() }).optional()
|
|
3204
|
+
});
|
|
3205
|
+
}
|
|
3206
|
+
});
|
|
3207
|
+
|
|
3119
3208
|
// src/pi.ts
|
|
3120
3209
|
import { Effect as Effect12 } from "effect";
|
|
3121
3210
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
@@ -3123,7 +3212,7 @@ import {
|
|
|
3123
3212
|
createAgentSession as createAgentSession2,
|
|
3124
3213
|
SessionManager as SessionManager2
|
|
3125
3214
|
} from "@earendil-works/pi-coding-agent";
|
|
3126
|
-
import { z as
|
|
3215
|
+
import { z as z14 } from "zod";
|
|
3127
3216
|
function toolOutput(value) {
|
|
3128
3217
|
const parsed = ToolResultSchema.safeParse(value);
|
|
3129
3218
|
const text = parsed.success ? parsed.data.content?.filter((part) => part.type === "text").map((part) => part.text || "").join("\n") || "Completed without text output." : boundedJson(value, 4e3);
|
|
@@ -3138,7 +3227,10 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3138
3227
|
model,
|
|
3139
3228
|
modelRuntime: models2.runtime,
|
|
3140
3229
|
sessionManager: SessionManager2.continueRecent(cwd, sessionDirectory),
|
|
3141
|
-
customTools:
|
|
3230
|
+
customTools: [
|
|
3231
|
+
fileWriteTool(cwd),
|
|
3232
|
+
...run?.gitFlow && delivery2 ? [deliveryTool(sessionId, delivery2)] : []
|
|
3233
|
+
]
|
|
3142
3234
|
});
|
|
3143
3235
|
return {
|
|
3144
3236
|
get model() {
|
|
@@ -3162,6 +3254,12 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3162
3254
|
throw new Error(last.errorMessage ?? "The model request failed");
|
|
3163
3255
|
}
|
|
3164
3256
|
},
|
|
3257
|
+
async steer(message, attachments2 = []) {
|
|
3258
|
+
const context = await Effect12.runPromise(files.prepare(attachments2));
|
|
3259
|
+
if (!session.isStreaming)
|
|
3260
|
+
throw new Error("The response has finished. Send this message as a follow-up instead.");
|
|
3261
|
+
await session.steer(message + context.context, context.images);
|
|
3262
|
+
},
|
|
3165
3263
|
async abort() {
|
|
3166
3264
|
session.clearQueue();
|
|
3167
3265
|
await session.abort();
|
|
@@ -3226,16 +3324,23 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3226
3324
|
}
|
|
3227
3325
|
return false;
|
|
3228
3326
|
}
|
|
3327
|
+
const filePaths = /* @__PURE__ */ new Map();
|
|
3328
|
+
const FileArgsSchema = z14.object({ path: z14.string() });
|
|
3229
3329
|
function handleTool(event) {
|
|
3230
3330
|
if (event.type === "tool_execution_start") {
|
|
3231
3331
|
flushText();
|
|
3232
3332
|
setActivity("thinking");
|
|
3333
|
+
if (event.toolName === "edit" || event.toolName === "write") {
|
|
3334
|
+
const args = FileArgsSchema.safeParse(event.args);
|
|
3335
|
+
if (args.success) filePaths.set(event.toolCallId, args.data.path);
|
|
3336
|
+
}
|
|
3233
3337
|
emit({
|
|
3234
3338
|
type: "tool.started",
|
|
3235
3339
|
sessionId,
|
|
3236
3340
|
toolCallId: event.toolCallId,
|
|
3237
3341
|
tool: event.toolName,
|
|
3238
|
-
input: boundedJson(event.args, 1200)
|
|
3342
|
+
input: boundedJson(event.args, 1200),
|
|
3343
|
+
filePath: filePaths.get(event.toolCallId)
|
|
3239
3344
|
});
|
|
3240
3345
|
return true;
|
|
3241
3346
|
}
|
|
@@ -3247,8 +3352,10 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3247
3352
|
toolCallId: event.toolCallId,
|
|
3248
3353
|
tool: event.toolName,
|
|
3249
3354
|
output: toolOutput(event.result),
|
|
3355
|
+
fileChange: event.isError ? void 0 : completedFileChange(event.result, filePaths.get(event.toolCallId)),
|
|
3250
3356
|
isError: event.isError
|
|
3251
3357
|
});
|
|
3358
|
+
filePaths.delete(event.toolCallId);
|
|
3252
3359
|
setActivity("thinking");
|
|
3253
3360
|
return true;
|
|
3254
3361
|
}
|
|
@@ -3281,8 +3388,9 @@ var init_pi = __esm({
|
|
|
3281
3388
|
"use strict";
|
|
3282
3389
|
init_models();
|
|
3283
3390
|
init_delivery_tool();
|
|
3284
|
-
|
|
3285
|
-
|
|
3391
|
+
init_file_change();
|
|
3392
|
+
ToolResultSchema = z14.object({
|
|
3393
|
+
content: z14.array(z14.object({ type: z14.string(), text: z14.string().optional() })).optional()
|
|
3286
3394
|
}).passthrough();
|
|
3287
3395
|
boundedJson = (value, limit) => {
|
|
3288
3396
|
let text;
|
|
@@ -3297,9 +3405,9 @@ var init_pi = __esm({
|
|
|
3297
3405
|
});
|
|
3298
3406
|
|
|
3299
3407
|
// src/projects.ts
|
|
3300
|
-
import { access as access2, readdir as readdir2, realpath, stat as stat3, readFile as
|
|
3408
|
+
import { access as access2, readdir as readdir2, realpath, stat as stat3, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
3301
3409
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
3302
|
-
import { z as
|
|
3410
|
+
import { z as z15 } from "zod";
|
|
3303
3411
|
import { constants as constants2 } from "node:fs";
|
|
3304
3412
|
import { homedir as homedir3 } from "node:os";
|
|
3305
3413
|
import path8 from "node:path";
|
|
@@ -3334,7 +3442,7 @@ async function gitDirectory(directory) {
|
|
|
3334
3442
|
const location = path8.join(directory, ".git");
|
|
3335
3443
|
const info = await stat3(location);
|
|
3336
3444
|
if (info.isDirectory()) return location;
|
|
3337
|
-
const contents = await
|
|
3445
|
+
const contents = await readFile5(location, "utf8");
|
|
3338
3446
|
const match = /^gitdir: (.+)\s*$/m.exec(contents);
|
|
3339
3447
|
if (!match) throw new Error("Invalid Git worktree metadata.");
|
|
3340
3448
|
const resolved = await realpath(path8.resolve(directory, match[1].trim()));
|
|
@@ -3344,21 +3452,21 @@ async function gitDirectory(directory) {
|
|
|
3344
3452
|
async function identity(directory, preferred) {
|
|
3345
3453
|
const filename = path8.join(await gitDirectory(directory), "agentrific-project-id");
|
|
3346
3454
|
try {
|
|
3347
|
-
return ProjectId.parse(await
|
|
3455
|
+
return ProjectId.parse(await readFile5(filename, "utf8"));
|
|
3348
3456
|
} catch (error) {
|
|
3349
|
-
if (!
|
|
3457
|
+
if (!z15.object({ code: z15.literal("ENOENT") }).safeParse(error).success) throw error;
|
|
3350
3458
|
const legacyFilename = path8.join(await gitDirectory(directory), "chatplans-project-id");
|
|
3351
|
-
const legacy = await
|
|
3352
|
-
if (!
|
|
3459
|
+
const legacy = await readFile5(legacyFilename, "utf8").catch((cause) => {
|
|
3460
|
+
if (!z15.object({ code: z15.literal("ENOENT") }).safeParse(cause).success) throw cause;
|
|
3353
3461
|
return null;
|
|
3354
3462
|
});
|
|
3355
3463
|
const id = legacy === null ? preferred ?? randomUUID6() : ProjectId.parse(legacy);
|
|
3356
3464
|
try {
|
|
3357
|
-
await
|
|
3465
|
+
await writeFile4(filename, id + "\n", { flag: "wx" });
|
|
3358
3466
|
return id;
|
|
3359
3467
|
} catch (writeError) {
|
|
3360
|
-
if (!
|
|
3361
|
-
return ProjectId.parse(await
|
|
3468
|
+
if (!z15.object({ code: z15.literal("EEXIST") }).safeParse(writeError).success) throw writeError;
|
|
3469
|
+
return ProjectId.parse(await readFile5(filename, "utf8"));
|
|
3362
3470
|
}
|
|
3363
3471
|
}
|
|
3364
3472
|
}
|
|
@@ -3378,7 +3486,7 @@ function scanProjects(root, store2) {
|
|
|
3378
3486
|
let existing = registered.find((project2) => project2.id === id);
|
|
3379
3487
|
if (found.some((project2) => project2.id === id) || existing && existing.path !== directory && await stat3(path8.join(existing.path, ".git")).catch(() => null)) {
|
|
3380
3488
|
id = randomUUID6();
|
|
3381
|
-
await
|
|
3489
|
+
await writeFile4(
|
|
3382
3490
|
path8.join(await gitDirectory(directory), "agentrific-project-id"),
|
|
3383
3491
|
id + "\n"
|
|
3384
3492
|
);
|
|
@@ -3417,11 +3525,11 @@ function reconnectProject(root, id, input, store2) {
|
|
|
3417
3525
|
const filename = path8.join(await gitDirectory(directory), "agentrific-project-id");
|
|
3418
3526
|
const replacedId = await identity(directory);
|
|
3419
3527
|
const project = { id, name: path8.basename(directory), path: directory, available: true };
|
|
3420
|
-
await
|
|
3528
|
+
await writeFile4(filename, id + "\n");
|
|
3421
3529
|
try {
|
|
3422
3530
|
await Effect13.runPromise(store2.relocateProject(project, previous.path, replacedId));
|
|
3423
3531
|
} catch (error) {
|
|
3424
|
-
await
|
|
3532
|
+
await writeFile4(filename, replacedId + "\n");
|
|
3425
3533
|
throw error;
|
|
3426
3534
|
}
|
|
3427
3535
|
return project;
|
|
@@ -3434,31 +3542,31 @@ var init_projects = __esm({
|
|
|
3434
3542
|
"src/projects.ts"() {
|
|
3435
3543
|
"use strict";
|
|
3436
3544
|
locks2 = /* @__PURE__ */ new WeakMap();
|
|
3437
|
-
ProjectId =
|
|
3545
|
+
ProjectId = z15.string().trim().min(1).max(255);
|
|
3438
3546
|
}
|
|
3439
3547
|
});
|
|
3440
3548
|
|
|
3441
3549
|
// src/push-schema.ts
|
|
3442
|
-
import { z as
|
|
3550
|
+
import { z as z16 } from "zod";
|
|
3443
3551
|
var PushBindingSchema, PushStateSchema, PendingPushSchema;
|
|
3444
3552
|
var init_push_schema = __esm({
|
|
3445
3553
|
"src/push-schema.ts"() {
|
|
3446
3554
|
"use strict";
|
|
3447
3555
|
init_push();
|
|
3448
|
-
PushBindingSchema =
|
|
3449
|
-
bindingId:
|
|
3450
|
-
installationId:
|
|
3451
|
-
deviceId:
|
|
3556
|
+
PushBindingSchema = z16.object({
|
|
3557
|
+
bindingId: z16.string().uuid(),
|
|
3558
|
+
installationId: z16.string().uuid(),
|
|
3559
|
+
deviceId: z16.string().uuid()
|
|
3452
3560
|
});
|
|
3453
|
-
PushStateSchema =
|
|
3454
|
-
bindings:
|
|
3455
|
-
error:
|
|
3561
|
+
PushStateSchema = z16.object({
|
|
3562
|
+
bindings: z16.array(PushBindingSchema).max(10),
|
|
3563
|
+
error: z16.string().nullable()
|
|
3456
3564
|
});
|
|
3457
3565
|
PendingPushSchema = PushEventSchema.extend({
|
|
3458
|
-
remaining:
|
|
3459
|
-
createdAt:
|
|
3460
|
-
attempts:
|
|
3461
|
-
nextAt:
|
|
3566
|
+
remaining: z16.array(PushBindingSchema),
|
|
3567
|
+
createdAt: z16.number(),
|
|
3568
|
+
attempts: z16.number().int().nonnegative(),
|
|
3569
|
+
nextAt: z16.number()
|
|
3462
3570
|
});
|
|
3463
3571
|
}
|
|
3464
3572
|
});
|
|
@@ -3468,7 +3576,7 @@ import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "n
|
|
|
3468
3576
|
import path9 from "node:path";
|
|
3469
3577
|
import { DatabaseSync } from "node:sqlite";
|
|
3470
3578
|
import { Context as Context3, Data as Data3, Effect as Effect14, Layer as Layer3 } from "effect";
|
|
3471
|
-
import { z as
|
|
3579
|
+
import { z as z17 } from "zod";
|
|
3472
3580
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
3473
3581
|
var StorageError, PayloadRow, EventRow, SqliteSessionStore, SessionStore;
|
|
3474
3582
|
var init_store = __esm({
|
|
@@ -3481,8 +3589,8 @@ var init_store = __esm({
|
|
|
3481
3589
|
init_src();
|
|
3482
3590
|
StorageError = class extends Data3.TaggedError("StorageError") {
|
|
3483
3591
|
};
|
|
3484
|
-
PayloadRow =
|
|
3485
|
-
EventRow =
|
|
3592
|
+
PayloadRow = z17.object({ payload: z17.string() });
|
|
3593
|
+
EventRow = z17.object({ sequence: z17.number(), recorded_at: z17.string(), payload: z17.string() });
|
|
3486
3594
|
SqliteSessionStore = class {
|
|
3487
3595
|
constructor(db) {
|
|
3488
3596
|
this.db = db;
|
|
@@ -3533,7 +3641,7 @@ var init_store = __esm({
|
|
|
3533
3641
|
const row = PayloadRow.parse(
|
|
3534
3642
|
this.db.prepare("SELECT payload FROM settings WHERE id = 'server-id'").get()
|
|
3535
3643
|
);
|
|
3536
|
-
return
|
|
3644
|
+
return z17.string().uuid().parse(JSON.parse(row.payload));
|
|
3537
3645
|
});
|
|
3538
3646
|
}
|
|
3539
3647
|
saveDevice(device, tokenHash) {
|
|
@@ -3621,7 +3729,7 @@ var init_store = __esm({
|
|
|
3621
3729
|
`).all();
|
|
3622
3730
|
const insert = this.db.prepare("INSERT OR IGNORE INTO projects(id, payload) VALUES (?, ?)");
|
|
3623
3731
|
for (const row of legacy) {
|
|
3624
|
-
const id =
|
|
3732
|
+
const id = z17.object({ id: z17.string() }).parse(row).id;
|
|
3625
3733
|
insert.run(
|
|
3626
3734
|
id,
|
|
3627
3735
|
JSON.stringify({ id, name: id, path: path9.join(root, id), available: false })
|
|
@@ -3831,7 +3939,7 @@ var init_store = __esm({
|
|
|
3831
3939
|
if (event.type === "message.user") this.claimAttachments(event.attachments ?? []);
|
|
3832
3940
|
const result = this.db.prepare("INSERT INTO events(session_id, recorded_at, payload) VALUES (?, ?, ?)").run(sessionId, recordedAt, JSON.stringify(event));
|
|
3833
3941
|
if (notification) {
|
|
3834
|
-
const count =
|
|
3942
|
+
const count = z17.object({ total: z17.number() }).parse(this.db.prepare("SELECT COUNT(*) AS total FROM push_outbox").get());
|
|
3835
3943
|
if (count.total >= 1e3)
|
|
3836
3944
|
this.db.prepare(
|
|
3837
3945
|
"DELETE FROM push_outbox WHERE rowid IN (SELECT rowid FROM push_outbox ORDER BY rowid LIMIT 1)"
|
|
@@ -3883,7 +3991,7 @@ var init_store = __esm({
|
|
|
3883
3991
|
for (const suffix of ["-wal", "-shm"])
|
|
3884
3992
|
if (existsSync2(filename + suffix)) chmodSync(filename + suffix, 384);
|
|
3885
3993
|
try {
|
|
3886
|
-
const schema =
|
|
3994
|
+
const schema = z17.object({ user_version: z17.number() }).parse(db.prepare("PRAGMA user_version").get());
|
|
3887
3995
|
if (schema.user_version > 1)
|
|
3888
3996
|
throw new Error("This database requires a newer Agentrific server.");
|
|
3889
3997
|
db.exec(`
|
|
@@ -4004,7 +4112,7 @@ import { homedir as homedir4 } from "node:os";
|
|
|
4004
4112
|
import path11 from "node:path";
|
|
4005
4113
|
import { WebSocket } from "ws";
|
|
4006
4114
|
import { Effect as Effect16, ManagedRuntime } from "effect";
|
|
4007
|
-
import { z as
|
|
4115
|
+
import { z as z18 } from "zod";
|
|
4008
4116
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
4009
4117
|
function publish(event, workflowUpdate = false) {
|
|
4010
4118
|
const previous = event.type === "session.updated" ? Effect16.runSync(store.get(event.session.id)) : void 0;
|
|
@@ -4495,7 +4603,7 @@ var init_index = __esm({
|
|
|
4495
4603
|
return project ?? reply.code(404).send({ error: "Project not found on this server." });
|
|
4496
4604
|
});
|
|
4497
4605
|
app.post("/api/projects/:id/reconnect", async (request, reply) => {
|
|
4498
|
-
const parsed =
|
|
4606
|
+
const parsed = z18.object({ path: z18.string().trim().min(1).max(4096) }).safeParse(request.body);
|
|
4499
4607
|
if (!parsed.success)
|
|
4500
4608
|
return reply.code(400).send({ error: "Enter the project's new folder path." });
|
|
4501
4609
|
const root = await Effect16.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
@@ -4627,9 +4735,9 @@ var init_index = __esm({
|
|
|
4627
4735
|
}
|
|
4628
4736
|
})
|
|
4629
4737
|
);
|
|
4630
|
-
HistoryQuery =
|
|
4631
|
-
after:
|
|
4632
|
-
limit:
|
|
4738
|
+
HistoryQuery = z18.object({
|
|
4739
|
+
after: z18.coerce.number().int().nonnegative().default(0),
|
|
4740
|
+
limit: z18.coerce.number().int().min(1).max(1e3).default(500)
|
|
4633
4741
|
});
|
|
4634
4742
|
app.get("/api/sessions/:id/events", async (request, reply) => {
|
|
4635
4743
|
const query = HistoryQuery.safeParse(request.query);
|
|
@@ -4648,7 +4756,7 @@ var init_index = __esm({
|
|
|
4648
4756
|
if (!result.session) return reply.code(result.status).send({ error: result.error });
|
|
4649
4757
|
return reply.code(201).send(result.session);
|
|
4650
4758
|
});
|
|
4651
|
-
StartTaskBody =
|
|
4759
|
+
StartTaskBody = z18.object({ prompt: z18.string().trim().max(2e4).optional() }).default({});
|
|
4652
4760
|
app.post("/api/tasks/:id/start", async (request, reply) => {
|
|
4653
4761
|
const body = StartTaskBody.safeParse(request.body ?? {});
|
|
4654
4762
|
if (!body.success) return reply.code(400).send({ error: "The task prompt is too long." });
|
|
@@ -4683,7 +4791,7 @@ var init_index = __esm({
|
|
|
4683
4791
|
return automation.configure(parsed.data);
|
|
4684
4792
|
});
|
|
4685
4793
|
app.patch("/api/automation/queue", async (request, reply) => {
|
|
4686
|
-
const parsed =
|
|
4794
|
+
const parsed = z18.object({ taskId: z18.string().min(1), beforeId: z18.string().min(1).nullable() }).safeParse(request.body);
|
|
4687
4795
|
if (!parsed.success)
|
|
4688
4796
|
return reply.code(400).send({ error: "Choose a task and a queue position." });
|
|
4689
4797
|
try {
|
|
@@ -4713,7 +4821,13 @@ var init_index = __esm({
|
|
|
4713
4821
|
attachments.resolve(session.projectId, parsed.data.attachmentIds ?? [])
|
|
4714
4822
|
);
|
|
4715
4823
|
await Effect16.runPromise(
|
|
4716
|
-
runtime.send(
|
|
4824
|
+
runtime.send(
|
|
4825
|
+
session,
|
|
4826
|
+
parsed.data.message,
|
|
4827
|
+
files,
|
|
4828
|
+
parsed.data.clientMessageId,
|
|
4829
|
+
parsed.data.mode
|
|
4830
|
+
)
|
|
4717
4831
|
);
|
|
4718
4832
|
return reply.code(202).send({ accepted: true });
|
|
4719
4833
|
} catch (error) {
|
|
@@ -4783,7 +4897,7 @@ var init_index = __esm({
|
|
|
4783
4897
|
|
|
4784
4898
|
// src/cli.ts
|
|
4785
4899
|
init_warnings();
|
|
4786
|
-
import { mkdir as
|
|
4900
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
4787
4901
|
import { homedir as homedir5 } from "node:os";
|
|
4788
4902
|
import path12 from "node:path";
|
|
4789
4903
|
import { fileURLToPath } from "node:url";
|
|
@@ -4884,7 +4998,7 @@ init_version();
|
|
|
4884
4998
|
init_pairing();
|
|
4885
4999
|
import { spawn as spawn2 } from "node:child_process";
|
|
4886
5000
|
import { parseArgs } from "node:util";
|
|
4887
|
-
import { z as
|
|
5001
|
+
import { z as z19 } from "zod";
|
|
4888
5002
|
|
|
4889
5003
|
// src/terminal.ts
|
|
4890
5004
|
init_pairing();
|
|
@@ -5365,10 +5479,10 @@ function startTerminal(dataDir, publicUrl, tunnel) {
|
|
|
5365
5479
|
|
|
5366
5480
|
// src/cli.ts
|
|
5367
5481
|
init_cli_output();
|
|
5368
|
-
var ConfigSchema =
|
|
5369
|
-
dataDir:
|
|
5370
|
-
projectsDir:
|
|
5371
|
-
publicUrl:
|
|
5482
|
+
var ConfigSchema = z19.object({
|
|
5483
|
+
dataDir: z19.string(),
|
|
5484
|
+
projectsDir: z19.string(),
|
|
5485
|
+
publicUrl: z19.string().url().optional()
|
|
5372
5486
|
});
|
|
5373
5487
|
function parseCommandArgs() {
|
|
5374
5488
|
return parseArgs({
|
|
@@ -5402,8 +5516,8 @@ async function main() {
|
|
|
5402
5516
|
const filename = path12.join(dataDir, "config.json");
|
|
5403
5517
|
const config = ConfigSchema.parse(
|
|
5404
5518
|
JSON.parse(
|
|
5405
|
-
await
|
|
5406
|
-
if (!
|
|
5519
|
+
await readFile6(filename, "utf8").catch((error) => {
|
|
5520
|
+
if (!z19.object({ code: z19.literal("ENOENT") }).safeParse(error).success) throw error;
|
|
5407
5521
|
return JSON.stringify({ dataDir, projectsDir: path12.join(homedir5(), "projects") });
|
|
5408
5522
|
})
|
|
5409
5523
|
)
|
|
@@ -5421,11 +5535,11 @@ async function main() {
|
|
|
5421
5535
|
throw new Error("Unknown command. Run agentrific --help.");
|
|
5422
5536
|
}
|
|
5423
5537
|
async function initCommand({ values, dataDir, filename, config }) {
|
|
5424
|
-
await
|
|
5538
|
+
await mkdir6(dataDir, { recursive: true, mode: 448 });
|
|
5425
5539
|
const publicUrl = values["public-url"] ?? config.publicUrl;
|
|
5426
5540
|
if (publicUrl && new URL(publicUrl).protocol !== "https:")
|
|
5427
5541
|
throw new Error("Configure an HTTPS URL from Tailscale Serve or Cloudflare Tunnel.");
|
|
5428
|
-
await
|
|
5542
|
+
await writeFile5(filename, JSON.stringify({ ...config, publicUrl }, null, 2) + "\n", {
|
|
5429
5543
|
mode: 384
|
|
5430
5544
|
});
|
|
5431
5545
|
console.log(`Configuration saved to ${filename}. Start with agentrific start.`);
|
|
@@ -5453,8 +5567,8 @@ async function startCommand({ values, dataDir, filename, config }) {
|
|
|
5453
5567
|
const publicUrl = connection.url;
|
|
5454
5568
|
try {
|
|
5455
5569
|
if (connection.remember && publicUrl) {
|
|
5456
|
-
await
|
|
5457
|
-
await
|
|
5570
|
+
await mkdir6(dataDir, { recursive: true, mode: 448 });
|
|
5571
|
+
await writeFile5(filename, JSON.stringify({ ...config, publicUrl }, null, 2) + "\n", {
|
|
5458
5572
|
mode: 384
|
|
5459
5573
|
});
|
|
5460
5574
|
}
|
|
@@ -5510,11 +5624,11 @@ async function pairCommand({ values, dataDir, config }) {
|
|
|
5510
5624
|
return;
|
|
5511
5625
|
}
|
|
5512
5626
|
async function maintenanceCommand({ values, positionals, dataDir }) {
|
|
5513
|
-
const action =
|
|
5627
|
+
const action = z19.enum(["enter", "resume", "drain"]).parse(positionals[1]);
|
|
5514
5628
|
const result = await controlRequest(dataDir, {
|
|
5515
5629
|
command: "maintenance",
|
|
5516
5630
|
action,
|
|
5517
|
-
timeoutMs:
|
|
5631
|
+
timeoutMs: z19.coerce.number().int().min(0).max(6e4).parse(values["timeout-ms"] ?? 3e4)
|
|
5518
5632
|
});
|
|
5519
5633
|
console.log(JSON.stringify(result, null, 2));
|
|
5520
5634
|
if (action === "drain" && "drained" in result && !result.drained) process.exitCode = 1;
|
|
@@ -5523,7 +5637,7 @@ async function maintenanceCommand({ values, positionals, dataDir }) {
|
|
|
5523
5637
|
async function devicesCommand({ dataDir, command }) {
|
|
5524
5638
|
console.log(
|
|
5525
5639
|
JSON.stringify(
|
|
5526
|
-
await controlRequest(dataDir, { command:
|
|
5640
|
+
await controlRequest(dataDir, { command: z19.enum(["devices", "status"]).parse(command) }),
|
|
5527
5641
|
null,
|
|
5528
5642
|
2
|
|
5529
5643
|
)
|
|
@@ -5531,7 +5645,7 @@ async function devicesCommand({ dataDir, command }) {
|
|
|
5531
5645
|
return;
|
|
5532
5646
|
}
|
|
5533
5647
|
async function revokeCommand({ positionals, dataDir }) {
|
|
5534
|
-
const id =
|
|
5648
|
+
const id = z19.string().uuid().parse(positionals[1]);
|
|
5535
5649
|
console.log(JSON.stringify(await controlRequest(dataDir, { command: "revoke", id })));
|
|
5536
5650
|
return;
|
|
5537
5651
|
}
|