@agentrific/server 0.4.0 → 0.5.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 +8 -0
- package/cli.js +208 -38
- package/cli.js.map +4 -4
- package/index.js +199 -37
- package/index.js.map +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @agentrific/server
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fbb6281: Group matching repositories across connected servers while keeping each task and chat associated with its server. Show repository identity and Git status, support explicit checkout association for older servers, and choose a task server directly in the native composer. Allow never-run backlog tasks to move safely between compatible servers.
|
|
8
|
+
|
|
9
|
+
Preserve native navigation and floating glass tabs, center and truncate conversation titles, show compact status dots, and improve navigation performance. Fix native task editor character loss, server picker alignment, and tool-call grid sizing.
|
|
10
|
+
|
|
3
11
|
## 0.4.0
|
|
4
12
|
|
|
5
13
|
### 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.5.0",
|
|
34
34
|
description: "Self-hosted control plane for long-running coding agents",
|
|
35
35
|
keywords: [
|
|
36
36
|
"agentrific",
|
|
@@ -246,7 +246,7 @@ function createPairingLink(payload) {
|
|
|
246
246
|
link.searchParams.set("data", JSON.stringify(PairingPayloadSchema.parse(payload)));
|
|
247
247
|
return link.toString();
|
|
248
248
|
}
|
|
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
|
+
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, RepositoryIdentitySchema, ProjectGitStatusSchema, ProjectSchema, TaskStatusSchema, TaskSchema, CreateTaskSchema, UpdateTaskSchema, PROTOCOL_VERSION, ServerUpdateStatusSchema, ServerHealthSchema, PairingPayloadSchema, PairingInvitationSchema, PairingRequestSchema, DeviceSchema, PairingResultSchema, HandshakeSchema, HandshakeAckSchema;
|
|
250
250
|
var init_src = __esm({
|
|
251
251
|
"../../packages/contracts/src/index.ts"() {
|
|
252
252
|
cronBounds = [
|
|
@@ -476,11 +476,25 @@ var init_src = __esm({
|
|
|
476
476
|
githubCli: z2.object({ installed: z2.boolean(), authenticated: z2.boolean() })
|
|
477
477
|
});
|
|
478
478
|
UpdateSettingsSchema = z2.object({ projectsRoot: z2.string().trim().min(1).max(4096) });
|
|
479
|
+
RepositoryIdentitySchema = z2.object({
|
|
480
|
+
key: z2.string(),
|
|
481
|
+
display: z2.string()
|
|
482
|
+
});
|
|
483
|
+
ProjectGitStatusSchema = z2.object({
|
|
484
|
+
observedAt: z2.string().datetime(),
|
|
485
|
+
state: z2.enum(["available", "unavailable"]),
|
|
486
|
+
repository: RepositoryIdentitySchema.nullable(),
|
|
487
|
+
head: z2.string().nullable(),
|
|
488
|
+
branch: z2.string().nullable(),
|
|
489
|
+
trackedChanges: z2.number().int().nonnegative(),
|
|
490
|
+
untrackedChanges: z2.number().int().nonnegative()
|
|
491
|
+
});
|
|
479
492
|
ProjectSchema = z2.object({
|
|
480
493
|
id: z2.string(),
|
|
481
494
|
name: z2.string(),
|
|
482
495
|
path: z2.string(),
|
|
483
|
-
available: z2.boolean().default(true)
|
|
496
|
+
available: z2.boolean().default(true),
|
|
497
|
+
repository: RepositoryIdentitySchema.nullable().optional()
|
|
484
498
|
});
|
|
485
499
|
TaskStatusSchema = z2.enum(["backlog", "needs_user_input", "running", "done"]);
|
|
486
500
|
TaskSchema = z2.object({
|
|
@@ -499,6 +513,7 @@ var init_src = __esm({
|
|
|
499
513
|
deletedAt: z2.string().datetime().optional()
|
|
500
514
|
});
|
|
501
515
|
CreateTaskSchema = z2.object({
|
|
516
|
+
clientTaskId: z2.string().min(1).max(128).regex(/^[a-zA-Z0-9_-]+$/).optional(),
|
|
502
517
|
projectId: z2.string().min(1),
|
|
503
518
|
title: z2.string().trim().min(1).max(120),
|
|
504
519
|
prompt: z2.string().trim().max(2e4).default(""),
|
|
@@ -1871,7 +1886,7 @@ function editTask(store2, id, input, resolveAttachments) {
|
|
|
1871
1886
|
});
|
|
1872
1887
|
}
|
|
1873
1888
|
function deleteTask(store2, id, cleanup = async () => {
|
|
1874
|
-
}) {
|
|
1889
|
+
}, unrunUpdatedAt) {
|
|
1875
1890
|
return withTaskLock(id, async () => {
|
|
1876
1891
|
const task = await findTask(store2, id);
|
|
1877
1892
|
const sessions = await Effect6.runPromise(store2.list());
|
|
@@ -1882,6 +1897,8 @@ function deleteTask(store2, id, cleanup = async () => {
|
|
|
1882
1897
|
const associated = sessions.filter(
|
|
1883
1898
|
(session) => session.taskId === task.id || session.id === task.sessionId
|
|
1884
1899
|
);
|
|
1900
|
+
if (unrunUpdatedAt !== void 0 && (task.updatedAt !== unrunUpdatedAt || task.sessionId || associated.length || task.status !== "backlog"))
|
|
1901
|
+
throw new TaskActionError("This task changed or has already run. It cannot be moved.", 409);
|
|
1885
1902
|
for (const session of associated) await cleanup(session);
|
|
1886
1903
|
await Effect6.runPromise(
|
|
1887
1904
|
store2.purgeTask(
|
|
@@ -2320,7 +2337,7 @@ import { promisify as promisify2, stripVTControlCharacters } from "node:util";
|
|
|
2320
2337
|
function workflowLog(text) {
|
|
2321
2338
|
return stripVTControlCharacters(text).slice(-32e3);
|
|
2322
2339
|
}
|
|
2323
|
-
|
|
2340
|
+
function workflowEnvironment() {
|
|
2324
2341
|
const env = { ...process.env };
|
|
2325
2342
|
for (const name of [
|
|
2326
2343
|
"GIT_DIR",
|
|
@@ -2340,6 +2357,10 @@ async function workflowCommand(cwd, command, args, signal, managedPreparation =
|
|
|
2340
2357
|
"GIT_REPLACE_REF_BASE"
|
|
2341
2358
|
])
|
|
2342
2359
|
delete env[name];
|
|
2360
|
+
return env;
|
|
2361
|
+
}
|
|
2362
|
+
async function workflowCommand(cwd, command, args, signal, managedPreparation = false) {
|
|
2363
|
+
const env = workflowEnvironment();
|
|
2343
2364
|
if (managedPreparation) env.AGENTRIFIC_MANAGED_PREPARATION = "1";
|
|
2344
2365
|
const result = await exec(command, args, {
|
|
2345
2366
|
cwd,
|
|
@@ -3573,6 +3594,113 @@ var init_projects = __esm({
|
|
|
3573
3594
|
}
|
|
3574
3595
|
});
|
|
3575
3596
|
|
|
3597
|
+
// src/project-git.ts
|
|
3598
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
3599
|
+
import { promisify as promisify3 } from "node:util";
|
|
3600
|
+
function repositoryIdentity(remote) {
|
|
3601
|
+
const input = remote.trim();
|
|
3602
|
+
if (!input || input.startsWith("/") || input.startsWith(".")) return null;
|
|
3603
|
+
const scp = /^(?:[^@/\s]+@)?([^/:\s]+):([^\s]+)$/.exec(input);
|
|
3604
|
+
let url;
|
|
3605
|
+
try {
|
|
3606
|
+
url = new URL(scp && !input.includes("://") ? `ssh://${scp[1]}/${scp[2]}` : input);
|
|
3607
|
+
} catch {
|
|
3608
|
+
return null;
|
|
3609
|
+
}
|
|
3610
|
+
if (!["https:", "http:", "ssh:", "git:"].includes(url.protocol)) return null;
|
|
3611
|
+
if (url.search || url.hash || url.port) return null;
|
|
3612
|
+
const host2 = url.hostname.toLowerCase();
|
|
3613
|
+
let path13 = url.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "");
|
|
3614
|
+
if (!host2 || !path13 || path13.includes("%") || path13.includes("//")) return null;
|
|
3615
|
+
if (["github.com", "bitbucket.org"].includes(host2)) path13 = path13.toLowerCase();
|
|
3616
|
+
const key = `${host2}/${path13}`;
|
|
3617
|
+
return { key, display: key };
|
|
3618
|
+
}
|
|
3619
|
+
async function git(directory, args) {
|
|
3620
|
+
const { stdout } = await execute3("git", ["-C", directory, ...args], {
|
|
3621
|
+
timeout: 3e3,
|
|
3622
|
+
maxBuffer: 1024 * 1024,
|
|
3623
|
+
env: { ...workflowEnvironment(), GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" }
|
|
3624
|
+
});
|
|
3625
|
+
return stdout.trim();
|
|
3626
|
+
}
|
|
3627
|
+
async function readRepositoryIdentity(directory) {
|
|
3628
|
+
try {
|
|
3629
|
+
const remotes = (await git(directory, ["remote"])).split("\n").filter(Boolean);
|
|
3630
|
+
const remote = remotes.includes("origin") ? "origin" : remotes.length === 1 ? remotes[0] : null;
|
|
3631
|
+
if (!remote) return null;
|
|
3632
|
+
return repositoryIdentity(await git(directory, ["remote", "get-url", remote]));
|
|
3633
|
+
} catch {
|
|
3634
|
+
return null;
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
async function inspect(directory) {
|
|
3638
|
+
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3639
|
+
try {
|
|
3640
|
+
const [repository, head, branch, status] = await Promise.all([
|
|
3641
|
+
readRepositoryIdentity(directory),
|
|
3642
|
+
git(directory, ["rev-parse", "--verify", "HEAD"]),
|
|
3643
|
+
git(directory, ["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => null),
|
|
3644
|
+
// NUL-separated records avoid ambiguous filenames/newlines. --no-renames
|
|
3645
|
+
// keeps each entry one record, so we only need its two status characters.
|
|
3646
|
+
execute3(
|
|
3647
|
+
"git",
|
|
3648
|
+
[
|
|
3649
|
+
"-C",
|
|
3650
|
+
directory,
|
|
3651
|
+
"status",
|
|
3652
|
+
"--porcelain=v1",
|
|
3653
|
+
"-z",
|
|
3654
|
+
"--no-renames",
|
|
3655
|
+
"--untracked-files=normal"
|
|
3656
|
+
],
|
|
3657
|
+
{
|
|
3658
|
+
timeout: 3e3,
|
|
3659
|
+
maxBuffer: 1024 * 1024,
|
|
3660
|
+
env: { ...workflowEnvironment(), GIT_OPTIONAL_LOCKS: "0" }
|
|
3661
|
+
}
|
|
3662
|
+
)
|
|
3663
|
+
]);
|
|
3664
|
+
const records = status.stdout.split("\0").filter(Boolean);
|
|
3665
|
+
return {
|
|
3666
|
+
observedAt,
|
|
3667
|
+
state: "available",
|
|
3668
|
+
repository,
|
|
3669
|
+
head,
|
|
3670
|
+
branch,
|
|
3671
|
+
trackedChanges: records.filter((record) => !record.startsWith("??")).length,
|
|
3672
|
+
untrackedChanges: records.filter((record) => record.startsWith("??")).length
|
|
3673
|
+
};
|
|
3674
|
+
} catch {
|
|
3675
|
+
return {
|
|
3676
|
+
observedAt,
|
|
3677
|
+
state: "unavailable",
|
|
3678
|
+
repository: null,
|
|
3679
|
+
head: null,
|
|
3680
|
+
branch: null,
|
|
3681
|
+
trackedChanges: 0,
|
|
3682
|
+
untrackedChanges: 0
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
function projectGitStatus(directory) {
|
|
3687
|
+
const cached = observations.get(directory);
|
|
3688
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
3689
|
+
if (observations.size >= 200) observations.delete(observations.keys().next().value);
|
|
3690
|
+
const value = inspect(directory);
|
|
3691
|
+
observations.set(directory, { expires: Date.now() + 15e3, value });
|
|
3692
|
+
return value;
|
|
3693
|
+
}
|
|
3694
|
+
var execute3, observations;
|
|
3695
|
+
var init_project_git = __esm({
|
|
3696
|
+
"src/project-git.ts"() {
|
|
3697
|
+
"use strict";
|
|
3698
|
+
init_workflow_command();
|
|
3699
|
+
execute3 = promisify3(execFile3);
|
|
3700
|
+
observations = /* @__PURE__ */ new Map();
|
|
3701
|
+
}
|
|
3702
|
+
});
|
|
3703
|
+
|
|
3576
3704
|
// src/push-schema.ts
|
|
3577
3705
|
import { z as z16 } from "zod";
|
|
3578
3706
|
var PushBindingSchema, PushStateSchema, PendingPushSchema;
|
|
@@ -4170,8 +4298,8 @@ import { styleText as styleText4 } from "node:util";
|
|
|
4170
4298
|
import cors from "@fastify/cors";
|
|
4171
4299
|
import multipart from "@fastify/multipart";
|
|
4172
4300
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
4173
|
-
import { execFile as
|
|
4174
|
-
import { promisify as
|
|
4301
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
4302
|
+
import { promisify as promisify4 } from "node:util";
|
|
4175
4303
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
4176
4304
|
import { homedir as homedir4 } from "node:os";
|
|
4177
4305
|
import path11 from "node:path";
|
|
@@ -4406,6 +4534,7 @@ var init_index = __esm({
|
|
|
4406
4534
|
init_models();
|
|
4407
4535
|
init_pi();
|
|
4408
4536
|
init_projects();
|
|
4537
|
+
init_project_git();
|
|
4409
4538
|
init_store();
|
|
4410
4539
|
init_tasks();
|
|
4411
4540
|
init_worktree_cleanup();
|
|
@@ -4416,7 +4545,7 @@ var init_index = __esm({
|
|
|
4416
4545
|
worktreesRoot = path11.resolve(
|
|
4417
4546
|
process.env.AGENTRIFIC_WORKTREES_DIR ?? path11.join(homedir4(), ".agentrific", "workspaces")
|
|
4418
4547
|
);
|
|
4419
|
-
execFileAsync =
|
|
4548
|
+
execFileAsync = promisify4(execFile4);
|
|
4420
4549
|
if (host !== "127.0.0.1" && host !== "::1")
|
|
4421
4550
|
throw new Error("Bind Agentrific to loopback and expose it with Tailscale Serve or cloudflared.");
|
|
4422
4551
|
app = Fastify({
|
|
@@ -4660,7 +4789,24 @@ var init_index = __esm({
|
|
|
4660
4789
|
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4661
4790
|
const result = await Effect17.runPromise(discoverProjects(root, store).pipe(Effect17.result));
|
|
4662
4791
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4663
|
-
return
|
|
4792
|
+
return Effect17.runPromise(
|
|
4793
|
+
Effect17.forEach(
|
|
4794
|
+
result.success,
|
|
4795
|
+
(project) => Effect17.promise(async () => ({
|
|
4796
|
+
...project,
|
|
4797
|
+
repository: project.available ? await readRepositoryIdentity(project.path) : null
|
|
4798
|
+
})),
|
|
4799
|
+
{ concurrency: 4 }
|
|
4800
|
+
)
|
|
4801
|
+
);
|
|
4802
|
+
});
|
|
4803
|
+
app.get("/api/projects/:id/git-status", async (request, reply) => {
|
|
4804
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4805
|
+
const projects = await Effect17.runPromise(discoverProjects(root, store));
|
|
4806
|
+
const project = projects.find((item) => item.id === request.params.id);
|
|
4807
|
+
if (!project?.available)
|
|
4808
|
+
return reply.code(404).send({ error: "Project folder unavailable on this server." });
|
|
4809
|
+
return projectGitStatus(project.path);
|
|
4664
4810
|
});
|
|
4665
4811
|
app.get("/api/projects/:id", async (request, reply) => {
|
|
4666
4812
|
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
@@ -4684,39 +4830,51 @@ var init_index = __esm({
|
|
|
4684
4830
|
"/api/tasks",
|
|
4685
4831
|
(request) => boardTasks(store, request.query.projectId)
|
|
4686
4832
|
);
|
|
4833
|
+
app.get("/api/task-moves", () => ({ supported: true }));
|
|
4687
4834
|
app.post("/api/tasks", async (request, reply) => {
|
|
4688
4835
|
const parsed = CreateTaskSchema.safeParse(request.body);
|
|
4689
4836
|
if (!parsed.success) return reply.code(400).send({ error: "Enter a task title." });
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4837
|
+
const save = async () => {
|
|
4838
|
+
if (parsed.data.clientTaskId) {
|
|
4839
|
+
const existing = await Effect17.runPromise(store.getTask(parsed.data.clientTaskId));
|
|
4840
|
+
if (existing) {
|
|
4841
|
+
if (existing.deletedAt || existing.projectId !== parsed.data.projectId || existing.title !== parsed.data.title || existing.prompt !== parsed.data.prompt || JSON.stringify(existing.gitFlow) !== JSON.stringify(parsed.data.gitFlow) || JSON.stringify(existing.model) !== JSON.stringify(parsed.data.model))
|
|
4842
|
+
return reply.code(409).send({ error: "This task creation request has already been used." });
|
|
4843
|
+
return reply.code(201).send(existing);
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
if (parsed.data.gitFlow?.delivery === "pull_request") {
|
|
4847
|
+
const github = await githubCliStatus();
|
|
4848
|
+
if (!github.githubCli.authenticated)
|
|
4849
|
+
return reply.code(400).send({ error: "GitHub CLI must be authenticated on the server to open a PR." });
|
|
4850
|
+
}
|
|
4851
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4852
|
+
const projects = await Effect17.runPromise(
|
|
4853
|
+
discoverProjects(projectsRoot, store).pipe(Effect17.result)
|
|
4705
4854
|
);
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4855
|
+
if (projects._tag === "Failure" || !projects.success.some((project) => project.id === parsed.data.projectId && project.available))
|
|
4856
|
+
return reply.code(404).send({ error: "Project not found in the server's projects folder." });
|
|
4857
|
+
let files;
|
|
4858
|
+
try {
|
|
4859
|
+
files = await Effect17.runPromise(
|
|
4860
|
+
attachments.resolve(parsed.data.projectId, parsed.data.attachmentIds ?? [])
|
|
4861
|
+
);
|
|
4862
|
+
} catch (error) {
|
|
4863
|
+
return reply.code(400).send({ error: String(error) });
|
|
4864
|
+
}
|
|
4865
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4866
|
+
const task = TaskSchema.parse({
|
|
4867
|
+
id: parsed.data.clientTaskId ?? randomUUID9(),
|
|
4868
|
+
...parsed.data,
|
|
4869
|
+
attachments: files,
|
|
4870
|
+
status: "backlog",
|
|
4871
|
+
sessionId: null,
|
|
4872
|
+
createdAt: now,
|
|
4873
|
+
updatedAt: now
|
|
4874
|
+
});
|
|
4875
|
+
return reply.code(201).send(await Effect17.runPromise(store.saveTask(task)));
|
|
4876
|
+
};
|
|
4877
|
+
return parsed.data.clientTaskId ? withTaskLock(`create-task:${parsed.data.clientTaskId}`, save) : save();
|
|
4720
4878
|
});
|
|
4721
4879
|
app.patch("/api/tasks/:id", async (request, reply) => {
|
|
4722
4880
|
const parsed = UpdateTaskSchema.safeParse(request.body);
|
|
@@ -4743,6 +4901,18 @@ var init_index = __esm({
|
|
|
4743
4901
|
throw error;
|
|
4744
4902
|
}
|
|
4745
4903
|
});
|
|
4904
|
+
app.post("/api/tasks/:id/discard-unrun", async (request, reply) => {
|
|
4905
|
+
const parsed = z18.object({ updatedAt: z18.string().datetime() }).safeParse(request.body);
|
|
4906
|
+
if (!parsed.success) return reply.code(400).send({ error: "The task revision is required." });
|
|
4907
|
+
try {
|
|
4908
|
+
await deleteTask(store, request.params.id, void 0, parsed.data.updatedAt);
|
|
4909
|
+
return reply.code(204).send();
|
|
4910
|
+
} catch (error) {
|
|
4911
|
+
if (error instanceof TaskActionError)
|
|
4912
|
+
return reply.code(error.status).send({ error: error.message });
|
|
4913
|
+
throw error;
|
|
4914
|
+
}
|
|
4915
|
+
});
|
|
4746
4916
|
app.delete("/api/tasks/:id", async (request, reply) => {
|
|
4747
4917
|
try {
|
|
4748
4918
|
await deleteTask(store, request.params.id, async (session) => {
|