@anchrd/intel-api 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cloudflare/cloudflare.js +0 -68
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +0 -39
- package/dist/adapters/db/db-flows.js +3 -2
- package/dist/adapters/db/db-grants.d.ts +1 -1
- package/dist/adapters/db/db-grants.js +1 -1
- package/dist/adapters/db/db.js +16 -113
- package/dist/adapters/openid/openid.js +1 -1
- package/dist/adapters/remote-tools/remote-tools.js +1 -1
- package/dist/bundle/bundle.js +31 -141
- package/dist/bundle/bundle.types.d.ts +1 -1
- package/dist/cli/cli.js +18 -11
- package/dist/flows/flows.d.ts +1 -1
- package/dist/flows/flows.js +1 -1
- package/dist/flows/flows.types.d.ts +4 -1
- package/dist/http/http.js +43 -238
- package/dist/http/http.types.d.ts +0 -8
- package/dist/indexing/indexing.js +18 -89
- package/dist/intel/intel.js +4 -9
- package/dist/intel/intel.types.d.ts +0 -6
- package/dist/mcp/mcp.js +84 -361
- package/dist/mcp/mcp.types.d.ts +2 -7
- package/dist/nodes/document-links/document-links.d.ts +6 -8
- package/dist/nodes/document-links/document-links.js +8 -31
- package/dist/nodes/nodes.js +24 -890
- package/dist/nodes/nodes.types.d.ts +14 -173
- package/dist/tools/tool-servers/tool-servers.d.ts +1 -1
- package/dist/tools/tool-servers/tool-servers.js +1 -1
- package/dist/tools/tools.js +37 -148
- package/dist/tools/tools.types.d.ts +1 -22
- package/migrations/0018_no_context_policy_at_last.sql +13 -6
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/package.json +2 -2
- package/dist/adapters/cloudflare-api/cloudflare-api.d.ts +0 -22
- package/dist/adapters/cloudflare-api/cloudflare-api.js +0 -306
- package/dist/adapters/cloudflare-api/cloudflare-api.types.d.ts +0 -64
- package/dist/adapters/cloudflare-api/cloudflare-api.types.js +0 -1
- package/dist/adapters/gate-applications/gate-applications.d.ts +0 -23
- package/dist/adapters/gate-applications/gate-applications.js +0 -88
- package/dist/adapters/tool-delegation/tool-delegation.d.ts +0 -22
- package/dist/adapters/tool-delegation/tool-delegation.js +0 -90
- package/dist/agent-costs/agent-costs.d.ts +0 -16
- package/dist/agent-costs/agent-costs.js +0 -105
- package/dist/agent-costs/agent-costs.types.d.ts +0 -30
- package/dist/agent-costs/agent-costs.types.js +0 -1
- package/dist/agent-runtime/agent-runtime.d.ts +0 -16
- package/dist/agent-runtime/agent-runtime.js +0 -150
- package/dist/agent-runtime/agent-runtime.types.d.ts +0 -122
- package/dist/agent-runtime/agent-runtime.types.js +0 -1
- package/dist/model-catalog/model-catalog.d.ts +0 -2
- package/dist/model-catalog/model-catalog.js +0 -99
- package/dist/model-catalog/model-catalog.types.d.ts +0 -15
- package/dist/model-catalog/model-catalog.types.js +0 -1
- package/dist/nodes/board/board.d.ts +0 -61
- package/dist/nodes/board/board.js +0 -826
- package/dist/nodes/board/board.types.d.ts +0 -38
- package/dist/nodes/board/board.types.js +0 -1
- package/migrations/0013_agents_in_the_tree.sql +0 -76
- package/migrations/0014_agent_applications.sql +0 -25
- package/migrations/0015_tools_delegated_from_a_connection.sql +0 -15
- package/migrations/0016_boards_in_the_tree.sql +0 -80
package/dist/http/http.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ArchiveFlowInput, CreateFlowInput, GetFlowInput, GetFlowVersionInput, ListFlowsInput, PreviewFlowPublishInput, PublishFlowInput, RelationGraphInput, SaveFlowVersionInput, UnpublishFlowInput, UpdateFlowInput, } from "@anchrd/intel-contract/flow";
|
|
2
|
+
import { CancelFlowRunInput, CompleteFlowRunStepInput, GetFlowRunInput, ListFlowRunsInput, StartFlowRunInput, } from "@anchrd/intel-contract/flow-run";
|
|
3
|
+
import { ArchiveNodeInput, CreateNodeInput, GetNodeInput, GetNodeVersionInput, ListNodesInput, NodeGraphInput, ResolveNodeLinksInput, SaveAttachmentInput, SaveNodeVersionInput, SearchInput, UpdateNodeInput, } from "@anchrd/intel-contract/node";
|
|
4
|
+
import { RevokeGrantInput, ShareInput } from "@anchrd/intel-contract/share";
|
|
5
|
+
import { AppendTableRowsInput, DefineTableInput, DeleteTableRowsInput, RedefineTableInput, UpdateTableRowsInput, } from "@anchrd/intel-contract/table";
|
|
6
|
+
import { ExecuteToolInput, TestToolInput } from "@anchrd/intel-contract/tool";
|
|
2
7
|
import { Hono } from "hono";
|
|
3
8
|
import { z } from "zod";
|
|
4
9
|
import { authorizeBearer, bearer, permits, } from "../shared/gate-authorization/gate-authorization.js";
|
|
@@ -78,16 +83,6 @@ function asFlowActor(authorization) {
|
|
|
78
83
|
isAdmin: authorization.can("intel", "admin"),
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
|
-
// The same `agents/run` the agent Worker asks for, resolved once at Intel's door. `intel/admin`
|
|
82
|
-
// opens it for the same reason it opens every other one — `permits` is where that lives.
|
|
83
|
-
function asAgentActor(authorization) {
|
|
84
|
-
return {
|
|
85
|
-
id: authorization.identity.id,
|
|
86
|
-
email: authorization.identity.email,
|
|
87
|
-
isAdmin: authorization.can("intel", "admin"),
|
|
88
|
-
canRun: permits(authorization, "agents", "run"),
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
86
|
export function createHttp(deps) {
|
|
92
87
|
const app = new Hono();
|
|
93
88
|
async function authenticated(headers) {
|
|
@@ -129,103 +124,20 @@ export function createHttp(deps) {
|
|
|
129
124
|
// The caller's own identity, so the shell can name who is signed in. It needs no capability —
|
|
130
125
|
// the answer is the token's own subject — and it returns nothing but id, email, and name.
|
|
131
126
|
app.get("/session", (context) => {
|
|
132
|
-
const
|
|
127
|
+
const authorization = context.get("authorization");
|
|
128
|
+
const { identity } = authorization;
|
|
133
129
|
return context.json({
|
|
134
130
|
id: identity.id,
|
|
135
131
|
email: identity.email,
|
|
136
132
|
name: identity.name ?? null,
|
|
133
|
+
// ⚠️ What the shell may DRAW, never what it may do (#416). Every admin route below still asks
|
|
134
|
+
// `requireCapability` for itself, and a browser that lied here would gain nothing but a
|
|
135
|
+
// button that answers 403.
|
|
136
|
+
isAdmin: authorization.can("intel", "admin"),
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
|
-
// What this deployment is equipped to do (#190) — facts about the installation, not about the
|
|
140
|
-
// caller, which is why it needs no capability beyond being signed in. `/session` stays identity
|
|
141
|
-
// only on purpose; this is the separate answer the UI asks for instead of reading a 503 as one.
|
|
142
|
-
// It sits behind the auth middleware like everything else, so an anonymous probe still learns
|
|
143
|
-
// nothing about whether a runtime exists.
|
|
144
|
-
app.get("/capabilities", (context) => {
|
|
145
|
-
return context.json({ agentRuntime: deps.agents.available() });
|
|
146
|
-
});
|
|
147
|
-
/**
|
|
148
|
-
* What the models on offer cost and how much they hold (#257).
|
|
149
|
-
*
|
|
150
|
-
* ⚠️ Intel asks Cloudflare, never the browser. The account endpoint needs a token and a token does
|
|
151
|
-
* not belong in a SPA — which is the sentence the hard-coded table in `packages/ui` used to carry
|
|
152
|
-
* as the reason it stayed hard-coded. This route is the answer to it: the figures cross the wire,
|
|
153
|
-
* the credential does not.
|
|
154
|
-
*
|
|
155
|
-
* ⚠️ No capability beyond being signed in. It is a fact about the installation and about nobody's
|
|
156
|
-
* data — the same footing `/capabilities` stands on.
|
|
157
|
-
*
|
|
158
|
-
* ⚠️ The `cache-control` is the caching the ticket asks for, and it is the caching that matters:
|
|
159
|
-
* the waste it names is "one fetch every time the select is opened", and that fetch is the
|
|
160
|
-
* browser's. A Worker isolate has nowhere durable to keep a table between requests, and inventing
|
|
161
|
-
* storage for a list of prices would cost more than the request it saved.
|
|
162
|
-
*/
|
|
163
|
-
app.get("/models", async (context) => {
|
|
164
|
-
context.header("cache-control", "private, max-age=3600");
|
|
165
|
-
return context.json(await deps.models.list());
|
|
166
|
-
});
|
|
167
|
-
/**
|
|
168
|
-
* The agent runtime, reached through Intel.
|
|
169
|
-
*
|
|
170
|
-
* ⚠️ This list IS the door. The runtime serves more than what stands here — `/mcp` is the agent's
|
|
171
|
-
* own MCP surface, which the portal dials with its own credential (ADR-0005 §7), and
|
|
172
|
-
* `/mail/inbound` is authorized by a webhook signature. Neither is a browser path, and neither
|
|
173
|
-
* becomes one by being reachable: anything not named below is a 404 from Hono's router, so there
|
|
174
|
-
* is no allow-list beside the routes that could go quietly out of step with them.
|
|
175
|
-
*
|
|
176
|
-
* ⚠️ `agents/run` — not `knowledge/read`. Reading an agent's page is a knowledge read; driving the
|
|
177
|
-
* agent is the capability the runtime itself insists on, and Intel refuses on the same terms
|
|
178
|
-
* BEFORE the binding is touched, so a denial never reaches the runtime at all.
|
|
179
|
-
*/
|
|
180
|
-
const runtimeRoutes = [
|
|
181
|
-
{ method: "GET", pattern: "/state", path: () => "/state" },
|
|
182
|
-
{ method: "GET", pattern: "/runs", path: () => "/runs" },
|
|
183
|
-
{
|
|
184
|
-
method: "GET",
|
|
185
|
-
pattern: "/runs/:runId",
|
|
186
|
-
path: (context) => `/runs/${encodeURIComponent(context.req.param("runId") ?? "")}`,
|
|
187
|
-
},
|
|
188
|
-
{ method: "GET", pattern: "/log", path: () => "/log" },
|
|
189
|
-
{ method: "POST", pattern: "/chat", path: () => "/chat" },
|
|
190
|
-
{ method: "POST", pattern: "/pause", path: () => "/pause" },
|
|
191
|
-
{ method: "POST", pattern: "/resume", path: () => "/resume" },
|
|
192
|
-
{ method: "POST", pattern: "/run", path: () => "/run" },
|
|
193
|
-
];
|
|
194
|
-
/**
|
|
195
|
-
* What this agent has cost — answered by Intel itself, not forwarded (#251).
|
|
196
|
-
*
|
|
197
|
-
* ⚠️ It stands under `/agents/:agentId` and is NOT in `runtimeRoutes`, which is the whole point of
|
|
198
|
-
* writing it out here. The runtime has no idea what anything costs: the figures come from
|
|
199
|
-
* Cloudflare's AI Gateway log, which needs an `AI Gateway: Read` token, and that token belongs in
|
|
200
|
-
* Intel rather than in the Worker that executes model-driven tool calls — the runtime's own
|
|
201
|
-
* `CLAUDE.md` keeps the number of credentials there deliberately small. The runtime's part is the
|
|
202
|
-
* stamp on the model call and nothing more.
|
|
203
|
-
*
|
|
204
|
-
* ⚠️ `agents/run`, matching the run list this stands beside. What an agent spends is as sensitive
|
|
205
|
-
* as what it did, and the two must not be reachable on different terms.
|
|
206
|
-
*/
|
|
207
|
-
app.get("/agents/:agentId/costs", async (context) => {
|
|
208
|
-
const authorization = context.get("authorization");
|
|
209
|
-
return context.json(await deps.agentCosts.read(asAgentActor(authorization), context.req.param("agentId")));
|
|
210
|
-
});
|
|
211
|
-
for (const route of runtimeRoutes) {
|
|
212
|
-
app.on(route.method, `/agents/:agentId${route.pattern}`, async (context) => {
|
|
213
|
-
const authorization = context.get("authorization");
|
|
214
|
-
// The runtime's answer is returned untouched, headers included: the chat is a stream, and a
|
|
215
|
-
// body read here to inspect it would be a stream nobody could read afterwards.
|
|
216
|
-
return await deps.agents.forward({
|
|
217
|
-
actor: asAgentActor(authorization),
|
|
218
|
-
token: context.get("token"),
|
|
219
|
-
agentId: context.req.param("agentId"),
|
|
220
|
-
method: route.method,
|
|
221
|
-
path: route.path(context),
|
|
222
|
-
...(route.method === "POST" ? { body: await context.req.text() } : {}),
|
|
223
|
-
signal: context.req.raw.signal,
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
139
|
app.get("/nodes", async (context) => {
|
|
228
|
-
const auth = requireCapability(context, "
|
|
140
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
229
141
|
const url = new URL(context.req.url);
|
|
230
142
|
const unknownQuery = [];
|
|
231
143
|
url.searchParams.forEach((_value, key) => {
|
|
@@ -244,69 +156,24 @@ export function createHttp(deps) {
|
|
|
244
156
|
return context.json(await deps.nodes.list(asActor(auth), input));
|
|
245
157
|
});
|
|
246
158
|
app.get("/nodes/graph", async (context) => {
|
|
247
|
-
const auth = requireCapability(context, "
|
|
159
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
248
160
|
const url = new URL(context.req.url);
|
|
249
161
|
const input = NodeGraphInput.parse({
|
|
250
162
|
limit: url.searchParams.has("limit") ? Number(url.searchParams.get("limit")) : undefined,
|
|
251
163
|
});
|
|
252
164
|
return context.json(await deps.nodes.graph(asActor(auth), input));
|
|
253
165
|
});
|
|
254
|
-
// ⚠️ Before `/nodes/:nodeId`, or the router would read "agents" as a node ID. Same order and same
|
|
255
|
-
// reason as `/nodes/graph` above.
|
|
256
|
-
app.get("/nodes/agents", async (context) => {
|
|
257
|
-
const auth = requireCapability(context, "knowledge", "read");
|
|
258
|
-
const url = new URL(context.req.url);
|
|
259
|
-
requireKnownQuery(url, ["parentId", "includeArchived"]);
|
|
260
|
-
const parentId = url.searchParams.get("parentId");
|
|
261
|
-
// ⚠️ The key is only set when the caller sent it. Omitted means the whole tree and `null` means
|
|
262
|
-
// the root level, so defaulting a missing parameter to `null` here would silently answer a
|
|
263
|
-
// different question than the one asked.
|
|
264
|
-
const input = ListAgentsInput.parse({
|
|
265
|
-
...(url.searchParams.has("parentId") ? { parentId: parentId === "" ? null : parentId } : {}),
|
|
266
|
-
includeArchived: url.searchParams.get("includeArchived") === "true",
|
|
267
|
-
});
|
|
268
|
-
return context.json(await deps.nodes.listAgents(asActor(auth), input));
|
|
269
|
-
});
|
|
270
|
-
/**
|
|
271
|
-
* ⚠️ NO response in Intel's HTTP surface carries a credential any more (D29, #207).
|
|
272
|
-
*
|
|
273
|
-
* Creating an agent still mints its Gate Application with the caller's own bearer — so this needs
|
|
274
|
-
* `applications:write` in Gate on top of `knowledge:create` in Intel — but the key Gate issues
|
|
275
|
-
* once goes straight into the agent runtime over the service binding, inside this request. The
|
|
276
|
-
* answer is the same shape every agent read has. Until #207 it carried the key in plain text and
|
|
277
|
-
* a person had to put it into a Worker secret by hand, which is why an agent created here could
|
|
278
|
-
* never run (#200).
|
|
279
|
-
*/
|
|
280
|
-
app.post("/nodes/agents", async (context) => {
|
|
281
|
-
const auth = requireCapability(context, "knowledge", "create");
|
|
282
|
-
const input = CreateAgentInput.parse(await context.req.json().catch(() => null));
|
|
283
|
-
return context.json(await deps.nodes.createAgent(asActor(auth), input, { token: context.get("token") }), 201);
|
|
284
|
-
});
|
|
285
|
-
/**
|
|
286
|
-
* The repair path, and the only one an agent that lost its key has (D29, #207).
|
|
287
|
-
*
|
|
288
|
-
* ⚠️ `knowledge/write`, not `agents/run`. Which principal an agent acts as is a property of the
|
|
289
|
-
* agent, changed by whoever may change the agent; being allowed to press "Run now" is a different
|
|
290
|
-
* question and answers a different one. The resource ACL on the node is checked by the service.
|
|
291
|
-
*
|
|
292
|
-
* ⚠️ Before `/nodes/:nodeId`, like every other literal segment under `/nodes`.
|
|
293
|
-
*/
|
|
294
|
-
app.post("/nodes/agents/:nodeId/rotate-key", async (context) => {
|
|
295
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
296
|
-
const input = RotateAgentKeyInput.parse({ nodeId: context.req.param("nodeId") });
|
|
297
|
-
return context.json(await deps.nodes.rotateAgentKey(asActor(auth), input, { token: context.get("token") }));
|
|
298
|
-
});
|
|
299
166
|
// ⚠️ Before `/nodes/:nodeId`, or the router would read "export" as a node ID. The root has no
|
|
300
167
|
// node to address, so the whole installation — as this caller may read it — exports here (#136).
|
|
301
168
|
app.get("/nodes/export", async (context) => {
|
|
302
|
-
const auth = requireCapability(context, "
|
|
169
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
303
170
|
return zipResponse(await deps.bundle.exportSubtree(asBundleActor(auth), null));
|
|
304
171
|
});
|
|
305
172
|
// The reverse door (#137): a zip bundle — or a naked zipped folder — lands as a new subtree.
|
|
306
173
|
// The body IS the zip, streamed to the service rather than read whole here; `knowledge/create`
|
|
307
174
|
// like every other way of making nodes, and the resource-level write check is the service's.
|
|
308
175
|
async function importBundle(context, targetNodeId) {
|
|
309
|
-
const auth = requireCapability(context, "
|
|
176
|
+
const auth = requireCapability(context, "nodes", "create");
|
|
310
177
|
const idempotencyKey = ImportIdempotencyKey.parse(context.req.header("idempotency-key"));
|
|
311
178
|
const body = context.req.raw.body;
|
|
312
179
|
if (body === null) {
|
|
@@ -324,7 +191,7 @@ export function createHttp(deps) {
|
|
|
324
191
|
// ⚠️ Before `/nodes/:nodeId`, or the router would read "import" as a node ID.
|
|
325
192
|
app.post("/nodes/import", async (context) => await importBundle(context, null));
|
|
326
193
|
app.get("/nodes/:nodeId", async (context) => {
|
|
327
|
-
const auth = requireCapability(context, "
|
|
194
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
328
195
|
return context.json(await deps.nodes.get(asActor(auth), context.req.param("nodeId")));
|
|
329
196
|
});
|
|
330
197
|
app.post("/nodes/:nodeId/import", async (context) => {
|
|
@@ -333,33 +200,19 @@ export function createHttp(deps) {
|
|
|
333
200
|
});
|
|
334
201
|
// One subtree — a folder with everything beneath it, or a single node — as a zip bundle (#136).
|
|
335
202
|
app.get("/nodes/:nodeId/export", async (context) => {
|
|
336
|
-
const auth = requireCapability(context, "
|
|
203
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
337
204
|
const input = GetNodeInput.parse({ nodeId: context.req.param("nodeId") });
|
|
338
205
|
return zipResponse(await deps.bundle.exportSubtree(asBundleActor(auth), input.nodeId));
|
|
339
206
|
});
|
|
340
|
-
app.get("/nodes/:nodeId/agent", async (context) => {
|
|
341
|
-
const auth = requireCapability(context, "knowledge", "read");
|
|
342
|
-
return context.json(await deps.nodes.getAgent(asActor(auth), { nodeId: context.req.param("nodeId") }));
|
|
343
|
-
});
|
|
344
|
-
app.post("/nodes/:nodeId/agent", async (context) => {
|
|
345
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
346
|
-
const input = SaveAgentDefinitionInput.parse(await context.req.json().catch(() => null));
|
|
347
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
348
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
349
|
-
}
|
|
350
|
-
// ⚠️ The caller's own bearer travels on, like it does when an agent is created: writing a
|
|
351
|
-
// definition also arms what it schedules, and that call is made as the person saving (#214).
|
|
352
|
-
return context.json(await deps.nodes.saveAgentDefinition(asActor(auth), input, { token: context.get("token") }), 201);
|
|
353
|
-
});
|
|
354
207
|
app.get("/nodes/:nodeId/versions", async (context) => {
|
|
355
|
-
const auth = requireCapability(context, "
|
|
208
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
356
209
|
return context.json(await deps.nodes.listVersions(asActor(auth), context.req.param("nodeId")));
|
|
357
210
|
});
|
|
358
211
|
// The content behind one row of the list above (#147): what a version-pinned citation points at.
|
|
359
212
|
// Read capability like every other node read; the resource ACL and the node/version pairing are
|
|
360
213
|
// the service's to refuse.
|
|
361
214
|
app.get("/nodes/:nodeId/versions/:versionId/content", async (context) => {
|
|
362
|
-
const auth = requireCapability(context, "
|
|
215
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
363
216
|
const input = GetNodeVersionInput.parse({
|
|
364
217
|
nodeId: context.req.param("nodeId"),
|
|
365
218
|
versionId: context.req.param("versionId"),
|
|
@@ -371,13 +224,13 @@ export function createHttp(deps) {
|
|
|
371
224
|
// resource address says nothing to an HTTP client, whose address for the bytes is the sibling
|
|
372
225
|
// route below.
|
|
373
226
|
app.get("/nodes/:nodeId/attachment/meta", async (context) => {
|
|
374
|
-
const auth = requireCapability(context, "
|
|
227
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
375
228
|
const input = GetNodeInput.parse({ nodeId: context.req.param("nodeId") });
|
|
376
229
|
const { node, version } = await deps.nodes.getAttachment(asActor(auth), input.nodeId);
|
|
377
230
|
return context.json({ node, version });
|
|
378
231
|
});
|
|
379
232
|
app.get("/nodes/:nodeId/attachment", async (context) => {
|
|
380
|
-
const auth = requireCapability(context, "
|
|
233
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
381
234
|
const { attachment, body } = await deps.nodes.readAttachment(asActor(auth), context.req.param("nodeId"));
|
|
382
235
|
return new Response(body, {
|
|
383
236
|
headers: {
|
|
@@ -389,13 +242,13 @@ export function createHttp(deps) {
|
|
|
389
242
|
});
|
|
390
243
|
});
|
|
391
244
|
app.get("/nodes/:nodeId/table", async (context) => {
|
|
392
|
-
const auth = requireCapability(context, "
|
|
245
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
393
246
|
return context.json(await deps.nodes.getTable(asActor(auth), context.req.param("nodeId")));
|
|
394
247
|
});
|
|
395
248
|
// Defining the header and appending rows are two routes because they are two decisions: the
|
|
396
249
|
// header is written once and is the contract, an append is the everyday write (#40).
|
|
397
250
|
app.post("/nodes/:nodeId/table", async (context) => {
|
|
398
|
-
const auth = requireCapability(context, "
|
|
251
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
399
252
|
const input = DefineTableInput.parse(await context.req.json().catch(() => null));
|
|
400
253
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
401
254
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -403,7 +256,7 @@ export function createHttp(deps) {
|
|
|
403
256
|
return context.json(await deps.nodes.defineTable(asActor(auth), input), 201);
|
|
404
257
|
});
|
|
405
258
|
app.post("/nodes/:nodeId/table/rows", async (context) => {
|
|
406
|
-
const auth = requireCapability(context, "
|
|
259
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
407
260
|
const input = AppendTableRowsInput.parse(await context.req.json().catch(() => null));
|
|
408
261
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
409
262
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -415,7 +268,7 @@ export function createHttp(deps) {
|
|
|
415
268
|
// the `baseVersionId` that says which state they mean. Each writes one snapshot version, so the
|
|
416
269
|
// 201 is the same statement the append's 201 makes.
|
|
417
270
|
app.post("/nodes/:nodeId/table/rows/update", async (context) => {
|
|
418
|
-
const auth = requireCapability(context, "
|
|
271
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
419
272
|
const input = UpdateTableRowsInput.parse(await context.req.json().catch(() => null));
|
|
420
273
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
421
274
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -423,75 +276,27 @@ export function createHttp(deps) {
|
|
|
423
276
|
return context.json(await deps.nodes.updateTableRows(asActor(auth), input), 201);
|
|
424
277
|
});
|
|
425
278
|
app.post("/nodes/:nodeId/table/rows/delete", async (context) => {
|
|
426
|
-
const auth = requireCapability(context, "
|
|
279
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
427
280
|
const input = DeleteTableRowsInput.parse(await context.req.json().catch(() => null));
|
|
428
281
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
429
282
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
430
283
|
}
|
|
431
284
|
return context.json(await deps.nodes.deleteTableRows(asActor(auth), input), 201);
|
|
432
285
|
});
|
|
433
|
-
app.
|
|
434
|
-
const auth = requireCapability(context, "
|
|
286
|
+
app.patch("/nodes/:nodeId/table", async (context) => {
|
|
287
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
435
288
|
const input = RedefineTableInput.parse(await context.req.json().catch(() => null));
|
|
436
289
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
437
290
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
438
291
|
}
|
|
439
292
|
return context.json(await deps.nodes.redefineTable(asActor(auth), input), 201);
|
|
440
293
|
});
|
|
441
|
-
// The board (#285). One read and five task operations, all action-style POSTs for the same reason
|
|
442
|
-
// the table mutations are: what they address is a task ID in the body, and none of them has an
|
|
443
|
-
// address of its own to PUT to. Every one writes a version, so they all answer 201 like the
|
|
444
|
-
// table's do.
|
|
445
|
-
app.get("/nodes/:nodeId/board", async (context) => {
|
|
446
|
-
const auth = requireCapability(context, "knowledge", "read");
|
|
447
|
-
return context.json(await deps.nodes.getBoard(asActor(auth), { nodeId: context.req.param("nodeId") }));
|
|
448
|
-
});
|
|
449
|
-
app.post("/nodes/:nodeId/board/configure", async (context) => {
|
|
450
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
451
|
-
const input = ConfigureBoardInput.parse(await context.req.json().catch(() => null));
|
|
452
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
453
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
454
|
-
}
|
|
455
|
-
return context.json(await deps.nodes.configureBoard(asActor(auth), input), 201);
|
|
456
|
-
});
|
|
457
|
-
app.post("/nodes/:nodeId/board/tasks", async (context) => {
|
|
458
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
459
|
-
const input = AddBoardTaskInput.parse(await context.req.json().catch(() => null));
|
|
460
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
461
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
462
|
-
}
|
|
463
|
-
return context.json(await deps.nodes.addBoardTask(asActor(auth), input), 201);
|
|
464
|
-
});
|
|
465
|
-
app.post("/nodes/:nodeId/board/tasks/update", async (context) => {
|
|
466
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
467
|
-
const input = UpdateBoardTaskInput.parse(await context.req.json().catch(() => null));
|
|
468
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
469
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
470
|
-
}
|
|
471
|
-
return context.json(await deps.nodes.updateBoardTask(asActor(auth), input), 201);
|
|
472
|
-
});
|
|
473
|
-
app.post("/nodes/:nodeId/board/tasks/move", async (context) => {
|
|
474
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
475
|
-
const input = MoveBoardTaskInput.parse(await context.req.json().catch(() => null));
|
|
476
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
477
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
478
|
-
}
|
|
479
|
-
return context.json(await deps.nodes.moveBoardTask(asActor(auth), input), 201);
|
|
480
|
-
});
|
|
481
|
-
app.post("/nodes/:nodeId/board/tasks/delete", async (context) => {
|
|
482
|
-
const auth = requireCapability(context, "knowledge", "write");
|
|
483
|
-
const input = DeleteBoardTaskInput.parse(await context.req.json().catch(() => null));
|
|
484
|
-
if (input.nodeId !== context.req.param("nodeId")) {
|
|
485
|
-
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
486
|
-
}
|
|
487
|
-
return context.json(await deps.nodes.deleteBoardTask(asActor(auth), input), 201);
|
|
488
|
-
});
|
|
489
294
|
// ⚠️ There is deliberately no route for the sixth board operation. `board_repair_task_ids`
|
|
490
295
|
// (anchrd/intel#341) is MCP-only until a screen calls it: the routes here are what the browser
|
|
491
296
|
// reaches, nothing in `intel-data-provider` asks for it, and a route with no caller is the thing
|
|
492
297
|
// YAGNI is about. The screen is anchrd/intel#358, and it brings this route with it.
|
|
493
298
|
app.get("/nodes/:nodeId/links", async (context) => {
|
|
494
|
-
const auth = requireCapability(context, "
|
|
299
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
495
300
|
return context.json(await deps.nodes.listLinks(asActor(auth), context.req.param("nodeId")));
|
|
496
301
|
});
|
|
497
302
|
// A document link's name, for whoever is reading the text (#41). A POST because the list of IDs
|
|
@@ -502,27 +307,27 @@ export function createHttp(deps) {
|
|
|
502
307
|
// There is no route to create or delete a link any more. A relationship is written where it is
|
|
503
308
|
// meant, in the text, and `POST /nodes/:nodeId/versions` is what records it.
|
|
504
309
|
app.post("/nodes/links/resolve", async (context) => {
|
|
505
|
-
const auth = requireCapability(context, "
|
|
310
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
506
311
|
const input = ResolveNodeLinksInput.parse(await context.req.json().catch(() => null));
|
|
507
312
|
return context.json(await deps.nodes.resolveLinks(asActor(auth), input));
|
|
508
313
|
});
|
|
509
314
|
app.post("/nodes/search", async (context) => {
|
|
510
|
-
const auth = requireCapability(context, "
|
|
315
|
+
const auth = requireCapability(context, "nodes", "read");
|
|
511
316
|
const input = SearchInput.parse(await context.req.json().catch(() => null));
|
|
512
317
|
return context.json(await deps.nodes.search(asActor(auth), input));
|
|
513
318
|
});
|
|
514
|
-
app.post("/
|
|
319
|
+
app.post("/nodes/reindex", async (context) => {
|
|
515
320
|
const auth = requireCapability(context, "intel", "admin");
|
|
516
321
|
z.strictObject({}).parse(await context.req.json().catch(() => null));
|
|
517
322
|
return context.json(await deps.nodes.reindex(asActor(auth)), 202);
|
|
518
323
|
});
|
|
519
324
|
app.post("/nodes", async (context) => {
|
|
520
|
-
const auth = requireCapability(context, "
|
|
325
|
+
const auth = requireCapability(context, "nodes", "create");
|
|
521
326
|
const input = CreateNodeInput.parse(await context.req.json().catch(() => null));
|
|
522
327
|
return context.json(await deps.nodes.create(asActor(auth), input), 201);
|
|
523
328
|
});
|
|
524
329
|
app.post("/nodes/:nodeId/versions", async (context) => {
|
|
525
|
-
const auth = requireCapability(context, "
|
|
330
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
526
331
|
const input = SaveNodeVersionInput.parse(await context.req.json().catch(() => null));
|
|
527
332
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
528
333
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -530,7 +335,7 @@ export function createHttp(deps) {
|
|
|
530
335
|
return context.json(await deps.nodes.save(asActor(auth), input), 201);
|
|
531
336
|
});
|
|
532
337
|
app.post("/nodes/:nodeId/attachment", async (context) => {
|
|
533
|
-
const auth = requireCapability(context, "
|
|
338
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
534
339
|
const input = SaveAttachmentInput.parse(await context.req.json().catch(() => null));
|
|
535
340
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
536
341
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -538,7 +343,7 @@ export function createHttp(deps) {
|
|
|
538
343
|
return context.json(await deps.nodes.saveAttachment(asActor(auth), input), 201);
|
|
539
344
|
});
|
|
540
345
|
app.patch("/nodes/:nodeId", async (context) => {
|
|
541
|
-
const auth = requireCapability(context, "
|
|
346
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
542
347
|
const input = UpdateNodeInput.parse(await context.req.json().catch(() => null));
|
|
543
348
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
544
349
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
@@ -546,21 +351,21 @@ export function createHttp(deps) {
|
|
|
546
351
|
return context.json(await deps.nodes.update(asActor(auth), input));
|
|
547
352
|
});
|
|
548
353
|
app.post("/nodes/:nodeId/archive", async (context) => {
|
|
549
|
-
const auth = requireCapability(context, "
|
|
354
|
+
const auth = requireCapability(context, "nodes", "write");
|
|
550
355
|
const input = ArchiveNodeInput.parse(await context.req.json().catch(() => null));
|
|
551
356
|
if (input.nodeId !== context.req.param("nodeId")) {
|
|
552
357
|
throw new IntelError(400, "node_id_mismatch", "Path and body node IDs differ");
|
|
553
358
|
}
|
|
554
359
|
// The caller's own bearer, for the one thing archiving may have to do in Gate: switch off the
|
|
555
360
|
// Application behind an agent (#182). Every other kind never reaches it.
|
|
556
|
-
return context.json(await deps.nodes.archive(asActor(auth), input
|
|
361
|
+
return context.json(await deps.nodes.archive(asActor(auth), input));
|
|
557
362
|
});
|
|
558
363
|
app.get("/nodes/:nodeId/grants", async (context) => {
|
|
559
|
-
const auth = requireCapability(context, "
|
|
364
|
+
const auth = requireCapability(context, "nodes", "share");
|
|
560
365
|
return context.json(await deps.nodes.listGrants(asActor(auth), context.req.param("nodeId")));
|
|
561
366
|
});
|
|
562
367
|
app.post("/nodes/:nodeId/grants", async (context) => {
|
|
563
|
-
const auth = requireCapability(context, "
|
|
368
|
+
const auth = requireCapability(context, "nodes", "share");
|
|
564
369
|
const input = ShareInput.parse(await context.req.json().catch(() => null));
|
|
565
370
|
if (input.resourceId !== context.req.param("nodeId")) {
|
|
566
371
|
throw new IntelError(400, "node_id_mismatch", "Path and body resource IDs differ");
|
|
@@ -568,7 +373,7 @@ export function createHttp(deps) {
|
|
|
568
373
|
return context.json(await deps.nodes.share(asActor(auth), input), 201);
|
|
569
374
|
});
|
|
570
375
|
app.post("/nodes/:nodeId/grants/:grantId/revoke", async (context) => {
|
|
571
|
-
const auth = requireCapability(context, "
|
|
376
|
+
const auth = requireCapability(context, "nodes", "share");
|
|
572
377
|
const input = RevokeGrantInput.parse(await context.req.json().catch(() => null));
|
|
573
378
|
if (input.resourceId !== context.req.param("nodeId") ||
|
|
574
379
|
input.grantId !== context.req.param("grantId")) {
|
|
@@ -757,7 +562,7 @@ export function createHttp(deps) {
|
|
|
757
562
|
const input = GetFlowRunInput.parse({ runId: context.req.param("runId") });
|
|
758
563
|
return context.json(await deps.flows.listRunSteps(asFlowActor(auth), input.runId));
|
|
759
564
|
});
|
|
760
|
-
app.post("/flow-runs/:runId/complete", async (context) => {
|
|
565
|
+
app.post("/flow-runs/:runId/steps/complete", async (context) => {
|
|
761
566
|
const auth = requireCapability(context, "flows", "run");
|
|
762
567
|
const input = CompleteFlowRunStepInput.parse(await context.req.json().catch(() => null));
|
|
763
568
|
if (input.runId !== context.req.param("runId")) {
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { GateClient } from "@anchrd/gate-sdk";
|
|
2
|
-
import type { AgentCostsService } from "../agent-costs/agent-costs.types.js";
|
|
3
|
-
import type { AgentRuntimeService } from "../agent-runtime/agent-runtime.types.js";
|
|
4
2
|
import type { BrowserAuth } from "../auth/auth.types.js";
|
|
5
3
|
import type { BundleService } from "../bundle/bundle.types.js";
|
|
6
4
|
import type { FlowService } from "../flows/flows.types.js";
|
|
7
|
-
import type { ModelCatalogService } from "../model-catalog/model-catalog.types.js";
|
|
8
5
|
import type { NodeService } from "../nodes/nodes.types.js";
|
|
9
6
|
import type { ToolService } from "../tools/tools.types.js";
|
|
10
7
|
export interface HttpDeps {
|
|
@@ -13,11 +10,6 @@ export interface HttpDeps {
|
|
|
13
10
|
flows: FlowService;
|
|
14
11
|
tools: ToolService;
|
|
15
12
|
bundle: BundleService;
|
|
16
|
-
agents: AgentRuntimeService;
|
|
17
|
-
/** What an agent has cost, read out of the AI Gateway log rather than computed here (#251). */
|
|
18
|
-
agentCosts: AgentCostsService;
|
|
19
|
-
/** What the models on offer cost, read from Cloudflare rather than typed out here (#257). */
|
|
20
|
-
models: ModelCatalogService;
|
|
21
13
|
resource: string;
|
|
22
14
|
resourceMetadataUrl: string;
|
|
23
15
|
auth?: Pick<BrowserAuth, "resolve">;
|