@gdrl/kronos-lib 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.
@@ -0,0 +1,847 @@
1
+ // src/react/useKronos.ts
2
+ import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
3
+
4
+ // src/react/KronosProvider.tsx
5
+ import {
6
+ createContext,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useRef,
11
+ useState
12
+ } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ var KronosContext = createContext(null);
15
+ var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
16
+ function parseTokenExpiry(token) {
17
+ try {
18
+ const withoutPrefix = token.startsWith("kr_ft_") ? token.slice(6) : token;
19
+ const payloadB64 = withoutPrefix.split(".")[0];
20
+ if (!payloadB64) return null;
21
+ const base64 = payloadB64.replace(/-/g, "+").replace(/_/g, "/");
22
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
23
+ const json = atob(padded);
24
+ const payload = JSON.parse(json);
25
+ if (typeof payload.exp === "number") {
26
+ return payload.exp * 1e3;
27
+ }
28
+ } catch {
29
+ }
30
+ return null;
31
+ }
32
+ function KronosProvider({ baseUrl, getToken, children }) {
33
+ const [token, setToken] = useState(null);
34
+ const [isReady, setIsReady] = useState(false);
35
+ const timerRef = useRef(null);
36
+ const getTokenRef = useRef(getToken);
37
+ getTokenRef.current = getToken;
38
+ const fetchAndSchedule = useCallback(async () => {
39
+ try {
40
+ const newToken = await getTokenRef.current();
41
+ setToken(newToken);
42
+ setIsReady(true);
43
+ if (timerRef.current) clearTimeout(timerRef.current);
44
+ const expiryMs = parseTokenExpiry(newToken);
45
+ if (expiryMs) {
46
+ const delayMs = Math.max(expiryMs - Date.now() - REFRESH_BUFFER_MS, 1e4);
47
+ timerRef.current = setTimeout(() => {
48
+ void fetchAndSchedule();
49
+ }, delayMs);
50
+ }
51
+ } catch (err) {
52
+ console.error("[KronosProvider] Failed to fetch token:", err);
53
+ setIsReady(true);
54
+ }
55
+ }, []);
56
+ useEffect(() => {
57
+ void fetchAndSchedule();
58
+ return () => {
59
+ if (timerRef.current) clearTimeout(timerRef.current);
60
+ };
61
+ }, [fetchAndSchedule]);
62
+ const refreshToken = useCallback(async () => {
63
+ await fetchAndSchedule();
64
+ }, [fetchAndSchedule]);
65
+ const value = {
66
+ baseUrl: baseUrl.replace(/\/$/, ""),
67
+ token,
68
+ isReady,
69
+ refreshToken
70
+ };
71
+ return /* @__PURE__ */ jsx(KronosContext.Provider, { value, children });
72
+ }
73
+ function useKronosContext() {
74
+ return useContext(KronosContext);
75
+ }
76
+
77
+ // src/react/useKronos.ts
78
+ var INTERNAL_CONTEXT_GRAPH_SKILL_NAME = "__internal_context_graph__";
79
+ async function directFetch(baseUrl, token, path, options = {}) {
80
+ const res = await fetch(`${baseUrl}${path}`, {
81
+ method: options.method ?? "GET",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ Authorization: `Bearer ${token}`
85
+ },
86
+ body: options.body ? JSON.stringify(options.body) : void 0
87
+ });
88
+ const text = await res.text();
89
+ let data;
90
+ try {
91
+ data = text ? JSON.parse(text) : null;
92
+ } catch {
93
+ data = text;
94
+ }
95
+ if (!res.ok) {
96
+ const msg = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : `Request failed with status ${res.status}`;
97
+ throw new Error(msg);
98
+ }
99
+ return data;
100
+ }
101
+ function parseContextGraphProcedures(files) {
102
+ return files.filter((file) => file.path.startsWith("procedures/") && file.path.endsWith(".md")).map((file) => {
103
+ const content = file.content ?? "";
104
+ const normalized = content.replace(/\r\n/g, "\n");
105
+ if (normalized.startsWith("---\n")) {
106
+ const endIndex = normalized.indexOf("\n---\n", 4);
107
+ if (endIndex !== -1) {
108
+ const frontmatter = normalized.slice(4, endIndex);
109
+ const metadata = {};
110
+ for (const line of frontmatter.split("\n")) {
111
+ const separatorIndex = line.indexOf(":");
112
+ if (separatorIndex === -1) continue;
113
+ const key = line.slice(0, separatorIndex).trim();
114
+ const value = line.slice(separatorIndex + 1).trim();
115
+ if (key && value) {
116
+ metadata[key] = value;
117
+ }
118
+ }
119
+ return {
120
+ title: metadata.title || (file.path.split("/").pop() ?? file.path),
121
+ description: metadata.description || "",
122
+ path: file.path
123
+ };
124
+ }
125
+ }
126
+ const lines = content.split(/\r?\n/);
127
+ const titleLine = lines.find((line) => line.startsWith("# "));
128
+ const title = titleLine ? titleLine.replace(/^#\s+/, "").trim() : file.path.split("/").pop() ?? file.path;
129
+ const description = lines.slice(1).map((line) => line.trim()).find((line) => line.length > 0 && !line.startsWith("#")) ?? "";
130
+ return {
131
+ title,
132
+ description,
133
+ path: file.path
134
+ };
135
+ }).sort((a, b) => a.path.localeCompare(b.path));
136
+ }
137
+ function assertReadableContextGraphPath(path) {
138
+ if (path === "SKILL.md") return;
139
+ if (/^procedures\/[a-z0-9-]+\.md$/.test(path)) return;
140
+ throw new Error("Only SKILL.md or procedures/*.md paths are allowed");
141
+ }
142
+ function useKronos(args) {
143
+ const ctx = useKronosContext();
144
+ const isProvider = !!ctx && !args;
145
+ const legacyArgs = args;
146
+ const [triggers, setTriggers] = useState2(null);
147
+ const [triggersLoading, setTriggersLoading] = useState2(true);
148
+ const [triggersError, setTriggersError] = useState2(null);
149
+ const [createLoading, setCreateLoading] = useState2(false);
150
+ const [createError, setCreateError] = useState2(null);
151
+ const [updateLoading, setUpdateLoading] = useState2(false);
152
+ const [updateError, setUpdateError] = useState2(null);
153
+ const [deleteLoading, setDeleteLoading] = useState2(false);
154
+ const [deleteError, setDeleteError] = useState2(null);
155
+ const [memoryStats, setMemoryStats] = useState2(null);
156
+ const [memoryStatsLoading, setMemoryStatsLoading] = useState2(false);
157
+ const [memoryStatsError, setMemoryStatsError] = useState2(null);
158
+ const [scopes, setScopes] = useState2(null);
159
+ const [scopesLoading, setScopesLoading] = useState2(false);
160
+ const [scopesError, setScopesError] = useState2(null);
161
+ const [createScopeLoading, setCreateScopeLoading] = useState2(false);
162
+ const [createScopeError, setCreateScopeError] = useState2(null);
163
+ const [updateScopeLoading, setUpdateScopeLoading] = useState2(false);
164
+ const [updateScopeError, setUpdateScopeError] = useState2(null);
165
+ const [deleteScopeLoading, setDeleteScopeLoading] = useState2(false);
166
+ const [deleteScopeError, setDeleteScopeError] = useState2(null);
167
+ const [skills, setSkills] = useState2(null);
168
+ const [skillsLoading, setSkillsLoading] = useState2(false);
169
+ const [skillsError, setSkillsError] = useState2(null);
170
+ const [createSkillLoading, setCreateSkillLoading] = useState2(false);
171
+ const [createSkillError, setCreateSkillError] = useState2(null);
172
+ const [updateSkillLoading, setUpdateSkillLoading] = useState2(false);
173
+ const [updateSkillError, setUpdateSkillError] = useState2(null);
174
+ const [deleteSkillLoading, setDeleteSkillLoading] = useState2(false);
175
+ const [deleteSkillError, setDeleteSkillError] = useState2(null);
176
+ const loadTriggers = useCallback2(async () => {
177
+ setTriggersLoading(true);
178
+ setTriggersError(null);
179
+ try {
180
+ let response;
181
+ if (isProvider && ctx?.token) {
182
+ response = await directFetch(
183
+ ctx.baseUrl,
184
+ ctx.token,
185
+ "/v1/triggers"
186
+ );
187
+ } else if (legacyArgs) {
188
+ response = await legacyArgs.ops.listTriggers({
189
+ tenantUserId: legacyArgs.tenantUserId
190
+ });
191
+ } else {
192
+ throw new Error("useKronos: no KronosProvider and no ops provided");
193
+ }
194
+ setTriggers(Array.isArray(response.triggers) ? response.triggers : []);
195
+ } catch (error) {
196
+ setTriggersError(
197
+ error instanceof Error ? error.message : "Failed to load Kronos triggers"
198
+ );
199
+ setTriggers([]);
200
+ } finally {
201
+ setTriggersLoading(false);
202
+ }
203
+ }, [isProvider, ctx?.baseUrl, ctx?.token, legacyArgs?.ops, legacyArgs?.tenantUserId]);
204
+ useEffect2(() => {
205
+ if (isProvider && !ctx?.isReady) return;
206
+ void loadTriggers();
207
+ }, [loadTriggers, isProvider, ctx?.isReady]);
208
+ const refreshTriggers = useCallback2(async () => {
209
+ await loadTriggers();
210
+ }, [loadTriggers]);
211
+ const createNlTrigger = useCallback2(
212
+ async (params) => {
213
+ setCreateLoading(true);
214
+ setCreateError(null);
215
+ try {
216
+ let triggerId = null;
217
+ if (isProvider && ctx?.token) {
218
+ const res = await directFetch(
219
+ ctx.baseUrl,
220
+ ctx.token,
221
+ "/v1/triggers",
222
+ {
223
+ method: "POST",
224
+ body: {
225
+ trigger_type: "nl_webhook",
226
+ trigger_spec: { nl: params.nl },
227
+ title: params.title.trim(),
228
+ action_description: params.actionDescription,
229
+ webhook_url: ctx.baseUrl + "/v1/triggers/execute",
230
+ webhook_secret: "frontend-managed"
231
+ }
232
+ }
233
+ );
234
+ triggerId = res.trigger_id ?? null;
235
+ } else if (legacyArgs) {
236
+ const res = await legacyArgs.ops.createNlTrigger({
237
+ tenantUserId: legacyArgs.tenantUserId,
238
+ title: params.title,
239
+ nl: params.nl,
240
+ actionDescription: params.actionDescription
241
+ });
242
+ triggerId = res.triggerId;
243
+ } else {
244
+ throw new Error("useKronos: no KronosProvider and no ops provided");
245
+ }
246
+ void loadTriggers();
247
+ return { triggerId };
248
+ } catch (error) {
249
+ const message = error instanceof Error ? error.message : "Failed to create Kronos trigger";
250
+ setCreateError(message);
251
+ return { triggerId: null };
252
+ } finally {
253
+ setCreateLoading(false);
254
+ }
255
+ },
256
+ [isProvider, ctx?.baseUrl, ctx?.token, legacyArgs?.ops, legacyArgs?.tenantUserId, loadTriggers]
257
+ );
258
+ const updateTrigger = useCallback2(
259
+ async (params) => {
260
+ setUpdateLoading(true);
261
+ setUpdateError(null);
262
+ try {
263
+ if (!isProvider || !ctx?.token) {
264
+ throw new Error("updateTrigger requires KronosProvider");
265
+ }
266
+ const body = {};
267
+ if (params.title !== void 0) {
268
+ body.title = params.title;
269
+ }
270
+ if (params.actionDescription !== void 0) {
271
+ body.action_description = params.actionDescription;
272
+ }
273
+ if (params.triggerSpec !== void 0) {
274
+ body.trigger_spec = params.triggerSpec;
275
+ }
276
+ if (params.status !== void 0) {
277
+ body.status = params.status;
278
+ }
279
+ const res = await directFetch(
280
+ ctx.baseUrl,
281
+ ctx.token,
282
+ `/v1/triggers/${encodeURIComponent(params.triggerId)}`,
283
+ {
284
+ method: "PUT",
285
+ body
286
+ }
287
+ );
288
+ void loadTriggers();
289
+ return res;
290
+ } catch (error) {
291
+ const message = error instanceof Error ? error.message : "Failed to update trigger";
292
+ setUpdateError(message);
293
+ throw error;
294
+ } finally {
295
+ setUpdateLoading(false);
296
+ }
297
+ },
298
+ [isProvider, ctx?.baseUrl, ctx?.token, loadTriggers]
299
+ );
300
+ const deleteTriggerFn = useCallback2(
301
+ async (params) => {
302
+ setDeleteLoading(true);
303
+ setDeleteError(null);
304
+ try {
305
+ if (!isProvider || !ctx?.token) {
306
+ throw new Error("deleteTrigger requires KronosProvider");
307
+ }
308
+ const res = await directFetch(
309
+ ctx.baseUrl,
310
+ ctx.token,
311
+ `/v1/triggers/${encodeURIComponent(params.triggerId)}`,
312
+ { method: "DELETE" }
313
+ );
314
+ void loadTriggers();
315
+ return res;
316
+ } catch (error) {
317
+ const message = error instanceof Error ? error.message : "Failed to delete trigger";
318
+ setDeleteError(message);
319
+ throw error;
320
+ } finally {
321
+ setDeleteLoading(false);
322
+ }
323
+ },
324
+ [isProvider, ctx?.baseUrl, ctx?.token, loadTriggers]
325
+ );
326
+ const loadMemoryStats = useCallback2(async () => {
327
+ if (!isProvider || !ctx?.token) return;
328
+ setMemoryStatsLoading(true);
329
+ setMemoryStatsError(null);
330
+ try {
331
+ const res = await directFetch(
332
+ ctx.baseUrl,
333
+ ctx.token,
334
+ "/v1/memory-stats"
335
+ );
336
+ setMemoryStats(res);
337
+ } catch (error) {
338
+ setMemoryStatsError(
339
+ error instanceof Error ? error.message : "Failed to load memory stats"
340
+ );
341
+ } finally {
342
+ setMemoryStatsLoading(false);
343
+ }
344
+ }, [isProvider, ctx?.baseUrl, ctx?.token]);
345
+ const refreshMemoryStats = useCallback2(async () => {
346
+ await loadMemoryStats();
347
+ }, [loadMemoryStats]);
348
+ const loadScopes = useCallback2(async () => {
349
+ if (!isProvider || !ctx?.token) return;
350
+ setScopesLoading(true);
351
+ setScopesError(null);
352
+ try {
353
+ const res = await directFetch(
354
+ ctx.baseUrl,
355
+ ctx.token,
356
+ "/v1/scopes"
357
+ );
358
+ setScopes(Array.isArray(res.scopes) ? res.scopes : []);
359
+ } catch (error) {
360
+ setScopesError(
361
+ error instanceof Error ? error.message : "Failed to load scopes"
362
+ );
363
+ setScopes([]);
364
+ } finally {
365
+ setScopesLoading(false);
366
+ }
367
+ }, [isProvider, ctx?.baseUrl, ctx?.token]);
368
+ const refreshScopes = useCallback2(async () => {
369
+ await loadScopes();
370
+ }, [loadScopes]);
371
+ const getMCPServer = useCallback2(
372
+ async (params) => {
373
+ if (!isProvider || !ctx?.token) {
374
+ throw new Error("getMCPServer requires KronosProvider");
375
+ }
376
+ return directFetch(
377
+ ctx.baseUrl,
378
+ ctx.token,
379
+ `/v1/mcp-servers/${encodeURIComponent(params.serverId)}`
380
+ );
381
+ },
382
+ [isProvider, ctx?.baseUrl, ctx?.token]
383
+ );
384
+ const connectMCPServer = useCallback2(
385
+ async (params) => {
386
+ if (!isProvider || !ctx?.token) {
387
+ throw new Error("connectMCPServer requires KronosProvider");
388
+ }
389
+ return directFetch(
390
+ ctx.baseUrl,
391
+ ctx.token,
392
+ `/v1/mcp-servers/${encodeURIComponent(params.serverId)}/connect`,
393
+ {
394
+ method: "POST",
395
+ body: {}
396
+ }
397
+ );
398
+ },
399
+ [isProvider, ctx?.baseUrl, ctx?.token]
400
+ );
401
+ const createScope = useCallback2(
402
+ async (params) => {
403
+ setCreateScopeLoading(true);
404
+ setCreateScopeError(null);
405
+ try {
406
+ if (!isProvider || !ctx?.token) {
407
+ throw new Error("createScope requires KronosProvider");
408
+ }
409
+ const res = await directFetch(
410
+ ctx.baseUrl,
411
+ ctx.token,
412
+ "/v1/scopes",
413
+ {
414
+ method: "POST",
415
+ body: {
416
+ name: params.name,
417
+ include_query: params.include_query,
418
+ description: params.description
419
+ }
420
+ }
421
+ );
422
+ void loadScopes();
423
+ if (res.mcp_server_error) {
424
+ setCreateScopeError(res.mcp_server_error);
425
+ }
426
+ return {
427
+ scope_id: res.scope_id ?? null,
428
+ backfill_job_id: res.backfill_job_id,
429
+ mcp_server_id: res.mcp_server_id ?? null,
430
+ mcp_server_error: res.mcp_server_error ?? null
431
+ };
432
+ } catch (error) {
433
+ const message = error instanceof Error ? error.message : "Failed to create scope";
434
+ setCreateScopeError(message);
435
+ return { scope_id: null, mcp_server_id: null, mcp_server_error: message };
436
+ } finally {
437
+ setCreateScopeLoading(false);
438
+ }
439
+ },
440
+ [isProvider, ctx?.baseUrl, ctx?.token, loadScopes]
441
+ );
442
+ const updateScopeFn = useCallback2(
443
+ async (params) => {
444
+ setUpdateScopeLoading(true);
445
+ setUpdateScopeError(null);
446
+ try {
447
+ if (!isProvider || !ctx?.token) {
448
+ throw new Error("updateScope requires KronosProvider");
449
+ }
450
+ const res = await directFetch(
451
+ ctx.baseUrl,
452
+ ctx.token,
453
+ `/v1/scopes/${encodeURIComponent(params.scopeId)}`,
454
+ {
455
+ method: "PATCH",
456
+ body: params.patch
457
+ }
458
+ );
459
+ void loadScopes();
460
+ return res;
461
+ } catch (error) {
462
+ const message = error instanceof Error ? error.message : "Failed to update scope";
463
+ setUpdateScopeError(message);
464
+ throw error;
465
+ } finally {
466
+ setUpdateScopeLoading(false);
467
+ }
468
+ },
469
+ [isProvider, ctx?.baseUrl, ctx?.token, loadScopes]
470
+ );
471
+ const deleteScopeFn = useCallback2(
472
+ async (params) => {
473
+ setDeleteScopeLoading(true);
474
+ setDeleteScopeError(null);
475
+ try {
476
+ if (!isProvider || !ctx?.token) {
477
+ throw new Error("deleteScope requires KronosProvider");
478
+ }
479
+ const res = await directFetch(
480
+ ctx.baseUrl,
481
+ ctx.token,
482
+ `/v1/scopes/${encodeURIComponent(params.scopeId)}`,
483
+ { method: "DELETE" }
484
+ );
485
+ void loadScopes();
486
+ return res;
487
+ } catch (error) {
488
+ const message = error instanceof Error ? error.message : "Failed to delete scope";
489
+ setDeleteScopeError(message);
490
+ throw error;
491
+ } finally {
492
+ setDeleteScopeLoading(false);
493
+ }
494
+ },
495
+ [isProvider, ctx?.baseUrl, ctx?.token, loadScopes]
496
+ );
497
+ const refreshSkills = useCallback2(async () => {
498
+ setSkillsLoading(true);
499
+ setSkillsError(null);
500
+ try {
501
+ if (!isProvider || !ctx?.token) {
502
+ throw new Error("refreshSkills requires KronosProvider");
503
+ }
504
+ const res = await directFetch(
505
+ ctx.baseUrl,
506
+ ctx.token,
507
+ "/v1/skills/manage",
508
+ {
509
+ method: "POST",
510
+ body: {
511
+ operation: "list"
512
+ }
513
+ }
514
+ );
515
+ setSkills(Array.isArray(res.skills) ? res.skills : []);
516
+ } catch (error) {
517
+ setSkillsError(
518
+ error instanceof Error ? error.message : "Failed to load skills"
519
+ );
520
+ setSkills([]);
521
+ } finally {
522
+ setSkillsLoading(false);
523
+ }
524
+ }, [isProvider, ctx?.baseUrl, ctx?.token]);
525
+ const createSkill = useCallback2(
526
+ async (params) => {
527
+ setCreateSkillLoading(true);
528
+ setCreateSkillError(null);
529
+ try {
530
+ if (!isProvider || !ctx?.token) {
531
+ throw new Error("createSkill requires KronosProvider");
532
+ }
533
+ const res = await directFetch(
534
+ ctx.baseUrl,
535
+ ctx.token,
536
+ "/v1/skills/manage",
537
+ {
538
+ method: "POST",
539
+ body: {
540
+ operation: "create",
541
+ name: params.name.trim(),
542
+ description: params.description.trim(),
543
+ instructions: params.instructions,
544
+ files: params.files,
545
+ metadata: params.metadata
546
+ }
547
+ }
548
+ );
549
+ void refreshSkills();
550
+ return { skillId: res.skill_id ?? null };
551
+ } catch (error) {
552
+ const message = error instanceof Error ? error.message : "Failed to create skill";
553
+ setCreateSkillError(message);
554
+ return { skillId: null };
555
+ } finally {
556
+ setCreateSkillLoading(false);
557
+ }
558
+ },
559
+ [isProvider, ctx?.baseUrl, ctx?.token, refreshSkills]
560
+ );
561
+ const readSkill = useCallback2(
562
+ async (params) => {
563
+ if (!isProvider || !ctx?.token) {
564
+ throw new Error("readSkill requires KronosProvider");
565
+ }
566
+ return directFetch(
567
+ ctx.baseUrl,
568
+ ctx.token,
569
+ "/v1/skills/manage",
570
+ {
571
+ method: "POST",
572
+ body: {
573
+ operation: "read",
574
+ skill_id: params.skillId,
575
+ mode: params.mode,
576
+ files: params.files,
577
+ fileContent: params.fileContent
578
+ }
579
+ }
580
+ );
581
+ },
582
+ [isProvider, ctx?.baseUrl, ctx?.token]
583
+ );
584
+ const updateSkill = useCallback2(
585
+ async (params) => {
586
+ setUpdateSkillLoading(true);
587
+ setUpdateSkillError(null);
588
+ try {
589
+ if (!isProvider || !ctx?.token) {
590
+ throw new Error("updateSkill requires KronosProvider");
591
+ }
592
+ const res = await directFetch(
593
+ ctx.baseUrl,
594
+ ctx.token,
595
+ "/v1/skills/manage",
596
+ {
597
+ method: "POST",
598
+ body: {
599
+ operation: "update",
600
+ skill_id: params.skillId,
601
+ patch: params.patch
602
+ }
603
+ }
604
+ );
605
+ void refreshSkills();
606
+ return res;
607
+ } catch (error) {
608
+ const message = error instanceof Error ? error.message : "Failed to update skill";
609
+ setUpdateSkillError(message);
610
+ throw error;
611
+ } finally {
612
+ setUpdateSkillLoading(false);
613
+ }
614
+ },
615
+ [isProvider, ctx?.baseUrl, ctx?.token, refreshSkills]
616
+ );
617
+ const deleteSkill = useCallback2(
618
+ async (params) => {
619
+ setDeleteSkillLoading(true);
620
+ setDeleteSkillError(null);
621
+ try {
622
+ if (!isProvider || !ctx?.token) {
623
+ throw new Error("deleteSkill requires KronosProvider");
624
+ }
625
+ const res = await directFetch(
626
+ ctx.baseUrl,
627
+ ctx.token,
628
+ "/v1/skills/manage",
629
+ {
630
+ method: "POST",
631
+ body: {
632
+ operation: "delete",
633
+ skill_id: params.skillId
634
+ }
635
+ }
636
+ );
637
+ void refreshSkills();
638
+ return res;
639
+ } catch (error) {
640
+ const message = error instanceof Error ? error.message : "Failed to delete skill";
641
+ setDeleteSkillError(message);
642
+ throw error;
643
+ } finally {
644
+ setDeleteSkillLoading(false);
645
+ }
646
+ },
647
+ [isProvider, ctx?.baseUrl, ctx?.token, refreshSkills]
648
+ );
649
+ const getContextGraph = useCallback2(async () => {
650
+ if (!isProvider || !ctx?.token) {
651
+ throw new Error("getContextGraph requires KronosProvider");
652
+ }
653
+ const listed = await directFetch(
654
+ ctx.baseUrl,
655
+ ctx.token,
656
+ "/v1/skills/manage",
657
+ {
658
+ method: "POST",
659
+ body: {
660
+ operation: "list",
661
+ include_internal: true
662
+ }
663
+ }
664
+ );
665
+ const match = (listed.skills ?? []).find(
666
+ (skill) => skill.name === INTERNAL_CONTEXT_GRAPH_SKILL_NAME
667
+ );
668
+ if (!match?.skill_id) {
669
+ return {
670
+ skill_id: null,
671
+ procedures: []
672
+ };
673
+ }
674
+ const treeResult = await directFetch(
675
+ ctx.baseUrl,
676
+ ctx.token,
677
+ "/v1/skills/manage",
678
+ {
679
+ method: "POST",
680
+ body: {
681
+ operation: "read",
682
+ skill_id: match.skill_id,
683
+ mode: "tree"
684
+ }
685
+ }
686
+ );
687
+ const procedurePaths = (treeResult.tree ?? []).filter(
688
+ (node) => node.kind === "file" && node.path.startsWith("procedures/") && node.path.endsWith(".md")
689
+ ).map((node) => node.path);
690
+ if (procedurePaths.length === 0) {
691
+ return {
692
+ skill_id: match.skill_id,
693
+ procedures: []
694
+ };
695
+ }
696
+ const filesResult = await directFetch(
697
+ ctx.baseUrl,
698
+ ctx.token,
699
+ "/v1/skills/manage",
700
+ {
701
+ method: "POST",
702
+ body: {
703
+ operation: "read",
704
+ skill_id: match.skill_id,
705
+ mode: "files",
706
+ files: procedurePaths,
707
+ fileContent: "full"
708
+ }
709
+ }
710
+ );
711
+ return {
712
+ skill_id: match.skill_id,
713
+ procedures: parseContextGraphProcedures(filesResult.files ?? [])
714
+ };
715
+ }, [isProvider, ctx?.baseUrl, ctx?.token]);
716
+ const readContextGraph = useCallback2(
717
+ async (params) => {
718
+ if (!isProvider || !ctx?.token) {
719
+ throw new Error("readContextGraph requires KronosProvider");
720
+ }
721
+ const requestedPath = params?.path?.trim() || "SKILL.md";
722
+ assertReadableContextGraphPath(requestedPath);
723
+ const listed = await directFetch(
724
+ ctx.baseUrl,
725
+ ctx.token,
726
+ "/v1/skills/manage",
727
+ {
728
+ method: "POST",
729
+ body: {
730
+ operation: "list",
731
+ include_internal: true
732
+ }
733
+ }
734
+ );
735
+ const match = (listed.skills ?? []).find(
736
+ (skill) => skill.name === INTERNAL_CONTEXT_GRAPH_SKILL_NAME
737
+ );
738
+ if (!match?.skill_id) {
739
+ return {
740
+ skill_id: null,
741
+ path: requestedPath,
742
+ content: null
743
+ };
744
+ }
745
+ if (requestedPath === "SKILL.md") {
746
+ const result2 = await directFetch(
747
+ ctx.baseUrl,
748
+ ctx.token,
749
+ "/v1/skills/manage",
750
+ {
751
+ method: "POST",
752
+ body: {
753
+ operation: "read",
754
+ skill_id: match.skill_id,
755
+ mode: "instructions"
756
+ }
757
+ }
758
+ );
759
+ return {
760
+ skill_id: match.skill_id,
761
+ path: "SKILL.md",
762
+ content: result2.instructions ?? null
763
+ };
764
+ }
765
+ const result = await directFetch(
766
+ ctx.baseUrl,
767
+ ctx.token,
768
+ "/v1/skills/manage",
769
+ {
770
+ method: "POST",
771
+ body: {
772
+ operation: "read",
773
+ skill_id: match.skill_id,
774
+ mode: "files",
775
+ files: [requestedPath],
776
+ fileContent: "full"
777
+ }
778
+ }
779
+ );
780
+ const file = (result.files ?? []).find(
781
+ (entry) => entry.path === requestedPath
782
+ );
783
+ return {
784
+ skill_id: match.skill_id,
785
+ path: requestedPath,
786
+ content: file?.content ?? null
787
+ };
788
+ },
789
+ [isProvider, ctx?.baseUrl, ctx?.token]
790
+ );
791
+ return {
792
+ triggers,
793
+ triggersLoading,
794
+ triggersError,
795
+ refreshTriggers,
796
+ createNlTrigger,
797
+ createLoading,
798
+ createError,
799
+ updateTrigger,
800
+ updateLoading,
801
+ updateError,
802
+ deleteTrigger: deleteTriggerFn,
803
+ deleteLoading,
804
+ deleteError,
805
+ memoryStats,
806
+ memoryStatsLoading,
807
+ memoryStatsError,
808
+ refreshMemoryStats,
809
+ scopes,
810
+ scopesLoading,
811
+ scopesError,
812
+ refreshScopes,
813
+ getMCPServer,
814
+ connectMCPServer,
815
+ createScope,
816
+ createScopeLoading,
817
+ createScopeError,
818
+ updateScope: updateScopeFn,
819
+ updateScopeLoading,
820
+ updateScopeError,
821
+ deleteScope: deleteScopeFn,
822
+ deleteScopeLoading,
823
+ deleteScopeError,
824
+ skills,
825
+ skillsLoading,
826
+ skillsError,
827
+ refreshSkills,
828
+ createSkill,
829
+ createSkillLoading,
830
+ createSkillError,
831
+ readSkill,
832
+ updateSkill,
833
+ updateSkillLoading,
834
+ updateSkillError,
835
+ deleteSkill,
836
+ deleteSkillLoading,
837
+ deleteSkillError,
838
+ getContextGraph,
839
+ readContextGraph
840
+ };
841
+ }
842
+ export {
843
+ KronosProvider,
844
+ useKronos,
845
+ useKronosContext
846
+ };
847
+ //# sourceMappingURL=index.mjs.map