@anchrd/intel-ui 0.1.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 ADDED
@@ -0,0 +1,18 @@
1
+ # @anchrd/intel-ui
2
+
3
+ The source-built Intel browser application. Install it next to `@anchrd/intel`, then run:
4
+
5
+ ```bash
6
+ npx intel build
7
+ ```
8
+
9
+ The CLI applies `intel.json` at build time and writes the customer-owned static output to
10
+ `.intel/ui`. Theme CSS changes shadcn semantic variables, so customer design never requires editing
11
+ React components.
12
+
13
+ The browser application uses same-origin `HttpOnly` sessions established by the Intel Worker against
14
+ Gate OAuth. It contains no service key or browser-side access-token storage.
15
+
16
+ Knowledge uses BlockNote, Flows uses React Flow, and Tools presents the MCP catalog. Route-level
17
+ code splitting keeps both editors outside the initial application bundle. Every product component
18
+ uses shadcn semantic tokens so an `intel.json` theme can replace the customer design safely.
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": { "css": "src/styles.css", "baseColor": "neutral", "cssVariables": true },
7
+ "aliases": {
8
+ "components": "@/components",
9
+ "utils": "@/lib/utils",
10
+ "ui": "@/components/ui",
11
+ "lib": "@/lib",
12
+ "hooks": "@/hooks"
13
+ },
14
+ "iconLibrary": "lucide"
15
+ }
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" href="/src/branding/favicon.svg" />
7
+ <title>Intel</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@anchrd/intel-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "UNLICENSED",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/anchrd/intel.git",
9
+ "directory": "packages/ui"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "files": [
15
+ "src",
16
+ "!src/**/*.unit.ts",
17
+ "!src/**/*.unit.tsx",
18
+ "index.html",
19
+ "vite.config.ts",
20
+ "tsconfig.json",
21
+ "components.json"
22
+ ],
23
+ "scripts": {
24
+ "build": "vite build",
25
+ "dev": "vite",
26
+ "lint:tokens": "! grep -rnE --exclude='*.unit.ts' --exclude='*.unit.tsx' '#[0-9a-fA-F]{3,8}|(bg|text|border|ring|shadow|fill|stroke)-(white|black)|(bg|text|border|ring|shadow|fill|stroke)-(slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]{2,3}' src",
27
+ "test": "vitest run",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "@anchrd/intel-contract": "^0.1.0",
32
+ "@blocknote/core": "^0.52.1",
33
+ "@blocknote/react": "^0.52.1",
34
+ "@blocknote/shadcn": "^0.52.1",
35
+ "@react-sigma/core": "^5.0.6",
36
+ "@tailwindcss/vite": "^4.3.3",
37
+ "@tanstack/react-query": "^5.101.4",
38
+ "@tanstack/react-router": "^1.170.18",
39
+ "@vitejs/plugin-react": "^6.0.4",
40
+ "@xyflow/react": "^12.11.2",
41
+ "class-variance-authority": "^0.7.1",
42
+ "clsx": "^2.1.1",
43
+ "colorjs.io": "^0.7.1",
44
+ "graphology": "^0.26.0",
45
+ "lucide-react": "^1.25.0",
46
+ "react": "^19.2.0",
47
+ "react-aria-components": "^1.19.0",
48
+ "react-dom": "^19.2.0",
49
+ "sigma": "^3.0.3",
50
+ "tailwind-merge": "^3.6.0",
51
+ "tailwindcss": "^4.3.3",
52
+ "vite": "^8.1.5",
53
+ "zod": "^4.4.3"
54
+ },
55
+ "devDependencies": {
56
+ "@testing-library/react": "^16.3.2",
57
+ "@types/node": "^24.13.3",
58
+ "@types/react": "^19.2.17",
59
+ "@types/react-dom": "^19.2.3",
60
+ "jsdom": "^29.1.1",
61
+ "vitest": "^4.1.10"
62
+ }
63
+ }
@@ -0,0 +1,62 @@
1
+ import { useMutation } from "@tanstack/react-query";
2
+ import { Link, Outlet } from "@tanstack/react-router";
3
+ import { BookOpen, LogOut, Network, Wrench } from "lucide-react";
4
+ import { AppLogo } from "@/branding/branding.tsx";
5
+ import { loginPath } from "@/data/intel-data-provider/intel-data-provider.ts";
6
+ import { useIntelRouterContext } from "@/router/router-context.ts";
7
+
8
+ export function App() {
9
+ const { data, i18n } = useIntelRouterContext();
10
+ const logout = useMutation({
11
+ mutationFn: () => data.logout(),
12
+ onSuccess: () => window.location.assign(loginPath()),
13
+ });
14
+ const navigation = [
15
+ { to: "/knowledge" as const, label: i18n.t("nav.knowledge"), icon: BookOpen },
16
+ { to: "/flows" as const, label: i18n.t("nav.flows"), icon: Network },
17
+ { to: "/tools" as const, label: i18n.t("nav.tools"), icon: Wrench },
18
+ ];
19
+
20
+ return (
21
+ <div className="min-h-screen bg-background text-foreground">
22
+ <aside className="fixed inset-y-0 left-0 z-20 flex w-64 flex-col border-r border-sidebar-border bg-sidebar p-4 text-sidebar-foreground">
23
+ <Link to="/knowledge" className="mb-8 flex items-center gap-3 px-2 font-semibold">
24
+ <AppLogo className="size-9" />
25
+ <span>{i18n.t("app.name")}</span>
26
+ </Link>
27
+ <nav aria-label={i18n.t("nav.primary")} className="space-y-1">
28
+ {navigation.map(({ to, label, icon: Icon }) => (
29
+ <Link
30
+ key={to}
31
+ to={to}
32
+ activeProps={{ "aria-current": "page" }}
33
+ className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm outline-none transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring aria-[current=page]:bg-sidebar-accent aria-[current=page]:font-medium aria-[current=page]:text-sidebar-accent-foreground"
34
+ >
35
+ <Icon aria-hidden="true" className="size-4" />
36
+ {label}
37
+ </Link>
38
+ ))}
39
+ </nav>
40
+ <div className="mt-auto space-y-2">
41
+ {logout.isError ? (
42
+ <p role="alert" className="px-3 text-sm text-destructive">
43
+ {i18n.t("auth.signOutFailed")}
44
+ </p>
45
+ ) : null}
46
+ <button
47
+ type="button"
48
+ onClick={() => logout.mutate()}
49
+ disabled={logout.isPending}
50
+ className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm outline-none hover:bg-sidebar-accent focus-visible:ring-2 focus-visible:ring-sidebar-ring disabled:opacity-60"
51
+ >
52
+ <LogOut aria-hidden="true" className="size-4" />
53
+ {i18n.t("auth.signOut")}
54
+ </button>
55
+ </div>
56
+ </aside>
57
+ <main className="ml-64 min-h-screen">
58
+ <Outlet />
59
+ </main>
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,13 @@
1
+ import { BlockNoteView as LibraryBlockNoteView } from "@blocknote/shadcn";
2
+ import type { ComponentType } from "react";
3
+
4
+ // BlockNote 0.52's generic view declaration conflicts with exactOptionalPropertyTypes even though
5
+ // useCreateBlockNote returns its matching default editor. Keep that upstream type seam in one place.
6
+ const CompatibleBlockNoteView = LibraryBlockNoteView as unknown as ComponentType<{
7
+ editor: unknown;
8
+ onChange(): void;
9
+ }>;
10
+
11
+ export function BlockNoteView(props: { editor: unknown; onChange(): void }) {
12
+ return <CompatibleBlockNoteView {...props} />;
13
+ }
@@ -0,0 +1,17 @@
1
+ import { BrainCircuit } from "lucide-react";
2
+ import { cn } from "@/lib/utils";
3
+ import { customLogoUrl } from "./custom-logo.ts";
4
+
5
+ export function AppLogo({ className }: { className?: string }) {
6
+ if (customLogoUrl !== null) return <img src={customLogoUrl} alt="" className={className} />;
7
+ return (
8
+ <span
9
+ className={cn(
10
+ "flex aspect-square items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground",
11
+ className,
12
+ )}
13
+ >
14
+ <BrainCircuit aria-hidden="true" className="size-5" />
15
+ </span>
16
+ );
17
+ }
@@ -0,0 +1 @@
1
+ export const customLogoUrl: string | null = null;
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <title>Intel</title>
3
+ <rect width="32" height="32" rx="8" fill="oklch(0.45 0.17 265)"/>
4
+ <path d="M10 10h12v12H10zM7 14h3m12 0h3M14 7v3m4-3v3M14 22v3m4-3v3" fill="none" stroke="oklch(0.98 0.01 265)" stroke-width="2" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <title>Intel</title>
3
+ <rect width="32" height="32" rx="8" fill="oklch(0.45 0.17 265)"/>
4
+ <path d="M10 10h12v12H10zM7 14h3m12 0h3M14 7v3m4-3v3M14 22v3m4-3v3" fill="none" stroke="oklch(0.98 0.01 265)" stroke-width="2" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,338 @@
1
+ import {
2
+ ArchiveKnowledgeNodeInput,
3
+ CompleteFlowRunStepInput,
4
+ CreateFlowInput,
5
+ CreateKnowledgeLinkInput,
6
+ CreateKnowledgeNodeInput,
7
+ CreateToolSourceInput,
8
+ DeleteKnowledgeLinkInput,
9
+ DiscoverToolSourceInput,
10
+ Flow,
11
+ FlowDocument,
12
+ FlowList,
13
+ FlowRunStep,
14
+ KnowledgeDocument,
15
+ KnowledgeGraph,
16
+ KnowledgeLink,
17
+ KnowledgeLinkList,
18
+ KnowledgeNode,
19
+ KnowledgeNodeList,
20
+ KnowledgeVersionList,
21
+ ListKnowledgeNodesInput,
22
+ ProblemDetails,
23
+ PublishFlowInput,
24
+ ResourceGrant,
25
+ ResourceGrantList,
26
+ RevokeFlowGrantInput,
27
+ RevokeGrantResult,
28
+ RevokeKnowledgeGrantInput,
29
+ SaveFlowVersionInput,
30
+ SaveKnowledgeAttachmentInput,
31
+ SaveKnowledgeVersionInput,
32
+ SearchKnowledgeInput,
33
+ SearchKnowledgeResult,
34
+ ShareFlowInput,
35
+ ShareKnowledgeInput,
36
+ StartFlowRunInput,
37
+ TestToolInput,
38
+ ToolCatalog,
39
+ ToolSource,
40
+ ToolSourceList,
41
+ ToolTestResult,
42
+ UpdateKnowledgeNodeInput,
43
+ } from "@anchrd/intel-contract";
44
+ import { z } from "zod";
45
+ import type { IntelDataProvider, KnowledgeTreeNode } from "./intel-data-provider.types.ts";
46
+
47
+ const DeleteResult = z.strictObject({ deleted: z.boolean() });
48
+
49
+ // Attachment uploads and downloads share this budget, so it is generous rather than snappy.
50
+ const RequestTimeoutMs = 60_000;
51
+
52
+ // The Worker serves the login route, so the path belongs to the data layer rather than to any view.
53
+ export function loginPath(returnTo?: string): string {
54
+ return returnTo ? `/auth/login?returnTo=${encodeURIComponent(returnTo)}` : "/auth/login";
55
+ }
56
+
57
+ export function createIntelDataProvider(
58
+ deps: { fetch?: typeof fetch; baseUrl?: string; onUnauthorized?(): void } = {},
59
+ ): IntelDataProvider {
60
+ const doFetch = deps.fetch ?? fetch;
61
+ const baseUrl = deps.baseUrl?.replace(/\/$/, "") ?? "";
62
+ const unauthorized =
63
+ deps.onUnauthorized ??
64
+ (() => {
65
+ if (typeof window !== "undefined") {
66
+ const returnTo = `${window.location.pathname}${window.location.search}`;
67
+ window.location.assign(loginPath(returnTo));
68
+ }
69
+ });
70
+
71
+ async function response(path: string, init: RequestInit = {}): Promise<Response> {
72
+ // Without a deadline a stalled request would leave the view in a permanent loading state, and a
73
+ // caller-supplied signal (query cancellation) has to keep working alongside it.
74
+ const deadline = AbortSignal.timeout(RequestTimeoutMs);
75
+ const result = await doFetch(`${baseUrl}/api/v1${path}`, {
76
+ ...init,
77
+ credentials: "include",
78
+ signal: init.signal ? AbortSignal.any([init.signal, deadline]) : deadline,
79
+ headers: {
80
+ ...(init.body === undefined ? {} : { "content-type": "application/json" }),
81
+ ...init.headers,
82
+ },
83
+ });
84
+ if (result.status === 401) unauthorized();
85
+ if (!result.ok) {
86
+ const parsed = ProblemDetails.safeParse(
87
+ await result
88
+ .clone()
89
+ .json()
90
+ .catch(() => null),
91
+ );
92
+ throw new Error(
93
+ parsed.success
94
+ ? (parsed.data.detail ?? parsed.data.title)
95
+ : `Intel responded with ${result.status}`,
96
+ );
97
+ }
98
+ return result;
99
+ }
100
+
101
+ async function request<T>(
102
+ path: string,
103
+ schema: z.ZodType<T>,
104
+ init: RequestInit = {},
105
+ ): Promise<T> {
106
+ return schema.parse(await (await response(path, init)).json());
107
+ }
108
+
109
+ async function listKnowledge(
110
+ input: Partial<ListKnowledgeNodesInput> = {},
111
+ ): Promise<z.infer<typeof KnowledgeNodeList>> {
112
+ const query = ListKnowledgeNodesInput.parse(input);
113
+ const params = new URLSearchParams({ includeArchived: String(query.includeArchived) });
114
+ if (query.parentId !== null) params.set("parentId", query.parentId);
115
+ return await request(`/knowledge?${params}`, KnowledgeNodeList);
116
+ }
117
+
118
+ async function loadChildren(parentId: string | null): Promise<KnowledgeTreeNode[]> {
119
+ const { items } = await listKnowledge({ parentId });
120
+ return await Promise.all(
121
+ items.map(async (node) => ({
122
+ ...node,
123
+ children: node.kind === "folder" ? await loadChildren(node.id) : [],
124
+ })),
125
+ );
126
+ }
127
+
128
+ return {
129
+ listKnowledge,
130
+ async loadKnowledgeTree() {
131
+ return await loadChildren(null);
132
+ },
133
+ async getKnowledge(nodeId) {
134
+ return await request(`/knowledge/${encodeURIComponent(nodeId)}`, KnowledgeDocument);
135
+ },
136
+ async createKnowledge(input) {
137
+ return await request("/knowledge", KnowledgeNode, {
138
+ method: "POST",
139
+ body: JSON.stringify(CreateKnowledgeNodeInput.parse(input)),
140
+ });
141
+ },
142
+ async getKnowledgeGraph(limit = 250) {
143
+ return await request(`/knowledge/graph?limit=${encodeURIComponent(limit)}`, KnowledgeGraph);
144
+ },
145
+ async listKnowledgeLinks(nodeId) {
146
+ return await request(`/knowledge/${encodeURIComponent(nodeId)}/links`, KnowledgeLinkList);
147
+ },
148
+ async createKnowledgeLink(input) {
149
+ const parsed = CreateKnowledgeLinkInput.parse(input);
150
+ return await request(
151
+ `/knowledge/${encodeURIComponent(parsed.sourceNodeId)}/links`,
152
+ KnowledgeLink,
153
+ { method: "POST", body: JSON.stringify(parsed) },
154
+ );
155
+ },
156
+ async deleteKnowledgeLink(input) {
157
+ const parsed = DeleteKnowledgeLinkInput.parse(input);
158
+ return await request(
159
+ `/knowledge/${encodeURIComponent(parsed.sourceNodeId)}/links/${encodeURIComponent(parsed.linkId)}/revoke`,
160
+ DeleteResult,
161
+ { method: "POST", body: JSON.stringify(parsed) },
162
+ );
163
+ },
164
+ async saveKnowledge(input) {
165
+ const parsed = SaveKnowledgeVersionInput.parse(input);
166
+ return await request(
167
+ `/knowledge/${encodeURIComponent(parsed.nodeId)}/versions`,
168
+ KnowledgeDocument,
169
+ { method: "POST", body: JSON.stringify(parsed) },
170
+ );
171
+ },
172
+ async saveKnowledgeAttachment(input) {
173
+ const parsed = SaveKnowledgeAttachmentInput.parse(input);
174
+ return await request(
175
+ `/knowledge/${encodeURIComponent(parsed.nodeId)}/attachment`,
176
+ KnowledgeDocument,
177
+ { method: "POST", body: JSON.stringify(parsed) },
178
+ );
179
+ },
180
+ async getKnowledgeAttachment(nodeId) {
181
+ return await (await response(`/knowledge/${encodeURIComponent(nodeId)}/attachment`)).blob();
182
+ },
183
+ async listKnowledgeVersions(nodeId) {
184
+ return await request(
185
+ `/knowledge/${encodeURIComponent(nodeId)}/versions`,
186
+ KnowledgeVersionList,
187
+ );
188
+ },
189
+ async updateKnowledge(input) {
190
+ const parsed = UpdateKnowledgeNodeInput.parse(input);
191
+ return await request(`/knowledge/${encodeURIComponent(parsed.nodeId)}`, KnowledgeNode, {
192
+ method: "PATCH",
193
+ body: JSON.stringify(parsed),
194
+ });
195
+ },
196
+ async archiveKnowledge(input) {
197
+ const parsed = ArchiveKnowledgeNodeInput.parse(input);
198
+ return await request(
199
+ `/knowledge/${encodeURIComponent(parsed.nodeId)}/archive`,
200
+ KnowledgeNode,
201
+ { method: "POST", body: JSON.stringify(parsed) },
202
+ );
203
+ },
204
+ async searchKnowledge(input) {
205
+ return await request("/knowledge/search", SearchKnowledgeResult, {
206
+ method: "POST",
207
+ body: JSON.stringify(SearchKnowledgeInput.parse(input)),
208
+ });
209
+ },
210
+ async listKnowledgeGrants(resourceId) {
211
+ return await request(
212
+ `/knowledge/${encodeURIComponent(resourceId)}/grants`,
213
+ ResourceGrantList,
214
+ );
215
+ },
216
+ async shareKnowledge(input) {
217
+ const parsed = ShareKnowledgeInput.parse(input);
218
+ return await request(
219
+ `/knowledge/${encodeURIComponent(parsed.resourceId)}/grants`,
220
+ ResourceGrant,
221
+ { method: "POST", body: JSON.stringify(parsed) },
222
+ );
223
+ },
224
+ async revokeKnowledgeGrant(input) {
225
+ const parsed = RevokeKnowledgeGrantInput.parse(input);
226
+ return await request(
227
+ `/knowledge/${encodeURIComponent(parsed.resourceId)}/grants/${encodeURIComponent(parsed.grantId)}/revoke`,
228
+ RevokeGrantResult,
229
+ { method: "POST", body: JSON.stringify(parsed) },
230
+ );
231
+ },
232
+ async listFlows() {
233
+ return await request("/flows", FlowList);
234
+ },
235
+ async getFlow(flowId) {
236
+ return await request(`/flows/${encodeURIComponent(flowId)}`, FlowDocument);
237
+ },
238
+ async createFlow(input) {
239
+ return await request("/flows", Flow, {
240
+ method: "POST",
241
+ body: JSON.stringify(CreateFlowInput.parse(input)),
242
+ });
243
+ },
244
+ async listFlowGrants(flowId) {
245
+ return await request(`/flows/${encodeURIComponent(flowId)}/grants`, ResourceGrantList);
246
+ },
247
+ async shareFlow(input) {
248
+ const parsed = ShareFlowInput.parse(input);
249
+ return await request(
250
+ `/flows/${encodeURIComponent(parsed.resourceId)}/grants`,
251
+ ResourceGrant,
252
+ {
253
+ method: "POST",
254
+ body: JSON.stringify(parsed),
255
+ },
256
+ );
257
+ },
258
+ async revokeFlowGrant(input) {
259
+ const parsed = RevokeFlowGrantInput.parse(input);
260
+ return await request(
261
+ `/flows/${encodeURIComponent(parsed.resourceId)}/grants/${encodeURIComponent(parsed.grantId)}/revoke`,
262
+ RevokeGrantResult,
263
+ { method: "POST", body: JSON.stringify(parsed) },
264
+ );
265
+ },
266
+ async saveFlow(input) {
267
+ const parsed = SaveFlowVersionInput.parse(input);
268
+ return await request(`/flows/${encodeURIComponent(parsed.flowId)}/versions`, FlowDocument, {
269
+ method: "POST",
270
+ body: JSON.stringify(parsed),
271
+ });
272
+ },
273
+ async publishFlow(input) {
274
+ const parsed = PublishFlowInput.parse(input);
275
+ return await request(`/flows/${encodeURIComponent(parsed.flowId)}/publish`, Flow, {
276
+ method: "POST",
277
+ body: JSON.stringify(parsed),
278
+ });
279
+ },
280
+ async startFlow(input) {
281
+ const parsed = StartFlowRunInput.parse(input);
282
+ return await request(`/flows/${encodeURIComponent(parsed.flowId)}/runs`, FlowRunStep, {
283
+ method: "POST",
284
+ body: JSON.stringify(parsed),
285
+ });
286
+ },
287
+ async getFlowRun(runId) {
288
+ return await request(`/flow-runs/${encodeURIComponent(runId)}`, FlowRunStep);
289
+ },
290
+ async completeFlowStep(input) {
291
+ const parsed = CompleteFlowRunStepInput.parse(input);
292
+ return await request(`/flow-runs/${encodeURIComponent(parsed.runId)}/complete`, FlowRunStep, {
293
+ method: "POST",
294
+ body: JSON.stringify(parsed),
295
+ });
296
+ },
297
+ async listToolSources() {
298
+ return await request("/tool-sources", ToolSourceList);
299
+ },
300
+ async createToolSource(input) {
301
+ return await request("/tool-sources", ToolSource, {
302
+ method: "POST",
303
+ body: JSON.stringify(CreateToolSourceInput.parse(input)),
304
+ });
305
+ },
306
+ async discoverToolSource(input) {
307
+ const parsed = DiscoverToolSourceInput.parse(input);
308
+ return await request(
309
+ `/tool-sources/${encodeURIComponent(parsed.sourceId)}/discover`,
310
+ ToolCatalog,
311
+ {
312
+ method: "POST",
313
+ body: JSON.stringify(parsed),
314
+ },
315
+ );
316
+ },
317
+ async listTools() {
318
+ return await request("/tools", ToolCatalog);
319
+ },
320
+ async testTool(input) {
321
+ return await request("/tools/test", ToolTestResult, {
322
+ method: "POST",
323
+ body: JSON.stringify(TestToolInput.parse(input)),
324
+ });
325
+ },
326
+
327
+ toolSourceConnectUrl(sourceId, returnTo = "/tools") {
328
+ return `${baseUrl}/auth/connect/${encodeURIComponent(sourceId)}?returnTo=${encodeURIComponent(returnTo)}`;
329
+ },
330
+ async logout() {
331
+ const response = await doFetch(`${baseUrl}/auth/logout`, {
332
+ method: "POST",
333
+ credentials: "include",
334
+ });
335
+ if (!response.ok) throw new Error(`Intel responded with ${response.status}`);
336
+ },
337
+ };
338
+ }
@@ -0,0 +1,85 @@
1
+ import type {
2
+ ArchiveKnowledgeNodeInput,
3
+ CompleteFlowRunStepInput,
4
+ CreateFlowInput,
5
+ CreateKnowledgeLinkInput,
6
+ CreateKnowledgeNodeInput,
7
+ CreateToolSourceInput,
8
+ DeleteKnowledgeLinkInput,
9
+ DiscoverToolSourceInput,
10
+ Flow,
11
+ FlowDocument,
12
+ FlowList,
13
+ FlowRunStep,
14
+ KnowledgeDocument,
15
+ KnowledgeGraph,
16
+ KnowledgeLink,
17
+ KnowledgeLinkList,
18
+ KnowledgeNode,
19
+ KnowledgeNodeList,
20
+ KnowledgeVersionList,
21
+ ListKnowledgeNodesInput,
22
+ PublishFlowInput,
23
+ ResourceGrant,
24
+ ResourceGrantList,
25
+ RevokeFlowGrantInput,
26
+ RevokeGrantResult,
27
+ RevokeKnowledgeGrantInput,
28
+ SaveFlowVersionInput,
29
+ SaveKnowledgeAttachmentInput,
30
+ SaveKnowledgeVersionInput,
31
+ SearchKnowledgeInput,
32
+ SearchKnowledgeResult,
33
+ ShareFlowInput,
34
+ ShareKnowledgeInput,
35
+ StartFlowRunInput,
36
+ TestToolInput,
37
+ ToolCatalog,
38
+ ToolSource,
39
+ ToolSourceList,
40
+ ToolTestResult,
41
+ UpdateKnowledgeNodeInput,
42
+ } from "@anchrd/intel-contract";
43
+
44
+ export interface KnowledgeTreeNode extends KnowledgeNode {
45
+ children: KnowledgeTreeNode[];
46
+ }
47
+
48
+ export interface IntelDataProvider {
49
+ listKnowledge(input?: Partial<ListKnowledgeNodesInput>): Promise<KnowledgeNodeList>;
50
+ loadKnowledgeTree(): Promise<KnowledgeTreeNode[]>;
51
+ getKnowledge(nodeId: string): Promise<KnowledgeDocument>;
52
+ createKnowledge(input: CreateKnowledgeNodeInput): Promise<KnowledgeNode>;
53
+ getKnowledgeGraph(limit?: number): Promise<KnowledgeGraph>;
54
+ listKnowledgeLinks(nodeId: string): Promise<KnowledgeLinkList>;
55
+ createKnowledgeLink(input: CreateKnowledgeLinkInput): Promise<KnowledgeLink>;
56
+ deleteKnowledgeLink(input: DeleteKnowledgeLinkInput): Promise<{ deleted: boolean }>;
57
+ saveKnowledge(input: SaveKnowledgeVersionInput): Promise<KnowledgeDocument>;
58
+ saveKnowledgeAttachment(input: SaveKnowledgeAttachmentInput): Promise<KnowledgeDocument>;
59
+ getKnowledgeAttachment(nodeId: string): Promise<Blob>;
60
+ toolSourceConnectUrl(sourceId: string, returnTo?: string): string;
61
+ listKnowledgeVersions(nodeId: string): Promise<KnowledgeVersionList>;
62
+ updateKnowledge(input: UpdateKnowledgeNodeInput): Promise<KnowledgeNode>;
63
+ archiveKnowledge(input: ArchiveKnowledgeNodeInput): Promise<KnowledgeNode>;
64
+ searchKnowledge(input: SearchKnowledgeInput): Promise<SearchKnowledgeResult>;
65
+ listKnowledgeGrants(resourceId: string): Promise<ResourceGrantList>;
66
+ shareKnowledge(input: ShareKnowledgeInput): Promise<ResourceGrant>;
67
+ revokeKnowledgeGrant(input: RevokeKnowledgeGrantInput): Promise<RevokeGrantResult>;
68
+ listFlows(): Promise<FlowList>;
69
+ getFlow(flowId: string): Promise<FlowDocument>;
70
+ createFlow(input: CreateFlowInput): Promise<Flow>;
71
+ listFlowGrants(flowId: string): Promise<ResourceGrantList>;
72
+ shareFlow(input: ShareFlowInput): Promise<ResourceGrant>;
73
+ revokeFlowGrant(input: RevokeFlowGrantInput): Promise<RevokeGrantResult>;
74
+ saveFlow(input: SaveFlowVersionInput): Promise<FlowDocument>;
75
+ publishFlow(input: PublishFlowInput): Promise<Flow>;
76
+ startFlow(input: StartFlowRunInput): Promise<FlowRunStep>;
77
+ getFlowRun(runId: string): Promise<FlowRunStep>;
78
+ completeFlowStep(input: CompleteFlowRunStepInput): Promise<FlowRunStep>;
79
+ listToolSources(): Promise<ToolSourceList>;
80
+ createToolSource(input: CreateToolSourceInput): Promise<ToolSource>;
81
+ discoverToolSource(input: DiscoverToolSourceInput): Promise<ToolCatalog>;
82
+ listTools(): Promise<ToolCatalog>;
83
+ testTool(input: TestToolInput): Promise<ToolTestResult>;
84
+ logout(): Promise<void>;
85
+ }