@ultrst-armada/kloks 1.4.8 → 1.5.2

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "packageName": "@ultrst-armada/kloks",
4
- "version": "1.4.8",
5
- "sourceCommit": "6ab0d5dfede2344d9dd35ce366b67ac44b808401",
4
+ "version": "1.5.2",
5
+ "sourceCommit": "07f2e1a49da642bbfd42cd79422306c93195493f",
6
6
  "releaseWorkflow": "npm Release",
7
- "workflowRunId": "39557640590"
7
+ "workflowRunId": "39969373482"
8
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultrst-armada/kloks",
3
- "version": "1.4.8",
3
+ "version": "1.5.2",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -104,6 +104,11 @@
104
104
  "types": "./packages/client/dist/index.d.ts",
105
105
  "import": "./packages/client/dist/index.js",
106
106
  "default": "./packages/client/dist/index.js"
107
+ },
108
+ "./development": {
109
+ "types": "./packages/backend/dist/development/index.d.ts",
110
+ "import": "./packages/backend/dist/development/index.js",
111
+ "default": "./packages/backend/dist/development/index.js"
107
112
  }
108
113
  },
109
114
  "scripts": {
@@ -119,7 +124,8 @@
119
124
  "bcryptjs": "^2.4.3",
120
125
  "ws": "^8.18.0",
121
126
  "zod": "^3.24.1",
122
- "zustand": "^5.0.15"
127
+ "zustand": "^5.0.15",
128
+ "@webcontainer/api": "^1.6.4"
123
129
  },
