@ultrst-armada/kloks 1.7.0 → 1.7.1

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.7.0",
5
- "sourceCommit": "b794ff2065167edac2d5c817897b5244e9136952",
4
+ "version": "1.7.1",
5
+ "sourceCommit": "67d641336636d076f15240809b73c0a4f9f438b5",
6
6
  "releaseWorkflow": "npm Release",
7
- "workflowRunId": "40036485979"
7
+ "workflowRunId": "40193283076"
8
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultrst-armada/kloks",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,7 +1,7 @@
1
1
  import { SkipoBase } from '@ultrst-armada/kloks/functions';
2
2
 
3
3
  type StorageMode = 'local' | 'local_hosted' | 'remote';
4
- type StorageProviderType = 'local_fs' | 's3' | 'backblaze_b2' | 'b2' | 'cloudflare_r2' | 'r2' | 'supabase' | 'firebase' | 'google_drive';
4
+ type StorageProviderType = 'local_fs' | 'backblaze_b2' | 'b2' | 'supabase' | 'firebase' | 'google_drive';
5
5
  interface StorageConfig {
6
6
  provider: StorageProviderType;
7
7
  mode?: StorageMode;
@@ -19,6 +19,11 @@ interface StorageConfig {
19
19
  keyId?: string;
20
20
  applicationKey?: string;
21
21
  accountId?: string;
22
+ googleClientId?: string;
23
+ googleRedirectUri?: string;
24
+ googleAccessToken?: string;
25
+ googleRefreshToken?: string;
26
+ googleRootFolderId?: string;
22
27
  }
23
28
  interface FileInfo {
24
29
  path: string;
@@ -121,9 +121,10 @@ var SrklClient = class {
121
121
  const creds = account.credentials || {};
122
122
  const sId = (account.serviceId || "").toLowerCase();
123
123
  const isB2 = sId.includes("backblaze") || sId === "b2";
124
+ const isGoogle = sId.includes("google_drive") || sId.includes("googledrive") || sId === "google";
124
125
  return {
125
126
  id: `conn-srkl-${account.id}`,
126
- provider: isB2 ? "backblaze_b2" : "aws_s3",
127
+ provider: isB2 ? "backblaze_b2" : isGoogle ? "google_drive" : "aws_s3",
127
128
  label: `SRKL: ${account.name || (isB2 ? "Backblaze" : "S3")}`,
128
129
  role: "redundant_clone",
129
130
  enabled: true,
@@ -137,7 +138,13 @@ var SrklClient = class {
137
138
  s3SecretAccessKey: creds.secretAccessKey || creds.applicationKey || creds.s3SecretAccessKey,
138
139
  s3BucketName: creds.bucketName || creds.s3BucketName,
139
140
  s3Region: creds.region || creds.s3Region,
140
- s3Endpoint: creds.endpoint || creds.s3Endpoint
141
+ s3Endpoint: creds.endpoint || creds.s3Endpoint,
142
+ googleEmail: creds.email || account.credentials?.email || account.name,
143
+ googleRootFolderId: creds.rootFolderId,
144
+ googleRootFolderName: creds.rootFolderName,
145
+ googleAccessToken: creds.accessToken,
146
+ googleAccessTokenExpiresAt: creds.accessTokenExpiresAt,
147
+ googleRefreshToken: creds.refreshToken
141
148
  };
142
149
  }
143
150
  };
@@ -1,13 +1,9 @@
1
1
  import {
2
2
  createWorkspace,
3
3
  deleteWorkspace,
4
- deleteWorkspaceFile,
5
4
  findUserWorkspace,
6
5
  getWorkspaces,
7
- listWorkspaceFiles,
8
- readWorkspaceFile,
9
- updateWorkspace,
10
- writeWorkspaceFile
6
+ updateWorkspace
11
7
  } from "./chunk-6K75XXFD.js";
12
8
  import {
13
9
  b2DownloadFile,
@@ -135,58 +131,6 @@ var WorkspaceCloneSyncEngine = class extends SkipoBase3 {
135
131
  }
136
132
  };
137
133
 
138
- // src/workspace/RemoteWorkspaceService.ts
139
- var RemoteWorkspaceService = class {
140
- type = "remote";
141
- async findWorkspace(workspaceId, env) {
142
- return await findUserWorkspace(workspaceId, env);
143
- }
144
- async listWorkspaces(userId, env) {
145
- return await getWorkspaces(userId, env);
146
- }
147
- async createWorkspace(userId, data, env) {
148
- return await createWorkspace(userId, data, env);
149
- }
150
- async updateWorkspace(userId, params, env) {
151
- return await updateWorkspace(userId, params, env);
152
- }
153
- async deleteWorkspace(userId, workspaceId, env) {
154
- return await deleteWorkspace(userId, workspaceId, env);
155
- }
156
- async readFile(workspaceId, filePath, env) {
157
- const { workspace, connection } = await this.findWorkspace(workspaceId, env);
158
- if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
159
- const data = await readWorkspaceFile(workspace, connection, filePath);
160
- if (!data) throw new Error(`File "${filePath}" not found in workspace "${workspaceId}".`);
161
- return data;
162
- }
163
- async writeFile(workspaceId, filePath, content, env) {
164
- const { workspace, connection } = await this.findWorkspace(workspaceId, env);
165
- if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
166
- await writeWorkspaceFile(workspace, connection, filePath, content);
167
- const size = typeof content === "string" ? new TextEncoder().encode(content).length : content.byteLength;
168
- return { success: true, path: filePath, sizeBytes: size };
169
- }
170
- async deleteFile(workspaceId, filePath, env) {
171
- const { workspace, connection } = await this.findWorkspace(workspaceId, env);
172
- if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
173
- const deleted = await deleteWorkspaceFile(workspace, connection, filePath);
174
- return { success: deleted };
175
- }
176
- async listFiles(workspaceId, subfolder = "", env) {
177
- const { workspace, connection } = await this.findWorkspace(workspaceId, env);
178
- if (!workspace) throw new Error(`Workspace "${workspaceId}" not found.`);
179
- const files = await listWorkspaceFiles(workspace, connection, subfolder);
180
- return files.map((f) => ({
181
- path: f.fileName,
182
- name: f.fileName.split("/").pop() || f.fileName,
183
- sizeBytes: f.sizeBytes,
184
- updatedAt: f.uploadTimestamp
185
- }));
186
- }
187
- };
188
- var remoteWorkspaceService = new RemoteWorkspaceService();
189
-
190
134
  // src/workspace/WorkingWorkspaceService.ts
191
135
  import fs2 from "fs";
192
136
  import path2 from "path";
@@ -245,8 +189,10 @@ function setWorkingWorkspaceConfig(cfg) {
245
189
  }
246
190
  return updated;
247
191
  }
248
- function getWorkingWorkspaceDir() {
249
- const dir = getWorkingWorkspaceConfig().workingDirectory;
192
+ function getWorkingWorkspaceDir(workspaceId = "default") {
193
+ const root = getWorkingWorkspaceConfig().workingDirectory;
194
+ const safeId = workspaceId.replace(/[^a-zA-Z0-9._-]/g, "_");
195
+ const dir = path.join(root, safeId);
250
196
  if (!fs.existsSync(dir)) {
251
197
  fs.mkdirSync(dir, { recursive: true });
252
198
  }
@@ -331,7 +277,7 @@ async function syncWorkspace(workspaceId) {
331
277
  }
332
278
  const { workspace, user, connection } = match;
333
279
  const folderKey = getWorkspaceFolderKey(workspace, user);
334
- const workingDir = getWorkingWorkspaceDir();
280
+ const workingDir = getWorkingWorkspaceDir(workspaceId);
335
281
  const isB2 = workspace.storageProvider === "backblaze_b2" || workspace.storageConfig?.type === "b2";
336
282
  const syncedFiles = [];
337
283
  let uploadedCount = 0;
@@ -505,8 +451,8 @@ var WorkingWorkspaceService = class {
505
451
  }
506
452
  workingDirResolver;
507
453
  type = "working";
508
- getWorkingDir() {
509
- return this.workingDirResolver ? this.workingDirResolver() : getWorkingWorkspaceDir();
454
+ getWorkingDir(workspaceId) {
455
+ return this.workingDirResolver ? this.workingDirResolver(workspaceId) : getWorkingWorkspaceDir(workspaceId);
510
456
  }
511
457
  async findWorkspace(workspaceId, env) {
512
458
  return await findUserWorkspace(workspaceId, env);
@@ -516,7 +462,7 @@ var WorkingWorkspaceService = class {
516
462
  }
517
463
  async createWorkspace(userId, data, env) {
518
464
  const res = await createWorkspace(userId, data, env);
519
- const dir = this.getWorkingDir();
465
+ const dir = this.getWorkingDir(res.workspace?.id || data?.id || "default");
520
466
  try {
521
467
  await fs2.promises.mkdir(dir, { recursive: true });
522
468
  } catch {
@@ -531,12 +477,12 @@ var WorkingWorkspaceService = class {
531
477
  }
532
478
  async readFile(_workspaceId, filePath) {
533
479
  const cleanRel = filePath.replace(/^\/+/, "");
534
- const fullPath = path2.resolve(this.getWorkingDir(), cleanRel);
480
+ const fullPath = path2.resolve(this.getWorkingDir(_workspaceId), cleanRel);
535
481
  return await fs2.promises.readFile(fullPath);
536
482
  }
537
483
  async writeFile(_workspaceId, filePath, content) {
538
484
  const cleanRel = filePath.replace(/^\/+/, "");
539
- const fullPath = path2.resolve(this.getWorkingDir(), cleanRel);
485
+ const fullPath = path2.resolve(this.getWorkingDir(_workspaceId), cleanRel);
540
486
  await fs2.promises.mkdir(path2.dirname(fullPath), { recursive: true });
541
487
  const buffer = typeof content === "string" ? Buffer.from(content) : Buffer.from(content.buffer, content.byteOffset, content.byteLength);
542
488
  await fs2.promises.writeFile(fullPath, buffer);
@@ -553,7 +499,7 @@ var WorkingWorkspaceService = class {
553
499
  }
554
500
  }
555
501
  async listFiles(_workspaceId, subfolder = "") {
556
- const baseDir = path2.resolve(this.getWorkingDir(), subfolder.replace(/^\/+/, ""));
502
+ const baseDir = path2.resolve(this.getWorkingDir(_workspaceId), subfolder.replace(/^\/+/, ""));
557
503
  if (!fs2.existsSync(baseDir)) return [];
558
504
  const entries = await fs2.promises.readdir(baseDir, { withFileTypes: true });
559
505
  return entries.map((e) => {
@@ -567,7 +513,7 @@ var WorkingWorkspaceService = class {
567
513
  } catch {
568
514
  }
569
515
  return {
570
- path: path2.relative(this.getWorkingDir(), full),
516
+ path: path2.relative(this.getWorkingDir(_workspaceId), full),
571
517
  name: e.name,
572
518
  isFolder: e.isDirectory(),
573
519
  sizeBytes,
@@ -581,12 +527,46 @@ var WorkingWorkspaceService = class {
581
527
  };
582
528
  var workingWorkspaceService = new WorkingWorkspaceService();
583
529
 
530
+ // src/workspace/RemoteWorkspaceService.ts
531
+ var RemoteWorkspaceService = class {
532
+ type = "working";
533
+ findWorkspace(id, env) {
534
+ return workingWorkspaceService.findWorkspace(id, env);
535
+ }
536
+ listWorkspaces(userId, env) {
537
+ return workingWorkspaceService.listWorkspaces(userId, env);
538
+ }
539
+ createWorkspace(userId, data, env) {
540
+ return workingWorkspaceService.createWorkspace(userId, data, env);
541
+ }
542
+ updateWorkspace(userId, params, env) {
543
+ return workingWorkspaceService.updateWorkspace(userId, params, env);
544
+ }
545
+ deleteWorkspace(userId, id, env) {
546
+ return workingWorkspaceService.deleteWorkspace(userId, id, env);
547
+ }
548
+ readFile(id, path3, env) {
549
+ return workingWorkspaceService.readFile(id, path3);
550
+ }
551
+ writeFile(id, path3, content, env) {
552
+ return workingWorkspaceService.writeFile(id, path3, content);
553
+ }
554
+ deleteFile(id, path3, env) {
555
+ return workingWorkspaceService.deleteFile(id, path3);
556
+ }
557
+ listFiles(id, subfolder, env) {
558
+ return workingWorkspaceService.listFiles(id, subfolder);
559
+ }
560
+ sync(id, options, env) {
561
+ return workingWorkspaceService.sync(id, options);
562
+ }
563
+ };
564
+ var remoteWorkspaceService = workingWorkspaceService;
565
+
584
566
  export {
585
567
  WorkspaceFS,
586
568
  SyncEngine,
587
569
  WorkspaceCloneSyncEngine,
588
- RemoteWorkspaceService,
589
- remoteWorkspaceService,
590
570
  getDefaultWorkingWorkspaceDir,
591
571
  getWorkingWorkspaceConfig,
592
572
  setWorkingWorkspaceConfig,
@@ -595,5 +575,7 @@ export {
595
575
  getWorkspaceFolderKey,
596
576
  syncWorkspace,
597
577
  WorkingWorkspaceService,
598
- workingWorkspaceService
578
+ workingWorkspaceService,
579
+ RemoteWorkspaceService,
580
+ remoteWorkspaceService
599
581
  };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  workspaceGraphService
3
- } from "./chunk-KSZ67VZJ.js";
3
+ } from "./chunk-F6IDQLC2.js";
4
4
  import {
5
5
  getSrklAccounts,
6
6
  srklListBackblazeBuckets
7
- } from "./chunk-N4Z57FVR.js";
7
+ } from "./chunk-XHADDA75.js";
8
8
  import {
9
9
  StorageAdapterFactory
10
- } from "./chunk-J2J24JVC.js";
10
+ } from "./chunk-R45CEV46.js";
11
11
  import {
12
12
  b2ListBuckets
13
13
  } from "./chunk-4QEMALXF.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageAdapterFactory
3
- } from "./chunk-J2J24JVC.js";
3
+ } from "./chunk-R45CEV46.js";
4
4
  import {
5
5
  findUserWorkspace
6
6
  } from "./chunk-6K75XXFD.js";
@@ -0,0 +1,134 @@
1
+ import {
2
+ BaseStorageAdapter,
3
+ ensureS3DirectoryMarker,
4
+ permanentDeleteS3Folder,
5
+ permanentDeleteS3Object,
6
+ renameS3Folder
7
+ } from "./chunk-R45CEV46.js";
8
+
9
+ // src/storage/S3Adapter.ts
10
+ var S3Client;
11
+ var PutObjectCommand;
12
+ var GetObjectCommand;
13
+ var ListObjectsV2Command;
14
+ var HeadObjectCommand;
15
+ var S3Adapter = class extends BaseStorageAdapter {
16
+ client;
17
+ bucket;
18
+ constructor(config) {
19
+ super(config);
20
+ this.bucket = config.bucket || "";
21
+ }
22
+ async ensureClient() {
23
+ if (this.client) return;
24
+ try {
25
+ const aws = await import("@aws-sdk/client-s3");
26
+ S3Client = aws.S3Client;
27
+ PutObjectCommand = aws.PutObjectCommand;
28
+ GetObjectCommand = aws.GetObjectCommand;
29
+ ListObjectsV2Command = aws.ListObjectsV2Command;
30
+ HeadObjectCommand = aws.HeadObjectCommand;
31
+ this.client = new S3Client({
32
+ region: this.config.region || "us-east-1",
33
+ endpoint: this.config.endpoint,
34
+ credentials: {
35
+ accessKeyId: this.config.accessKey,
36
+ secretAccessKey: this.config.secretKey
37
+ },
38
+ forcePathStyle: Boolean(this.config.endpoint)
39
+ });
40
+ } catch (e) {
41
+ throw new Error(`@aws-sdk/client-s3 failed to initialize: ${e.message}`);
42
+ }
43
+ }
44
+ async read(pathStr) {
45
+ await this.ensureClient();
46
+ const command = new GetObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
47
+ const response = await this.client.send(command);
48
+ return response.Body ? await response.Body.transformToByteArray() : new Uint8Array();
49
+ }
50
+ async write(pathStr, data, contentType = "application/octet-stream") {
51
+ await this.ensureClient();
52
+ const body = typeof data === "string" ? Buffer.from(data, "utf-8") : Buffer.from(data);
53
+ const command = new PutObjectCommand({
54
+ Bucket: this.bucket,
55
+ Key: this.normalizePath(pathStr),
56
+ Body: body,
57
+ ContentType: contentType
58
+ });
59
+ await this.client.send(command);
60
+ }
61
+ async delete(pathStr) {
62
+ await this.ensureClient();
63
+ await permanentDeleteS3Object(this.client, this.bucket, this.normalizePath(pathStr));
64
+ }
65
+ async deleteFolder(pathStr) {
66
+ await this.ensureClient();
67
+ return await permanentDeleteS3Folder(this.client, this.bucket, this.normalizePath(pathStr));
68
+ }
69
+ async renameFolder(oldPath, newPath) {
70
+ await this.ensureClient();
71
+ return await renameS3Folder(this.client, this.bucket, this.normalizePath(oldPath), this.normalizePath(newPath));
72
+ }
73
+ async ensureDirectory(pathStr) {
74
+ await this.ensureClient();
75
+ await ensureS3DirectoryMarker(this.client, this.bucket, this.normalizePath(pathStr));
76
+ }
77
+ async exists(pathStr) {
78
+ await this.ensureClient();
79
+ try {
80
+ const command = new HeadObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
81
+ await this.client.send(command);
82
+ return true;
83
+ } catch (e) {
84
+ if (e.name === "NotFound" || e.$metadata?.httpStatusCode === 404) return false;
85
+ throw e;
86
+ }
87
+ }
88
+ async list(pathStr = "") {
89
+ await this.ensureClient();
90
+ const prefix = pathStr ? this.normalizePath(pathStr).replace(/\/+$/, "") + "/" : "";
91
+ const command = new ListObjectsV2Command({ Bucket: this.bucket, Prefix: prefix });
92
+ const response = await this.client.send(command);
93
+ return (response.Contents || []).map((item) => ({
94
+ path: item.Key,
95
+ size: item.Size || 0,
96
+ modifiedAt: item.LastModified ? item.LastModified.getTime() : 0
97
+ }));
98
+ }
99
+ async stat(pathStr) {
100
+ await this.ensureClient();
101
+ const command = new HeadObjectCommand({ Bucket: this.bucket, Key: this.normalizePath(pathStr) });
102
+ const response = await this.client.send(command);
103
+ return {
104
+ path: pathStr,
105
+ size: response.ContentLength || 0,
106
+ modifiedAt: response.LastModified ? response.LastModified.getTime() : 0
107
+ };
108
+ }
109
+ async mkdir(pathStr) {
110
+ await this.ensureDirectory(pathStr);
111
+ }
112
+ async testConnection() {
113
+ const start = performance.now();
114
+ try {
115
+ await this.ensureClient();
116
+ const res = await this.client.send(new ListObjectsV2Command({ Bucket: this.bucket, MaxKeys: 1 }));
117
+ return {
118
+ ok: true,
119
+ latencyMs: Math.round(performance.now() - start),
120
+ details: { bucket: this.bucket, keyCount: res.KeyCount ?? 0 }
121
+ };
122
+ } catch (e) {
123
+ return {
124
+ ok: false,
125
+ latencyMs: Math.round(performance.now() - start),
126
+ error: e.message
127
+ };
128
+ }
129
+ }
130
+ };
131
+
132
+ export {
133
+ S3Adapter
134
+ };