@agentrific/server 0.3.0 → 0.4.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 +133 -72
- package/cli.js.map +4 -4
- package/index.js +126 -72
- package/index.js.map +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agentrific/server
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d5dd726: Add persistent project chat with native Board and Chat tabs, project titles and a Reset action that starts a fresh conversation while preserving history. Correct native route parameters, header spacing and composer placement above the keyboard and Android tab bar.
|
|
8
|
+
|
|
3
9
|
## 0.3.0
|
|
4
10
|
|
|
5
11
|
### Minor 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.4.0",
|
|
34
34
|
description: "Self-hosted control plane for long-running coding agents",
|
|
35
35
|
keywords: [
|
|
36
36
|
"agentrific",
|
|
@@ -345,6 +345,7 @@ var init_src = __esm({
|
|
|
345
345
|
SessionSchema = z2.object({
|
|
346
346
|
id: z2.string(),
|
|
347
347
|
taskId: z2.string().nullable().optional(),
|
|
348
|
+
kind: z2.enum(["task", "repo_chat"]).optional(),
|
|
348
349
|
projectId: z2.string(),
|
|
349
350
|
title: z2.string(),
|
|
350
351
|
status: SessionStatusSchema,
|
|
@@ -1833,7 +1834,9 @@ async function boardTasks(store2, projectId) {
|
|
|
1833
1834
|
status: taskStatusForSession(session.status, session)
|
|
1834
1835
|
};
|
|
1835
1836
|
});
|
|
1836
|
-
const legacyTasks = sessions.filter(
|
|
1837
|
+
const legacyTasks = sessions.filter(
|
|
1838
|
+
(session) => session.kind !== "repo_chat" && !session.taskId && !linkedSessionIds.has(session.id)
|
|
1839
|
+
).filter((session) => !projectId || session.projectId === projectId).map(sessionTask);
|
|
1837
1840
|
return [...linkedTasks, ...legacyTasks].map((task) => TaskSchema.parse(task)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1838
1841
|
}
|
|
1839
1842
|
async function findTask(store2, id) {
|
|
@@ -4121,6 +4124,44 @@ var init_worktree_cleanup = __esm({
|
|
|
4121
4124
|
}
|
|
4122
4125
|
});
|
|
4123
4126
|
|
|
4127
|
+
// src/repo-chat.ts
|
|
4128
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
4129
|
+
import { Effect as Effect16 } from "effect";
|
|
4130
|
+
function openRepoChat(store2, projectId, workspacePath, defaultModel, reset = false) {
|
|
4131
|
+
return withTaskLock(`repo-chat:${projectId}`, async () => {
|
|
4132
|
+
const existing = (await Effect16.runPromise(store2.list())).filter((session2) => session2.projectId === projectId && session2.kind === "repo_chat").sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0];
|
|
4133
|
+
if (existing && !reset) return existing;
|
|
4134
|
+
if (existing?.status === "running")
|
|
4135
|
+
throw new Error("Wait for the current reply before resetting chat.");
|
|
4136
|
+
const model = await defaultModel();
|
|
4137
|
+
if (!model) throw new Error("Choose an available default model in Settings before chatting.");
|
|
4138
|
+
const now = new Date(
|
|
4139
|
+
Math.max(Date.now(), existing ? Date.parse(existing.createdAt) + 1 : 0)
|
|
4140
|
+
).toISOString();
|
|
4141
|
+
const session = {
|
|
4142
|
+
id: randomUUID8(),
|
|
4143
|
+
projectId,
|
|
4144
|
+
kind: "repo_chat",
|
|
4145
|
+
title: workspacePath.split("/").at(-1) ?? projectId,
|
|
4146
|
+
status: "waiting_for_input",
|
|
4147
|
+
agent: "pi",
|
|
4148
|
+
model,
|
|
4149
|
+
branch: null,
|
|
4150
|
+
workspacePath,
|
|
4151
|
+
createdAt: now,
|
|
4152
|
+
updatedAt: now
|
|
4153
|
+
};
|
|
4154
|
+
await Effect16.runPromise(store2.append({ type: "session.updated", session }));
|
|
4155
|
+
return session;
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
var init_repo_chat = __esm({
|
|
4159
|
+
"src/repo-chat.ts"() {
|
|
4160
|
+
"use strict";
|
|
4161
|
+
init_tasks();
|
|
4162
|
+
}
|
|
4163
|
+
});
|
|
4164
|
+
|
|
4124
4165
|
// src/index.ts
|
|
4125
4166
|
var index_exports = {};
|
|
4126
4167
|
import Fastify from "fastify";
|
|
@@ -4128,18 +4169,18 @@ import pretty from "pino-pretty";
|
|
|
4128
4169
|
import { styleText as styleText4 } from "node:util";
|
|
4129
4170
|
import cors from "@fastify/cors";
|
|
4130
4171
|
import multipart from "@fastify/multipart";
|
|
4131
|
-
import { randomUUID as
|
|
4172
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
4132
4173
|
import { execFile as execFile3 } from "node:child_process";
|
|
4133
4174
|
import { promisify as promisify3 } from "node:util";
|
|
4134
4175
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
4135
4176
|
import { homedir as homedir4 } from "node:os";
|
|
4136
4177
|
import path11 from "node:path";
|
|
4137
4178
|
import { WebSocket } from "ws";
|
|
4138
|
-
import { Effect as
|
|
4179
|
+
import { Effect as Effect17, ManagedRuntime } from "effect";
|
|
4139
4180
|
import { z as z18 } from "zod";
|
|
4140
4181
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
4141
4182
|
function publish(event, workflowUpdate = false) {
|
|
4142
|
-
const previous = event.type === "session.updated" ?
|
|
4183
|
+
const previous = event.type === "session.updated" ? Effect17.runSync(store.get(event.session.id)) : void 0;
|
|
4143
4184
|
const emittedEvent = event.type === "session.updated" && previous && !workflowUpdate ? {
|
|
4144
4185
|
type: "session.updated",
|
|
4145
4186
|
session: {
|
|
@@ -4149,7 +4190,7 @@ function publish(event, workflowUpdate = false) {
|
|
|
4149
4190
|
}
|
|
4150
4191
|
} : event;
|
|
4151
4192
|
const notification = emittedEvent.type === "session.updated" ? push.pending(emittedEvent.session, previous) : void 0;
|
|
4152
|
-
const record =
|
|
4193
|
+
const record = Effect17.runSync(store.append(emittedEvent, notification));
|
|
4153
4194
|
const message = JSON.stringify({ type: "event", record });
|
|
4154
4195
|
for (const client of security.sockets.keys()) {
|
|
4155
4196
|
if (!(client instanceof WebSocket) || client.readyState !== WebSocket.OPEN) continue;
|
|
@@ -4189,8 +4230,8 @@ async function selectSessionModel(input, hasImages) {
|
|
|
4189
4230
|
}
|
|
4190
4231
|
}
|
|
4191
4232
|
async function resolveSessionProject(projectId) {
|
|
4192
|
-
const projectsRoot = await
|
|
4193
|
-
const project = (await
|
|
4233
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4234
|
+
const project = (await Effect17.runPromise(discoverProjects(projectsRoot, store))).find(
|
|
4194
4235
|
(item) => item.id === projectId && item.available
|
|
4195
4236
|
);
|
|
4196
4237
|
if (!project)
|
|
@@ -4232,7 +4273,7 @@ async function createAndStartSession(input) {
|
|
|
4232
4273
|
const projectPath = location.projectPath;
|
|
4233
4274
|
let files;
|
|
4234
4275
|
try {
|
|
4235
|
-
files = await
|
|
4276
|
+
files = await Effect17.runPromise(
|
|
4236
4277
|
attachments.resolve(input.projectId, input.attachmentIds ?? [])
|
|
4237
4278
|
);
|
|
4238
4279
|
} catch (error) {
|
|
@@ -4245,11 +4286,11 @@ async function createAndStartSession(input) {
|
|
|
4245
4286
|
if (selection.error !== void 0) return { error: selection.error, status: selection.status };
|
|
4246
4287
|
const selectedModel = selection.model;
|
|
4247
4288
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4248
|
-
const task = input.taskId ? await
|
|
4289
|
+
const task = input.taskId ? await Effect17.runPromise(store.getTask(input.taskId)) : void 0;
|
|
4249
4290
|
if (input.taskId && sessionTaskUnavailable(task, input.projectId))
|
|
4250
4291
|
return { error: "Task not found.", status: 404 };
|
|
4251
4292
|
const session = SessionSchema.parse({
|
|
4252
|
-
id:
|
|
4293
|
+
id: randomUUID9(),
|
|
4253
4294
|
taskId: input.taskId ?? null,
|
|
4254
4295
|
projectId: input.projectId,
|
|
4255
4296
|
title: task?.title ?? input.prompt.slice(0, 80),
|
|
@@ -4275,7 +4316,7 @@ async function createAndStartSession(input) {
|
|
|
4275
4316
|
);
|
|
4276
4317
|
} catch (error) {
|
|
4277
4318
|
if (task)
|
|
4278
|
-
await
|
|
4319
|
+
await Effect17.runPromise(
|
|
4279
4320
|
store.saveTask({ ...task, sessionId: session.id, updatedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4280
4321
|
);
|
|
4281
4322
|
return {
|
|
@@ -4284,7 +4325,7 @@ async function createAndStartSession(input) {
|
|
|
4284
4325
|
};
|
|
4285
4326
|
}
|
|
4286
4327
|
try {
|
|
4287
|
-
await
|
|
4328
|
+
await Effect17.runPromise(runtime.send(running, input.prompt, files));
|
|
4288
4329
|
} catch (error) {
|
|
4289
4330
|
publish({
|
|
4290
4331
|
type: "session.updated",
|
|
@@ -4293,18 +4334,18 @@ async function createAndStartSession(input) {
|
|
|
4293
4334
|
publish({ type: "error", sessionId: session.id, message: String(error) });
|
|
4294
4335
|
return { error: "Could not start the agent session.", status: 500 };
|
|
4295
4336
|
}
|
|
4296
|
-
return { session: await
|
|
4337
|
+
return { session: await Effect17.runPromise(store.get(session.id)), status: 201 };
|
|
4297
4338
|
}
|
|
4298
4339
|
function startTask(id, instructionsOverride, automated = false) {
|
|
4299
4340
|
return withTaskLock(
|
|
4300
4341
|
"agent-dispatch",
|
|
4301
4342
|
() => withTaskLock(id, async () => {
|
|
4302
4343
|
maintenance.assertReady();
|
|
4303
|
-
const task = await
|
|
4344
|
+
const task = await Effect17.runPromise(store.getTask(id));
|
|
4304
4345
|
if (!task || task.deletedAt) throw new TaskActionError("Task not found.", 404);
|
|
4305
4346
|
if (automated) {
|
|
4306
|
-
const state = await
|
|
4307
|
-
if (activeAgents(await
|
|
4347
|
+
const state = await Effect17.runPromise(store.automation());
|
|
4348
|
+
if (activeAgents(await Effect17.runPromise(store.list())) >= state.config.parallelism) return;
|
|
4308
4349
|
const backlog = await boardTasks(store);
|
|
4309
4350
|
if (!backlog.some((item) => item.id === id && item.status === "backlog")) return;
|
|
4310
4351
|
}
|
|
@@ -4312,7 +4353,7 @@ function startTask(id, instructionsOverride, automated = false) {
|
|
|
4312
4353
|
const prompt = instructions ? `${task.title}
|
|
4313
4354
|
|
|
4314
4355
|
${instructions}` : task.title;
|
|
4315
|
-
const linkedSession = task.sessionId ? await
|
|
4356
|
+
const linkedSession = task.sessionId ? await Effect17.runPromise(store.get(task.sessionId)) : void 0;
|
|
4316
4357
|
if (linkedSession && (["starting", "running", "queued"].includes(linkedSession.status) || linkedSession.gitDelivery?.status === "pending"))
|
|
4317
4358
|
throw new TaskActionError("This task already has a running agent.", 409);
|
|
4318
4359
|
const result = await createAndStartSession({
|
|
@@ -4326,7 +4367,7 @@ ${instructions}` : task.title;
|
|
|
4326
4367
|
});
|
|
4327
4368
|
if (!result.session)
|
|
4328
4369
|
throw new TaskActionError(result.error ?? "Could not start task.", result.status);
|
|
4329
|
-
const updatedTask = await
|
|
4370
|
+
const updatedTask = await Effect17.runPromise(
|
|
4330
4371
|
store.saveTask({
|
|
4331
4372
|
...task,
|
|
4332
4373
|
status: taskStatusForSession(result.session.status, result.session),
|
|
@@ -4339,7 +4380,7 @@ ${instructions}` : task.title;
|
|
|
4339
4380
|
);
|
|
4340
4381
|
}
|
|
4341
4382
|
async function withSessionTaskLock(id, operation) {
|
|
4342
|
-
const session = await
|
|
4383
|
+
const session = await Effect17.runPromise(store.get(id));
|
|
4343
4384
|
const lockId = session ? await sessionTaskLockId(store, session) : `session-${id}`;
|
|
4344
4385
|
return withTaskLock(lockId, operation);
|
|
4345
4386
|
}
|
|
@@ -4368,6 +4409,7 @@ var init_index = __esm({
|
|
|
4368
4409
|
init_store();
|
|
4369
4410
|
init_tasks();
|
|
4370
4411
|
init_worktree_cleanup();
|
|
4412
|
+
init_repo_chat();
|
|
4371
4413
|
host = process.env.AGENTRIFIC_HOST ?? "127.0.0.1";
|
|
4372
4414
|
port = Number(process.env.AGENTRIFIC_PORT ?? 4318);
|
|
4373
4415
|
defaultProjectsRoot = path11.resolve(process.env.AGENTRIFIC_PROJECTS_DIR ?? "./projects");
|
|
@@ -4399,7 +4441,7 @@ var init_index = __esm({
|
|
|
4399
4441
|
releaseLock = await acquireServerLock(dataRoot);
|
|
4400
4442
|
effects = ManagedRuntime.make(SessionStore.layer(path11.join(dataRoot, "sessions.sqlite")));
|
|
4401
4443
|
store = await effects.runPromise(SessionStore);
|
|
4402
|
-
security = new ServerSecurity(store, await
|
|
4444
|
+
security = new ServerSecurity(store, await Effect17.runPromise(store.serverIdentity()));
|
|
4403
4445
|
testPushUrl = process.env.AGENTRIFIC_PUSH_TEST_URL;
|
|
4404
4446
|
if (testPushUrl && (process.env.AGENTRIFIC_TEST_MODE !== "1" || !new URL(testPushUrl).hostname.endsWith(".ts.net") || new URL(testPushUrl).protocol !== "https:"))
|
|
4405
4447
|
throw new Error("Test relay requires explicit test mode and Tailscale HTTPS.");
|
|
@@ -4422,10 +4464,10 @@ var init_index = __esm({
|
|
|
4422
4464
|
Attachments.layer(path11.join(dataRoot, "attachments"), store)
|
|
4423
4465
|
);
|
|
4424
4466
|
attachments = await attachmentEffects.runPromise(Attachments);
|
|
4425
|
-
await
|
|
4467
|
+
await Effect17.runPromise(attachments.cleanup());
|
|
4426
4468
|
attachmentCleanup = setInterval(
|
|
4427
4469
|
() => {
|
|
4428
|
-
void
|
|
4470
|
+
void Effect17.runPromise(attachments.cleanup()).catch((error) => app.log.error(error));
|
|
4429
4471
|
},
|
|
4430
4472
|
60 * 60 * 1e3
|
|
4431
4473
|
);
|
|
@@ -4436,14 +4478,14 @@ var init_index = __esm({
|
|
|
4436
4478
|
app.post(
|
|
4437
4479
|
"/api/projects/:projectId/attachments",
|
|
4438
4480
|
async (request, reply) => {
|
|
4439
|
-
const root = await
|
|
4440
|
-
const projects = await
|
|
4481
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4482
|
+
const projects = await Effect17.runPromise(discoverProjects(root, store));
|
|
4441
4483
|
if (!projects.some((project) => project.id === request.params.projectId && project.available))
|
|
4442
4484
|
return reply.code(404).send({ error: "Project not found." });
|
|
4443
4485
|
try {
|
|
4444
4486
|
const file = await request.file();
|
|
4445
4487
|
if (!file) return reply.code(400).send({ error: "Choose a file to upload." });
|
|
4446
|
-
return reply.code(201).send(await
|
|
4488
|
+
return reply.code(201).send(await Effect17.runPromise(attachments.upload(request.params.projectId, file)));
|
|
4447
4489
|
} catch (error) {
|
|
4448
4490
|
return reply.code(400).send({ error: error instanceof Error ? error.message : "Upload failed." });
|
|
4449
4491
|
}
|
|
@@ -4452,7 +4494,7 @@ var init_index = __esm({
|
|
|
4452
4494
|
app.get(
|
|
4453
4495
|
"/api/projects/:projectId/attachments/:id/content",
|
|
4454
4496
|
async (request, reply) => {
|
|
4455
|
-
const attachment = await
|
|
4497
|
+
const attachment = await Effect17.runPromise(store.getAttachment(request.params.id));
|
|
4456
4498
|
if (!attachment || attachment.projectId !== request.params.projectId)
|
|
4457
4499
|
return reply.code(404).send({ error: "Attachment not found." });
|
|
4458
4500
|
const preview = request.query.preview === "1" && attachment.kind === "image";
|
|
@@ -4470,8 +4512,8 @@ var init_index = __esm({
|
|
|
4470
4512
|
publish: (event) => publish(event, true),
|
|
4471
4513
|
draft: deliveryWriter(models),
|
|
4472
4514
|
projectPath: async (session) => {
|
|
4473
|
-
const projectsRoot = await
|
|
4474
|
-
const project = (await
|
|
4515
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4516
|
+
const project = (await Effect17.runPromise(discoverProjects(projectsRoot, store))).find(
|
|
4475
4517
|
(item) => item.id === session.projectId && item.available
|
|
4476
4518
|
);
|
|
4477
4519
|
if (!project) throw new Error("Reconnect the project folder before delivering changes.");
|
|
@@ -4485,7 +4527,7 @@ var init_index = __esm({
|
|
|
4485
4527
|
publish,
|
|
4486
4528
|
worktreesRoot,
|
|
4487
4529
|
path11.join(dataRoot, "pi"),
|
|
4488
|
-
(sessionId, messageId) =>
|
|
4530
|
+
(sessionId, messageId) => Effect17.runSync(store.hasMessage(sessionId, messageId)),
|
|
4489
4531
|
(session) => delivery.settled(session.id)
|
|
4490
4532
|
)
|
|
4491
4533
|
);
|
|
@@ -4520,7 +4562,7 @@ var init_index = __esm({
|
|
|
4520
4562
|
() => maintenance.ready(),
|
|
4521
4563
|
(error) => app.log.error(error)
|
|
4522
4564
|
);
|
|
4523
|
-
for (const session of await
|
|
4565
|
+
for (const session of await Effect17.runPromise(store.list())) {
|
|
4524
4566
|
if (session.gitDelivery?.status === "pending") {
|
|
4525
4567
|
publish(
|
|
4526
4568
|
{
|
|
@@ -4550,7 +4592,7 @@ var init_index = __esm({
|
|
|
4550
4592
|
await delivery.recover();
|
|
4551
4593
|
app.get("/api/connections", async () => githubCliStatus());
|
|
4552
4594
|
app.get("/api/settings", async () => ({
|
|
4553
|
-
projectsRoot: await
|
|
4595
|
+
projectsRoot: await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot)),
|
|
4554
4596
|
defaultModel: await models.defaultIdentity()
|
|
4555
4597
|
}));
|
|
4556
4598
|
app.get("/api/models", async (_request, reply) => {
|
|
@@ -4578,7 +4620,7 @@ var init_index = __esm({
|
|
|
4578
4620
|
try {
|
|
4579
4621
|
const defaultModel = await models.setDefault(parsed.data);
|
|
4580
4622
|
return {
|
|
4581
|
-
projectsRoot: await
|
|
4623
|
+
projectsRoot: await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot)),
|
|
4582
4624
|
defaultModel
|
|
4583
4625
|
};
|
|
4584
4626
|
} catch {
|
|
@@ -4591,13 +4633,13 @@ var init_index = __esm({
|
|
|
4591
4633
|
"/api/sessions/:id/model",
|
|
4592
4634
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4593
4635
|
const parsed = ModelReferenceSchema.safeParse(request.body);
|
|
4594
|
-
const session = await
|
|
4636
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4595
4637
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4596
4638
|
if (!parsed.success)
|
|
4597
4639
|
return reply.code(400).send({ error: "Choose a model from the server catalogue." });
|
|
4598
4640
|
try {
|
|
4599
4641
|
await models.resolve(parsed.data);
|
|
4600
|
-
return await
|
|
4642
|
+
return await Effect17.runPromise(runtime.setModel(session, parsed.data));
|
|
4601
4643
|
} catch {
|
|
4602
4644
|
return reply.code(409).send({
|
|
4603
4645
|
error: "Could not change the model. Wait for the current reply to finish and choose an available model."
|
|
@@ -4608,21 +4650,21 @@ var init_index = __esm({
|
|
|
4608
4650
|
app.post("/api/settings", async (request, reply) => {
|
|
4609
4651
|
const parsed = UpdateSettingsSchema.safeParse(request.body);
|
|
4610
4652
|
if (!parsed.success) return reply.code(400).send({ error: "Enter a projects folder." });
|
|
4611
|
-
const result = await
|
|
4612
|
-
resolveProjectsRoot(parsed.data.projectsRoot).pipe(
|
|
4653
|
+
const result = await Effect17.runPromise(
|
|
4654
|
+
resolveProjectsRoot(parsed.data.projectsRoot).pipe(Effect17.result)
|
|
4613
4655
|
);
|
|
4614
4656
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4615
|
-
return
|
|
4657
|
+
return Effect17.runPromise(store.setProjectsRoot(result.success));
|
|
4616
4658
|
});
|
|
4617
4659
|
app.get("/api/projects", async (_request, reply) => {
|
|
4618
|
-
const root = await
|
|
4619
|
-
const result = await
|
|
4660
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4661
|
+
const result = await Effect17.runPromise(discoverProjects(root, store).pipe(Effect17.result));
|
|
4620
4662
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4621
4663
|
return result.success;
|
|
4622
4664
|
});
|
|
4623
4665
|
app.get("/api/projects/:id", async (request, reply) => {
|
|
4624
|
-
const root = await
|
|
4625
|
-
const result = await
|
|
4666
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4667
|
+
const result = await Effect17.runPromise(discoverProjects(root, store).pipe(Effect17.result));
|
|
4626
4668
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4627
4669
|
const project = result.success.find((item) => item.id === request.params.id);
|
|
4628
4670
|
return project ?? reply.code(404).send({ error: "Project not found on this server." });
|
|
@@ -4631,13 +4673,13 @@ var init_index = __esm({
|
|
|
4631
4673
|
const parsed = z18.object({ path: z18.string().trim().min(1).max(4096) }).safeParse(request.body);
|
|
4632
4674
|
if (!parsed.success)
|
|
4633
4675
|
return reply.code(400).send({ error: "Enter the project's new folder path." });
|
|
4634
|
-
const root = await
|
|
4635
|
-
const result = await
|
|
4636
|
-
reconnectProject(root, request.params.id, parsed.data.path, store).pipe(
|
|
4676
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4677
|
+
const result = await Effect17.runPromise(
|
|
4678
|
+
reconnectProject(root, request.params.id, parsed.data.path, store).pipe(Effect17.result)
|
|
4637
4679
|
);
|
|
4638
4680
|
return result._tag === "Failure" ? reply.code(400).send({ error: result.failure.message }) : result.success;
|
|
4639
4681
|
});
|
|
4640
|
-
app.get("/api/sessions", () =>
|
|
4682
|
+
app.get("/api/sessions", () => Effect17.runPromise(store.list()));
|
|
4641
4683
|
app.get(
|
|
4642
4684
|
"/api/tasks",
|
|
4643
4685
|
(request) => boardTasks(store, request.query.projectId)
|
|
@@ -4650,15 +4692,15 @@ var init_index = __esm({
|
|
|
4650
4692
|
if (!github.githubCli.authenticated)
|
|
4651
4693
|
return reply.code(400).send({ error: "GitHub CLI must be authenticated on the server to open a PR." });
|
|
4652
4694
|
}
|
|
4653
|
-
const projectsRoot = await
|
|
4654
|
-
const projects = await
|
|
4655
|
-
discoverProjects(projectsRoot, store).pipe(
|
|
4695
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4696
|
+
const projects = await Effect17.runPromise(
|
|
4697
|
+
discoverProjects(projectsRoot, store).pipe(Effect17.result)
|
|
4656
4698
|
);
|
|
4657
4699
|
if (projects._tag === "Failure" || !projects.success.some((project) => project.id === parsed.data.projectId && project.available))
|
|
4658
4700
|
return reply.code(404).send({ error: "Project not found in the server's projects folder." });
|
|
4659
4701
|
let files;
|
|
4660
4702
|
try {
|
|
4661
|
-
files = await
|
|
4703
|
+
files = await Effect17.runPromise(
|
|
4662
4704
|
attachments.resolve(parsed.data.projectId, parsed.data.attachmentIds ?? [])
|
|
4663
4705
|
);
|
|
4664
4706
|
} catch (error) {
|
|
@@ -4666,7 +4708,7 @@ var init_index = __esm({
|
|
|
4666
4708
|
}
|
|
4667
4709
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4668
4710
|
const task = TaskSchema.parse({
|
|
4669
|
-
id:
|
|
4711
|
+
id: randomUUID9(),
|
|
4670
4712
|
...parsed.data,
|
|
4671
4713
|
attachments: files,
|
|
4672
4714
|
status: "backlog",
|
|
@@ -4674,7 +4716,7 @@ var init_index = __esm({
|
|
|
4674
4716
|
createdAt: now,
|
|
4675
4717
|
updatedAt: now
|
|
4676
4718
|
});
|
|
4677
|
-
return reply.code(201).send(await
|
|
4719
|
+
return reply.code(201).send(await Effect17.runPromise(store.saveTask(task)));
|
|
4678
4720
|
});
|
|
4679
4721
|
app.patch("/api/tasks/:id", async (request, reply) => {
|
|
4680
4722
|
const parsed = UpdateTaskSchema.safeParse(request.body);
|
|
@@ -4689,8 +4731,8 @@ var init_index = __esm({
|
|
|
4689
4731
|
return reply.code(400).send({ error: "GitHub CLI must be authenticated on the server to open a PR." });
|
|
4690
4732
|
}
|
|
4691
4733
|
return await editTask(store, request.params.id, parsed.data, async (projectId, ids) => {
|
|
4692
|
-
const result = await
|
|
4693
|
-
attachments.resolve(projectId, ids).pipe(
|
|
4734
|
+
const result = await Effect17.runPromise(
|
|
4735
|
+
attachments.resolve(projectId, ids).pipe(Effect17.result)
|
|
4694
4736
|
);
|
|
4695
4737
|
if (result._tag === "Failure") throw new TaskActionError(result.failure.message, 400);
|
|
4696
4738
|
return result.success;
|
|
@@ -4704,11 +4746,11 @@ var init_index = __esm({
|
|
|
4704
4746
|
app.delete("/api/tasks/:id", async (request, reply) => {
|
|
4705
4747
|
try {
|
|
4706
4748
|
await deleteTask(store, request.params.id, async (session) => {
|
|
4707
|
-
await
|
|
4749
|
+
await Effect17.runPromise(runtime.forget(session.id));
|
|
4708
4750
|
await worktreeCleanup.remove(session);
|
|
4709
4751
|
await removeRunFiles(dataRoot, session.id);
|
|
4710
4752
|
});
|
|
4711
|
-
await
|
|
4753
|
+
await Effect17.runPromise(attachments.cleanup());
|
|
4712
4754
|
return reply.code(204).send();
|
|
4713
4755
|
} catch (error) {
|
|
4714
4756
|
if (error instanceof TaskActionError)
|
|
@@ -4717,14 +4759,14 @@ var init_index = __esm({
|
|
|
4717
4759
|
}
|
|
4718
4760
|
});
|
|
4719
4761
|
app.get("/api/sessions/:id", async (request, reply) => {
|
|
4720
|
-
const session = await
|
|
4762
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4721
4763
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4722
4764
|
return session;
|
|
4723
4765
|
});
|
|
4724
4766
|
app.post(
|
|
4725
4767
|
"/api/sessions/:id/delivery",
|
|
4726
4768
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4727
|
-
const session = await
|
|
4769
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4728
4770
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4729
4771
|
try {
|
|
4730
4772
|
const result = await delivery.retry(session.id);
|
|
@@ -4741,10 +4783,10 @@ var init_index = __esm({
|
|
|
4741
4783
|
try {
|
|
4742
4784
|
const prepared = await preparation.retry(request.params.id);
|
|
4743
4785
|
preparedSession = prepared.session;
|
|
4744
|
-
const files = await
|
|
4786
|
+
const files = await Effect17.runPromise(
|
|
4745
4787
|
attachments.resolve(prepared.session.projectId, prepared.input.attachmentIds ?? [])
|
|
4746
4788
|
);
|
|
4747
|
-
await
|
|
4789
|
+
await Effect17.runPromise(runtime.send(prepared.session, prepared.input.prompt, files));
|
|
4748
4790
|
return reply.code(202).send(prepared.session);
|
|
4749
4791
|
} catch (error) {
|
|
4750
4792
|
if (preparedSession) {
|
|
@@ -4767,9 +4809,28 @@ var init_index = __esm({
|
|
|
4767
4809
|
app.get("/api/sessions/:id/events", async (request, reply) => {
|
|
4768
4810
|
const query = HistoryQuery.safeParse(request.query);
|
|
4769
4811
|
if (!query.success) return reply.code(400).send({ error: "Invalid history cursor or limit." });
|
|
4770
|
-
const session = await
|
|
4812
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4771
4813
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4772
|
-
return
|
|
4814
|
+
return Effect17.runPromise(store.history(session.id, query.data.after, query.data.limit));
|
|
4815
|
+
});
|
|
4816
|
+
app.post("/api/projects/:id/chat", async (request, reply) => {
|
|
4817
|
+
maintenance.assertReady();
|
|
4818
|
+
const location = await resolveSessionProject(request.params.id);
|
|
4819
|
+
if (location.error !== void 0)
|
|
4820
|
+
return reply.code(location.status).send({ error: location.error });
|
|
4821
|
+
const parsed = z18.object({ reset: z18.boolean().optional() }).safeParse(request.body ?? {});
|
|
4822
|
+
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
4823
|
+
try {
|
|
4824
|
+
return await openRepoChat(
|
|
4825
|
+
store,
|
|
4826
|
+
request.params.id,
|
|
4827
|
+
location.projectPath,
|
|
4828
|
+
() => models.defaultIdentity(),
|
|
4829
|
+
parsed.data.reset
|
|
4830
|
+
);
|
|
4831
|
+
} catch (error) {
|
|
4832
|
+
return reply.code(409).send({ error: String(error) });
|
|
4833
|
+
}
|
|
4773
4834
|
});
|
|
4774
4835
|
app.post("/api/sessions", async (request, reply) => {
|
|
4775
4836
|
const parsed = CreateSessionSchema.safeParse(request.body);
|
|
@@ -4805,8 +4866,8 @@ var init_index = __esm({
|
|
|
4805
4866
|
const parsed = AutomationConfigSchema.safeParse(request.body);
|
|
4806
4867
|
if (!parsed.success)
|
|
4807
4868
|
return reply.code(400).send({ error: parsed.error.issues.map((issue) => issue.message).join(" ") });
|
|
4808
|
-
const root = await
|
|
4809
|
-
const projects = await
|
|
4869
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4870
|
+
const projects = await Effect17.runPromise(discoverProjects(root, store));
|
|
4810
4871
|
if (parsed.data.projectIds.some(
|
|
4811
4872
|
(id) => !projects.some((project) => project.id === id && project.available)
|
|
4812
4873
|
))
|
|
@@ -4834,7 +4895,7 @@ var init_index = __esm({
|
|
|
4834
4895
|
app.post(
|
|
4835
4896
|
"/api/sessions/:id/messages",
|
|
4836
4897
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4837
|
-
const session = await
|
|
4898
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4838
4899
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4839
4900
|
const parsed = SendMessageSchema.safeParse(request.body);
|
|
4840
4901
|
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
@@ -4842,10 +4903,10 @@ var init_index = __esm({
|
|
|
4842
4903
|
await delivery.assertCanWork(session.id);
|
|
4843
4904
|
if (session.preparation && session.preparation.status !== "ready")
|
|
4844
4905
|
throw new Error("Retry workspace preparation before sending a message.");
|
|
4845
|
-
const files = await
|
|
4906
|
+
const files = await Effect17.runPromise(
|
|
4846
4907
|
attachments.resolve(session.projectId, parsed.data.attachmentIds ?? [])
|
|
4847
4908
|
);
|
|
4848
|
-
await
|
|
4909
|
+
await Effect17.runPromise(
|
|
4849
4910
|
runtime.send(
|
|
4850
4911
|
session,
|
|
4851
4912
|
parsed.data.message,
|
|
@@ -4863,15 +4924,15 @@ var init_index = __esm({
|
|
|
4863
4924
|
app.post(
|
|
4864
4925
|
"/api/sessions/:id/stop",
|
|
4865
4926
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4866
|
-
const session = await
|
|
4927
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4867
4928
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4868
|
-
await
|
|
4929
|
+
await Effect17.runPromise(runtime.stop(session));
|
|
4869
4930
|
return reply.code(202).send({ accepted: true });
|
|
4870
4931
|
})
|
|
4871
4932
|
);
|
|
4872
4933
|
attachEventServer(app, security, allowedOrigins, () => ({
|
|
4873
4934
|
type: "snapshot",
|
|
4874
|
-
sessions:
|
|
4935
|
+
sessions: Effect17.runSync(store.list())
|
|
4875
4936
|
}));
|
|
4876
4937
|
closeControl = await startControl(
|
|
4877
4938
|
dataRoot,
|
|
@@ -4883,7 +4944,7 @@ var init_index = __esm({
|
|
|
4883
4944
|
connectedDevices: new Set(security.sockets.values()).size,
|
|
4884
4945
|
connectedSockets: security.sockets.size,
|
|
4885
4946
|
memory: process.memoryUsage().rss,
|
|
4886
|
-
activeRuns: activeAgents(
|
|
4947
|
+
activeRuns: activeAgents(Effect17.runSync(store.list())),
|
|
4887
4948
|
maintenance: maintenance.status()
|
|
4888
4949
|
}),
|
|
4889
4950
|
maintenance
|
|
@@ -4901,7 +4962,7 @@ var init_index = __esm({
|
|
|
4901
4962
|
clearInterval(automationTimer);
|
|
4902
4963
|
await withTaskLock("automation", async () => {
|
|
4903
4964
|
});
|
|
4904
|
-
await
|
|
4965
|
+
await Effect17.runPromise(runtime.close());
|
|
4905
4966
|
await agentEffects.dispose();
|
|
4906
4967
|
await attachmentEffects.dispose();
|
|
4907
4968
|
await effects.dispose();
|