124
130
  "peerDependencies": {
125
131
  "@neondatabase/serverless": "^0.10.0 || ^1.0.0",
@@ -0,0 +1,383 @@
1
+ import {
2
+ createService,
3
+ createServiceDefinition
4
+ } from "./chunk-TY32TUNI.js";
5
+
6
+ // src/development/githubActionsService.ts
7
+ var GitHubActionsClient = class {
8
+ constructor(config) {
9
+ this.config = config;
10
+ this.baseUrl = config.apiBaseUrl ?? "https://api.github.com";
11
+ this.fetchImpl = config.fetchImpl ?? fetch;
12
+ }
13
+ config;
14
+ baseUrl;
15
+ fetchImpl;
16
+ async request(path, init = {}) {
17
+ const response = await this.fetchImpl(this.baseUrl + path, {
18
+ ...init,
19
+ headers: {
20
+ Accept: "application/vnd.github+json",
21
+ Authorization: `Bearer ${this.config.token}`,
22
+ "X-GitHub-Api-Version": "2022-11-28",
23
+ ...init.headers ?? {}
24
+ }
25
+ });
26
+ const text = await response.text();
27
+ const data = text ? JSON.parse(text) : void 0;
28
+ if (!response.ok) {
29
+ throw new Error(`GitHub Actions request failed (${response.status}): ${data?.message ?? response.statusText}`);
30
+ }
31
+ return data;
32
+ }
33
+ listWorkflowRuns(repository, workflowId, branch) {
34
+ const suffix = workflowId ? `/actions/workflows/${workflowId}/runs` : "/actions/runs";
35
+ const query = branch ? `?branch=${encodeURIComponent(branch)}` : "";
36
+ return this.request(`/repos/${repository}${suffix}${query}`);
37
+ }
38
+ getWorkflowRun(repository, runId) {
39
+ return this.request(`/repos/${repository}/actions/runs/${runId}`);
40
+ }
41
+ getWorkflowJobs(repository, runId) {
42
+ return this.request(
43
+ `/repos/${repository}/actions/runs/${runId}/jobs`
44
+ );
45
+ }
46
+ getJobLogs(repository, jobId) {
47
+ return this.request(`/repos/${repository}/actions/jobs/${jobId}/logs`);
48
+ }
49
+ listArtifacts(repository, runId) {
50
+ const path = runId ? `/repos/${repository}/actions/runs/${runId}/artifacts` : `/repos/${repository}/actions/artifacts`;
51
+ return this.request(path);
52
+ }
53
+ dispatchWorkflow(repository, workflowId, ref, inputs = {}) {
54
+ return this.request(`/repos/${repository}/actions/workflows/${workflowId}/dispatches`, {
55
+ method: "POST",
56
+ body: JSON.stringify({ ref, inputs }),
57
+ headers: { "Content-Type": "application/json" }
58
+ });
59
+ }
60
+ rerunWorkflow(repository, runId) {
61
+ return this.request(`/repos/${repository}/actions/runs/${runId}/rerun`, { method: "POST" });
62
+ }
63
+ rerunFailedJobs(repository, runId) {
64
+ return this.request(`/repos/${repository}/actions/runs/${runId}/rerun-failed-jobs`, { method: "POST" });
65
+ }
66
+ cancelWorkflow(repository, runId) {
67
+ return this.request(`/repos/${repository}/actions/runs/${runId}/cancel`, { method: "POST" });
68
+ }
69
+ };
70
+ var GitHubActionsServiceDefinition = createServiceDefinition({
71
+ id: "kloks.backend.githubActions",
72
+ name: "GitHubActionsService",
73
+ version: "1.0.0",
74
+ operations: {
75
+ listWorkflowRuns: { id: "GitHubActionsService.listWorkflowRuns", name: "listWorkflowRuns", handler: async (i) => new GitHubActionsClient({ token: i.token }).listWorkflowRuns(i.repository, i.workflowId, i.branch) },
76
+ getWorkflowRun: { id: "GitHubActionsService.getWorkflowRun", name: "getWorkflowRun", handler: async (i) => new GitHubActionsClient({ token: i.token }).getWorkflowRun(i.repository, i.runId) },
77
+ getWorkflowJobs: { id: "GitHubActionsService.getWorkflowJobs", name: "getWorkflowJobs", handler: async (i) => new GitHubActionsClient({ token: i.token }).getWorkflowJobs(i.repository, i.runId) },
78
+ getJobLogs: { id: "GitHubActionsService.getJobLogs", name: "getJobLogs", handler: async (i) => new GitHubActionsClient({ token: i.token }).getJobLogs(i.repository, i.jobId) },
79
+ listArtifacts: { id: "GitHubActionsService.listArtifacts", name: "listArtifacts", handler: async (i) => new GitHubActionsClient({ token: i.token }).listArtifacts(i.repository, i.runId) },
80
+ dispatchWorkflow: { id: "GitHubActionsService.dispatchWorkflow", name: "dispatchWorkflow", handler: async (i) => new GitHubActionsClient({ token: i.token }).dispatchWorkflow(i.repository, i.workflowId, i.ref, i.inputs) },
81
+ rerunWorkflow: { id: "GitHubActionsService.rerunWorkflow", name: "rerunWorkflow", handler: async (i) => new GitHubActionsClient({ token: i.token }).rerunWorkflow(i.repository, i.runId) },
82
+ rerunFailedJobs: { id: "GitHubActionsService.rerunFailedJobs", name: "rerunFailedJobs", handler: async (i) => new GitHubActionsClient({ token: i.token }).rerunFailedJobs(i.repository, i.runId) },
83
+ cancelWorkflow: { id: "GitHubActionsService.cancelWorkflow", name: "cancelWorkflow", handler: async (i) => new GitHubActionsClient({ token: i.token }).cancelWorkflow(i.repository, i.runId) }
84
+ }
85
+ });
86
+ var GitHubActionsServiceFacade = {
87
+ id: GitHubActionsServiceDefinition.id,
88
+ name: GitHubActionsServiceDefinition.name,
89
+ version: GitHubActionsServiceDefinition.version,
90
+ runtime: "server",
91
+ deployment: "remote",
92
+ definition: GitHubActionsServiceDefinition,
93
+ createInstance: () => createService(GitHubActionsServiceDefinition),
94
+ execute: async (op, input) => {
95
+ const instance = createService(GitHubActionsServiceDefinition);
96
+ await instance.start();
97
+ return instance.call(op, input);
98
+ }
99
+ };
100
+
101
+ // src/development/webContainerService.ts
102
+ var COMMANDS = {
103
+ install: { command: "pnpm", args: ["install", "--no-frozen-lockfile"] },
104
+ lint: { command: "pnpm", args: ["lint"] },
105
+ test: { command: "pnpm", args: ["test"] },
106
+ build: { command: "pnpm", args: ["build"] },
107
+ validate: { command: "pnpm", args: ["lint"] },
108
+ check: { command: "pnpm", args: ["lint"] }
109
+ };
110
+ var runQueue = Promise.resolve();
111
+ var WebContainerRuntime = class {
112
+ container = null;
113
+ instancePromise = null;
114
+ async boot() {
115
+ if (typeof window !== "undefined" && !window.crossOriginIsolated) {
116
+ throw new Error("WebContainer requires a cross-origin-isolated page. Ensure COOP=same-origin and COEP=require-corp are served.");
117
+ }
118
+ if (this.container) return this.container;
119
+ if (!this.instancePromise) {
120
+ this.instancePromise = import("@webcontainer/api").then(async (module) => {
121
+ const container = await module.WebContainer.boot({
122
+ coep: "require-corp",
123
+ workdirName: "kloks-development"
124
+ });
125
+ this.container = container;
126
+ return container;
127
+ }).catch((error) => {
128
+ this.instancePromise = null;
129
+ throw error;
130
+ });
131
+ }
132
+ return this.instancePromise;
133
+ }
134
+ async mount(files) {
135
+ const container = await this.boot();
136
+ await container.mount(files);
137
+ return { mounted: true };
138
+ }
139
+ async runProcess(command, args, cwd, env) {
140
+ const container = await this.boot();
141
+ const process = await container.spawn(command, args, { cwd, env });
142
+ let output = "";
143
+ const reader = process.output.getReader();
144
+ try {
145
+ while (true) {
146
+ const next = await reader.read();
147
+ if (next.done) break;
148
+ output += next.value;
149
+ }
150
+ } finally {
151
+ reader.releaseLock();
152
+ }
153
+ return { exitCode: await process.exit, output };
154
+ }
155
+ async run(task, cwd, env) {
156
+ const spec = COMMANDS[task];
157
+ return this.runCommand(spec.command, spec.args, cwd, env);
158
+ }
159
+ async runCommand(command, args = [], cwd, env) {
160
+ const previous = runQueue;
161
+ let release;
162
+ runQueue = new Promise((resolve) => {
163
+ release = resolve;
164
+ });
165
+ await previous;
166
+ try {
167
+ return await this.runProcess(command, args, cwd, env);
168
+ } finally {
169
+ release();
170
+ }
171
+ }
172
+ async teardown() {
173
+ const container = this.container;
174
+ this.container = null;
175
+ this.instancePromise = null;
176
+ container?.teardown();
177
+ return { tornDown: true };
178
+ }
179
+ };
180
+ var runtime = new WebContainerRuntime();
181
+ var WebContainerServiceDefinition = createServiceDefinition({
182
+ id: "kloks.backend.webContainer",
183
+ name: "WebContainerService",
184
+ version: "1.1.0",
185
+ operations: {
186
+ boot: { id: "WebContainerService.boot", name: "boot", handler: async () => {
187
+ await runtime.boot();
188
+ return { ready: true };
189
+ } },
190
+ mount: { id: "WebContainerService.mount", name: "mount", handler: async (input) => runtime.mount(input.files) },
191
+ run: { id: "WebContainerService.run", name: "run", handler: async (input) => runtime.run(input.task, input.cwd, input.env) },
192
+ runCommand: { id: "WebContainerService.runCommand", name: "runCommand", handler: async (input) => runtime.runCommand(input.command, input.args, input.cwd, input.env) },
193
+ teardown: { id: "WebContainerService.teardown", name: "teardown", handler: async () => runtime.teardown() },
194
+ command: { id: "WebContainerService.command", name: "command", handler: async (input) => ({ task: input.task, command: COMMANDS[input.task] }) }
195
+ }
196
+ });
197
+ var WebContainerServiceFacade = {
198
+ id: WebContainerServiceDefinition.id,
199
+ name: WebContainerServiceDefinition.name,
200
+ version: WebContainerServiceDefinition.version,
201
+ runtime: "client",
202
+ deployment: "local",
203
+ definition: WebContainerServiceDefinition,
204
+ createInstance: () => createService(WebContainerServiceDefinition),
205
+ execute: async (op, input) => {
206
+ const instance = createService(WebContainerServiceDefinition);
207
+ await instance.start();
208
+ return instance.call(op, input);
209
+ }
210
+ };
211
+
212
+ // src/development/developmentService.ts
213
+ function resolveDevelopmentCommand(task) {
214
+ const commands = {
215
+ install: ["pnpm", ["install", "--no-frozen-lockfile"]],
216
+ lint: ["pnpm", ["lint"]],
217
+ test: ["pnpm", ["test"]],
218
+ build: ["pnpm", ["build"]],
219
+ validate: ["pnpm", ["validate"]],
220
+ check: ["pnpm", ["lint"]]
221
+ };
222
+ return commands[task];
223
+ }
224
+ var DevelopmentServiceDefinition = createServiceDefinition({
225
+ id: "kloks.backend.development",
226
+ name: "DevelopmentService",
227
+ version: "1.0.0",
228
+ operations: {
229
+ plan: {
230
+ id: "DevelopmentService.plan",
231
+ name: "plan",
232
+ handler: async (input) => {
233
+ const [command, args] = resolveDevelopmentCommand(input.task);
234
+ return { task: input.task, command, args };
235
+ }
236
+ },
237
+ describeProviders: {
238
+ id: "DevelopmentService.describeProviders",
239
+ name: "describeProviders",
240
+ handler: async () => ({
241
+ providers: [
242
+ { id: "webcontainer", capabilities: ["install", "lint", "test", "build", "validate", "check"] },
243
+ { id: "github-actions", capabilities: ["dispatch", "observe", "rerun", "cancel"] }
244
+ ]
245
+ })
246
+ }
247
+ }
248
+ });
249
+ var DevelopmentRuntime = class {
250
+ constructor(webContainer = new WebContainerRuntime(), githubActions) {
251
+ this.webContainer = webContainer;
252
+ this.githubActions = githubActions;
253
+ }
254
+ webContainer;
255
+ githubActions;
256
+ async run(request) {
257
+ if (request.provider === "webcontainer") {
258
+ const [command, args] = resolveDevelopmentCommand(request.task);
259
+ const result = await this.webContainer.run(request.task, request.cwd, request.env);
260
+ return { provider: request.provider, task: request.task, ok: result.exitCode === 0, exitCode: result.exitCode, output: result.output };
261
+ }
262
+ if (!this.githubActions || !request.repository || !request.workflowId || !request.ref) {
263
+ throw new Error("GitHub Actions execution requires repository, workflowId, ref, and an authenticated client");
264
+ }
265
+ await this.githubActions.dispatchWorkflow(request.repository, request.workflowId, request.ref, {
266
+ task: request.task,
267
+ ...request.workflowInputs ?? {}
268
+ });
269
+ return { provider: request.provider, task: request.task, ok: true, output: "Workflow dispatched; observe its run for final status." };
270
+ }
271
+ };
272
+ var DevelopmentServiceFacade = {
273
+ id: DevelopmentServiceDefinition.id,
274
+ name: DevelopmentServiceDefinition.name,
275
+ version: DevelopmentServiceDefinition.version,
276
+ runtime: "server",
277
+ deployment: "local",
278
+ definition: DevelopmentServiceDefinition,
279
+ createInstance: () => createService(DevelopmentServiceDefinition),
280
+ execute: async (op, input) => {
281
+ const instance = createService(DevelopmentServiceDefinition);
282
+ await instance.start();
283
+ return instance.call(op, input);
284
+ }
285
+ };
286
+
287
+ // src/development/codeValidationService.ts
288
+ var COMMANDS2 = {
289
+ syntax: { command: "pnpm", args: ["exec", "tsc", "--noEmit"] },
290
+ lint: { command: "pnpm", args: ["run", "lint", "--if-present"] },
291
+ test: { command: "pnpm", args: ["run", "test", "--if-present"] },
292
+ build: { command: "pnpm", args: ["run", "build", "--if-present"] },
293
+ krnl: { command: "pnpm", args: ["exec", "krnl", "validate"] },
294
+ frnl: { command: "pnpm", args: ["exec", "frnl", "validate"] }
295
+ };
296
+ var DEFAULT_LAYERS = ["syntax", "lint", "krnl", "frnl"];
297
+ var commandText = (command, args) => [command, ...args].join(" ");
298
+ var CodeValidationRuntime = class {
299
+ constructor(runtime3 = new WebContainerRuntime()) {
300
+ this.runtime = runtime3;
301
+ }
302
+ runtime;
303
+ async validate(input = {}) {
304
+ if (input.files) await this.runtime.mount(input.files);
305
+ const diagnostics = [];
306
+ if (input.install) {
307
+ const installed = await this.runtime.runCommand("pnpm", ["install", "--no-frozen-lockfile"], input.cwd, input.env);
308
+ diagnostics.push({ layer: "lint", ok: installed.exitCode === 0, exitCode: installed.exitCode, output: installed.output, command: "pnpm install --no-frozen-lockfile" });
309
+ if (installed.exitCode !== 0) return { ok: false, diagnostics, failedLayers: ["lint"] };
310
+ }
311
+ const layers = input.layers?.length ? input.layers : DEFAULT_LAYERS;
312
+ for (const layer of layers) {
313
+ if (layer === "law-l9") {
314
+ diagnostics.push(await this.validateLawL9(input.cwd, input.maxLines ?? 200));
315
+ continue;
316
+ }
317
+ const spec = COMMANDS2[layer];
318
+ const result = await this.runtime.runCommand(spec.command, spec.args, input.cwd, input.env);
319
+ diagnostics.push({ layer, ok: result.exitCode === 0, exitCode: result.exitCode, output: result.output, command: commandText(spec.command, spec.args) });
320
+ if (result.exitCode !== 0) break;
321
+ }
322
+ return { ok: diagnostics.every((item) => item.ok), diagnostics, failedLayers: diagnostics.filter((item) => !item.ok).map((item) => item.layer) };
323
+ }
324
+ async validateLawL9(cwd = ".", maxLines = 200) {
325
+ const script = `python3 -c 'import os,sys; bad=[]; root="src"; [bad.append(os.path.join(r,f)+": "+str(len(open(os.path.join(r,f),encoding="utf-8",errors="ignore").readlines()))+" lines") for r,_,fs in os.walk(root) for f in fs if f.endswith((".ts",".tsx",".js",".jsx")) and not f.endswith(".d.ts") and len(open(os.path.join(r,f),encoding="utf-8",errors="ignore").readlines())>200]; print("\\n".join(bad)); sys.exit(1 if bad else 0)'`;
326
+ const result = await this.runtime.runCommand("sh", ["-lc", script], cwd);
327
+ return { layer: "law-l9", ok: result.exitCode === 0, exitCode: result.exitCode, output: result.output, command: script };
328
+ }
329
+ };
330
+ var runtime2 = new CodeValidationRuntime();
331
+ var CodeValidationServiceDefinition = createServiceDefinition({
332
+ id: "kloks.backend.codeValidation",
333
+ name: "CodeValidationService",
334
+ version: "1.0.0",
335
+ operations: { validate: { id: "CodeValidationService.validate", name: "validate", handler: async (input) => runtime2.validate(input) } }
336
+ });
337
+ var CodeValidationServiceFacade = {
338
+ id: CodeValidationServiceDefinition.id,
339
+ name: CodeValidationServiceDefinition.name,
340
+ version: CodeValidationServiceDefinition.version,
341
+ runtime: "client",
342
+ deployment: "local",
343
+ definition: CodeValidationServiceDefinition,
344
+ createInstance: () => createService(CodeValidationServiceDefinition),
345
+ execute: async (op, input) => {
346
+ const instance = createService(CodeValidationServiceDefinition);
347
+ await instance.start();
348
+ return instance.call(op, input);
349
+ }
350
+ };
351
+
352
+ // src/development/codeDiagnosticsService.ts
353
+ var CodeDiagnosticsService = class {
354
+ constructor(runtime3 = new WebContainerRuntime()) {
355
+ this.runtime = runtime3;
356
+ }
357
+ runtime;
358
+ async diagnostics(input = {}) {
359
+ const args = ["exec", "tsc", "--noEmit", "--pretty", "false"];
360
+ if (input.file) args.push(input.file);
361
+ const result = await this.runtime.runCommand("pnpm", args, input.cwd);
362
+ return { ok: result.exitCode === 0, output: result.output, command: ["pnpm", ...args].join(" ") };
363
+ }
364
+ };
365
+ var codeDiagnosticsService = new CodeDiagnosticsService();
366
+
367
+ export {
368
+ GitHubActionsClient,
369
+ GitHubActionsServiceDefinition,
370
+ GitHubActionsServiceFacade,
371
+ WebContainerRuntime,
372
+ WebContainerServiceDefinition,
373
+ WebContainerServiceFacade,
374
+ resolveDevelopmentCommand,
375
+ DevelopmentServiceDefinition,
376
+ DevelopmentRuntime,
377
+ DevelopmentServiceFacade,
378
+ CodeValidationRuntime,
379
+ CodeValidationServiceDefinition,
380
+ CodeValidationServiceFacade,
381
+ CodeDiagnosticsService,
382
+ codeDiagnosticsService
383
+ };
@@ -0,0 +1,159 @@
1
+ import {
2
+ StorageAdapterFactory
3
+ } from "./chunk-J2J24JVC.js";
4
+ import {
5
+ findUserWorkspace
6
+ } from "./chunk-E72GXBXF.js";
7
+
8
+ // src/services/workspaceGraphService.ts
9
+ function slugify(name) {
10
+ const value = String(name || "").trim().replace(/[/\\]+/g, "_").replace(/[<>:"|?*]/g, "").slice(0, 80);
11
+ return value || "New Graph";
12
+ }
13
+ function storage() {
14
+ return StorageAdapterFactory.create({
15
+ provider: "local_fs",
16
+ basePath: process.env.KLOKS_GRAPH_STORAGE_ROOT || "./workspaces-data"
17
+ });
18
+ }
19
+ function graphPath(workspaceId, slug) {
20
+ return `workspaces/${workspaceId}/graphs/${slug}.graph.json`;
21
+ }
22
+ async function assertWorkspace(userId, workspaceId) {
23
+ const result = await findUserWorkspace(workspaceId);
24
+ if (!result.workspace || !result.user || result.user.id !== userId) {
25
+ throw new Error(`Workspace ${workspaceId} does not exist`);
26
+ }
27
+ return result.workspace;
28
+ }
29
+ async function readGraph(workspaceId, slug) {
30
+ const adapter = storage();
31
+ const path = graphPath(workspaceId, slug);
32
+ if (!await adapter.exists(path)) return null;
33
+ const raw = new TextDecoder().decode(await adapter.read(path));
34
+ return JSON.parse(raw);
35
+ }
36
+ async function writeGraph(document) {
37
+ const adapter = storage();
38
+ await adapter.write(graphPath(document.workspaceId, document.graphSlug), JSON.stringify(document, null, 2), "application/json");
39
+ }
40
+ function toDocument(workspaceId, id, name, slug, nodes = [], edges = [], createdAt = (/* @__PURE__ */ new Date()).toISOString()) {
41
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42
+ return {
43
+ $schema: "https://skipo.dev/schemas/node-graph.v1.json",
44
+ version: "1.0.0",
45
+ appId: workspaceId,
46
+ appName: name,
47
+ createdAt,
48
+ updatedAt: now,
49
+ exportedAt: now,
50
+ workspaceId,
51
+ graphId: id,
52
+ graphSlug: slug,
53
+ graph: { nodes, edges }
54
+ };
55
+ }
56
+ var workspaceGraphService = {
57
+ async listGraphs(userId, workspaceId) {
58
+ await assertWorkspace(userId, workspaceId);
59
+ const adapter = storage();
60
+ const files = await adapter.list(`workspaces/${workspaceId}/graphs`);
61
+ const graphs = [];
62
+ for (const file of files.filter((item) => item.path.endsWith(".graph.json"))) {
63
+ try {
64
+ const raw = new TextDecoder().decode(await adapter.read(file.path));
65
+ const graph = JSON.parse(raw);
66
+ graphs.push({
67
+ id: graph.graphId,
68
+ name: graph.appName,
69
+ slug: graph.graphSlug,
70
+ createdAt: graph.createdAt,
71
+ updatedAt: graph.updatedAt,
72
+ nodeCount: graph.graph.nodes.length
73
+ });
74
+ } catch {
75
+ }
76
+ }
77
+ graphs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
78
+ return { success: true, graphs };
79
+ },
80
+ async createGraph(userId, workspaceId, input) {
81
+ await assertWorkspace(userId, workspaceId);
82
+ const targetName = String(input?.name || "").trim().slice(0, 80) || "New Graph";
83
+ const baseSlug = slugify(targetName);
84
+ let slug = baseSlug;
85
+ let n = 2;
86
+ while (await readGraph(workspaceId, slug)) slug = `${baseSlug} (${n++})`;
87
+ const graph = toDocument(workspaceId, `graph-${Date.now()}`, targetName, slug);
88
+ await writeGraph(graph);
89
+ return { success: true, graph, graphs: (await this.listGraphs(userId, workspaceId)).graphs };
90
+ },
91
+ async getGraph(userId, workspaceId, slug) {
92
+ await assertWorkspace(userId, workspaceId);
93
+ const graph = await readGraph(workspaceId, slug);
94
+ if (!graph) throw new Error(`Graph "${slug}" not found`);
95
+ return { success: true, graph };
96
+ },
97
+ async saveGraph(userId, workspaceId, slug, patch) {
98
+ await assertWorkspace(userId, workspaceId);
99
+ const existing = await readGraph(workspaceId, slug);
100
+ if (!existing) throw new Error(`Graph "${slug}" not found`);
101
+ const next = {
102
+ ...existing,
103
+ appName: typeof patch.name === "string" && patch.name.trim() ? patch.name.trim().slice(0, 80) : existing.appName,
104
+ graph: {
105
+ nodes: Array.isArray(patch.nodes) ? patch.nodes : existing.graph.nodes,
106
+ edges: Array.isArray(patch.edges) ? patch.edges : existing.graph.edges
107
+ },
108
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
109
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString()
110
+ };
111
+ await writeGraph(next);
112
+ return { success: true, graph: next };
113
+ },
114
+ async deleteGraph(userId, workspaceId, slug) {
115
+ await assertWorkspace(userId, workspaceId);
116
+ const adapter = storage();
117
+ const path = graphPath(workspaceId, slug);
118
+ if (!await adapter.exists(path)) throw new Error(`Graph "${slug}" not found`);
119
+ await adapter.delete(path);
120
+ return { success: true, deletedSlug: slug, graphs: (await this.listGraphs(userId, workspaceId)).graphs };
121
+ },
122
+ async duplicateGraph(userId, workspaceId, slug, newName) {
123
+ const existing = (await this.getGraph(userId, workspaceId, slug)).graph;
124
+ const targetName = (newName || `${existing.appName} (Copy)`).trim().slice(0, 80);
125
+ const baseSlug = slugify(targetName);
126
+ let newSlug = baseSlug;
127
+ let n = 2;
128
+ while (await readGraph(workspaceId, newSlug)) newSlug = `${baseSlug} (${n++})`;
129
+ const now = (/* @__PURE__ */ new Date()).toISOString();
130
+ const graph = {
131
+ ...JSON.parse(JSON.stringify(existing)),
132
+ graphId: `graph-${Date.now()}`,
133
+ graphSlug: newSlug,
134
+ appName: targetName,
135
+ createdAt: now,
136
+ updatedAt: now,
137
+ exportedAt: now
138
+ };
139
+ await writeGraph(graph);
140
+ return { success: true, graph, graphs: (await this.listGraphs(userId, workspaceId)).graphs };
141
+ },
142
+ async clearGraph(userId, workspaceId, slug) {
143
+ const existing = (await this.getGraph(userId, workspaceId, slug)).graph;
144
+ return this.saveGraph(userId, workspaceId, slug, { nodes: [], edges: [] });
145
+ },
146
+ async getGraphDocument(userId, workspaceId, slug) {
147
+ return (await this.getGraph(userId, workspaceId, slug)).graph;
148
+ },
149
+ async saveGraphDocument(userId, workspaceId, document) {
150
+ await assertWorkspace(userId, workspaceId);
151
+ if (document.workspaceId !== workspaceId) throw new Error("Graph workspace mismatch");
152
+ await writeGraph({ ...document, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), exportedAt: (/* @__PURE__ */ new Date()).toISOString() });
153
+ return { success: true, graph: document };
154
+ }
155
+ };
156
+
157
+ export {
158
+ workspaceGraphService
159
+ };
@@ -1,7 +1,13 @@
1
+ import {
2
+ workspaceGraphService
3
+ } from "./chunk-DBZVGMJ7.js";
1
4
  import {
2
5
  getSrklAccounts,
3
6
  srklListBackblazeBuckets
4
7
  } from "./chunk-VDROUXFT.js";
8
+ import {
9
+ StorageAdapterFactory
10
+ } from "./chunk-J2J24JVC.js";
5
11
  import {
6
12
  b2ListBuckets
7
13
  } from "./chunk-4QEMALXF.js";
@@ -9,20 +15,6 @@ import {
9
15
  formatCanonicalWorkspaceFolderName,
10
16
  sanitizeWorkspaceName
11
17
  } from "./chunk-FZKBTF34.js";
12
- import {
13
- RateLimiter,
14
- SessionManager,
15
- loginSystemUser,
16
- resolveUserFromToken
17
- } from "./chunk-ZDH4XI7W.js";
18
- import {
19
- createService,
20
- createServiceDefinition,
21
- workspaceGraphService
22
- } from "./chunk-ZFCEXTNI.js";
23
- import {
24
- StorageAdapterFactory
25
- } from "./chunk-J2J24JVC.js";
26
18
  import {
27
19
  createWorkspace,
28
20
  deleteWorkspace,
@@ -31,6 +23,16 @@ import {
31
23
  queryUnifiedDb,
32
24
  updateWorkspace
33
25
  } from "./chunk-E72GXBXF.js";
26
+ import {
27
+ RateLimiter,
28
+ SessionManager,
29
+ loginSystemUser,
30
+ resolveUserFromToken
31
+ } from "./chunk-ZDH4XI7W.js";
32
+ import {
33
+ createService,
34
+ createServiceDefinition
35
+ } from "./chunk-TY32TUNI.js";
34
36
 
35
37
  // src/krnl-services.ts
36
38
  var AuthServiceDefinition = createServiceDefinition({
@@ -2,7 +2,7 @@ import {
2
2
  createService,
3
3
  createServiceDefinition,
4
4
  createServiceProxy
5
- } from "./chunk-ZFCEXTNI.js";
5
+ } from "./chunk-TY32TUNI.js";
6
6
 
7
7
  // src/services/ServiceBoundary.ts
8
8
  function createLocalService(definition) {