@anchrd/intel-api 0.37.0 → 0.38.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/db/db-feed.js +12 -0
- package/dist/adapters/db/db.js +80 -8
- package/dist/mcp/mcp.js +76 -5
- package/dist/nodes/nodes.js +17 -0
- package/dist/nodes/nodes.types.d.ts +11 -0
- package/package.json +3 -3
|
@@ -76,6 +76,17 @@ function parseMetadata(raw) {
|
|
|
76
76
|
* exist. Both halves fall back on the metadata `parentId` for the one event whose row is gone.
|
|
77
77
|
*/
|
|
78
78
|
const FLOW_PARENT = "COALESCE(flow.parent_id, json_extract(e.metadata_json, '$.parentId'))";
|
|
79
|
+
// ⚠️ A card whose title is NULL names nothing, and `FeedEvent.resourceTitle` refuses it — the reader
|
|
80
|
+
// gets a validation error instead of a feed. It is a real state and not a theoretical one (#763,
|
|
81
|
+
// found against the running installation): an ADMIN passes `flowInSubtree` on its first branch
|
|
82
|
+
// alone, so events about a flow whose row is gone reach this far, and only `flows.purge` carries a
|
|
83
|
+
// title in its metadata. `flows.create` and `flows.archive` carry none, and there is nothing left
|
|
84
|
+
// to read one from.
|
|
85
|
+
//
|
|
86
|
+
// ⚠️ The node half cannot reach this state, and that asymmetry is the whole reason it was missed:
|
|
87
|
+
// there the check is `COALESCE(n.id, …) IN (SELECT id FROM allowed)`, a join against real rows with
|
|
88
|
+
// no admin short-circuit in front of it, so a deleted node's events drop out on their own.
|
|
89
|
+
const HAS_TITLE = "COALESCE(n.title, flow.title, json_extract(e.metadata_json, '$.title')) IS NOT NULL";
|
|
79
90
|
const NODE_PARENT = "COALESCE(n.id, json_extract(e.metadata_json, '$.parentId'))";
|
|
80
91
|
export const feedPageQuery = `${subtreeCte}
|
|
81
92
|
SELECT e.id, e.actor_id, e.action, e.resource_id,
|
|
@@ -98,6 +109,7 @@ export const feedPageQuery = `${subtreeCte}
|
|
|
98
109
|
AND ${flowInSubtreeOver(FLOW_PARENT)}
|
|
99
110
|
)
|
|
100
111
|
)
|
|
112
|
+
AND ${HAS_TITLE}
|
|
101
113
|
AND (? IS NULL OR e.actor_id = ?)
|
|
102
114
|
AND (
|
|
103
115
|
? IS NULL
|
package/dist/adapters/db/db.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { descendantsCte, grantColumns as grantColumnsFor, grantInForce, mapGrant, nodeVerbBindings, nodeVerbQuery, principalColumns, subtreeBindings, subtreeCte, } from "./db-grants.js";
|
|
1
|
+
import { descendantsCte, flowInSubtree, flowInSubtreeBindings, grantColumns as grantColumnsFor, grantInForce, mapGrant, nodeVerbBindings, nodeVerbQuery, principalColumns, subtreeBindings, subtreeCte, } from "./db-grants.js";
|
|
2
2
|
const versionColumns = `id, node_id, sequence, content_key, media_type, content_hash,
|
|
3
3
|
size, segment, created_by, created_at`;
|
|
4
4
|
const grantColumns = grantColumnsFor("node_id");
|
|
@@ -70,6 +70,18 @@ export function createNodeRepository(deps) {
|
|
|
70
70
|
// same question of the same table.
|
|
71
71
|
const visibleCte = subtreeCte;
|
|
72
72
|
const readBindings = (actor) => subtreeBindings(actor, "read", deps.now().toISOString());
|
|
73
|
+
/**
|
|
74
|
+
* What a LEVEL query binds: the walk over `nodes`, then the flow half of `has_children`, in the
|
|
75
|
+
* order the two appear in the statement.
|
|
76
|
+
*
|
|
77
|
+
* ⚠️ One clock reading for both halves. Two calls to `deps.now()` inside one statement would
|
|
78
|
+
* measure the same `expires_at` against two different moments, and a grant that expires between
|
|
79
|
+
* them would be honoured on one side of the OR and refused on the other.
|
|
80
|
+
*/
|
|
81
|
+
const levelBindings = (actor) => {
|
|
82
|
+
const now = deps.now().toISOString();
|
|
83
|
+
return [...subtreeBindings(actor, "read", now), ...flowInSubtreeBindings(actor, "read", now)];
|
|
84
|
+
};
|
|
73
85
|
/**
|
|
74
86
|
* The optional folder cut a search carries (#126), in the three places one statement needs it:
|
|
75
87
|
* the extra CTE, the extra join, and the one binding between the actor's and the query's. All
|
|
@@ -126,8 +138,14 @@ export function createNodeRepository(deps) {
|
|
|
126
138
|
* everywhere would make a link to a task a dead end.
|
|
127
139
|
*/
|
|
128
140
|
const notInTree = (alias) => `${alias}.kind <> 'task'`;
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
/**
|
|
142
|
+
* The chevron column, and it is drawn ONLY for the tree (#777). The bounded read the relation
|
|
143
|
+
* graph makes discards it, and since #777 it is no longer free: computing it costs a second
|
|
144
|
+
* correlated `EXISTS` per row and six bindings that statement has no other use for. Leaving it
|
|
145
|
+
* out there is not a divergence in the visibility predicate, which is what the two forms share;
|
|
146
|
+
* it is one column the caller never reads.
|
|
147
|
+
*/
|
|
148
|
+
const chevronColumn = `
|
|
131
149
|
-- Whether this row has children THIS actor may see (#59), asked of the same allowed set one
|
|
132
150
|
-- level down. A second rule written here would drift from the one above it, and the drift
|
|
133
151
|
-- would show as a chevron that opens onto nothing.
|
|
@@ -137,11 +155,29 @@ export function createNodeRepository(deps) {
|
|
|
137
155
|
-- actor can see, and the chevron falls away by itself (D66, #376). Leaving it out here would
|
|
138
156
|
-- draw a chevron that opens onto an empty level — exactly the drift the comment above warns
|
|
139
157
|
-- about, arriving through the door it was written for.
|
|
140
|
-
|
|
158
|
+
--
|
|
159
|
+
-- ⚠️ And the SECOND half of the level, because the tree is shared: a folder holds nodes and
|
|
160
|
+
-- flows side by side (ADR-0004 §1), and a flow is not a node. Asking only the nodes table
|
|
161
|
+
-- left a folder whose only children are flows answering "empty": no chevron, no way down,
|
|
162
|
+
-- while clicking the folder listed both kinds perfectly well (#777). It is the same root as
|
|
163
|
+
-- #457, where a count over nodes alone held such a folder for empty on the delete path; that
|
|
164
|
+
-- one was fixed, this one was not.
|
|
165
|
+
--
|
|
166
|
+
-- ⚠️ flowInSubtree rather than a hand-written condition, for the reason stated above it: it
|
|
167
|
+
-- is the very predicate db-flows.ts draws the level with, so the chevron and the level it
|
|
168
|
+
-- opens onto cannot answer differently. Its folder clause is already satisfied here (the
|
|
169
|
+
-- flow's parent is n, and n is joined to allowed), so today it can only widen the answer,
|
|
170
|
+
-- never narrow it, which is the only direction a chevron may drift in at all.
|
|
171
|
+
(EXISTS (
|
|
141
172
|
SELECT 1 FROM nodes child
|
|
142
173
|
JOIN allowed AS allowed_child ON allowed_child.id = child.id
|
|
143
174
|
WHERE child.parent_id = n.id AND child.archived_at IS NULL AND ${notInTree("child")}
|
|
144
|
-
)
|
|
175
|
+
) OR EXISTS (
|
|
176
|
+
SELECT 1 FROM flows flow
|
|
177
|
+
WHERE flow.parent_id = n.id AND flow.archived_at IS NULL AND ${flowInSubtree}
|
|
178
|
+
)) AS has_children`;
|
|
179
|
+
const visibleChildren = (bounded) => `${visibleCte}
|
|
180
|
+
SELECT ${nodeColumns}${bounded ? ", COUNT(*) OVER () AS total" : `,${chevronColumn}`}
|
|
145
181
|
FROM nodes n
|
|
146
182
|
JOIN allowed ON allowed.id = n.id
|
|
147
183
|
WHERE ${levelPredicate} AND (? = 1 OR n.archived_at IS NULL) AND ${notInTree("n")}
|
|
@@ -166,9 +202,17 @@ export function createNodeRepository(deps) {
|
|
|
166
202
|
async listVisible(actor, input) {
|
|
167
203
|
const result = await deps.db
|
|
168
204
|
.prepare(input.archivedOnly ? archivedEverywhere : visibleChildren(false))
|
|
169
|
-
.bind(
|
|
170
|
-
|
|
171
|
-
|
|
205
|
+
.bind(
|
|
206
|
+
// ⚠️ The archive statement answers `has_children` as a literal 0, so it carries no flow
|
|
207
|
+
// bindings; the level statement carries both halves. Same branch, two bind lists.
|
|
208
|
+
...(input.archivedOnly
|
|
209
|
+
? readBindings(actor)
|
|
210
|
+
: [
|
|
211
|
+
...levelBindings(actor),
|
|
212
|
+
input.parentId,
|
|
213
|
+
input.parentId,
|
|
214
|
+
input.includeArchived ? 1 : 0,
|
|
215
|
+
]))
|
|
172
216
|
.all();
|
|
173
217
|
const rows = result.results ?? [];
|
|
174
218
|
return {
|
|
@@ -179,6 +223,9 @@ export function createNodeRepository(deps) {
|
|
|
179
223
|
async listVisibleBounded(actor, input) {
|
|
180
224
|
const result = await deps.db
|
|
181
225
|
.prepare(visibleChildren(true))
|
|
226
|
+
// ⚠️ `readBindings`, not `levelBindings`: this form carries no chevron column, so the
|
|
227
|
+
// flow half's six bindings have no placeholder to land in. D1 answers a miscount with
|
|
228
|
+
// `Wrong number of parameter bindings`, so the two cannot silently go out of step.
|
|
182
229
|
.bind(...readBindings(actor), input.parentId, input.parentId, 0, input.limit)
|
|
183
230
|
.all();
|
|
184
231
|
const rows = result.results ?? [];
|
|
@@ -1340,6 +1387,31 @@ export function createNodeRepository(deps) {
|
|
|
1340
1387
|
.all();
|
|
1341
1388
|
return { nodes, links: (linkResult.results ?? []).map(mapLink) };
|
|
1342
1389
|
},
|
|
1390
|
+
/**
|
|
1391
|
+
* The same `allowed` set every other read joins against, narrowed to attachments (#773).
|
|
1392
|
+
*
|
|
1393
|
+
* ⚠️ Ordered and paged by `n.id` rather than by title: the id is the cursor, and a cursor has to
|
|
1394
|
+
* be unique or a page boundary falling between two identical titles silently drops one of them.
|
|
1395
|
+
* `graphVisible` above orders by title because it draws a picture and takes the whole set.
|
|
1396
|
+
*/
|
|
1397
|
+
async listVisibleAttachments(actor, input) {
|
|
1398
|
+
const result = await deps.db
|
|
1399
|
+
.prepare(`${visibleCte}
|
|
1400
|
+
SELECT ${nodeColumns}
|
|
1401
|
+
FROM nodes n
|
|
1402
|
+
JOIN allowed ON allowed.id = n.id
|
|
1403
|
+
WHERE n.archived_at IS NULL
|
|
1404
|
+
AND n.kind = 'attachment'
|
|
1405
|
+
AND (? IS NULL OR n.id > ?)
|
|
1406
|
+
ORDER BY n.id
|
|
1407
|
+
LIMIT ?`)
|
|
1408
|
+
// ⚠️ Positional throughout, and the cursor is bound TWICE. Numbered parameters (?1, ?2)
|
|
1409
|
+
// would count from the first placeholder of the whole statement — and `visibleCte` above
|
|
1410
|
+
// has already spent several of them.
|
|
1411
|
+
.bind(...readBindings(actor), input.after, input.after, input.limit)
|
|
1412
|
+
.all();
|
|
1413
|
+
return (result.results ?? []).map(mapNode);
|
|
1414
|
+
},
|
|
1343
1415
|
async setGrant(input) {
|
|
1344
1416
|
const grant = input.grant;
|
|
1345
1417
|
const { type: principalType, id: principalId } = principalColumns(grant.principal);
|
package/dist/mcp/mcp.js
CHANGED
|
@@ -4,7 +4,7 @@ import { BoardAssigneeResolveInput, BoardAssigneeSearchInput, BoardGetInput, Boa
|
|
|
4
4
|
import { FeedListRequest } from "@anchrd/intel-contract/feed";
|
|
5
5
|
import { ArchiveFlowInput, CreateFlowInput, GetFlowInput, GetFlowVersionInput, ListFlowsInput, PreviewFlowPublishInput, PublishFlowInput, PurgeFlowInput, RelationGraphInput, SaveFlowVersionInput, UnpublishFlowInput, UpdateFlowInput, } from "@anchrd/intel-contract/flow";
|
|
6
6
|
import { CancelFlowRunInput, CompleteFlowRunStepInput, GetFlowRunInput, ListFlowRunsInput, StartFlowRunInput, } from "@anchrd/intel-contract/flow-run";
|
|
7
|
-
import { ArchiveNodeInput, CreateNodeInput, GetNodeInput, GetNodeVersionInput, ListNodesInput, NodeGraphInput, PurgeNodeInput, PurgeNodePreviewInput, ResolveNodeLinksInput, SaveAttachmentInput, SaveNodeVersionInput, SearchInput, UpdateNodeInput, } from "@anchrd/intel-contract/node";
|
|
7
|
+
import { ArchiveNodeInput, CreateNodeInput, GetAttachmentInput, GetNodeInput, GetNodeVersionInput, ListNodesInput, NodeGraphInput, PurgeNodeInput, PurgeNodePreviewInput, ResolveNodeLinksInput, SaveAttachmentInput, SaveNodeVersionInput, SearchInput, UpdateNodeInput, } from "@anchrd/intel-contract/node";
|
|
8
8
|
import { ListEffectiveAccessInput, ListFlowEffectiveAccessInput, ListFlowGrantsInput, ListGrantsInput, RevokeFlowGrantInput, RevokeGrantInput, ShareFlowInput, ShareInput, } from "@anchrd/intel-contract/share";
|
|
9
9
|
import { AppendTableRowsInput, DefineTableInput, DeleteTableRowsInput, GetTableInput, RedefineTableInput, UpdateTableRowsInput, } from "@anchrd/intel-contract/table";
|
|
10
10
|
import { ExecuteToolInput, TestToolInput } from "@anchrd/intel-contract/tool";
|
|
@@ -329,8 +329,11 @@ export async function handleMcp(request, deps) {
|
|
|
329
329
|
}, async (input) => text(await deps.nodes.getVersion(actor, input.nodeId, input.versionId)));
|
|
330
330
|
server.registerTool("node_attachment_get", {
|
|
331
331
|
title: "Get node attachment",
|
|
332
|
-
description: "Get authorized immutable attachment metadata and
|
|
333
|
-
|
|
332
|
+
description: "Get authorized immutable attachment metadata, and with includeContent the file itself. " +
|
|
333
|
+
"The resourceUri in the answer is this server's own address; a portal that aggregates " +
|
|
334
|
+
"several servers prefixes it, so read the file through includeContent unless the URI " +
|
|
335
|
+
"came from resources/list.",
|
|
336
|
+
inputSchema: GetAttachmentInput,
|
|
334
337
|
annotations: {
|
|
335
338
|
title: "Get node attachment",
|
|
336
339
|
readOnlyHint: true,
|
|
@@ -338,8 +341,76 @@ export async function handleMcp(request, deps) {
|
|
|
338
341
|
idempotentHint: true,
|
|
339
342
|
openWorldHint: false,
|
|
340
343
|
},
|
|
341
|
-
},
|
|
342
|
-
|
|
344
|
+
},
|
|
345
|
+
/**
|
|
346
|
+
* ⚠️ The size is checked BEFORE the stream is opened, which the resource handler below cannot
|
|
347
|
+
* do — it is handed the body by the SDK and has to cancel it. Here the metadata read is its
|
|
348
|
+
* own call, so an oversized attachment costs no R2 read at all.
|
|
349
|
+
*
|
|
350
|
+
* ⚠️ The bytes go out as an embedded RESOURCE block, never as text. A base64 string in a text
|
|
351
|
+
* block is something a model will try to read; a resource block is a file to a client that
|
|
352
|
+
* understands one, and the metadata stays in the first block either way (#773).
|
|
353
|
+
*/
|
|
354
|
+
async (input) => {
|
|
355
|
+
const metadata = await deps.nodes.getAttachment(actor, input.nodeId);
|
|
356
|
+
if (!input.includeContent)
|
|
357
|
+
return text(metadata);
|
|
358
|
+
if (metadata.version.size > AttachmentInlineLimit) {
|
|
359
|
+
throw new IntelError(413, "attachment_too_large", "Attachment is too large to inline; read it over HTTP instead");
|
|
360
|
+
}
|
|
361
|
+
const { attachment, body } = await deps.nodes.readAttachment(actor, input.nodeId);
|
|
362
|
+
return {
|
|
363
|
+
content: [
|
|
364
|
+
{ type: "text", text: JSON.stringify(attachment) },
|
|
365
|
+
{
|
|
366
|
+
type: "resource",
|
|
367
|
+
resource: {
|
|
368
|
+
uri: `intel://nodes/${attachment.node.id}/attachment`,
|
|
369
|
+
mimeType: attachment.version.mediaType,
|
|
370
|
+
blob: await base64(body),
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
isError: false,
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
server.registerResource("node-attachment",
|
|
378
|
+
/**
|
|
379
|
+
* ⚠️ `list` is what makes the attachments findable at all, and it was `undefined` until #773.
|
|
380
|
+
* A client that cannot enumerate them has to GUESS the URI — and the one place it could copy
|
|
381
|
+
* one from, `node_attachment_get`, answers this server's own address, which a portal that
|
|
382
|
+
* aggregates several servers prefixes. Measured on 2026-08-24: the URI out of that tool was
|
|
383
|
+
* refused as `Resource not found` while the prefixed spelling delivered the file.
|
|
384
|
+
*
|
|
385
|
+
* ⚠️ The listing is COMPLETE, and the loop below is why it has to be. `ListResourcesCallback`
|
|
386
|
+
* takes `RequestHandlerExtra` and nothing else — the SDK hands a template's list callback no
|
|
387
|
+
* cursor (checked against the 1.30.0 type: `(extra) => ListResourcesResult`), so a
|
|
388
|
+
* `nextCursor` in the answer is one nothing would ever send back. That leaves two honest
|
|
389
|
+
* options and one dishonest one: answer everything, refuse, or cut the list at some limit and
|
|
390
|
+
* let it read as complete. The cut is the one that is out — a reader would never learn which
|
|
391
|
+
* of their files are missing.
|
|
392
|
+
*
|
|
393
|
+
* The paging is therefore INTERNAL: D1 is asked in pages so one statement never has to carry
|
|
394
|
+
* the whole set, and the loop ends when the service stops handing back a cursor.
|
|
395
|
+
*/
|
|
396
|
+
new ResourceTemplate("intel://nodes/{nodeId}/attachment", {
|
|
397
|
+
list: async () => {
|
|
398
|
+
const resources = [];
|
|
399
|
+
let after = null;
|
|
400
|
+
do {
|
|
401
|
+
const page = await deps.nodes.listAttachments(actor, { limit: 100, after });
|
|
402
|
+
resources.push(...page.items.map((node) => ({
|
|
403
|
+
uri: `intel://nodes/${node.id}/attachment`,
|
|
404
|
+
name: node.title,
|
|
405
|
+
// ⚠️ No `mimeType`: it lives on the VERSION, so answering one would cost a version
|
|
406
|
+
// read per row. The read itself carries the real one.
|
|
407
|
+
description: node.description ?? undefined,
|
|
408
|
+
})));
|
|
409
|
+
after = page.nextCursor;
|
|
410
|
+
} while (after !== null);
|
|
411
|
+
return { resources };
|
|
412
|
+
},
|
|
413
|
+
}), { title: "Intel node attachment" }, async (uri, variables) => {
|
|
343
414
|
const { attachment, body } = await deps.nodes.readAttachment(actor, String(variables.nodeId));
|
|
344
415
|
if (attachment.version.size > AttachmentInlineLimit) {
|
|
345
416
|
await body.cancel();
|
package/dist/nodes/nodes.js
CHANGED
|
@@ -782,6 +782,23 @@ export function createNodes(deps) {
|
|
|
782
782
|
throw new IntelError(500, "content_missing", "Attachment is missing");
|
|
783
783
|
return { attachment: metadata, body };
|
|
784
784
|
},
|
|
785
|
+
/**
|
|
786
|
+
* One page of readable attachments for the MCP resource listing (#773).
|
|
787
|
+
*
|
|
788
|
+
* ⚠️ It asks the repository for one row MORE than it hands back. Without that, a page that
|
|
789
|
+
* happens to be exactly full is indistinguishable from a page with more behind it, and the
|
|
790
|
+
* choice is between always sending a cursor (one wasted round trip per listing) or never
|
|
791
|
+
* sending one (a silently truncated list, which reads as complete).
|
|
792
|
+
*/
|
|
793
|
+
async listAttachments(actor, input) {
|
|
794
|
+
const rows = await deps.repository.listVisibleAttachments(actor, {
|
|
795
|
+
limit: input.limit + 1,
|
|
796
|
+
after: input.after,
|
|
797
|
+
});
|
|
798
|
+
const items = rows.slice(0, input.limit);
|
|
799
|
+
const last = items.at(-1);
|
|
800
|
+
return { items, nextCursor: rows.length > input.limit && last ? last.id : null };
|
|
801
|
+
},
|
|
785
802
|
async getTable(actor, nodeId) {
|
|
786
803
|
const node = await requireVisible(actor, nodeId);
|
|
787
804
|
if (node.kind !== "table") {
|
|
@@ -56,6 +56,10 @@ export interface NodeRepository {
|
|
|
56
56
|
}): Promise<BoundedChildren>;
|
|
57
57
|
getVisible(actor: Actor, nodeId: string): Promise<Node | null>;
|
|
58
58
|
listVisibleSubtree(actor: Actor, rootId: string | null): Promise<SubtreeNode[]>;
|
|
59
|
+
listVisibleAttachments(actor: Actor, input: {
|
|
60
|
+
limit: number;
|
|
61
|
+
after: string | null;
|
|
62
|
+
}): Promise<Node[]>;
|
|
59
63
|
can(actor: Actor, nodeId: string, verb: ResourceVerb): Promise<boolean>;
|
|
60
64
|
findIdempotentNode(actorId: string, operation: "node.create" | "node.save" | "node.append" | "node.update" | "node.archive" | "node.share" | "node.revoke" | SnapshotOperation, idempotencyKey: string): Promise<string | null>;
|
|
61
65
|
findIdempotentRevocation(actorId: string, idempotencyKey: string): Promise<boolean | null>;
|
|
@@ -325,6 +329,13 @@ export interface NodeService {
|
|
|
325
329
|
redefineTable(actor: Actor, input: RedefineTableInput): Promise<NodeTable>;
|
|
326
330
|
getAttachment(actor: Actor, nodeId: string): Promise<NodeAttachment>;
|
|
327
331
|
readAttachment(actor: Actor, nodeId: string): Promise<NodeAttachmentBody>;
|
|
332
|
+
listAttachments(actor: Actor, input: {
|
|
333
|
+
limit: number;
|
|
334
|
+
after: string | null;
|
|
335
|
+
}): Promise<{
|
|
336
|
+
items: Node[];
|
|
337
|
+
nextCursor: string | null;
|
|
338
|
+
}>;
|
|
328
339
|
listVersions(actor: Actor, nodeId: string): Promise<{
|
|
329
340
|
items: NodeVersion[];
|
|
330
341
|
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anchrd/intel-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@anchrd/gate-sdk": "^0.
|
|
46
|
-
"@anchrd/intel-contract": "^0.
|
|
45
|
+
"@anchrd/gate-sdk": "^0.26.0",
|
|
46
|
+
"@anchrd/intel-contract": "^0.30.0",
|
|
47
47
|
"@cfworker/json-schema": "^4.1.1",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
49
|
"fflate": "^0.8.3",
|