@dalmasonto/taskflow-mcp 1.0.33 → 2.0.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/README.md +138 -193
- package/dist/attachment-download.d.ts +74 -0
- package/dist/attachment-download.js +193 -0
- package/dist/attachment-download.js.map +1 -0
- package/dist/attachments.d.ts +23 -0
- package/dist/attachments.js +66 -0
- package/dist/attachments.js.map +1 -0
- package/dist/client.d.ts +206 -0
- package/dist/client.js +279 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +137 -18
- package/dist/config.js +187 -106
- package/dist/config.js.map +1 -0
- package/dist/connect.d.ts +89 -0
- package/dist/connect.js +269 -0
- package/dist/connect.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +120 -0
- package/dist/doctor.js.map +1 -0
- package/dist/events.d.ts +186 -0
- package/dist/events.js +407 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +124 -184
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.js +114 -0
- package/dist/instructions.js.map +1 -0
- package/dist/mint.d.ts +62 -0
- package/dist/mint.js +135 -0
- package/dist/mint.js.map +1 -0
- package/dist/mirror.d.ts +68 -0
- package/dist/mirror.js +103 -0
- package/dist/mirror.js.map +1 -0
- package/dist/pane-queue.d.ts +29 -0
- package/dist/pane-queue.js +35 -0
- package/dist/pane-queue.js.map +1 -0
- package/dist/prompts.d.ts +79 -0
- package/dist/prompts.js +211 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resolve.d.ts +72 -0
- package/dist/resolve.js +89 -0
- package/dist/resolve.js.map +1 -0
- package/dist/runtime.d.ts +54 -0
- package/dist/runtime.js +321 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.js +793 -0
- package/dist/server.js.map +1 -0
- package/dist/session-identifier.d.ts +48 -0
- package/dist/session-identifier.js +44 -0
- package/dist/session-identifier.js.map +1 -0
- package/dist/sessions-store.d.ts +38 -0
- package/dist/sessions-store.js +88 -0
- package/dist/sessions-store.js.map +1 -0
- package/dist/tmux.d.ts +200 -0
- package/dist/tmux.js +580 -0
- package/dist/tmux.js.map +1 -0
- package/hooks/metadata.mjs +99 -0
- package/hooks/permission-prompt.mjs +100 -0
- package/hooks/taskflow-hook.mjs +499 -0
- package/hooks/tool-logging.mjs +63 -0
- package/package.json +38 -29
- package/dist/agent-registry.d.ts +0 -27
- package/dist/agent-registry.js +0 -153
- package/dist/db.d.ts +0 -5
- package/dist/db.js +0 -220
- package/dist/helpers.d.ts +0 -21
- package/dist/helpers.js +0 -27
- package/dist/resources.d.ts +0 -2
- package/dist/resources.js +0 -89
- package/dist/sse.d.ts +0 -10
- package/dist/sse.js +0 -766
- package/dist/tmux-bridge.d.ts +0 -12
- package/dist/tmux-bridge.js +0 -157
- package/dist/tools/activity.d.ts +0 -39
- package/dist/tools/activity.js +0 -152
- package/dist/tools/agent-inbox.d.ts +0 -12
- package/dist/tools/agent-inbox.js +0 -257
- package/dist/tools/agent.d.ts +0 -14
- package/dist/tools/agent.js +0 -168
- package/dist/tools/analytics.d.ts +0 -21
- package/dist/tools/analytics.js +0 -191
- package/dist/tools/checkpoint.d.ts +0 -27
- package/dist/tools/checkpoint.js +0 -105
- package/dist/tools/notifications.d.ts +0 -31
- package/dist/tools/notifications.js +0 -59
- package/dist/tools/projects.d.ts +0 -55
- package/dist/tools/projects.js +0 -112
- package/dist/tools/settings.d.ts +0 -19
- package/dist/tools/settings.js +0 -73
- package/dist/tools/tasks.d.ts +0 -105
- package/dist/tools/tasks.js +0 -403
- package/dist/tools/terminal.d.ts +0 -4
- package/dist/tools/terminal.js +0 -98
- package/dist/tools/timer.d.ts +0 -37
- package/dist/tools/timer.js +0 -154
- package/dist/types.d.ts +0 -82
- package/dist/types.js +0 -30
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve local file paths into message attachments.
|
|
3
|
+
*
|
|
4
|
+
* An attachment becomes a fetchable URL once uploaded, so an unrestricted path
|
|
5
|
+
* parameter would turn one bad instruction into an exfiltration channel. Every
|
|
6
|
+
* path is therefore confined to the project root — the directory holding
|
|
7
|
+
* `.taskflow.json` — and validated fully before any upload begins.
|
|
8
|
+
*/
|
|
9
|
+
/** Mirrors `MAX_ATTACHMENT_BYTES` in the backend (views.rs). */
|
|
10
|
+
export declare const MAX_ATTACHMENT_BYTES: number;
|
|
11
|
+
export declare class AttachmentError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export interface Attachment {
|
|
15
|
+
filename: string;
|
|
16
|
+
bytes: Buffer;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate and read every path, or throw. Nothing is returned unless all paths
|
|
20
|
+
* pass — a partially-attached message is worse than none, because the reader
|
|
21
|
+
* sees text citing a file that never arrived.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveAttachments(paths: string[], root: string): Promise<Attachment[]>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve local file paths into message attachments.
|
|
3
|
+
*
|
|
4
|
+
* An attachment becomes a fetchable URL once uploaded, so an unrestricted path
|
|
5
|
+
* parameter would turn one bad instruction into an exfiltration channel. Every
|
|
6
|
+
* path is therefore confined to the project root — the directory holding
|
|
7
|
+
* `.taskflow.json` — and validated fully before any upload begins.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
10
|
+
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
|
|
11
|
+
/** Mirrors `MAX_ATTACHMENT_BYTES` in the backend (views.rs). */
|
|
12
|
+
export const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
13
|
+
export class AttachmentError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "AttachmentError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* True when `candidate` is inside `root`.
|
|
21
|
+
*
|
|
22
|
+
* Uses `path.relative` rather than a string prefix test: `startsWith` would let
|
|
23
|
+
* `<root>_evil` pass, since it shares the root's characters without being under
|
|
24
|
+
* it. Both arguments must already be real (symlink-resolved) paths.
|
|
25
|
+
*/
|
|
26
|
+
function isInside(root, candidate) {
|
|
27
|
+
const rel = relative(root, candidate);
|
|
28
|
+
return rel !== "" && !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate and read every path, or throw. Nothing is returned unless all paths
|
|
32
|
+
* pass — a partially-attached message is worse than none, because the reader
|
|
33
|
+
* sees text citing a file that never arrived.
|
|
34
|
+
*/
|
|
35
|
+
export async function resolveAttachments(paths, root) {
|
|
36
|
+
const realRoot = await realpath(root);
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const input of paths) {
|
|
39
|
+
// Relative paths resolve against the project root, never process.cwd():
|
|
40
|
+
// a stdio MCP server inherits the client's cwd, which need not match.
|
|
41
|
+
const absolute = isAbsolute(input) ? input : resolve(realRoot, input);
|
|
42
|
+
// realpath BEFORE the containment check — a symlink inside the root that
|
|
43
|
+
// points outside it would pass a check made on the unresolved path.
|
|
44
|
+
let real;
|
|
45
|
+
try {
|
|
46
|
+
real = await realpath(absolute);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new AttachmentError(`Attachment not found: ${input}`);
|
|
50
|
+
}
|
|
51
|
+
if (!isInside(realRoot, real)) {
|
|
52
|
+
throw new AttachmentError(`Attachment is outside the project root: ${input} (resolved to ${real})`);
|
|
53
|
+
}
|
|
54
|
+
const info = await stat(real);
|
|
55
|
+
if (!info.isFile()) {
|
|
56
|
+
throw new AttachmentError(`Attachment is not a file: ${input}`);
|
|
57
|
+
}
|
|
58
|
+
if (info.size > MAX_ATTACHMENT_BYTES) {
|
|
59
|
+
const mb = (info.size / (1024 * 1024)).toFixed(1);
|
|
60
|
+
throw new AttachmentError(`Attachment "${input}" is ${mb}MB; the maximum attachment size is 25MB.`);
|
|
61
|
+
}
|
|
62
|
+
out.push({ filename: basename(real), bytes: await readFile(real) });
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEzE,gEAAgE;AAChE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAOD;;;;;;GAMG;AACH,SAAS,QAAQ,CAAC,IAAY,EAAE,SAAiB;IAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACvF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAe,EACf,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAiB,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,wEAAwE;QACxE,sEAAsE;QACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEtE,yEAAyE;QACzE,oEAAoE;QACpE,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,eAAe,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,eAAe,CACvB,2CAA2C,KAAK,iBAAiB,IAAI,GAAG,CACzE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,MAAM,IAAI,eAAe,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,GAAG,oBAAoB,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,IAAI,eAAe,CACvB,eAAe,KAAK,QAAQ,EAAE,0CAA0C,CACzE,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small typed HTTP client for the TaskFlow backend's agent-authed API.
|
|
3
|
+
*
|
|
4
|
+
* One method per endpoint in the agent surface (Stages 1–6). Every request is
|
|
5
|
+
* authenticated with the profile key via `Authorization: Agent <key>`; the base
|
|
6
|
+
* URL is the resolved profile's `server`. A non-2xx response is turned into a
|
|
7
|
+
* thrown {@link TaskflowApiError} carrying the backend's `detail`/text so the
|
|
8
|
+
* MCP tool (or hook) surfaces a real reason, never a bare status.
|
|
9
|
+
*
|
|
10
|
+
* `fetchImpl` is injectable so the client can be unit-tested against a stub
|
|
11
|
+
* without a live backend.
|
|
12
|
+
*/
|
|
13
|
+
export type FetchLike = (input: string, init?: {
|
|
14
|
+
method?: string;
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
body?: string | FormData;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}) => Promise<{
|
|
19
|
+
ok: boolean;
|
|
20
|
+
status: number;
|
|
21
|
+
statusText: string;
|
|
22
|
+
text: () => Promise<string>;
|
|
23
|
+
}>;
|
|
24
|
+
export interface TaskflowClientOptions {
|
|
25
|
+
/** Base server URL, e.g. `http://localhost:8000` (trailing slash trimmed). */
|
|
26
|
+
server: string;
|
|
27
|
+
/** The profile's raw `tfk_…` key. */
|
|
28
|
+
key: string;
|
|
29
|
+
/** Injectable fetch (defaults to the global `fetch`). */
|
|
30
|
+
fetchImpl?: FetchLike;
|
|
31
|
+
/** Per-request timeout in ms (default 15000). */
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/** An error carrying the backend's status + parsed detail. */
|
|
35
|
+
export declare class TaskflowApiError extends Error {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
readonly detail: string;
|
|
38
|
+
readonly method: string;
|
|
39
|
+
readonly path: string;
|
|
40
|
+
constructor(method: string, path: string, status: number, detail: string);
|
|
41
|
+
}
|
|
42
|
+
export interface Whoami {
|
|
43
|
+
agent_id: number;
|
|
44
|
+
display_name: string;
|
|
45
|
+
identifier: string;
|
|
46
|
+
project: number;
|
|
47
|
+
status: string;
|
|
48
|
+
}
|
|
49
|
+
export interface ChannelSummary {
|
|
50
|
+
id: number;
|
|
51
|
+
title: string;
|
|
52
|
+
kind: string;
|
|
53
|
+
topic: string | null;
|
|
54
|
+
}
|
|
55
|
+
export interface AgentSummary {
|
|
56
|
+
id: number;
|
|
57
|
+
display_name: string;
|
|
58
|
+
identifier: string;
|
|
59
|
+
status: string;
|
|
60
|
+
last_seen_at: string | null;
|
|
61
|
+
}
|
|
62
|
+
export interface MessagesPage {
|
|
63
|
+
messages: unknown[];
|
|
64
|
+
read_cursor: number | null;
|
|
65
|
+
/** Size of this page — with `unread: true`, the number of unread messages. */
|
|
66
|
+
unread_count?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface SessionRow {
|
|
69
|
+
id: number;
|
|
70
|
+
session_identifier: string;
|
|
71
|
+
status: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
export interface SendMessageInput {
|
|
75
|
+
channel: number;
|
|
76
|
+
body_markdown: string;
|
|
77
|
+
priority?: string;
|
|
78
|
+
client_nonce?: string;
|
|
79
|
+
/** Resolved by `resolveAttachments`; switches the POST to multipart. */
|
|
80
|
+
attachments?: {
|
|
81
|
+
filename: string;
|
|
82
|
+
bytes: Buffer;
|
|
83
|
+
}[];
|
|
84
|
+
}
|
|
85
|
+
export interface CreateTaskInput {
|
|
86
|
+
title: string;
|
|
87
|
+
description_markdown?: string;
|
|
88
|
+
priority?: string;
|
|
89
|
+
notes_markdown?: string;
|
|
90
|
+
claim?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Fields of a task that an agent may edit. Every one is optional: what is
|
|
94
|
+
* absent is left alone rather than blanked.
|
|
95
|
+
*
|
|
96
|
+
* Status, assignment and project are deliberately NOT here — they move through
|
|
97
|
+
* `updateTaskStatus` and `claimTask`, and the project is derived from the
|
|
98
|
+
* credential and never accepted from a caller.
|
|
99
|
+
*/
|
|
100
|
+
export interface UpdateTaskInput {
|
|
101
|
+
title?: string;
|
|
102
|
+
description_markdown?: string;
|
|
103
|
+
notes_markdown?: string;
|
|
104
|
+
priority?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface RegisterSessionInput {
|
|
107
|
+
session_identifier: string;
|
|
108
|
+
host?: string;
|
|
109
|
+
pid?: number;
|
|
110
|
+
cwd?: string;
|
|
111
|
+
transport?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface FrameInput {
|
|
114
|
+
content: string;
|
|
115
|
+
stream?: string;
|
|
116
|
+
task?: number;
|
|
117
|
+
}
|
|
118
|
+
export interface ActivityEventInput {
|
|
119
|
+
action: string;
|
|
120
|
+
body_markdown?: string;
|
|
121
|
+
metadata_json?: string;
|
|
122
|
+
task?: number;
|
|
123
|
+
/** Also mirror this event to the task's linked GitHub issue (opt-in, best-effort). */
|
|
124
|
+
post_to_github?: boolean;
|
|
125
|
+
}
|
|
126
|
+
export declare class TaskflowClient {
|
|
127
|
+
private readonly server;
|
|
128
|
+
private readonly key;
|
|
129
|
+
private readonly fetchImpl;
|
|
130
|
+
private readonly timeoutMs;
|
|
131
|
+
constructor(options: TaskflowClientOptions);
|
|
132
|
+
private request;
|
|
133
|
+
/** `GET /agents/whoami` — the identity behind the presented credential. */
|
|
134
|
+
whoami(): Promise<Whoami>;
|
|
135
|
+
/** `GET /agents/tasks?status=&assigned=` — tasks in the agent's project. */
|
|
136
|
+
listTasks(params?: {
|
|
137
|
+
status?: string;
|
|
138
|
+
assigned?: string;
|
|
139
|
+
}): Promise<unknown[]>;
|
|
140
|
+
/** `GET /agents/channels` — channels the agent may see. */
|
|
141
|
+
listChannels(): Promise<ChannelSummary[]>;
|
|
142
|
+
/** `GET /agents/agents` — the other agents in the caller's project. */
|
|
143
|
+
listAgents(): Promise<AgentSummary[]>;
|
|
144
|
+
/** `GET /agents/messages?channel=&since=&limit=` → `{ messages, read_cursor }`. */
|
|
145
|
+
listMessages(params: {
|
|
146
|
+
channel: number;
|
|
147
|
+
since?: number;
|
|
148
|
+
limit?: number;
|
|
149
|
+
/** Only messages past this agent's read cursor. */
|
|
150
|
+
unread?: boolean;
|
|
151
|
+
}): Promise<MessagesPage>;
|
|
152
|
+
/** `GET /agents/activity?task=&limit=` — recent activity, newest first. */
|
|
153
|
+
listActivity(params?: {
|
|
154
|
+
task?: number;
|
|
155
|
+
limit?: number;
|
|
156
|
+
}): Promise<unknown[]>;
|
|
157
|
+
/** `POST /agents/agent/messages` — speak as this agent in a channel. */
|
|
158
|
+
sendMessage(input: SendMessageInput): Promise<unknown>;
|
|
159
|
+
/** `POST /channels/{id}/agent/read` — advance this agent's read cursor. */
|
|
160
|
+
markRead(channel: number, lastReadMessage: number): Promise<unknown>;
|
|
161
|
+
/** `POST /agents/tasks` — author a task in the agent's project (optionally claim). */
|
|
162
|
+
createTask(input: CreateTaskInput): Promise<unknown>;
|
|
163
|
+
/**
|
|
164
|
+
* `POST /agents/tasks/{id}` — edit a task's content.
|
|
165
|
+
*
|
|
166
|
+
* Only the fields present in `input` are sent, and the server writes only what
|
|
167
|
+
* it receives: a partial edit must not blank what it does not mention, which
|
|
168
|
+
* is the difference between an edit and an overwrite.
|
|
169
|
+
*/
|
|
170
|
+
updateTask(task: number, input: UpdateTaskInput): Promise<unknown>;
|
|
171
|
+
/** `POST /agents/tasks/{id}/attachments` — hang files on a task (multipart). */
|
|
172
|
+
uploadTaskAttachments(task: number, attachments: {
|
|
173
|
+
filename: string;
|
|
174
|
+
bytes: Buffer;
|
|
175
|
+
}[]): Promise<unknown>;
|
|
176
|
+
/** `POST /agents/tasks/{id}/status` — advance a task's status. */
|
|
177
|
+
updateTaskStatus(task: number, status: string): Promise<unknown>;
|
|
178
|
+
/** `POST /agents/tasks/{id}/claim` — self-assign a task. */
|
|
179
|
+
claimTask(task: number): Promise<unknown>;
|
|
180
|
+
/** `POST /tasks/{id}/agent/review` — record a review verdict as this agent. */
|
|
181
|
+
reportReview(task: number, decision: string, bodyMarkdown?: string): Promise<unknown>;
|
|
182
|
+
/** `POST /agents/sessions` — register or reconnect a live session. */
|
|
183
|
+
registerSession(input: RegisterSessionInput): Promise<SessionRow>;
|
|
184
|
+
/** `POST /agents/sessions/{id}/heartbeat` — bump liveness (status hint idle/busy). */
|
|
185
|
+
heartbeat(session: number, status?: string): Promise<SessionRow>;
|
|
186
|
+
/** `POST /agents/sessions/{id}/frames` — append one terminal frame. */
|
|
187
|
+
appendFrame(session: number, frame: FrameInput): Promise<unknown>;
|
|
188
|
+
/** `POST /agents/sessions/{id}/frames` — append a batch of terminal frames. */
|
|
189
|
+
appendFrames(session: number, frames: FrameInput[]): Promise<unknown>;
|
|
190
|
+
/** `POST /agents/sessions/{id}/close` — disconnect a session. */
|
|
191
|
+
closeSession(session: number): Promise<SessionRow>;
|
|
192
|
+
/** `POST /agents/activity` — log one real activity event. */
|
|
193
|
+
logActivity(event: ActivityEventInput): Promise<{
|
|
194
|
+
created: number;
|
|
195
|
+
}>;
|
|
196
|
+
/** `POST /agents/activity` — log a batch of activity events. */
|
|
197
|
+
logActivityBatch(events: ActivityEventInput[]): Promise<{
|
|
198
|
+
created: number;
|
|
199
|
+
}>;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Pull a human message out of an error response body: prefer a JSON `detail`
|
|
203
|
+
* (the backend's convention) or `error`, else the raw text, else the status
|
|
204
|
+
* line. Kept resilient — a malformed body must never mask the real failure.
|
|
205
|
+
*/
|
|
206
|
+
export declare function extractDetail(raw: string, statusText: string): string;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small typed HTTP client for the TaskFlow backend's agent-authed API.
|
|
3
|
+
*
|
|
4
|
+
* One method per endpoint in the agent surface (Stages 1–6). Every request is
|
|
5
|
+
* authenticated with the profile key via `Authorization: Agent <key>`; the base
|
|
6
|
+
* URL is the resolved profile's `server`. A non-2xx response is turned into a
|
|
7
|
+
* thrown {@link TaskflowApiError} carrying the backend's `detail`/text so the
|
|
8
|
+
* MCP tool (or hook) surfaces a real reason, never a bare status.
|
|
9
|
+
*
|
|
10
|
+
* `fetchImpl` is injectable so the client can be unit-tested against a stub
|
|
11
|
+
* without a live backend.
|
|
12
|
+
*/
|
|
13
|
+
/** An error carrying the backend's status + parsed detail. */
|
|
14
|
+
export class TaskflowApiError extends Error {
|
|
15
|
+
status;
|
|
16
|
+
detail;
|
|
17
|
+
method;
|
|
18
|
+
path;
|
|
19
|
+
constructor(method, path, status, detail) {
|
|
20
|
+
super(`${method} ${path} → ${status}: ${detail}`);
|
|
21
|
+
this.name = "TaskflowApiError";
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.detail = detail;
|
|
24
|
+
this.method = method;
|
|
25
|
+
this.path = path;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const API_PREFIX = "/api/taskflow";
|
|
29
|
+
export class TaskflowClient {
|
|
30
|
+
server;
|
|
31
|
+
key;
|
|
32
|
+
fetchImpl;
|
|
33
|
+
timeoutMs;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.server = options.server.replace(/\/+$/, "");
|
|
36
|
+
this.key = options.key;
|
|
37
|
+
const globalFetch = globalThis.fetch;
|
|
38
|
+
const impl = options.fetchImpl ?? globalFetch;
|
|
39
|
+
if (!impl) {
|
|
40
|
+
throw new Error("No fetch implementation available (Node >=18 or pass fetchImpl).");
|
|
41
|
+
}
|
|
42
|
+
this.fetchImpl = impl;
|
|
43
|
+
this.timeoutMs = options.timeoutMs ?? 15000;
|
|
44
|
+
}
|
|
45
|
+
// ---- core request helper -------------------------------------------------
|
|
46
|
+
async request(method, path, options = {}) {
|
|
47
|
+
let url = `${this.server}${path}`;
|
|
48
|
+
if (options.query) {
|
|
49
|
+
const params = new URLSearchParams();
|
|
50
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
51
|
+
if (v !== undefined && v !== null && v !== "") {
|
|
52
|
+
params.set(k, String(v));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const qs = params.toString();
|
|
56
|
+
if (qs)
|
|
57
|
+
url += `?${qs}`;
|
|
58
|
+
}
|
|
59
|
+
const headers = {
|
|
60
|
+
Authorization: `Agent ${this.key}`,
|
|
61
|
+
Accept: "application/json",
|
|
62
|
+
};
|
|
63
|
+
const init = { method, headers };
|
|
64
|
+
if (options.form !== undefined) {
|
|
65
|
+
// Deliberately no Content-Type: fetch must set it to supply the
|
|
66
|
+
// multipart boundary. Setting it here produces a body the server
|
|
67
|
+
// cannot parse.
|
|
68
|
+
init.body = options.form;
|
|
69
|
+
}
|
|
70
|
+
else if (options.body !== undefined) {
|
|
71
|
+
headers["Content-Type"] = "application/json";
|
|
72
|
+
init.body = JSON.stringify(options.body);
|
|
73
|
+
}
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? this.timeoutMs);
|
|
76
|
+
init.signal = controller.signal;
|
|
77
|
+
let res;
|
|
78
|
+
try {
|
|
79
|
+
res = await this.fetchImpl(url, init);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
throw new TaskflowApiError(method, path, 0, `network error: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
const raw = await res.text();
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
throw new TaskflowApiError(method, path, res.status, extractDetail(raw, res.statusText));
|
|
90
|
+
}
|
|
91
|
+
if (!raw) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(raw);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// A 2xx with a non-JSON body (e.g. the health string) — hand it back raw.
|
|
99
|
+
return raw;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ---- READS ---------------------------------------------------------------
|
|
103
|
+
/** `GET /agents/whoami` — the identity behind the presented credential. */
|
|
104
|
+
whoami() {
|
|
105
|
+
return this.request("GET", `${API_PREFIX}/agents/whoami`);
|
|
106
|
+
}
|
|
107
|
+
/** `GET /agents/tasks?status=&assigned=` — tasks in the agent's project. */
|
|
108
|
+
listTasks(params = {}) {
|
|
109
|
+
return this.request("GET", `${API_PREFIX}/agents/tasks`, {
|
|
110
|
+
query: { status: params.status, assigned: params.assigned },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** `GET /agents/channels` — channels the agent may see. */
|
|
114
|
+
listChannels() {
|
|
115
|
+
return this.request("GET", `${API_PREFIX}/agents/channels`);
|
|
116
|
+
}
|
|
117
|
+
/** `GET /agents/agents` — the other agents in the caller's project. */
|
|
118
|
+
listAgents() {
|
|
119
|
+
return this.request("GET", `${API_PREFIX}/agents/agents`);
|
|
120
|
+
}
|
|
121
|
+
/** `GET /agents/messages?channel=&since=&limit=` → `{ messages, read_cursor }`. */
|
|
122
|
+
listMessages(params) {
|
|
123
|
+
return this.request("GET", `${API_PREFIX}/agents/messages`, {
|
|
124
|
+
query: {
|
|
125
|
+
channel: params.channel,
|
|
126
|
+
since: params.since,
|
|
127
|
+
limit: params.limit,
|
|
128
|
+
unread: params.unread,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** `GET /agents/activity?task=&limit=` — recent activity, newest first. */
|
|
133
|
+
listActivity(params = {}) {
|
|
134
|
+
return this.request("GET", `${API_PREFIX}/agents/activity`, {
|
|
135
|
+
query: { task: params.task, limit: params.limit },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// ---- WRITES: messages / read cursor --------------------------------------
|
|
139
|
+
/** `POST /agents/agent/messages` — speak as this agent in a channel. */
|
|
140
|
+
sendMessage(input) {
|
|
141
|
+
const { attachments, ...fields } = input;
|
|
142
|
+
if (!attachments?.length) {
|
|
143
|
+
return this.request("POST", `${API_PREFIX}/agents/agent/messages`, { body: fields });
|
|
144
|
+
}
|
|
145
|
+
const form = new FormData();
|
|
146
|
+
form.set("channel", String(fields.channel));
|
|
147
|
+
form.set("body_markdown", fields.body_markdown);
|
|
148
|
+
if (fields.priority)
|
|
149
|
+
form.set("priority", fields.priority);
|
|
150
|
+
if (fields.client_nonce)
|
|
151
|
+
form.set("client_nonce", fields.client_nonce);
|
|
152
|
+
for (const file of attachments) {
|
|
153
|
+
// The server treats a part as a file only when it carries a non-empty
|
|
154
|
+
// filename (views.rs:180-186), so the basename must be preserved.
|
|
155
|
+
form.append("files", new Blob([file.bytes]), file.filename);
|
|
156
|
+
}
|
|
157
|
+
// 25MB may not finish inside the default 15s.
|
|
158
|
+
return this.request("POST", `${API_PREFIX}/agents/agent/messages`, {
|
|
159
|
+
form,
|
|
160
|
+
timeoutMs: 120_000,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** `POST /channels/{id}/agent/read` — advance this agent's read cursor. */
|
|
164
|
+
markRead(channel, lastReadMessage) {
|
|
165
|
+
return this.request("POST", `${API_PREFIX}/channels/${channel}/agent/read`, {
|
|
166
|
+
body: { last_read_message: lastReadMessage },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// ---- WRITES: tasks -------------------------------------------------------
|
|
170
|
+
/** `POST /agents/tasks` — author a task in the agent's project (optionally claim). */
|
|
171
|
+
createTask(input) {
|
|
172
|
+
return this.request("POST", `${API_PREFIX}/agents/tasks`, { body: input });
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* `POST /agents/tasks/{id}` — edit a task's content.
|
|
176
|
+
*
|
|
177
|
+
* Only the fields present in `input` are sent, and the server writes only what
|
|
178
|
+
* it receives: a partial edit must not blank what it does not mention, which
|
|
179
|
+
* is the difference between an edit and an overwrite.
|
|
180
|
+
*/
|
|
181
|
+
updateTask(task, input) {
|
|
182
|
+
return this.request("POST", `${API_PREFIX}/agents/tasks/${task}`, { body: input });
|
|
183
|
+
}
|
|
184
|
+
/** `POST /agents/tasks/{id}/attachments` — hang files on a task (multipart). */
|
|
185
|
+
uploadTaskAttachments(task, attachments) {
|
|
186
|
+
const form = new FormData();
|
|
187
|
+
for (const file of attachments) {
|
|
188
|
+
// The server counts a part as a file only when it carries a non-empty
|
|
189
|
+
// filename, so the basename has to survive.
|
|
190
|
+
form.append("files", new Blob([file.bytes]), file.filename);
|
|
191
|
+
}
|
|
192
|
+
// 25MB may not finish inside the default 15s.
|
|
193
|
+
return this.request("POST", `${API_PREFIX}/agents/tasks/${task}/attachments`, {
|
|
194
|
+
form,
|
|
195
|
+
timeoutMs: 120_000,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** `POST /agents/tasks/{id}/status` — advance a task's status. */
|
|
199
|
+
updateTaskStatus(task, status) {
|
|
200
|
+
return this.request("POST", `${API_PREFIX}/agents/tasks/${task}/status`, {
|
|
201
|
+
body: { status },
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/** `POST /agents/tasks/{id}/claim` — self-assign a task. */
|
|
205
|
+
claimTask(task) {
|
|
206
|
+
return this.request("POST", `${API_PREFIX}/agents/tasks/${task}/claim`, { body: {} });
|
|
207
|
+
}
|
|
208
|
+
/** `POST /tasks/{id}/agent/review` — record a review verdict as this agent. */
|
|
209
|
+
reportReview(task, decision, bodyMarkdown) {
|
|
210
|
+
const body = { decision };
|
|
211
|
+
if (bodyMarkdown !== undefined)
|
|
212
|
+
body.body_markdown = bodyMarkdown;
|
|
213
|
+
return this.request("POST", `${API_PREFIX}/tasks/${task}/agent/review`, { body });
|
|
214
|
+
}
|
|
215
|
+
// ---- WRITES: sessions / frames / activity --------------------------------
|
|
216
|
+
/** `POST /agents/sessions` — register or reconnect a live session. */
|
|
217
|
+
registerSession(input) {
|
|
218
|
+
return this.request("POST", `${API_PREFIX}/agents/sessions`, { body: input });
|
|
219
|
+
}
|
|
220
|
+
/** `POST /agents/sessions/{id}/heartbeat` — bump liveness (status hint idle/busy). */
|
|
221
|
+
heartbeat(session, status) {
|
|
222
|
+
const body = {};
|
|
223
|
+
if (status !== undefined)
|
|
224
|
+
body.status = status;
|
|
225
|
+
return this.request("POST", `${API_PREFIX}/agents/sessions/${session}/heartbeat`, { body });
|
|
226
|
+
}
|
|
227
|
+
/** `POST /agents/sessions/{id}/frames` — append one terminal frame. */
|
|
228
|
+
appendFrame(session, frame) {
|
|
229
|
+
return this.request("POST", `${API_PREFIX}/agents/sessions/${session}/frames`, {
|
|
230
|
+
body: frame,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/** `POST /agents/sessions/{id}/frames` — append a batch of terminal frames. */
|
|
234
|
+
appendFrames(session, frames) {
|
|
235
|
+
return this.request("POST", `${API_PREFIX}/agents/sessions/${session}/frames`, {
|
|
236
|
+
body: { frames },
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/** `POST /agents/sessions/{id}/close` — disconnect a session. */
|
|
240
|
+
closeSession(session) {
|
|
241
|
+
return this.request("POST", `${API_PREFIX}/agents/sessions/${session}/close`, { body: {} });
|
|
242
|
+
}
|
|
243
|
+
/** `POST /agents/activity` — log one real activity event. */
|
|
244
|
+
logActivity(event) {
|
|
245
|
+
return this.request("POST", `${API_PREFIX}/agents/activity`, { body: event });
|
|
246
|
+
}
|
|
247
|
+
/** `POST /agents/activity` — log a batch of activity events. */
|
|
248
|
+
logActivityBatch(events) {
|
|
249
|
+
return this.request("POST", `${API_PREFIX}/agents/activity`, { body: { events } });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Pull a human message out of an error response body: prefer a JSON `detail`
|
|
254
|
+
* (the backend's convention) or `error`, else the raw text, else the status
|
|
255
|
+
* line. Kept resilient — a malformed body must never mask the real failure.
|
|
256
|
+
*/
|
|
257
|
+
export function extractDetail(raw, statusText) {
|
|
258
|
+
const trimmed = raw.trim();
|
|
259
|
+
if (trimmed) {
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(trimmed);
|
|
262
|
+
if (typeof parsed.detail === "string")
|
|
263
|
+
return parsed.detail;
|
|
264
|
+
if (typeof parsed.error === "string")
|
|
265
|
+
return parsed.error;
|
|
266
|
+
// Field-error envelope (e.g. { user: ["…"] }) — surface the first message.
|
|
267
|
+
for (const value of Object.values(parsed)) {
|
|
268
|
+
if (Array.isArray(value) && typeof value[0] === "string")
|
|
269
|
+
return value[0];
|
|
270
|
+
}
|
|
271
|
+
return trimmed;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return trimmed;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return statusText || "request failed";
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|