@webapper/cloudsee-drive-mcp 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -74,15 +74,20 @@ or a local `.env` for development — see [`.env.example`](.env.example)).
74
74
  | `CLOUDSEE_API_KEY_ID` | ✅ | — | Your CloudSee Drive API key id. |
75
75
  | `CLOUDSEE_API_KEY_SECRET` | ✅ | — | The matching API key secret. **Never commit this.** |
76
76
  | `CLOUDSEE_API_BASE_URL` | — | `https://drive-api.cloudsee.cloud` | API base URL. UAT: `https://drive-api-uat.cloudsee.cloud`. |
77
+ | `CLOUDSEE_DEFAULT_BUCKET` | — | — | Default drive (S3 bucket) for tool calls that omit `bucketName`. Find the name in the CloudSee dashboard. |
77
78
  | `CLOUDSEE_LOG_LEVEL` | — | `info` | `error` \| `warn` \| `info` \| `debug` (logs go to stderr only). |
78
79
  | `CLOUDSEE_TIMEOUT_MS` | — | `30000` | Per-request timeout in milliseconds. |
79
80
 
80
81
  ## Tools
81
82
 
83
+ Most tools operate on one **drive** (an S3 bucket): pass `bucketName`, or set
84
+ `CLOUDSEE_DEFAULT_BUCKET` once and omit it. `recent_files` and `list_buckets` don't need a drive.
85
+
82
86
  | Tool | Description | Access |
83
87
  | --- | --- | --- |
84
88
  | `list_buckets` | List accessible buckets | read |
85
- | `browse_folder` | List a folder's contents (one level) | read |
89
+ | `list_files` | List all files in a drive (recursive) | read |
90
+ | `browse_folder` | List a folder's contents (indexed view) | read |
86
91
  | `search_files` | Find files/folders by name keyword | read |
87
92
  | `recent_files` | List recently used files | read |
88
93
  | `get_file_metadata` | Get a file's metadata | read |
@@ -118,12 +123,18 @@ authorizes every operation server-side; confirmation is not the security boundar
118
123
 
119
124
  This wraps CloudSee Drive's public API (the `drive-bridge` `/v1/*` gateway).
120
125
 
121
- - **Read & download tools are fully functional today.**
126
+ - **Almost every tool needs a drive** (`bucketName`, or `CLOUDSEE_DEFAULT_BUCKET`). Without one,
127
+ drive-scoped tools return a clear "specify a drive" message.
128
+ - **Read & download tools are functional today** once a drive is given — `list_files`,
129
+ `get_file_metadata`, `get_file_tags`, `download_file`, `share_link` all return real data.
130
+ - **`list_buckets` currently returns an empty list** (drive enumeration isn't wired for public
131
+ API keys yet), so supply the drive name from the CloudSee dashboard for now.
132
+ - **`browse_folder`/`search_files` use the search-indexed view** and can return empty for
133
+ un-indexed content — use **`list_files`** for a complete, reliable listing of a drive.
122
134
  - **Write/delete/share tools are present but their end-to-end authorization is sequenced
123
135
  behind the gateway's RBAC/scope wiring (CSD-572).** Until that lands, the gateway may deny
124
136
  these operations — the tools surface that cleanly; it is expected, not a bug here.
125
137
  - `upload_file` supports single-file uploads up to 8 MiB; multipart (larger files) is planned.
126
- - `search_files` matches a single folder level (not a whole-tree recursive search).
127
138
 
128
139
  ## Development
129
140
 
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ var envSchema = z.object({
33
33
  CLOUDSEE_API_KEY_ID: z.string().trim().min(1, "is required (your CloudSee Drive API key id)"),
34
34
  CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, "is required (the matching API key secret)"),
35
35
  CLOUDSEE_API_BASE_URL: z.string().url("must be a valid URL").optional(),
36
+ CLOUDSEE_DEFAULT_BUCKET: z.string().trim().min(1).optional(),
36
37
  CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),
37
38
  CLOUDSEE_LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).optional()
38
39
  });
@@ -53,6 +54,7 @@ Set the required environment variables (see .env.example or the README).`,
53
54
  apiKeyId: e.CLOUDSEE_API_KEY_ID,
54
55
  apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,
55
56
  baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
57
+ defaultBucket: e.CLOUDSEE_DEFAULT_BUCKET,
56
58
  timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,
57
59
  logLevel: e.CLOUDSEE_LOG_LEVEL ?? "info"
58
60
  };
@@ -273,7 +275,7 @@ function sleep(ms) {
273
275
  }
274
276
 
275
277
  // src/tools/read.ts
276
- import { z as z2 } from "zod";
278
+ import { z as z3 } from "zod";
277
279
 
278
280
  // src/tools/format.ts
279
281
  var DEFAULT_MAX_ITEMS = 100;
@@ -318,6 +320,20 @@ function withCursor(body, nextCursor) {
318
320
  }
319
321
 
320
322
  // src/tools/types.ts
323
+ import { z as z2 } from "zod";
324
+ var bucketField = z2.string().min(1).optional().describe(
325
+ "The CloudSee drive (S3 bucket) name to operate in, e.g. 'max-2778abc0' \u2014 find it in the CloudSee dashboard. Required unless CLOUDSEE_DEFAULT_BUCKET is configured on the server."
326
+ );
327
+ function resolveBucket(bucket, defaultBucket) {
328
+ const resolved = (bucket ?? defaultBucket ?? "").trim();
329
+ if (!resolved) {
330
+ throw new CloudSeeError(
331
+ "No CloudSee drive specified. Pass `bucketName` (your drive name, shown in the CloudSee dashboard), or set CLOUDSEE_DEFAULT_BUCKET in the server config.",
332
+ { code: "no_bucket" }
333
+ );
334
+ }
335
+ return resolved;
336
+ }
321
337
  function textResult(text) {
322
338
  return { content: [{ type: "text", text }] };
323
339
  }
@@ -335,57 +351,85 @@ var listBuckets = {
335
351
  return textResult(summarize(data));
336
352
  }
337
353
  };
338
- var browseSchema = z2.object({
339
- path: z2.string().optional().describe("Folder path / prefix to list. Empty or omitted = the root."),
340
- sortOption: z2.string().optional().describe("Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'."),
341
- pageSize: z2.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
342
- cursor: z2.string().optional().describe("Opaque pagination cursor returned by a previous call.")
354
+ var browseSchema = z3.object({
355
+ bucketName: bucketField,
356
+ path: z3.string().optional().describe("Folder path / prefix within the drive to list. Empty or omitted = the drive root."),
357
+ sortOption: z3.string().optional().describe("Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'."),
358
+ pageSize: z3.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
359
+ cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
343
360
  });
344
361
  var browseFolder = {
345
362
  name: "browse_folder",
346
363
  title: "Browse folder",
347
- description: "List the files and sub-folders directly inside a folder (one level, non-recursive), with sorting and pagination. To filter by keyword use 'search_files'.",
364
+ description: "List the files and sub-folders inside a folder of a drive (the indexed view), with sorting and pagination. Requires the drive (bucketName). To filter by keyword use 'search_files'; for a complete, recursive file listing straight from storage use 'list_files'.",
348
365
  endpoint: { method: "POST", path: "/storage/list", scopes: ["drive:read"] },
349
366
  inputSchema: browseSchema.shape,
350
367
  annotations: { readOnlyHint: true, openWorldHint: true },
351
- handler: async (args, { client }) => {
368
+ handler: async (args, { client, defaultBucket }) => {
352
369
  const a = browseSchema.parse(args);
370
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
353
371
  const { data, nextCursor } = await client.postPaged(
354
372
  "/storage/list",
355
- { dirPath: a.path ?? "", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },
373
+ { bucketName, dirPath: a.path ?? "", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },
356
374
  "nextPage",
357
375
  a.cursor
358
376
  );
359
377
  return textResult(withCursor(summarize(data), nextCursor));
360
378
  }
361
379
  };
362
- var searchSchema = z2.object({
363
- query: z2.string().min(1).describe("Keyword to match against file and folder names."),
364
- path: z2.string().optional().describe("Folder to search within (this folder level, non-recursive). Empty = root."),
365
- pageSize: z2.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
366
- cursor: z2.string().optional().describe("Opaque pagination cursor returned by a previous call.")
380
+ var searchSchema = z3.object({
381
+ bucketName: bucketField,
382
+ query: z3.string().min(1).describe("Keyword to match against file and folder names."),
383
+ path: z3.string().optional().describe("Folder within the drive to search under. Empty = drive root."),
384
+ pageSize: z3.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
385
+ cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
367
386
  });
368
387
  var searchFiles = {
369
388
  name: "search_files",
370
389
  title: "Search files",
371
- description: "Search for files and folders by name keyword within a folder. Note: this matches the given folder level and is NOT a whole-tree recursive search \u2014 browse into sub-folders to search deeper. Returns matches with pagination.",
390
+ description: "Search for files and folders by name keyword within a drive (the indexed view). Requires the drive (bucketName). Returns matches with pagination.",
372
391
  endpoint: { method: "POST", path: "/storage/list", scopes: ["drive:read"] },
373
392
  inputSchema: searchSchema.shape,
374
393
  annotations: { readOnlyHint: true, openWorldHint: true },
375
- handler: async (args, { client }) => {
394
+ handler: async (args, { client, defaultBucket }) => {
376
395
  const a = searchSchema.parse(args);
396
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
377
397
  const { data, nextCursor } = await client.postPaged(
378
398
  "/storage/list",
379
- { dirPath: a.path ?? "", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },
399
+ { bucketName, dirPath: a.path ?? "", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },
380
400
  "nextPage",
381
401
  a.cursor
382
402
  );
383
403
  return textResult(withCursor(summarize(data), nextCursor));
384
404
  }
385
405
  };
386
- var recentSchema = z2.object({
387
- limit: z2.number().int().min(1).max(200).optional().describe("Max items (1-200, default 50)."),
388
- cursor: z2.string().optional().describe("Opaque pagination cursor returned by a previous call.")
406
+ var listFilesSchema = z3.object({
407
+ bucketName: bucketField,
408
+ deep: z3.boolean().optional().describe("Recurse into sub-folders (default true)."),
409
+ cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
410
+ });
411
+ var listFiles = {
412
+ name: "list_files",
413
+ title: "List files in a drive",
414
+ description: "List the files in a drive straight from storage, recursively by default \u2014 the most reliable way to see what a drive actually contains. Requires the drive (bucketName). Returns names, sizes, storage classes and keys, with pagination.",
415
+ endpoint: { method: "POST", path: "/storage/bucket/files", scopes: ["drive:read"] },
416
+ inputSchema: listFilesSchema.shape,
417
+ annotations: { readOnlyHint: true, openWorldHint: true },
418
+ handler: async (args, { client, defaultBucket }) => {
419
+ const a = listFilesSchema.parse(args);
420
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
421
+ const { data, nextCursor } = await client.postPaged(
422
+ "/storage/bucket/files",
423
+ { bucketName, deepQuery: a.deep ?? true },
424
+ "marker",
425
+ a.cursor
426
+ );
427
+ return textResult(withCursor(summarize(data), nextCursor));
428
+ }
429
+ };
430
+ var recentSchema = z3.object({
431
+ limit: z3.number().int().min(1).max(200).optional().describe("Max items (1-200, default 50)."),
432
+ cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
389
433
  });
390
434
  var recentFiles = {
391
435
  name: "recent_files",
@@ -402,34 +446,39 @@ var recentFiles = {
402
446
  return textResult(withCursor(summarize(data), hasMore ? nextCursor : void 0));
403
447
  }
404
448
  };
405
- var metaSchema = z2.object({ objectKey: z2.string().min(1).describe("Full object key (path) of the file.") });
449
+ var metaSchema = z3.object({
450
+ bucketName: bucketField,
451
+ objectKey: z3.string().min(1).describe("Full object key (path) of the file within the drive.")
452
+ });
406
453
  var getFileMetadata = {
407
454
  name: "get_file_metadata",
408
455
  title: "Get file metadata",
409
- description: "Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key.",
456
+ description: "Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key. Requires the drive (bucketName).",
410
457
  endpoint: { method: "POST", path: "/storage/object/detail", scopes: ["drive:read"] },
411
458
  inputSchema: metaSchema.shape,
412
459
  annotations: { readOnlyHint: true, openWorldHint: true },
413
- handler: async (args, { client }) => {
460
+ handler: async (args, { client, defaultBucket }) => {
414
461
  const a = metaSchema.parse(args);
415
- const data = await client.post("/storage/object/detail", { objectKey: a.objectKey });
462
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
463
+ const data = await client.post("/storage/object/detail", { bucketName, objectKey: a.objectKey });
416
464
  return textResult(summarize(data));
417
465
  }
418
466
  };
419
- var tagsSchema = z2.object({
420
- objectKey: z2.string().min(1).describe("Full object key (path) of the file."),
421
- bucketName: z2.string().optional().describe("Bucket name, if not implied by the key.")
467
+ var tagsSchema = z3.object({
468
+ bucketName: bucketField,
469
+ objectKey: z3.string().min(1).describe("Full object key (path) of the file within the drive.")
422
470
  });
423
471
  var getFileTags = {
424
472
  name: "get_file_tags",
425
473
  title: "Get file tags",
426
- description: "Get the S3 object tags (key/value pairs) attached to a file, by object key.",
474
+ description: "Get the S3 object tags (key/value pairs) attached to a file, by object key. Requires the drive (bucketName).",
427
475
  endpoint: { method: "POST", path: "/storage/object/tagging", scopes: ["drive:read"] },
428
476
  inputSchema: tagsSchema.shape,
429
477
  annotations: { readOnlyHint: true, openWorldHint: true },
430
- handler: async (args, { client }) => {
478
+ handler: async (args, { client, defaultBucket }) => {
431
479
  const a = tagsSchema.parse(args);
432
- const data = await client.post("/storage/object/tagging", { objectKey: a.objectKey, bucketName: a.bucketName });
480
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
481
+ const data = await client.post("/storage/object/tagging", { bucketName, objectKey: a.objectKey });
433
482
  return textResult(summarize(data));
434
483
  }
435
484
  };
@@ -437,28 +486,32 @@ var readTools = [
437
486
  listBuckets,
438
487
  browseFolder,
439
488
  searchFiles,
489
+ listFiles,
440
490
  recentFiles,
441
491
  getFileMetadata,
442
492
  getFileTags
443
493
  ];
444
494
 
445
495
  // src/tools/download.ts
446
- import { z as z3 } from "zod";
447
- var downloadSchema = z3.object({
448
- filePath: z3.string().min(1).describe("Object key (path) of the file to download."),
449
- forceDownload: z3.boolean().optional().describe("If true, the link forces an attachment download instead of inline view."),
450
- storageId: z3.string().optional().describe("Optional storage/index id.")
496
+ import { z as z4 } from "zod";
497
+ var downloadSchema = z4.object({
498
+ bucketName: bucketField,
499
+ filePath: z4.string().min(1).describe("Object key (path) of the file to download, within the drive."),
500
+ forceDownload: z4.boolean().optional().describe("If true, the link forces an attachment download instead of inline view."),
501
+ storageId: z4.string().optional().describe("Optional storage/index id.")
451
502
  });
452
503
  var downloadFile = {
453
504
  name: "download_file",
454
505
  title: "Download file",
455
- description: "Get a short-lived pre-signed download URL for a file. The URL is time-limited and grants read access to that one object \u2014 share it with care. Returns the URL; it does not load file bytes into the conversation.",
506
+ description: "Get a short-lived pre-signed download URL for a file. Requires the drive (bucketName). The URL is time-limited and grants read access to that one object \u2014 share it with care. Returns the URL; it does not load file bytes into the conversation.",
456
507
  endpoint: { method: "POST", path: "/storage/object/download-url", scopes: ["drive:read", "drive:download"] },
457
508
  inputSchema: downloadSchema.shape,
458
509
  annotations: { readOnlyHint: true, openWorldHint: true },
459
- handler: async (args, { client }) => {
510
+ handler: async (args, { client, defaultBucket }) => {
460
511
  const a = downloadSchema.parse(args);
512
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
461
513
  const data = await client.post("/storage/object/download-url", {
514
+ bucketName,
462
515
  filePath: a.filePath,
463
516
  download: a.forceDownload ?? false,
464
517
  storageId: a.storageId
@@ -466,20 +519,23 @@ var downloadFile = {
466
519
  return textResult(summarize(data));
467
520
  }
468
521
  };
469
- var shareSchema = z3.object({
470
- filePath: z3.string().min(1).describe("Object key (path) of the file to share."),
471
- storageId: z3.string().optional().describe("Optional storage/index id.")
522
+ var shareSchema = z4.object({
523
+ bucketName: bucketField,
524
+ filePath: z4.string().min(1).describe("Object key (path) of the file to share, within the drive."),
525
+ storageId: z4.string().optional().describe("Optional storage/index id.")
472
526
  });
473
527
  var shareLink = {
474
528
  name: "share_link",
475
529
  title: "Create share link",
476
- description: "Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.",
530
+ description: "Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). Requires the drive (bucketName). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.",
477
531
  endpoint: { method: "POST", path: "/storage/object/download-url", scopes: ["drive:read", "drive:download"] },
478
532
  inputSchema: shareSchema.shape,
479
533
  annotations: { readOnlyHint: true, openWorldHint: true },
480
- handler: async (args, { client }) => {
534
+ handler: async (args, { client, defaultBucket }) => {
481
535
  const a = shareSchema.parse(args);
536
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
482
537
  const data = await client.post("/storage/object/download-url", {
538
+ bucketName,
483
539
  filePath: a.filePath,
484
540
  shareableLink: true,
485
541
  storageId: a.storageId
@@ -492,12 +548,12 @@ var downloadTools = [downloadFile, shareLink];
492
548
  // src/tools/write.ts
493
549
  import { readFile, stat } from "fs/promises";
494
550
  import { basename } from "path";
495
- import { z as z5 } from "zod";
551
+ import { z as z6 } from "zod";
496
552
 
497
553
  // src/confirm.ts
498
- import { z as z4 } from "zod";
554
+ import { z as z5 } from "zod";
499
555
  var confirmShape = {
500
- confirm: z4.boolean().optional().describe(
556
+ confirm: z5.boolean().optional().describe(
501
557
  "Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes."
502
558
  )
503
559
  };
@@ -543,22 +599,24 @@ function pickKey(data) {
543
599
  }
544
600
  return void 0;
545
601
  }
546
- var uploadSchema = z5.object({
547
- localPath: z5.string().min(1).describe("Path to the local file to upload (absolute, or relative to the server's working directory)."),
548
- destinationFolder: z5.string().describe("Destination folder/prefix in CloudSee Drive. Empty = root."),
549
- fileName: z5.string().optional().describe("Name to store the file as. Defaults to the local file's name."),
550
- contentType: z5.string().optional().describe("MIME type. Defaults to application/octet-stream."),
551
- storageClass: z5.string().optional().describe("Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).")
602
+ var uploadSchema = z6.object({
603
+ bucketName: bucketField,
604
+ localPath: z6.string().min(1).describe("Path to the local file to upload (absolute, or relative to the server's working directory)."),
605
+ destinationFolder: z6.string().describe("Destination folder/prefix in the drive. Empty = drive root."),
606
+ fileName: z6.string().optional().describe("Name to store the file as. Defaults to the local file's name."),
607
+ contentType: z6.string().optional().describe("MIME type. Defaults to application/octet-stream."),
608
+ storageClass: z6.string().optional().describe("Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).")
552
609
  });
553
610
  var uploadFile = {
554
611
  name: "upload_file",
555
612
  title: "Upload file",
556
- description: "Upload a local file to a CloudSee Drive folder. Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object." + GATED,
613
+ description: "Upload a local file to a folder in a drive. Requires the drive (bucketName). Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object." + GATED,
557
614
  endpoint: { method: "POST", path: "/storage/upload/url", scopes: ["drive:write"] },
558
615
  inputSchema: uploadSchema.shape,
559
616
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
560
- handler: async (args, { client }) => {
617
+ handler: async (args, { client, defaultBucket }) => {
561
618
  const a = uploadSchema.parse(args);
619
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
562
620
  const fileName = a.fileName ?? basename(a.localPath);
563
621
  const contentType = a.contentType ?? "application/octet-stream";
564
622
  let info;
@@ -576,6 +634,7 @@ var uploadFile = {
576
634
  }
577
635
  const bytes = await readFile(a.localPath);
578
636
  const presign = await client.post("/storage/upload/url", {
637
+ bucketName,
579
638
  dirPath: a.destinationFolder,
580
639
  fileName,
581
640
  contentType,
@@ -595,6 +654,7 @@ var uploadFile = {
595
654
  let complete;
596
655
  try {
597
656
  complete = await client.post("/storage/upload/complete", {
657
+ bucketName,
598
658
  dirPath: a.destinationFolder,
599
659
  objects: [{ fileName, key, contentType, size: info.size }]
600
660
  });
@@ -610,45 +670,51 @@ var uploadFile = {
610
670
  ${summarize(complete)}`);
611
671
  }
612
672
  };
613
- var createFolderSchema = z5.object({
614
- parentPath: z5.string().describe("Parent folder/prefix. Empty = root."),
615
- name: z5.string().min(1).describe("New folder name.")
673
+ var createFolderSchema = z6.object({
674
+ bucketName: bucketField,
675
+ parentPath: z6.string().describe("Parent folder/prefix in the drive. Empty = drive root."),
676
+ name: z6.string().min(1).describe("New folder name.")
616
677
  });
617
678
  var createFolder = {
618
679
  name: "create_folder",
619
680
  title: "Create folder",
620
- description: "Create a new folder at the given path." + GATED,
681
+ description: "Create a new folder at the given path in a drive. Requires the drive (bucketName)." + GATED,
621
682
  endpoint: { method: "POST", path: "/storage/folder/create", scopes: ["drive:write"] },
622
683
  inputSchema: createFolderSchema.shape,
623
684
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
624
- handler: async (args, { client }) => {
685
+ handler: async (args, { client, defaultBucket }) => {
625
686
  const a = createFolderSchema.parse(args);
626
- const data = await client.post("/storage/folder/create", { dirPath: a.parentPath, object: a.name });
687
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
688
+ const data = await client.post("/storage/folder/create", { bucketName, dirPath: a.parentPath, object: a.name });
627
689
  return textResult(`Created folder "${a.name}" in "${a.parentPath || "/"}".
628
690
 
629
691
  ${summarize(data)}`);
630
692
  }
631
693
  };
632
- var renameSchema = z5.object({
633
- oldObjectId: z5.string().min(1).describe("Current object key/id of the file or folder."),
634
- newName: z5.string().min(1).describe("New name."),
635
- isFolder: z5.boolean().optional().describe("Set true when renaming a folder."),
636
- storageId: z5.string().optional().describe("Optional storage/index id."),
694
+ var renameSchema = z6.object({
695
+ bucketName: bucketField,
696
+ oldObjectId: z6.string().min(1).describe("Current object key/id of the file or folder."),
697
+ newName: z6.string().min(1).describe("New name."),
698
+ isFolder: z6.boolean().optional().describe("Set true when renaming a folder."),
699
+ storageId: z6.string().optional().describe("Optional storage/index id."),
637
700
  ...confirmShape
638
701
  });
639
702
  var renameFile = {
640
703
  name: "rename_file",
641
704
  title: "Rename file or folder",
642
- description: "Rename a file or folder. Destructive (changes the object's key). Requires confirm=true." + GATED,
705
+ description: "Rename a file or folder in a drive. Requires the drive (bucketName). Destructive (changes the object's key). Requires confirm=true." + GATED,
643
706
  endpoint: { method: "POST", path: "/storage/object/rename", scopes: ["drive:write"] },
644
707
  inputSchema: renameSchema.shape,
645
708
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
646
- handler: async (args, { client }) => {
709
+ handler: async (args, { client, defaultBucket }) => {
647
710
  const a = renameSchema.parse(args);
711
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
648
712
  if (!a.confirm) {
649
- return confirmationPreview(`rename ${a.isFolder ? "folder" : "file"} to "${a.newName}"`, `Target: ${a.oldObjectId}`);
713
+ return confirmationPreview(`rename ${a.isFolder ? "folder" : "file"} to "${a.newName}"`, `Drive: ${bucketName}
714
+ Target: ${a.oldObjectId}`);
650
715
  }
651
716
  const data = await client.post("/storage/object/rename", {
717
+ bucketName,
652
718
  oldObjectId: a.oldObjectId,
653
719
  newObjectName: a.newName,
654
720
  isFolder: a.isFolder ?? false,
@@ -659,29 +725,32 @@ var renameFile = {
659
725
  ${summarize(data)}`);
660
726
  }
661
727
  };
662
- var moveSchema = z5.object({
663
- sourcePath: z5.string().min(1).describe("Source object key/prefix."),
664
- destinationPath: z5.string().min(1).describe("Destination prefix."),
665
- asCopy: z5.boolean().optional().describe("Copy instead of move (copy is non-destructive; the source is kept)."),
666
- isFolder: z5.boolean().optional().describe("Set true for a folder."),
667
- destinationBucket: z5.string().optional().describe("Target bucket, if different."),
668
- storageId: z5.string().optional().describe("Optional storage/index id."),
728
+ var moveSchema = z6.object({
729
+ bucketName: bucketField,
730
+ sourcePath: z6.string().min(1).describe("Source object key/prefix."),
731
+ destinationPath: z6.string().min(1).describe("Destination prefix."),
732
+ asCopy: z6.boolean().optional().describe("Copy instead of move (copy is non-destructive; the source is kept)."),
733
+ isFolder: z6.boolean().optional().describe("Set true for a folder."),
734
+ destinationBucket: z6.string().optional().describe("Target drive, if different from the source drive."),
735
+ storageId: z6.string().optional().describe("Optional storage/index id."),
669
736
  ...confirmShape
670
737
  });
671
738
  var moveFile = {
672
739
  name: "move_file",
673
740
  title: "Move or copy file",
674
- description: "Move (or copy, with asCopy=true) a file or folder to a new location. A move removes the source and is destructive, so it requires confirm=true; a copy does not." + GATED,
741
+ description: "Move (or copy, with asCopy=true) a file or folder to a new location. Requires the source drive (bucketName). A move removes the source and is destructive, so it requires confirm=true; a copy does not." + GATED,
675
742
  endpoint: { method: "POST", path: "/storage/object/move", scopes: ["drive:write"] },
676
743
  inputSchema: moveSchema.shape,
677
744
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
678
- handler: async (args, { client }) => {
745
+ handler: async (args, { client, defaultBucket }) => {
679
746
  const a = moveSchema.parse(args);
747
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
680
748
  const isMove = !a.asCopy;
681
749
  if (isMove && !a.confirm) {
682
750
  return confirmationPreview(`move "${a.sourcePath}" \u2192 "${a.destinationPath}"`, "This removes the source. Pass asCopy=true to copy instead.");
683
751
  }
684
752
  const data = await client.post("/storage/object/move", {
753
+ bucketName,
685
754
  asCopy: a.asCopy ?? false,
686
755
  isFolder: a.isFolder ?? false,
687
756
  sourcePath: a.sourcePath,
@@ -694,93 +763,104 @@ var moveFile = {
694
763
  ${summarize(data)}`);
695
764
  }
696
765
  };
697
- var duplicateSchema = z5.object({
698
- objectKey: z5.string().min(1).describe("Source object key to duplicate."),
699
- storageId: z5.string().optional().describe("Optional storage/index id.")
766
+ var duplicateSchema = z6.object({
767
+ bucketName: bucketField,
768
+ objectKey: z6.string().min(1).describe("Source object key to duplicate."),
769
+ storageId: z6.string().optional().describe("Optional storage/index id.")
700
770
  });
701
771
  var duplicateFile = {
702
772
  name: "duplicate_file",
703
773
  title: "Duplicate file",
704
- description: "Create a copy of a file in the same location." + GATED,
774
+ description: "Create a copy of a file in the same location. Requires the drive (bucketName)." + GATED,
705
775
  endpoint: { method: "POST", path: "/storage/object/duplicate", scopes: ["drive:write"] },
706
776
  inputSchema: duplicateSchema.shape,
707
777
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
708
- handler: async (args, { client }) => {
778
+ handler: async (args, { client, defaultBucket }) => {
709
779
  const a = duplicateSchema.parse(args);
710
- const data = await client.post("/storage/object/duplicate", { objectKey: a.objectKey, storageId: a.storageId });
780
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
781
+ const data = await client.post("/storage/object/duplicate", { bucketName, objectKey: a.objectKey, storageId: a.storageId });
711
782
  return textResult(`Duplicated "${a.objectKey}".
712
783
 
713
784
  ${summarize(data)}`);
714
785
  }
715
786
  };
716
- var deleteSchema = z5.object({
717
- objectKeys: z5.array(z5.string().min(1)).min(1).describe("Object keys (paths) to permanently delete."),
787
+ var deleteSchema = z6.object({
788
+ bucketName: bucketField,
789
+ objectKeys: z6.array(z6.string().min(1)).min(1).describe("Object keys (paths) to permanently delete."),
718
790
  ...confirmShape
719
791
  });
720
792
  var deleteFiles = {
721
793
  name: "delete_files",
722
794
  title: "Delete files",
723
- description: "Permanently delete one or more files/objects by key. Destructive and irreversible. Requires confirm=true." + GATED,
795
+ description: "Permanently delete one or more files/objects by key from a drive. Requires the drive (bucketName). Destructive and irreversible. Requires confirm=true." + GATED,
724
796
  endpoint: { method: "POST", path: "/storage/objects/delete", scopes: ["drive:delete"] },
725
797
  inputSchema: deleteSchema.shape,
726
798
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
727
- handler: async (args, { client }) => {
799
+ handler: async (args, { client, defaultBucket }) => {
728
800
  const a = deleteSchema.parse(args);
801
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
729
802
  if (!a.confirm) {
730
- return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s)`, a.objectKeys.map((k) => ` - ${k}`).join("\n"));
803
+ return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s) from "${bucketName}"`, a.objectKeys.map((k) => ` - ${k}`).join("\n"));
731
804
  }
732
- const data = await client.post("/storage/objects/delete", { deletedObjects: a.objectKeys });
805
+ const data = await client.post("/storage/objects/delete", { bucketName, deletedObjects: a.objectKeys });
733
806
  return textResult(`Deleted ${a.objectKeys.length} object(s).
734
807
 
735
808
  ${summarize(data)}`);
736
809
  }
737
810
  };
738
- var updateMetaSchema = z5.object({
739
- objectKey: z5.string().min(1).describe("Object key whose metadata to update."),
740
- metadata: z5.record(z5.string(), z5.unknown()).describe("Metadata fields to set (key/value map). Overwrites the listed fields."),
811
+ var updateMetaSchema = z6.object({
812
+ bucketName: bucketField,
813
+ objectKey: z6.string().min(1).describe("Object key whose metadata to update."),
814
+ metadata: z6.record(z6.string(), z6.unknown()).describe("Metadata fields to set (key/value map). Overwrites the listed fields."),
741
815
  ...confirmShape
742
816
  });
743
817
  var updateMetadata = {
744
818
  name: "update_metadata",
745
819
  title: "Update file metadata",
746
- description: "Update a file's metadata. Destructive (overwrites the listed metadata fields). Requires confirm=true." + GATED,
820
+ description: "Update a file's metadata in a drive. Requires the drive (bucketName). Destructive (overwrites the listed metadata fields). Requires confirm=true." + GATED,
747
821
  endpoint: { method: "POST", path: "/storage/object/metadata", scopes: ["drive:write"] },
748
822
  inputSchema: updateMetaSchema.shape,
749
823
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
750
- handler: async (args, { client }) => {
824
+ handler: async (args, { client, defaultBucket }) => {
751
825
  const a = updateMetaSchema.parse(args);
826
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
752
827
  if (!a.confirm) {
753
- return confirmationPreview(`update metadata on "${a.objectKey}"`, `Fields: ${Object.keys(a.metadata).join(", ") || "(none)"}`);
828
+ return confirmationPreview(`update metadata on "${a.objectKey}"`, `Drive: ${bucketName}
829
+ Fields: ${Object.keys(a.metadata).join(", ") || "(none)"}`);
754
830
  }
755
- const data = await client.post("/storage/object/metadata", { objectKey: a.objectKey, metadata: a.metadata });
831
+ const data = await client.post("/storage/object/metadata", { bucketName, objectKey: a.objectKey, metadata: a.metadata });
756
832
  return textResult(`Updated metadata on "${a.objectKey}".
757
833
 
758
834
  ${summarize(data)}`);
759
835
  }
760
836
  };
761
- var restoreSchema = z5.object({
762
- objectKey: z5.string().min(1).describe("Object key of the archived (Glacier) object to restore."),
763
- days: z5.number().int().min(1).optional().describe("How many days to keep the restored copy available."),
764
- retrievalTier: z5.enum(["Expedited", "Standard", "Bulk"]).optional().describe("Glacier retrieval tier (default Standard)."),
765
- storageId: z5.string().optional().describe("Optional storage/index id."),
837
+ var restoreSchema = z6.object({
838
+ bucketName: bucketField,
839
+ objectKey: z6.string().min(1).describe("Object key of the archived (Glacier) object to restore."),
840
+ days: z6.number().int().min(1).optional().describe("How many days to keep the restored copy available."),
841
+ retrievalTier: z6.enum(["Expedited", "Standard", "Bulk"]).optional().describe("Glacier retrieval tier (default Standard)."),
842
+ storageId: z6.string().optional().describe("Optional storage/index id."),
766
843
  ...confirmShape
767
844
  });
768
845
  var restoreArchivedFile = {
769
846
  name: "restore_archived_file",
770
847
  title: "Restore archived file",
771
- description: "Begin restoring an archived (S3 Glacier) object so it can be downloaded. This is Glacier un-archiving \u2014 NOT recovery of a deleted file \u2014 and may incur retrieval cost and take minutes to hours. Requires confirm=true." + GATED,
848
+ description: "Begin restoring an archived (S3 Glacier) object so it can be downloaded. Requires the drive (bucketName). This is Glacier un-archiving \u2014 NOT recovery of a deleted file \u2014 and may incur retrieval cost and take minutes to hours. Requires confirm=true." + GATED,
772
849
  endpoint: { method: "POST", path: "/storage/object/restore", scopes: ["drive:write"] },
773
850
  inputSchema: restoreSchema.shape,
774
851
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
775
- handler: async (args, { client }) => {
852
+ handler: async (args, { client, defaultBucket }) => {
776
853
  const a = restoreSchema.parse(args);
854
+ const bucketName = resolveBucket(a.bucketName, defaultBucket);
777
855
  if (!a.confirm) {
778
856
  return confirmationPreview(
779
857
  `restore archived object "${a.objectKey}"`,
780
- `Tier: ${a.retrievalTier ?? "Standard"}${a.days ? `, ${a.days} day(s)` : ""}. May incur retrieval cost.`
858
+ `Drive: ${bucketName}
859
+ Tier: ${a.retrievalTier ?? "Standard"}${a.days ? `, ${a.days} day(s)` : ""}. May incur retrieval cost.`
781
860
  );
782
861
  }
783
862
  const data = await client.post("/storage/object/restore", {
863
+ bucketName,
784
864
  objectKey: a.objectKey,
785
865
  days: a.days,
786
866
  retrievalTier: a.retrievalTier,
@@ -820,7 +900,7 @@ function createServer(config) {
820
900
  },
821
901
  async (args) => {
822
902
  try {
823
- return await tool.handler(args ?? {}, { client });
903
+ return await tool.handler(args ?? {}, { client, defaultBucket: config.defaultBucket });
824
904
  } catch (err) {
825
905
  const message = err instanceof Error ? err.message : String(err);
826
906
  logger.error(`tool "${tool.name}" failed`, message);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/logger.ts","../src/server.ts","../src/version.ts","../src/client/pagination.ts","../src/client/CloudSeeClient.ts","../src/tools/read.ts","../src/tools/format.ts","../src/tools/types.ts","../src/tools/download.ts","../src/tools/write.ts","../src/confirm.ts","../src/tools/index.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { loadConfig, type Config } from \"./config\";\r\nimport { configureLogger, logger } from \"./logger\";\r\nimport { createServer } from \"./server\";\r\nimport { VERSION } from \"./version\";\r\n\r\nfunction loadConfigOrExit(): Config {\r\n try {\r\n return loadConfig();\r\n } catch (err) {\r\n // Config errors are fatal and printed to stderr (not stdout — the transport).\r\n process.stderr.write(`\\n${err instanceof Error ? err.message : String(err)}\\n\\n`);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const config = loadConfigOrExit();\r\n // Register the secret for redaction before anything else runs.\r\n configureLogger({ level: config.logLevel, redact: [config.apiKeySecret] });\r\n\r\n const server = createServer(config);\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n\r\n logger.info(`cloudsee-drive-mcp v${VERSION} ready on stdio (base: ${config.baseUrl})`);\r\n}\r\n\r\nmain().catch((err) => {\r\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\r\n process.exit(1);\r\n});\r\n","import { z } from \"zod\";\r\nimport { CloudSeeError } from \"./errors\";\r\nimport type { LogLevel } from \"./logger\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://drive-api.cloudsee.cloud\";\r\nexport const DEFAULT_TIMEOUT_MS = 30_000;\r\n\r\nexport interface Config {\r\n apiKeyId: string;\r\n apiKeySecret: string;\r\n baseUrl: string;\r\n timeoutMs: number;\r\n logLevel: LogLevel;\r\n}\r\n\r\nconst envSchema = z.object({\r\n CLOUDSEE_API_KEY_ID: z.string().trim().min(1, \"is required (your CloudSee Drive API key id)\"),\r\n CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, \"is required (the matching API key secret)\"),\r\n CLOUDSEE_API_BASE_URL: z.string().url(\"must be a valid URL\").optional(),\r\n CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),\r\n CLOUDSEE_LOG_LEVEL: z.enum([\"error\", \"warn\", \"info\", \"debug\"]).optional(),\r\n});\r\n\r\n/**\r\n * Load and validate configuration from the environment. Throws a CloudSeeError\r\n * with an actionable, secret-free message when required vars are missing.\r\n */\r\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\r\n const parsed = envSchema.safeParse(env);\r\n if (!parsed.success) {\r\n const issues = parsed.error.issues.map((i) => ` • ${i.path.join(\".\")} ${i.message}`).join(\"\\n\");\r\n throw new CloudSeeError(\r\n `CloudSee Drive MCP server is not configured:\\n${issues}\\n\\n` +\r\n `Set the required environment variables (see .env.example or the README).`,\r\n { code: \"config\" },\r\n );\r\n }\r\n const e = parsed.data;\r\n return {\r\n apiKeyId: e.CLOUDSEE_API_KEY_ID,\r\n apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,\r\n baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\r\n timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,\r\n logLevel: e.CLOUDSEE_LOG_LEVEL ?? \"info\",\r\n };\r\n}\r\n","export interface CloudSeeErrorOptions {\r\n code?: string;\r\n status?: number;\r\n retryable?: boolean;\r\n}\r\n\r\n/** Error raised for any failed CloudSee API interaction. Messages are redacted\r\n * by the client before construction and must never contain the API secret. */\r\nexport class CloudSeeError extends Error {\r\n readonly code: string;\r\n readonly status?: number;\r\n readonly retryable: boolean;\r\n\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message);\r\n this.name = \"CloudSeeError\";\r\n this.code = options.code ?? \"error\";\r\n this.status = options.status;\r\n this.retryable = options.retryable ?? false;\r\n }\r\n}\r\n\r\n/** Authentication/authorization failure (HTTP 401/403). Terminal — never retried. */\r\nexport class AuthError extends CloudSeeError {\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message, { ...options, code: options.code ?? \"auth\" });\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n","// Structured logging that writes to **stderr only**. stdout is reserved for the\r\n// MCP stdio transport — anything written there corrupts the protocol stream.\r\n// All output is passed through secret redaction as a defense-in-depth backstop;\r\n// the real rule is to never hand the API secret to the logger in the first place.\r\n\r\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\";\r\n\r\nconst LEVEL_WEIGHT: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 };\r\n\r\nlet currentLevel: LogLevel = \"info\";\r\nlet secrets: string[] = [];\r\n\r\nexport function configureLogger(options: { level?: LogLevel; redact?: string[] }): void {\r\n if (options.level) currentLevel = options.level;\r\n if (options.redact) {\r\n secrets = options.redact.filter((s): s is string => typeof s === \"string\" && s.length > 0);\r\n }\r\n}\r\n\r\n/** Replace any registered secret with `***` anywhere it appears in `text`. */\r\nexport function redactSecrets(text: string): string {\r\n let out = text;\r\n for (const secret of secrets) {\r\n if (out.includes(secret)) out = out.split(secret).join(\"***\");\r\n }\r\n return out;\r\n}\r\n\r\nfunction stringify(meta: unknown): string {\r\n if (meta === undefined) return \"\";\r\n if (typeof meta === \"string\") return meta;\r\n try {\r\n return JSON.stringify(meta);\r\n } catch {\r\n return String(meta);\r\n }\r\n}\r\n\r\nfunction emit(level: LogLevel, message: string, meta?: unknown): void {\r\n if (LEVEL_WEIGHT[level] > LEVEL_WEIGHT[currentLevel]) return;\r\n const suffix = meta === undefined ? \"\" : ` ${stringify(meta)}`;\r\n process.stderr.write(`[cloudsee-drive-mcp] ${level.toUpperCase()}: ${redactSecrets(message + suffix)}\\n`);\r\n}\r\n\r\nexport const logger = {\r\n error: (message: string, meta?: unknown): void => emit(\"error\", message, meta),\r\n warn: (message: string, meta?: unknown): void => emit(\"warn\", message, meta),\r\n info: (message: string, meta?: unknown): void => emit(\"info\", message, meta),\r\n debug: (message: string, meta?: unknown): void => emit(\"debug\", message, meta),\r\n};\r\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { CloudSeeClient } from \"./client/CloudSeeClient\";\r\nimport { allTools } from \"./tools/index\";\r\nimport { logger } from \"./logger\";\r\nimport { VERSION } from \"./version\";\r\nimport type { Config } from \"./config\";\r\n\r\n/**\r\n * Build the MCP server: one CloudSeeClient shared across all tools, each tool\r\n * registered with its Zod input schema and MCP annotations. Tool handler errors\r\n * are caught and returned as `isError` results (never thrown across the\r\n * transport), and are logged to stderr with the secret redacted.\r\n */\r\nexport function createServer(config: Config): McpServer {\r\n const client = new CloudSeeClient(config);\r\n const server = new McpServer({ name: \"cloudsee-drive-mcp\", version: VERSION });\r\n\r\n for (const tool of allTools) {\r\n server.registerTool(\r\n tool.name,\r\n {\r\n title: tool.title,\r\n description: tool.description,\r\n inputSchema: tool.inputSchema,\r\n annotations: { title: tool.title, ...tool.annotations },\r\n },\r\n async (args: Record<string, unknown>) => {\r\n try {\r\n return await tool.handler(args ?? {}, { client });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n logger.error(`tool \"${tool.name}\" failed`, message);\r\n return { content: [{ type: \"text\" as const, text: `Error: ${message}` }], isError: true };\r\n }\r\n },\r\n );\r\n }\r\n\r\n logger.info(`registered ${allTools.length} tools`);\r\n return server;\r\n}\r\n","// Single source of truth for the package version, surfaced to the MCP client\r\n// handshake and the User-Agent. Kept in sync with package.json by release tooling.\r\nexport const VERSION = \"0.1.0\";\r\n","import { CloudSeeError } from \"../errors\";\r\n\r\n// The data plane uses five different pagination param names across endpoint\r\n// families (C10): `nextPage`, `marker`, `lastEvaluatedKey`, `pageToken`,\r\n// `nextToken`. Tools expose a single opaque `cursor`; this module encodes the\r\n// dialect + raw token into that cursor and decodes it back, so a cursor minted\r\n// for one operation can't be misapplied to another.\r\n\r\nexport type PaginationDialect = \"nextPage\" | \"marker\" | \"lastEvaluatedKey\" | \"pageToken\" | \"nextToken\";\r\n\r\ninterface CursorPayload {\r\n d: PaginationDialect;\r\n v: string;\r\n}\r\n\r\nexport function encodeCursor(dialect: PaginationDialect, value: string | null | undefined): string | undefined {\r\n if (value === null || value === undefined || value === \"\") return undefined;\r\n const payload: CursorPayload = { d: dialect, v: String(value) };\r\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\");\r\n}\r\n\r\nexport function decodeCursor(cursor: string, expected: PaginationDialect): string {\r\n let payload: CursorPayload;\r\n try {\r\n payload = JSON.parse(Buffer.from(cursor, \"base64url\").toString(\"utf8\")) as CursorPayload;\r\n } catch {\r\n throw new CloudSeeError(\"Invalid pagination cursor.\", { code: \"bad_cursor\" });\r\n }\r\n if (!payload || typeof payload.v !== \"string\" || payload.d !== expected) {\r\n throw new CloudSeeError(\"Pagination cursor is not valid for this operation.\", { code: \"bad_cursor\" });\r\n }\r\n return payload.v;\r\n}\r\n\r\nconst RESPONSE_FIELDS = [\"nextPage\", \"nextPageToken\", \"marker\", \"lastEvaluatedKey\", \"pageToken\", \"nextToken\"];\r\n\r\n/**\r\n * Pull the raw continuation token out of a response envelope's `data`, tolerant\r\n * of the exact field name (which varies by endpoint and isn't guaranteed by the\r\n * RPC contract). Prefers the operation's own dialect field, then known aliases.\r\n */\r\nexport function extractNextToken(data: unknown, dialect: PaginationDialect): string | undefined {\r\n if (!data || typeof data !== \"object\") return undefined;\r\n const record = data as Record<string, unknown>;\r\n for (const field of [dialect, ...RESPONSE_FIELDS]) {\r\n const value = record[field];\r\n if (typeof value === \"string\" && value !== \"\") return value;\r\n if (value && typeof value === \"object\") {\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n /* ignore non-serializable */\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n","import { AuthError, CloudSeeError } from \"../errors\";\r\nimport { logger } from \"../logger\";\r\nimport { VERSION } from \"../version\";\r\nimport type { Config } from \"../config\";\r\nimport { decodeCursor, encodeCursor, extractNextToken, type PaginationDialect } from \"./pagination\";\r\n\r\ninterface Envelope<T> {\r\n success?: boolean;\r\n data?: T;\r\n errorMessage?: string;\r\n code?: string;\r\n}\r\n\r\nexport interface RequestOptions {\r\n retries?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport interface PagedResult<T> {\r\n data: T;\r\n nextCursor?: string;\r\n}\r\n\r\ninterface ParsedResponse<T> {\r\n data: T;\r\n envelope: Record<string, unknown> | undefined;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 3;\r\nconst BASE_BACKOFF_MS = 300;\r\nconst MAX_BACKOFF_MS = 8_000;\r\n\r\n/**\r\n * Typed wrapper over the drive-bridge `/v1/*` data plane. Every endpoint is an\r\n * RPC `POST /noun/verb` returning the platform `{ success, data, errorMessage,\r\n * code }` envelope. The client:\r\n * - sets the `X-Api-Key-Id` / `X-Api-Key-Secret` auth headers from config,\r\n * - retries 429/5xx with exponential backoff honoring `Retry-After` (the\r\n * `csd-mcp` usage plan is dormant, so the server must self-throttle — C6),\r\n * - unwraps the envelope and surfaces `success:false` as a CloudSeeError,\r\n * - never logs the secret and redacts it from any error message.\r\n */\r\nexport class CloudSeeClient {\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private readonly secret: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: Config) {\r\n this.baseUrl = config.baseUrl;\r\n this.timeoutMs = config.timeoutMs;\r\n this.secret = config.apiKeySecret;\r\n this.headers = {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n \"X-Api-Key-Id\": config.apiKeyId,\r\n \"X-Api-Key-Secret\": config.apiKeySecret,\r\n \"User-Agent\": `cloudsee-drive-mcp/${VERSION}`,\r\n };\r\n }\r\n\r\n /** POST a data-plane RPC endpoint (path WITHOUT `/v1`). Returns unwrapped `data`. */\r\n async post<T = unknown>(path: string, body: Record<string, unknown> = {}, options: RequestOptions = {}): Promise<T> {\r\n return (await this.request<T>(path, body, options)).data;\r\n }\r\n\r\n /** POST a paginated endpoint. Translates the opaque `cursor` to/from the\r\n * endpoint's pagination dialect and returns a normalized `nextCursor`. The\r\n * continuation token can sit at the envelope level (e.g. `/storage/recent`\r\n * returns `nextToken` as a sibling of `data`) or inside `data` — check the\r\n * envelope first, then `data`, so a token is never silently dropped. */\r\n async postPaged<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n dialect: PaginationDialect,\r\n cursor?: string,\r\n options: RequestOptions = {},\r\n ): Promise<PagedResult<T>> {\r\n const requestBody = { ...body };\r\n if (cursor) requestBody[dialect] = decodeCursor(cursor, dialect);\r\n const { data, envelope } = await this.request<T>(path, requestBody, options);\r\n const token = extractNextToken(envelope, dialect) ?? extractNextToken(data, dialect);\r\n return { data, nextCursor: encodeCursor(dialect, token) };\r\n }\r\n\r\n /** Run the request with retry/backoff, returning the unwrapped `data` together\r\n * with the raw response envelope (needed for envelope-level pagination tokens). */\r\n private async request<T>(path: string, body: Record<string, unknown>, options: RequestOptions): Promise<ParsedResponse<T>> {\r\n const url = `${this.baseUrl}/v1${path}`;\r\n const retries = options.retries ?? DEFAULT_RETRIES;\r\n let attempt = 0;\r\n for (;;) {\r\n attempt += 1;\r\n const response = await this.fetchOnce(url, path, body, options.signal);\r\n\r\n if (response.status === 401 || response.status === 403) {\r\n throw new AuthError(\r\n \"Authentication failed (HTTP \" +\r\n response.status +\r\n \"). Check CLOUDSEE_API_KEY_ID and CLOUDSEE_API_KEY_SECRET, and that the key is active and not expired.\",\r\n { status: response.status },\r\n );\r\n }\r\n\r\n if ((response.status === 429 || response.status >= 500) && attempt <= retries) {\r\n const waitMs = retryAfterMs(response.headers.get(\"retry-after\")) ?? backoffMs(attempt);\r\n logger.warn(`HTTP ${response.status} from ${path}; retrying in ${waitMs}ms (attempt ${attempt}/${retries}).`);\r\n await sleep(waitMs);\r\n continue;\r\n }\r\n\r\n return await this.parse<T>(response, path);\r\n }\r\n }\r\n\r\n private async fetchOnce(\r\n url: string,\r\n path: string,\r\n body: Record<string, unknown>,\r\n external?: AbortSignal,\r\n ): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n const onAbort = (): void => controller.abort();\r\n if (external) external.addEventListener(\"abort\", onAbort, { once: true });\r\n try {\r\n return await fetch(url, {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } catch (err) {\r\n const reason = controller.signal.aborted ? `timed out after ${this.timeoutMs}ms` : this.redact(errMessage(err));\r\n throw new CloudSeeError(`Network error calling ${path}: ${reason}`, { code: \"network\", retryable: true });\r\n } finally {\r\n clearTimeout(timer);\r\n if (external) external.removeEventListener(\"abort\", onAbort);\r\n }\r\n }\r\n\r\n private async parse<T>(response: Response, path: string): Promise<ParsedResponse<T>> {\r\n const text = await response.text();\r\n let json: Envelope<T> | undefined;\r\n if (text) {\r\n try {\r\n json = JSON.parse(text) as Envelope<T>;\r\n } catch {\r\n throw new CloudSeeError(\r\n response.ok\r\n ? `CloudSee API returned a non-JSON response for ${path}.`\r\n : `CloudSee API returned HTTP ${response.status} for ${path}.`,\r\n { status: response.status, code: response.ok ? \"bad_response\" : \"http_error\" },\r\n );\r\n }\r\n }\r\n if (!response.ok) {\r\n const message = json?.errorMessage ?? `HTTP ${response.status}`;\r\n throw new CloudSeeError(`CloudSee API error for ${path}: ${this.redact(message)}`, {\r\n status: response.status,\r\n code: json?.code ?? \"http_error\",\r\n });\r\n }\r\n if (json && typeof json === \"object\" && json.success === false) {\r\n throw new CloudSeeError(this.redact(json.errorMessage || `Operation failed (${json.code ?? \"unknown\"}).`), {\r\n code: json.code ?? \"operation_failed\",\r\n status: response.status,\r\n });\r\n }\r\n const envelope = json && typeof json === \"object\" ? (json as Record<string, unknown>) : undefined;\r\n if (json && typeof json === \"object\" && \"data\" in json) return { data: json.data as T, envelope };\r\n return { data: (json ?? ({} as T)) as T, envelope };\r\n }\r\n\r\n private redact(text: string): string {\r\n return this.secret && text.includes(this.secret) ? text.split(this.secret).join(\"***\") : text;\r\n }\r\n}\r\n\r\nfunction errMessage(err: unknown): string {\r\n return err instanceof Error ? err.message : String(err);\r\n}\r\n\r\nfunction backoffMs(attempt: number): number {\r\n const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** (attempt - 1));\r\n // Full jitter over [ceiling/2, ceiling] to avoid thundering-herd retries.\r\n return Math.round(ceiling / 2 + Math.random() * (ceiling / 2));\r\n}\r\n\r\nfunction retryAfterMs(header: string | null): number | undefined {\r\n if (!header) return undefined;\r\n const seconds = Number(header);\r\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\r\n const date = Date.parse(header);\r\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\r\n return undefined;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize, withCursor } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// ---- list_buckets → POST /storage/buckets (storageBucketList, drive:read) ----\r\nconst listBuckets: ToolDef = {\r\n name: \"list_buckets\",\r\n title: \"List buckets\",\r\n description:\r\n \"List the storage buckets (Amazon S3 buckets) the authenticated CloudSee Drive account can access. Use this first to discover available buckets before browsing or searching.\",\r\n endpoint: { method: \"POST\", path: \"/storage/buckets\", scopes: [\"drive:read\"] },\r\n inputSchema: {},\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (_args, { client }) => {\r\n const data = await client.post(\"/storage/buckets\", {});\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- browse_folder → POST /storage/list (storageList, drive:read) ----\r\nconst browseSchema = z.object({\r\n path: z.string().optional().describe(\"Folder path / prefix to list. Empty or omitted = the root.\"),\r\n sortOption: z.string().optional().describe(\"Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst browseFolder: ToolDef = {\r\n name: \"browse_folder\",\r\n title: \"Browse folder\",\r\n description:\r\n \"List the files and sub-folders directly inside a folder (one level, non-recursive), with sorting and pagination. To filter by keyword use 'search_files'.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: browseSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = browseSchema.parse(args);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { dirPath: a.path ?? \"\", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- search_files → POST /storage/list with searchingKeyword (drive:read) ----\r\nconst searchSchema = z.object({\r\n query: z.string().min(1).describe(\"Keyword to match against file and folder names.\"),\r\n path: z.string().optional().describe(\"Folder to search within (this folder level, non-recursive). Empty = root.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst searchFiles: ToolDef = {\r\n name: \"search_files\",\r\n title: \"Search files\",\r\n description:\r\n \"Search for files and folders by name keyword within a folder. Note: this matches the given folder level and is NOT a whole-tree recursive search — browse into sub-folders to search deeper. Returns matches with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: searchSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = searchSchema.parse(args);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { dirPath: a.path ?? \"\", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- recent_files → POST /storage/recent (storageRecentFiles, drive:read) ----\r\nconst recentSchema = z.object({\r\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst recentFiles: ToolDef = {\r\n name: \"recent_files\",\r\n title: \"Recent files\",\r\n description: \"List the account's most recently accessed or modified files, newest first, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/recent\", scopes: [\"drive:read\"] },\r\n inputSchema: recentSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = recentSchema.parse(args);\r\n const limit = a.limit ?? 50;\r\n // /storage/recent paginates with `nextToken` (a composite key returned at the\r\n // envelope level), not `nextPage`. It echoes that token even when the list is\r\n // exhausted, so only advertise a next page when this page came back full.\r\n const { data, nextCursor } = await client.postPaged(\"/storage/recent\", { limit }, \"nextToken\", a.cursor);\r\n const hasMore = Array.isArray(data) && data.length >= limit;\r\n return textResult(withCursor(summarize(data), hasMore ? nextCursor : undefined));\r\n },\r\n};\r\n\r\n// ---- get_file_metadata → POST /storage/object/detail (storageDetail, drive:read) ----\r\nconst metaSchema = z.object({ objectKey: z.string().min(1).describe(\"Full object key (path) of the file.\") });\r\nconst getFileMetadata: ToolDef = {\r\n name: \"get_file_metadata\",\r\n title: \"Get file metadata\",\r\n description:\r\n \"Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/detail\", scopes: [\"drive:read\"] },\r\n inputSchema: metaSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = metaSchema.parse(args);\r\n const data = await client.post(\"/storage/object/detail\", { objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- get_file_tags → POST /storage/object/tagging (getObjectTagging, drive:read) ----\r\nconst tagsSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file.\"),\r\n bucketName: z.string().optional().describe(\"Bucket name, if not implied by the key.\"),\r\n});\r\nconst getFileTags: ToolDef = {\r\n name: \"get_file_tags\",\r\n title: \"Get file tags\",\r\n description: \"Get the S3 object tags (key/value pairs) attached to a file, by object key.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/tagging\", scopes: [\"drive:read\"] },\r\n inputSchema: tagsSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = tagsSchema.parse(args);\r\n const data = await client.post(\"/storage/object/tagging\", { objectKey: a.objectKey, bucketName: a.bucketName });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const readTools: ToolDef[] = [\r\n listBuckets,\r\n browseFolder,\r\n searchFiles,\r\n recentFiles,\r\n getFileMetadata,\r\n getFileTags,\r\n];\r\n","// Output bounding so large listings never flood the model context (R8 / AC).\r\n// Arrays are capped, deeply, and the whole rendering is hard-capped by length.\r\n\r\nconst DEFAULT_MAX_ITEMS = 100;\r\nconst DEFAULT_MAX_CHARS = 8_000;\r\n\r\nexport function summarize(data: unknown, opts: { maxItems?: number; maxChars?: number } = {}): string {\r\n const maxItems = opts.maxItems ?? DEFAULT_MAX_ITEMS;\r\n const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;\r\n let json: string;\r\n try {\r\n json = JSON.stringify(boundArrays(data, maxItems), null, 2);\r\n } catch {\r\n json = String(data);\r\n }\r\n if (json.length > maxChars) {\r\n json = `${json.slice(0, maxChars)}\\n… (output truncated at ${maxChars} characters — narrow your query or paginate).`;\r\n }\r\n return json;\r\n}\r\n\r\nfunction boundArrays(value: unknown, maxItems: number): unknown {\r\n if (Array.isArray(value)) {\r\n const capped: unknown[] = value.slice(0, maxItems).map((v) => boundArrays(v, maxItems));\r\n if (value.length > maxItems) {\r\n capped.push(`… ${value.length - maxItems} more item(s) omitted — paginate for the rest.`);\r\n }\r\n return capped;\r\n }\r\n if (value && typeof value === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\r\n out[k] = boundArrays(v, maxItems);\r\n }\r\n return out;\r\n }\r\n return value;\r\n}\r\n\r\n/** Append a continuation hint when the API returned another page. */\r\nexport function withCursor(body: string, nextCursor?: string): string {\r\n if (!nextCursor) return body;\r\n return `${body}\\n\\n↪ More results available. Call this tool again with cursor=\"${nextCursor}\" for the next page.`;\r\n}\r\n","import type { ZodRawShape } from \"zod\";\r\nimport type { CloudSeeClient } from \"../client/CloudSeeClient\";\r\n\r\nexport interface ToolContext {\r\n client: CloudSeeClient;\r\n}\r\n\r\nexport interface ToolResult {\r\n // Index signature mirrors the MCP SDK's CallToolResult (which permits extra\r\n // fields like _meta); lets ToolResult satisfy the registerTool callback type.\r\n [x: string]: unknown;\r\n content: Array<{ type: \"text\"; text: string }>;\r\n isError?: boolean;\r\n}\r\n\r\nexport interface ToolAnnotations {\r\n readOnlyHint?: boolean;\r\n destructiveHint?: boolean;\r\n idempotentHint?: boolean;\r\n openWorldHint?: boolean;\r\n}\r\n\r\n/** The real data-plane endpoint a tool wraps — asserted against the committed\r\n * contract snapshot by the drift test. `path` excludes the `/v1` prefix. */\r\nexport interface ToolEndpoint {\r\n method: \"POST\";\r\n path: string;\r\n scopes: string[];\r\n}\r\n\r\nexport interface ToolDef {\r\n name: string;\r\n title: string;\r\n description: string;\r\n endpoint: ToolEndpoint;\r\n inputSchema: ZodRawShape;\r\n annotations: ToolAnnotations;\r\n handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>;\r\n}\r\n\r\nexport function textResult(text: string): ToolResult {\r\n return { content: [{ type: \"text\", text }] };\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// Both tools wrap POST /storage/object/download-url (getObjectUrl, drive:read +\r\n// drive:download). The API returns a short-lived pre-signed URL, never AWS\r\n// credentials. We surface the URL (a capability link, not a secret); the model\r\n// can hand it to the user. We never download bytes into the conversation.\r\n\r\n// ---- download_file ----\r\nconst downloadSchema = z.object({\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to download.\"),\r\n forceDownload: z.boolean().optional().describe(\"If true, the link forces an attachment download instead of inline view.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst downloadFile: ToolDef = {\r\n name: \"download_file\",\r\n title: \"Download file\",\r\n description:\r\n \"Get a short-lived pre-signed download URL for a file. The URL is time-limited and grants read access to that one object — share it with care. Returns the URL; it does not load file bytes into the conversation.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: downloadSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = downloadSchema.parse(args);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n filePath: a.filePath,\r\n download: a.forceDownload ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- share_link ----\r\nconst shareSchema = z.object({\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to share.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst shareLink: ToolDef = {\r\n name: \"share_link\",\r\n title: \"Create share link\",\r\n description:\r\n \"Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: shareSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = shareSchema.parse(args);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n filePath: a.filePath,\r\n shareableLink: true,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const downloadTools: ToolDef[] = [downloadFile, shareLink];\r\n","import { readFile, stat } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { z } from \"zod\";\r\nimport { CloudSeeError } from \"../errors\";\r\nimport { confirmShape, confirmationPreview } from \"../confirm\";\r\nimport { summarize } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// Write/delete tools are present but their end-to-end success is sequenced\r\n// behind CSD-572 (the gateway enforces no scope/RBAC yet and the write path is\r\n// a Phase-1 stub). This note is appended to each tool's description so the model\r\n// and the user understand a gateway denial here is expected, not a tool bug.\r\nconst GATED =\r\n \" (Write access is authorized server-side; until CloudSee's public-API RBAC wiring (CSD-572) is live, the gateway may deny this — that is expected, not a tool failure.)\";\r\n\r\nconst MULTIPART_THRESHOLD = 8 * 1024 * 1024; // 8 MiB\r\n\r\nfunction pickUrl(data: unknown): string | undefined {\r\n if (typeof data === \"string\") return data;\r\n if (data && typeof data === \"object\") {\r\n const record = data as Record<string, unknown>;\r\n for (const key of [\"url\", \"uploadUrl\", \"signedUrl\", \"preSignedUrl\", \"presignedUrl\", \"putUrl\"]) {\r\n const value = record[key];\r\n if (typeof value === \"string\" && value) return value;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n// The object key that `complete` finalizes MUST come from the server's presign\r\n// response — never guessed — or we could register a record that doesn't match\r\n// the bytes actually written to the (server-chosen) S3 key.\r\nfunction pickKey(data: unknown): string | undefined {\r\n if (data && typeof data === \"object\") {\r\n const record = data as Record<string, unknown>;\r\n for (const key of [\"key\", \"objectKey\", \"Key\", \"objectPath\", \"filePath\"]) {\r\n const value = record[key];\r\n if (typeof value === \"string\" && value) return value;\r\n }\r\n const fields = record[\"fields\"];\r\n if (fields && typeof fields === \"object\") {\r\n const nested = (fields as Record<string, unknown>)[\"key\"];\r\n if (typeof nested === \"string\" && nested) return nested;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n// ---- upload_file → POST /storage/upload/url then /storage/upload/complete (drive:write) ----\r\nconst uploadSchema = z.object({\r\n localPath: z.string().min(1).describe(\"Path to the local file to upload (absolute, or relative to the server's working directory).\"),\r\n destinationFolder: z.string().describe(\"Destination folder/prefix in CloudSee Drive. Empty = root.\"),\r\n fileName: z.string().optional().describe(\"Name to store the file as. Defaults to the local file's name.\"),\r\n contentType: z.string().optional().describe(\"MIME type. Defaults to application/octet-stream.\"),\r\n storageClass: z.string().optional().describe(\"Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).\"),\r\n});\r\nconst uploadFile: ToolDef = {\r\n name: \"upload_file\",\r\n title: \"Upload file\",\r\n description:\r\n \"Upload a local file to a CloudSee Drive folder. Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/upload/url\", scopes: [\"drive:write\"] },\r\n inputSchema: uploadSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = uploadSchema.parse(args);\r\n const fileName = a.fileName ?? basename(a.localPath);\r\n const contentType = a.contentType ?? \"application/octet-stream\";\r\n\r\n let info;\r\n try {\r\n info = await stat(a.localPath);\r\n } catch {\r\n throw new CloudSeeError(`Local file not found: ${a.localPath}`, { code: \"file_not_found\" });\r\n }\r\n if (!info.isFile()) throw new CloudSeeError(`Not a regular file: ${a.localPath}`, { code: \"not_a_file\" });\r\n if (info.size > MULTIPART_THRESHOLD) {\r\n throw new CloudSeeError(\r\n `File is ${(info.size / 1048576).toFixed(1)} MiB. Multipart upload (>8 MiB) is not enabled in this release — upload a smaller file or use the CloudSee web app for large files.`,\r\n { code: \"too_large\" },\r\n );\r\n }\r\n\r\n const bytes = await readFile(a.localPath);\r\n const presign = await client.post<Record<string, unknown>>(\"/storage/upload/url\", {\r\n dirPath: a.destinationFolder,\r\n fileName,\r\n contentType,\r\n storageClass: a.storageClass,\r\n });\r\n const uploadUrl = pickUrl(presign);\r\n if (!uploadUrl) throw new CloudSeeError(\"The API did not return a usable upload URL.\", { code: \"no_upload_url\" });\r\n\r\n // Resolve the server-assigned key BEFORE uploading any bytes — if it's\r\n // absent we cannot finalize safely, so we fail without writing anything.\r\n const key = pickKey(presign);\r\n if (!key) {\r\n throw new CloudSeeError(\r\n \"The upload-URL response did not include the object key, so the upload cannot be finalized safely. (No bytes were uploaded.)\",\r\n { code: \"no_object_key\" },\r\n );\r\n }\r\n\r\n const put = await fetch(uploadUrl, { method: \"PUT\", headers: { \"Content-Type\": contentType }, body: new Uint8Array(bytes) });\r\n if (!put.ok) throw new CloudSeeError(`Upload to storage failed (HTTP ${put.status}).`, { status: put.status, code: \"upload_failed\" });\r\n\r\n let complete: unknown;\r\n try {\r\n complete = await client.post(\"/storage/upload/complete\", {\r\n dirPath: a.destinationFolder,\r\n objects: [{ fileName, key, contentType, size: info.size }],\r\n });\r\n } catch (err) {\r\n const reason = err instanceof Error ? err.message : String(err);\r\n throw new CloudSeeError(\r\n `Bytes were uploaded to storage but finalization failed: ${reason}. The object may exist without an index entry — retry the upload, or remove it via the CloudSee app.`,\r\n { code: \"finalize_failed\" },\r\n );\r\n }\r\n return textResult(`Uploaded \"${fileName}\" (${info.size} bytes) to \"${a.destinationFolder || \"/\"}\".\\n\\n${summarize(complete)}`);\r\n },\r\n};\r\n\r\n// ---- create_folder → POST /storage/folder/create (createFolder, drive:write) ----\r\nconst createFolderSchema = z.object({\r\n parentPath: z.string().describe(\"Parent folder/prefix. Empty = root.\"),\r\n name: z.string().min(1).describe(\"New folder name.\"),\r\n});\r\nconst createFolder: ToolDef = {\r\n name: \"create_folder\",\r\n title: \"Create folder\",\r\n description: \"Create a new folder at the given path.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/folder/create\", scopes: [\"drive:write\"] },\r\n inputSchema: createFolderSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = createFolderSchema.parse(args);\r\n const data = await client.post(\"/storage/folder/create\", { dirPath: a.parentPath, object: a.name });\r\n return textResult(`Created folder \"${a.name}\" in \"${a.parentPath || \"/\"}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- rename_file → POST /storage/object/rename (storageRename, drive:write) — destructive ----\r\nconst renameSchema = z.object({\r\n oldObjectId: z.string().min(1).describe(\"Current object key/id of the file or folder.\"),\r\n newName: z.string().min(1).describe(\"New name.\"),\r\n isFolder: z.boolean().optional().describe(\"Set true when renaming a folder.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst renameFile: ToolDef = {\r\n name: \"rename_file\",\r\n title: \"Rename file or folder\",\r\n description: \"Rename a file or folder. Destructive (changes the object's key). Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/rename\", scopes: [\"drive:write\"] },\r\n inputSchema: renameSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = renameSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`rename ${a.isFolder ? \"folder\" : \"file\"} to \"${a.newName}\"`, `Target: ${a.oldObjectId}`);\r\n }\r\n const data = await client.post(\"/storage/object/rename\", {\r\n oldObjectId: a.oldObjectId,\r\n newObjectName: a.newName,\r\n isFolder: a.isFolder ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`Renamed to \"${a.newName}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- move_file → POST /storage/object/move (storageMoveObject, drive:write) — destructive unless asCopy ----\r\nconst moveSchema = z.object({\r\n sourcePath: z.string().min(1).describe(\"Source object key/prefix.\"),\r\n destinationPath: z.string().min(1).describe(\"Destination prefix.\"),\r\n asCopy: z.boolean().optional().describe(\"Copy instead of move (copy is non-destructive; the source is kept).\"),\r\n isFolder: z.boolean().optional().describe(\"Set true for a folder.\"),\r\n destinationBucket: z.string().optional().describe(\"Target bucket, if different.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst moveFile: ToolDef = {\r\n name: \"move_file\",\r\n title: \"Move or copy file\",\r\n description:\r\n \"Move (or copy, with asCopy=true) a file or folder to a new location. A move removes the source and is destructive, so it requires confirm=true; a copy does not.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/move\", scopes: [\"drive:write\"] },\r\n inputSchema: moveSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = moveSchema.parse(args);\r\n const isMove = !a.asCopy;\r\n if (isMove && !a.confirm) {\r\n return confirmationPreview(`move \"${a.sourcePath}\" → \"${a.destinationPath}\"`, \"This removes the source. Pass asCopy=true to copy instead.\");\r\n }\r\n const data = await client.post(\"/storage/object/move\", {\r\n asCopy: a.asCopy ?? false,\r\n isFolder: a.isFolder ?? false,\r\n sourcePath: a.sourcePath,\r\n destinationPath: a.destinationPath,\r\n destinationBucket: a.destinationBucket,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`${isMove ? \"Moved\" : \"Copied\"} \"${a.sourcePath}\" → \"${a.destinationPath}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- duplicate_file → POST /storage/object/duplicate (duplicateFile, drive:write) ----\r\nconst duplicateSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Source object key to duplicate.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst duplicateFile: ToolDef = {\r\n name: \"duplicate_file\",\r\n title: \"Duplicate file\",\r\n description: \"Create a copy of a file in the same location.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/duplicate\", scopes: [\"drive:write\"] },\r\n inputSchema: duplicateSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = duplicateSchema.parse(args);\r\n const data = await client.post(\"/storage/object/duplicate\", { objectKey: a.objectKey, storageId: a.storageId });\r\n return textResult(`Duplicated \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- delete_files → POST /storage/objects/delete (deleteObjects, drive:delete) — destructive ----\r\nconst deleteSchema = z.object({\r\n objectKeys: z.array(z.string().min(1)).min(1).describe(\"Object keys (paths) to permanently delete.\"),\r\n ...confirmShape,\r\n});\r\nconst deleteFiles: ToolDef = {\r\n name: \"delete_files\",\r\n title: \"Delete files\",\r\n description: \"Permanently delete one or more files/objects by key. Destructive and irreversible. Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/objects/delete\", scopes: [\"drive:delete\"] },\r\n inputSchema: deleteSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = deleteSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s)`, a.objectKeys.map((k) => ` - ${k}`).join(\"\\n\"));\r\n }\r\n const data = await client.post(\"/storage/objects/delete\", { deletedObjects: a.objectKeys });\r\n return textResult(`Deleted ${a.objectKeys.length} object(s).\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- update_metadata → POST /storage/object/metadata (storageUpdateObjectMetadata, drive:write) — destructive ----\r\nconst updateMetaSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Object key whose metadata to update.\"),\r\n metadata: z.record(z.string(), z.unknown()).describe(\"Metadata fields to set (key/value map). Overwrites the listed fields.\"),\r\n ...confirmShape,\r\n});\r\nconst updateMetadata: ToolDef = {\r\n name: \"update_metadata\",\r\n title: \"Update file metadata\",\r\n description: \"Update a file's metadata. Destructive (overwrites the listed metadata fields). Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/metadata\", scopes: [\"drive:write\"] },\r\n inputSchema: updateMetaSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = updateMetaSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`update metadata on \"${a.objectKey}\"`, `Fields: ${Object.keys(a.metadata).join(\", \") || \"(none)\"}`);\r\n }\r\n const data = await client.post(\"/storage/object/metadata\", { objectKey: a.objectKey, metadata: a.metadata });\r\n return textResult(`Updated metadata on \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- restore_archived_file → POST /storage/object/restore (restoreObject, drive:write) — Glacier un-archive ----\r\nconst restoreSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Object key of the archived (Glacier) object to restore.\"),\r\n days: z.number().int().min(1).optional().describe(\"How many days to keep the restored copy available.\"),\r\n retrievalTier: z.enum([\"Expedited\", \"Standard\", \"Bulk\"]).optional().describe(\"Glacier retrieval tier (default Standard).\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst restoreArchivedFile: ToolDef = {\r\n name: \"restore_archived_file\",\r\n title: \"Restore archived file\",\r\n description:\r\n \"Begin restoring an archived (S3 Glacier) object so it can be downloaded. This is Glacier un-archiving — NOT recovery of a deleted file — and may incur retrieval cost and take minutes to hours. Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/restore\", scopes: [\"drive:write\"] },\r\n inputSchema: restoreSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = restoreSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(\r\n `restore archived object \"${a.objectKey}\"`,\r\n `Tier: ${a.retrievalTier ?? \"Standard\"}${a.days ? `, ${a.days} day(s)` : \"\"}. May incur retrieval cost.`,\r\n );\r\n }\r\n const data = await client.post(\"/storage/object/restore\", {\r\n objectKey: a.objectKey,\r\n days: a.days,\r\n retrievalTier: a.retrievalTier,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`Restore initiated for \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\nexport const writeTools: ToolDef[] = [\r\n uploadFile,\r\n createFolder,\r\n renameFile,\r\n moveFile,\r\n duplicateFile,\r\n deleteFiles,\r\n updateMetadata,\r\n restoreArchivedFile,\r\n];\r\n","import { z } from \"zod\";\r\nimport { textResult, type ToolResult } from \"./tools/types\";\r\n\r\n// Two-step confirmation for destructive tools. A first call WITHOUT confirm=true\r\n// returns a human-readable preview and performs no mutation; the model (or user)\r\n// must re-call with confirm=true to execute. This is UX, NOT the security\r\n// boundary — the CloudSee API authorizes every operation server-side (CSD-572).\r\n\r\nexport const confirmShape = {\r\n confirm: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n \"Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes.\",\r\n ),\r\n};\r\n\r\nconst SECURITY_NOTE =\r\n \"Note: this confirmation is a client-side safety prompt, not the security boundary — \" +\r\n \"the CloudSee API authorizes every operation server-side.\";\r\n\r\n/** The no-op preview returned when a destructive tool is called without confirm=true. */\r\nexport function confirmationPreview(action: string, details: string): ToolResult {\r\n return textResult(\r\n `⚠️ Confirmation required — no changes have been made.\\n\\n` +\r\n `About to: ${action}\\n${details}\\n\\n` +\r\n `Re-run this tool with confirm=true to proceed.\\n${SECURITY_NOTE}`,\r\n );\r\n}\r\n","import type { ToolDef } from \"./types\";\r\nimport { readTools } from \"./read\";\r\nimport { downloadTools } from \"./download\";\r\nimport { writeTools } from \"./write\";\r\n\r\n/** The complete, grounded tool set (CSD-586 §2.4). Read + download tools are\r\n * callable today; write/delete tools are present but gated on CSD-572 RBAC. */\r\nexport const allTools: ToolDef[] = [...readTools, ...downloadTools, ...writeTools];\r\n\r\nexport type { ToolDef } from \"./types\";\r\n"],"mappings":";;;AAAA,SAAS,4BAA4B;;;ACArC,SAAS,SAAS;;;ACQX,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,UAAgC,CAAC,GAAG;AAC/D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,SAAS,QAAQ;AACtB,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AACF;AAGO,IAAM,YAAN,cAAwB,cAAc;AAAA,EAC3C,YAAY,SAAiB,UAAgC,CAAC,GAAG;AAC/D,UAAM,SAAS,EAAE,GAAG,SAAS,MAAM,QAAQ,QAAQ,OAAO,CAAC;AAC3D,SAAK,OAAO;AAAA,EACd;AACF;;;ADxBO,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAUlC,IAAM,YAAY,EAAE,OAAO;AAAA,EACzB,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,8CAA8C;AAAA,EAC5F,yBAAyB,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,2CAA2C;AAAA,EAC7F,uBAAuB,EAAE,OAAO,EAAE,IAAI,qBAAqB,EAAE,SAAS;AAAA,EACtE,qBAAqB,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,EACjE,oBAAoB,EAAE,KAAK,CAAC,SAAS,QAAQ,QAAQ,OAAO,CAAC,EAAE,SAAS;AAC1E,CAAC;AAMM,SAAS,WAAW,MAAyB,QAAQ,KAAa;AACvE,QAAM,SAAS,UAAU,UAAU,GAAG;AACtC,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,MAAM,YAAO,EAAE,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AAC/F,UAAM,IAAI;AAAA,MACR;AAAA,EAAiD,MAAM;AAAA;AAAA;AAAA,MAEvD,EAAE,MAAM,SAAS;AAAA,IACnB;AAAA,EACF;AACA,QAAM,IAAI,OAAO;AACjB,SAAO;AAAA,IACL,UAAU,EAAE;AAAA,IACZ,cAAc,EAAE;AAAA,IAChB,UAAU,EAAE,yBAAyB,kBAAkB,QAAQ,QAAQ,EAAE;AAAA,IACzE,WAAW,EAAE,uBAAuB;AAAA,IACpC,UAAU,EAAE,sBAAsB;AAAA,EACpC;AACF;;;AEtCA,IAAM,eAAyC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE;AAEtF,IAAI,eAAyB;AAC7B,IAAI,UAAoB,CAAC;AAElB,SAAS,gBAAgB,SAAwD;AACtF,MAAI,QAAQ,MAAO,gBAAe,QAAQ;AAC1C,MAAI,QAAQ,QAAQ;AAClB,cAAU,QAAQ,OAAO,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC;AAAA,EAC3F;AACF;AAGO,SAAS,cAAc,MAAsB;AAClD,MAAI,MAAM;AACV,aAAW,UAAU,SAAS;AAC5B,QAAI,IAAI,SAAS,MAAM,EAAG,OAAM,IAAI,MAAM,MAAM,EAAE,KAAK,KAAK;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,SAAS,UAAU,MAAuB;AACxC,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI;AACF,WAAO,KAAK,UAAU,IAAI;AAAA,EAC5B,QAAQ;AACN,WAAO,OAAO,IAAI;AAAA,EACpB;AACF;AAEA,SAAS,KAAK,OAAiB,SAAiB,MAAsB;AACpE,MAAI,aAAa,KAAK,IAAI,aAAa,YAAY,EAAG;AACtD,QAAM,SAAS,SAAS,SAAY,KAAK,IAAI,UAAU,IAAI,CAAC;AAC5D,UAAQ,OAAO,MAAM,wBAAwB,MAAM,YAAY,CAAC,KAAK,cAAc,UAAU,MAAM,CAAC;AAAA,CAAI;AAC1G;AAEO,IAAM,SAAS;AAAA,EACpB,OAAO,CAAC,SAAiB,SAAyB,KAAK,SAAS,SAAS,IAAI;AAAA,EAC7E,MAAM,CAAC,SAAiB,SAAyB,KAAK,QAAQ,SAAS,IAAI;AAAA,EAC3E,MAAM,CAAC,SAAiB,SAAyB,KAAK,QAAQ,SAAS,IAAI;AAAA,EAC3E,OAAO,CAAC,SAAiB,SAAyB,KAAK,SAAS,SAAS,IAAI;AAC/E;;;ACjDA,SAAS,iBAAiB;;;ACEnB,IAAM,UAAU;;;ACahB,SAAS,aAAa,SAA4B,OAAsD;AAC7G,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,GAAI,QAAO;AAClE,QAAM,UAAyB,EAAE,GAAG,SAAS,GAAG,OAAO,KAAK,EAAE;AAC9D,SAAO,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,MAAM,EAAE,SAAS,WAAW;AAC1E;AAEO,SAAS,aAAa,QAAgB,UAAqC;AAChF,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO,KAAK,QAAQ,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EACxE,QAAQ;AACN,UAAM,IAAI,cAAc,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAAA,EAC9E;AACA,MAAI,CAAC,WAAW,OAAO,QAAQ,MAAM,YAAY,QAAQ,MAAM,UAAU;AACvE,UAAM,IAAI,cAAc,sDAAsD,EAAE,MAAM,aAAa,CAAC;AAAA,EACtG;AACA,SAAO,QAAQ;AACjB;AAEA,IAAM,kBAAkB,CAAC,YAAY,iBAAiB,UAAU,oBAAoB,aAAa,WAAW;AAOrG,SAAS,iBAAiB,MAAe,SAAgD;AAC9F,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,SAAS;AACf,aAAW,SAAS,CAAC,SAAS,GAAG,eAAe,GAAG;AACjD,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,OAAO,UAAU,YAAY,UAAU,GAAI,QAAO;AACtD,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAI;AACF,eAAO,KAAK,UAAU,KAAK;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC5BA,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAYhB,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgB;AAC1B,SAAK,UAAU,OAAO;AACtB,SAAK,YAAY,OAAO;AACxB,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU;AAAA,MACb,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,gBAAgB,OAAO;AAAA,MACvB,oBAAoB,OAAO;AAAA,MAC3B,cAAc,sBAAsB,OAAO;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,KAAkB,MAAc,OAAgC,CAAC,GAAG,UAA0B,CAAC,GAAe;AAClH,YAAQ,MAAM,KAAK,QAAW,MAAM,MAAM,OAAO,GAAG;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UACJ,MACA,MACA,SACA,QACA,UAA0B,CAAC,GACF;AACzB,UAAM,cAAc,EAAE,GAAG,KAAK;AAC9B,QAAI,OAAQ,aAAY,OAAO,IAAI,aAAa,QAAQ,OAAO;AAC/D,UAAM,EAAE,MAAM,SAAS,IAAI,MAAM,KAAK,QAAW,MAAM,aAAa,OAAO;AAC3E,UAAM,QAAQ,iBAAiB,UAAU,OAAO,KAAK,iBAAiB,MAAM,OAAO;AACnF,WAAO,EAAE,MAAM,YAAY,aAAa,SAAS,KAAK,EAAE;AAAA,EAC1D;AAAA;AAAA;AAAA,EAIA,MAAc,QAAW,MAAc,MAA+B,SAAqD;AACzH,UAAM,MAAM,GAAG,KAAK,OAAO,MAAM,IAAI;AACrC,UAAM,UAAU,QAAQ,WAAW;AACnC,QAAI,UAAU;AACd,eAAS;AACP,iBAAW;AACX,YAAM,WAAW,MAAM,KAAK,UAAU,KAAK,MAAM,MAAM,QAAQ,MAAM;AAErE,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI;AAAA,UACR,iCACE,SAAS,SACT;AAAA,UACF,EAAE,QAAQ,SAAS,OAAO;AAAA,QAC5B;AAAA,MACF;AAEA,WAAK,SAAS,WAAW,OAAO,SAAS,UAAU,QAAQ,WAAW,SAAS;AAC7E,cAAM,SAAS,aAAa,SAAS,QAAQ,IAAI,aAAa,CAAC,KAAK,UAAU,OAAO;AACrF,eAAO,KAAK,QAAQ,SAAS,MAAM,SAAS,IAAI,iBAAiB,MAAM,eAAe,OAAO,IAAI,OAAO,IAAI;AAC5G,cAAM,MAAM,MAAM;AAClB;AAAA,MACF;AAEA,aAAO,MAAM,KAAK,MAAS,UAAU,IAAI;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAc,UACZ,KACA,MACA,MACA,UACmB;AACnB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AACjE,UAAM,UAAU,MAAY,WAAW,MAAM;AAC7C,QAAI,SAAU,UAAS,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACxE,QAAI;AACF,aAAO,MAAM,MAAM,KAAK;AAAA,QACtB,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS,WAAW,OAAO,UAAU,mBAAmB,KAAK,SAAS,OAAO,KAAK,OAAO,WAAW,GAAG,CAAC;AAC9G,YAAM,IAAI,cAAc,yBAAyB,IAAI,KAAK,MAAM,IAAI,EAAE,MAAM,WAAW,WAAW,KAAK,CAAC;AAAA,IAC1G,UAAE;AACA,mBAAa,KAAK;AAClB,UAAI,SAAU,UAAS,oBAAoB,SAAS,OAAO;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,MAAc,MAAS,UAAoB,MAA0C;AACnF,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI;AACJ,QAAI,MAAM;AACR,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,SAAS,KACL,iDAAiD,IAAI,MACrD,8BAA8B,SAAS,MAAM,QAAQ,IAAI;AAAA,UAC7D,EAAE,QAAQ,SAAS,QAAQ,MAAM,SAAS,KAAK,iBAAiB,aAAa;AAAA,QAC/E;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,UAAU,MAAM,gBAAgB,QAAQ,SAAS,MAAM;AAC7D,YAAM,IAAI,cAAc,0BAA0B,IAAI,KAAK,KAAK,OAAO,OAAO,CAAC,IAAI;AAAA,QACjF,QAAQ,SAAS;AAAA,QACjB,MAAM,MAAM,QAAQ;AAAA,MACtB,CAAC;AAAA,IACH;AACA,QAAI,QAAQ,OAAO,SAAS,YAAY,KAAK,YAAY,OAAO;AAC9D,YAAM,IAAI,cAAc,KAAK,OAAO,KAAK,gBAAgB,qBAAqB,KAAK,QAAQ,SAAS,IAAI,GAAG;AAAA,QACzG,MAAM,KAAK,QAAQ;AAAA,QACnB,QAAQ,SAAS;AAAA,MACnB,CAAC;AAAA,IACH;AACA,UAAM,WAAW,QAAQ,OAAO,SAAS,WAAY,OAAmC;AACxF,QAAI,QAAQ,OAAO,SAAS,YAAY,UAAU,KAAM,QAAO,EAAE,MAAM,KAAK,MAAW,SAAS;AAChG,WAAO,EAAE,MAAO,QAAS,CAAC,GAAe,SAAS;AAAA,EACpD;AAAA,EAEQ,OAAO,MAAsB;AACnC,WAAO,KAAK,UAAU,KAAK,SAAS,KAAK,MAAM,IAAI,KAAK,MAAM,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI;AAAA,EAC3F;AACF;AAEA,SAAS,WAAW,KAAsB;AACxC,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAEA,SAAS,UAAU,SAAyB;AAC1C,QAAM,UAAU,KAAK,IAAI,gBAAgB,kBAAkB,MAAM,UAAU,EAAE;AAE7E,SAAO,KAAK,MAAM,UAAU,IAAI,KAAK,OAAO,KAAK,UAAU,EAAE;AAC/D;AAEA,SAAS,aAAa,QAA2C;AAC/D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,OAAO,SAAS,OAAO,EAAG,QAAO,KAAK,IAAI,GAAG,UAAU,GAAI;AAC/D,QAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC;AAC/D,SAAO;AACT;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;ACxMA,SAAS,KAAAA,UAAS;;;ACGlB,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAEnB,SAAS,UAAU,MAAe,OAAiD,CAAC,GAAW;AACpG,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,WAAW,KAAK,YAAY;AAClC,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,UAAU,YAAY,MAAM,QAAQ,GAAG,MAAM,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO,OAAO,IAAI;AAAA,EACpB;AACA,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC;AAAA,8BAA4B,QAAQ;AAAA,EACvE;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAAgB,UAA2B;AAC9D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,SAAoB,MAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACtF,QAAI,MAAM,SAAS,UAAU;AAC3B,aAAO,KAAK,UAAK,MAAM,SAAS,QAAQ,qDAAgD;AAAA,IAC1F;AACA,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,UAAI,CAAC,IAAI,YAAY,GAAG,QAAQ;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,SAAS,WAAW,MAAc,YAA6B;AACpE,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,GAAG,IAAI;AAAA;AAAA,mEAAmE,UAAU;AAC7F;;;ACHO,SAAS,WAAW,MAA0B;AACnD,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC,EAAE;AAC7C;;;AFrCA,IAAM,cAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,oBAAoB,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC7E,aAAa,CAAC;AAAA,EACd,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,OAAO,EAAE,OAAO,MAAM;AACpC,UAAM,OAAO,MAAM,OAAO,KAAK,oBAAoB,CAAC,CAAC;AACrD,WAAO,WAAW,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAGA,IAAM,eAAeC,GAAE,OAAO;AAAA,EAC5B,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4DAA4D;AAAA,EACjG,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sDAAsD;AAAA,EACjG,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,EACxG,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uDAAuD;AAChG,CAAC;AACD,IAAM,eAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,iBAAiB,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC1E,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,UAAM,EAAE,MAAM,WAAW,IAAI,MAAM,OAAO;AAAA,MACxC;AAAA,MACA,EAAE,SAAS,EAAE,QAAQ,IAAI,YAAY,EAAE,YAAY,UAAU,EAAE,YAAY,GAAG;AAAA,MAC9E;AAAA,MACA,EAAE;AAAA,IACJ;AACA,WAAO,WAAW,WAAW,UAAU,IAAI,GAAG,UAAU,CAAC;AAAA,EAC3D;AACF;AAGA,IAAM,eAAeA,GAAE,OAAO;AAAA,EAC5B,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,iDAAiD;AAAA,EACnF,MAAMA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2EAA2E;AAAA,EAChH,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,EACxG,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uDAAuD;AAChG,CAAC;AACD,IAAM,cAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,iBAAiB,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC1E,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,UAAM,EAAE,MAAM,WAAW,IAAI,MAAM,OAAO;AAAA,MACxC;AAAA,MACA,EAAE,SAAS,EAAE,QAAQ,IAAI,kBAAkB,EAAE,OAAO,UAAU,EAAE,YAAY,GAAG;AAAA,MAC/E;AAAA,MACA,EAAE;AAAA,IACJ;AACA,WAAO,WAAW,WAAW,UAAU,IAAI,GAAG,UAAU,CAAC;AAAA,EAC3D;AACF;AAGA,IAAM,eAAeA,GAAE,OAAO;AAAA,EAC5B,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,gCAAgC;AAAA,EAC5F,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uDAAuD;AAChG,CAAC;AACD,IAAM,cAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU,EAAE,QAAQ,QAAQ,MAAM,mBAAmB,QAAQ,CAAC,YAAY,EAAE;AAAA,EAC5E,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,UAAM,QAAQ,EAAE,SAAS;AAIzB,UAAM,EAAE,MAAM,WAAW,IAAI,MAAM,OAAO,UAAU,mBAAmB,EAAE,MAAM,GAAG,aAAa,EAAE,MAAM;AACvG,UAAM,UAAU,MAAM,QAAQ,IAAI,KAAK,KAAK,UAAU;AACtD,WAAO,WAAW,WAAW,UAAU,IAAI,GAAG,UAAU,aAAa,MAAS,CAAC;AAAA,EACjF;AACF;AAGA,IAAM,aAAaA,GAAE,OAAO,EAAE,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qCAAqC,EAAE,CAAC;AAC5G,IAAM,kBAA2B;AAAA,EAC/B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,0BAA0B,QAAQ,CAAC,YAAY,EAAE;AAAA,EACnF,aAAa,WAAW;AAAA,EACxB,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,WAAW,MAAM,IAAI;AAC/B,UAAM,OAAO,MAAM,OAAO,KAAK,0BAA0B,EAAE,WAAW,EAAE,UAAU,CAAC;AACnF,WAAO,WAAW,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAGA,IAAM,aAAaA,GAAE,OAAO;AAAA,EAC1B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qCAAqC;AAAA,EAC3E,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AACtF,CAAC;AACD,IAAM,cAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU,EAAE,QAAQ,QAAQ,MAAM,2BAA2B,QAAQ,CAAC,YAAY,EAAE;AAAA,EACpF,aAAa,WAAW;AAAA,EACxB,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,WAAW,MAAM,IAAI;AAC/B,UAAM,OAAO,MAAM,OAAO,KAAK,2BAA2B,EAAE,WAAW,EAAE,WAAW,YAAY,EAAE,WAAW,CAAC;AAC9G,WAAO,WAAW,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAEO,IAAM,YAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AG5IA,SAAS,KAAAC,UAAS;AAUlB,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EAC9B,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,4CAA4C;AAAA,EACjF,eAAeA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,yEAAyE;AAAA,EACxH,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AACxE,CAAC;AACD,IAAM,eAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,gCAAgC,QAAQ,CAAC,cAAc,gBAAgB,EAAE;AAAA,EAC3G,aAAa,eAAe;AAAA,EAC5B,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,eAAe,MAAM,IAAI;AACnC,UAAM,OAAO,MAAM,OAAO,KAAK,gCAAgC;AAAA,MAC7D,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE,iBAAiB;AAAA,MAC7B,WAAW,EAAE;AAAA,IACf,CAAC;AACD,WAAO,WAAW,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAGA,IAAM,cAAcA,GAAE,OAAO;AAAA,EAC3B,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yCAAyC;AAAA,EAC9E,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AACxE,CAAC;AACD,IAAM,YAAqB;AAAA,EACzB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE;AAAA,EACF,UAAU,EAAE,QAAQ,QAAQ,MAAM,gCAAgC,QAAQ,CAAC,cAAc,gBAAgB,EAAE;AAAA,EAC3G,aAAa,YAAY;AAAA,EACzB,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,EACvD,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,YAAY,MAAM,IAAI;AAChC,UAAM,OAAO,MAAM,OAAO,KAAK,gCAAgC;AAAA,MAC7D,UAAU,EAAE;AAAA,MACZ,eAAe;AAAA,MACf,WAAW,EAAE;AAAA,IACf,CAAC;AACD,WAAO,WAAW,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;AAEO,IAAM,gBAA2B,CAAC,cAAc,SAAS;;;AC1DhE,SAAS,UAAU,YAAY;AAC/B,SAAS,gBAAgB;AACzB,SAAS,KAAAC,UAAS;;;ACFlB,SAAS,KAAAC,UAAS;AAQX,IAAM,eAAe;AAAA,EAC1B,SAASC,GACN,QAAQ,EACR,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ;AAEA,IAAM,gBACJ;AAIK,SAAS,oBAAoB,QAAgB,SAA6B;AAC/E,SAAO;AAAA,IACL;AAAA;AAAA,YACe,MAAM;AAAA,EAAK,OAAO;AAAA;AAAA;AAAA,EACoB,aAAa;AAAA,EACpE;AACF;;;ADhBA,IAAM,QACJ;AAEF,IAAM,sBAAsB,IAAI,OAAO;AAEvC,SAAS,QAAQ,MAAmC;AAClD,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAM,SAAS;AACf,eAAW,OAAO,CAAC,OAAO,aAAa,aAAa,gBAAgB,gBAAgB,QAAQ,GAAG;AAC7F,YAAM,QAAQ,OAAO,GAAG;AACxB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,QAAQ,MAAmC;AAClD,MAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,UAAM,SAAS;AACf,eAAW,OAAO,CAAC,OAAO,aAAa,OAAO,cAAc,UAAU,GAAG;AACvE,YAAM,QAAQ,OAAO,GAAG;AACxB,UAAI,OAAO,UAAU,YAAY,MAAO,QAAO;AAAA,IACjD;AACA,UAAM,SAAS,OAAO,QAAQ;AAC9B,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,YAAM,SAAU,OAAmC,KAAK;AACxD,UAAI,OAAO,WAAW,YAAY,OAAQ,QAAO;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAGA,IAAM,eAAeC,GAAE,OAAO;AAAA,EAC5B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,6FAA6F;AAAA,EACnI,mBAAmBA,GAAE,OAAO,EAAE,SAAS,4DAA4D;AAAA,EACnG,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+DAA+D;AAAA,EACxG,aAAaA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAAA,EAC9F,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iEAAiE;AAChH,CAAC;AACD,IAAM,aAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE,sLAAsL;AAAA,EACxL,UAAU,EAAE,QAAQ,QAAQ,MAAM,uBAAuB,QAAQ,CAAC,aAAa,EAAE;AAAA,EACjF,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACvG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,UAAM,WAAW,EAAE,YAAY,SAAS,EAAE,SAAS;AACnD,UAAM,cAAc,EAAE,eAAe;AAErC,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,EAAE,SAAS;AAAA,IAC/B,QAAQ;AACN,YAAM,IAAI,cAAc,yBAAyB,EAAE,SAAS,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAAA,IAC5F;AACA,QAAI,CAAC,KAAK,OAAO,EAAG,OAAM,IAAI,cAAc,uBAAuB,EAAE,SAAS,IAAI,EAAE,MAAM,aAAa,CAAC;AACxG,QAAI,KAAK,OAAO,qBAAqB;AACnC,YAAM,IAAI;AAAA,QACR,YAAY,KAAK,OAAO,SAAS,QAAQ,CAAC,CAAC;AAAA,QAC3C,EAAE,MAAM,YAAY;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,SAAS,EAAE,SAAS;AACxC,UAAM,UAAU,MAAM,OAAO,KAA8B,uBAAuB;AAAA,MAChF,SAAS,EAAE;AAAA,MACX;AAAA,MACA;AAAA,MACA,cAAc,EAAE;AAAA,IAClB,CAAC;AACD,UAAM,YAAY,QAAQ,OAAO;AACjC,QAAI,CAAC,UAAW,OAAM,IAAI,cAAc,+CAA+C,EAAE,MAAM,gBAAgB,CAAC;AAIhH,UAAM,MAAM,QAAQ,OAAO;AAC3B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI;AAAA,QACR;AAAA,QACA,EAAE,MAAM,gBAAgB;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,WAAW,EAAE,QAAQ,OAAO,SAAS,EAAE,gBAAgB,YAAY,GAAG,MAAM,IAAI,WAAW,KAAK,EAAE,CAAC;AAC3H,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,cAAc,kCAAkC,IAAI,MAAM,MAAM,EAAE,QAAQ,IAAI,QAAQ,MAAM,gBAAgB,CAAC;AAEpI,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,OAAO,KAAK,4BAA4B;AAAA,QACvD,SAAS,EAAE;AAAA,QACX,SAAS,CAAC,EAAE,UAAU,KAAK,aAAa,MAAM,KAAK,KAAK,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,YAAM,IAAI;AAAA,QACR,2DAA2D,MAAM;AAAA,QACjE,EAAE,MAAM,kBAAkB;AAAA,MAC5B;AAAA,IACF;AACA,WAAO,WAAW,aAAa,QAAQ,MAAM,KAAK,IAAI,eAAe,EAAE,qBAAqB,GAAG;AAAA;AAAA,EAAS,UAAU,QAAQ,CAAC,EAAE;AAAA,EAC/H;AACF;AAGA,IAAM,qBAAqBA,GAAE,OAAO;AAAA,EAClC,YAAYA,GAAE,OAAO,EAAE,SAAS,qCAAqC;AAAA,EACrE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,kBAAkB;AACrD,CAAC;AACD,IAAM,eAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa,2CAA2C;AAAA,EACxD,UAAU,EAAE,QAAQ,QAAQ,MAAM,0BAA0B,QAAQ,CAAC,aAAa,EAAE;AAAA,EACpF,aAAa,mBAAmB;AAAA,EAChC,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACvG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,mBAAmB,MAAM,IAAI;AACvC,UAAM,OAAO,MAAM,OAAO,KAAK,0BAA0B,EAAE,SAAS,EAAE,YAAY,QAAQ,EAAE,KAAK,CAAC;AAClG,WAAO,WAAW,mBAAmB,EAAE,IAAI,SAAS,EAAE,cAAc,GAAG;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACnG;AACF;AAGA,IAAM,eAAeA,GAAE,OAAO;AAAA,EAC5B,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8CAA8C;AAAA,EACtF,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,WAAW;AAAA,EAC/C,UAAUA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC5E,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,EACtE,GAAG;AACL,CAAC;AACD,IAAM,aAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa,4FAA4F;AAAA,EACzG,UAAU,EAAE,QAAQ,QAAQ,MAAM,0BAA0B,QAAQ,CAAC,aAAa,EAAE;AAAA,EACpF,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACtG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,QAAI,CAAC,EAAE,SAAS;AACd,aAAO,oBAAoB,UAAU,EAAE,WAAW,WAAW,MAAM,QAAQ,EAAE,OAAO,KAAK,WAAW,EAAE,WAAW,EAAE;AAAA,IACrH;AACA,UAAM,OAAO,MAAM,OAAO,KAAK,0BAA0B;AAAA,MACvD,aAAa,EAAE;AAAA,MACf,eAAe,EAAE;AAAA,MACjB,UAAU,EAAE,YAAY;AAAA,MACxB,WAAW,EAAE;AAAA,IACf,CAAC;AACD,WAAO,WAAW,eAAe,EAAE,OAAO;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACtE;AACF;AAGA,IAAM,aAAaA,GAAE,OAAO;AAAA,EAC1B,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,2BAA2B;AAAA,EAClE,iBAAiBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qBAAqB;AAAA,EACjE,QAAQA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,qEAAqE;AAAA,EAC7G,UAAUA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,wBAAwB;AAAA,EAClE,mBAAmBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,EAChF,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,EACtE,GAAG;AACL,CAAC;AACD,IAAM,WAAoB;AAAA,EACxB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE,qKAAqK;AAAA,EACvK,UAAU,EAAE,QAAQ,QAAQ,MAAM,wBAAwB,QAAQ,CAAC,aAAa,EAAE;AAAA,EAClF,aAAa,WAAW;AAAA,EACxB,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACtG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,WAAW,MAAM,IAAI;AAC/B,UAAM,SAAS,CAAC,EAAE;AAClB,QAAI,UAAU,CAAC,EAAE,SAAS;AACxB,aAAO,oBAAoB,SAAS,EAAE,UAAU,aAAQ,EAAE,eAAe,KAAK,4DAA4D;AAAA,IAC5I;AACA,UAAM,OAAO,MAAM,OAAO,KAAK,wBAAwB;AAAA,MACrD,QAAQ,EAAE,UAAU;AAAA,MACpB,UAAU,EAAE,YAAY;AAAA,MACxB,YAAY,EAAE;AAAA,MACd,iBAAiB,EAAE;AAAA,MACnB,mBAAmB,EAAE;AAAA,MACrB,WAAW,EAAE;AAAA,IACf,CAAC;AACD,WAAO,WAAW,GAAG,SAAS,UAAU,QAAQ,KAAK,EAAE,UAAU,aAAQ,EAAE,eAAe;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACtH;AACF;AAGA,IAAM,kBAAkBA,GAAE,OAAO;AAAA,EAC/B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,iCAAiC;AAAA,EACvE,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AACxE,CAAC;AACD,IAAM,gBAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa,kDAAkD;AAAA,EAC/D,UAAU,EAAE,QAAQ,QAAQ,MAAM,6BAA6B,QAAQ,CAAC,aAAa,EAAE;AAAA,EACvF,aAAa,gBAAgB;AAAA,EAC7B,aAAa,EAAE,cAAc,OAAO,iBAAiB,OAAO,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACvG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,gBAAgB,MAAM,IAAI;AACpC,UAAM,OAAO,MAAM,OAAO,KAAK,6BAA6B,EAAE,WAAW,EAAE,WAAW,WAAW,EAAE,UAAU,CAAC;AAC9G,WAAO,WAAW,eAAe,EAAE,SAAS;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACxE;AACF;AAGA,IAAM,eAAeA,GAAE,OAAO;AAAA,EAC5B,YAAYA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS,4CAA4C;AAAA,EACnG,GAAG;AACL,CAAC;AACD,IAAM,cAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa,8GAA8G;AAAA,EAC3H,UAAU,EAAE,QAAQ,QAAQ,MAAM,2BAA2B,QAAQ,CAAC,cAAc,EAAE;AAAA,EACtF,aAAa,aAAa;AAAA,EAC1B,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACtG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,aAAa,MAAM,IAAI;AACjC,QAAI,CAAC,EAAE,SAAS;AACd,aAAO,oBAAoB,sBAAsB,EAAE,WAAW,MAAM,cAAc,EAAE,WAAW,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,IAClI;AACA,UAAM,OAAO,MAAM,OAAO,KAAK,2BAA2B,EAAE,gBAAgB,EAAE,WAAW,CAAC;AAC1F,WAAO,WAAW,WAAW,EAAE,WAAW,MAAM;AAAA;AAAA,EAAkB,UAAU,IAAI,CAAC,EAAE;AAAA,EACrF;AACF;AAGA,IAAM,mBAAmBA,GAAE,OAAO;AAAA,EAChC,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,sCAAsC;AAAA,EAC5E,UAAUA,GAAE,OAAOA,GAAE,OAAO,GAAGA,GAAE,QAAQ,CAAC,EAAE,SAAS,uEAAuE;AAAA,EAC5H,GAAG;AACL,CAAC;AACD,IAAM,iBAA0B;AAAA,EAC9B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa,0GAA0G;AAAA,EACvH,UAAU,EAAE,QAAQ,QAAQ,MAAM,4BAA4B,QAAQ,CAAC,aAAa,EAAE;AAAA,EACtF,aAAa,iBAAiB;AAAA,EAC9B,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACtG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,iBAAiB,MAAM,IAAI;AACrC,QAAI,CAAC,EAAE,SAAS;AACd,aAAO,oBAAoB,uBAAuB,EAAE,SAAS,KAAK,WAAW,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,KAAK,QAAQ,EAAE;AAAA,IAC/H;AACA,UAAM,OAAO,MAAM,OAAO,KAAK,4BAA4B,EAAE,WAAW,EAAE,WAAW,UAAU,EAAE,SAAS,CAAC;AAC3G,WAAO,WAAW,wBAAwB,EAAE,SAAS;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACjF;AACF;AAGA,IAAM,gBAAgBA,GAAE,OAAO;AAAA,EAC7B,WAAWA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,yDAAyD;AAAA,EAC/F,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,oDAAoD;AAAA,EACtG,eAAeA,GAAE,KAAK,CAAC,aAAa,YAAY,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,EACzH,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,EACtE,GAAG;AACL,CAAC;AACD,IAAM,sBAA+B;AAAA,EACnC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aACE,sOAA4N;AAAA,EAC9N,UAAU,EAAE,QAAQ,QAAQ,MAAM,2BAA2B,QAAQ,CAAC,aAAa,EAAE;AAAA,EACrF,aAAa,cAAc;AAAA,EAC3B,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,EACtG,SAAS,OAAO,MAAM,EAAE,OAAO,MAAM;AACnC,UAAM,IAAI,cAAc,MAAM,IAAI;AAClC,QAAI,CAAC,EAAE,SAAS;AACd,aAAO;AAAA,QACL,4BAA4B,EAAE,SAAS;AAAA,QACvC,SAAS,EAAE,iBAAiB,UAAU,GAAG,EAAE,OAAO,KAAK,EAAE,IAAI,YAAY,EAAE;AAAA,MAC7E;AAAA,IACF;AACA,UAAM,OAAO,MAAM,OAAO,KAAK,2BAA2B;AAAA,MACxD,WAAW,EAAE;AAAA,MACb,MAAM,EAAE;AAAA,MACR,eAAe,EAAE;AAAA,MACjB,WAAW,EAAE;AAAA,IACf,CAAC;AACD,WAAO,WAAW,0BAA0B,EAAE,SAAS;AAAA;AAAA,EAAS,UAAU,IAAI,CAAC,EAAE;AAAA,EACnF;AACF;AAEO,IAAM,aAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AEpTO,IAAM,WAAsB,CAAC,GAAG,WAAW,GAAG,eAAe,GAAG,UAAU;;;AVM1E,SAAS,aAAa,QAA2B;AACtD,QAAM,SAAS,IAAI,eAAe,MAAM;AACxC,QAAM,SAAS,IAAI,UAAU,EAAE,MAAM,sBAAsB,SAAS,QAAQ,CAAC;AAE7E,aAAW,QAAQ,UAAU;AAC3B,WAAO;AAAA,MACL,KAAK;AAAA,MACL;AAAA,QACE,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,aAAa,EAAE,OAAO,KAAK,OAAO,GAAG,KAAK,YAAY;AAAA,MACxD;AAAA,MACA,OAAO,SAAkC;AACvC,YAAI;AACF,iBAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;AAAA,QAClD,SAAS,KAAK;AACZ,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,iBAAO,MAAM,SAAS,KAAK,IAAI,YAAY,OAAO;AAClD,iBAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,UAAU,OAAO,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,QAC1F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,cAAc,SAAS,MAAM,QAAQ;AACjD,SAAO;AACT;;;AJlCA,SAAS,mBAA2B;AAClC,MAAI;AACF,WAAO,WAAW;AAAA,EACpB,SAAS,KAAK;AAEZ,YAAQ,OAAO,MAAM;AAAA,EAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,CAAM;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,SAAS,iBAAiB;AAEhC,kBAAgB,EAAE,OAAO,OAAO,UAAU,QAAQ,CAAC,OAAO,YAAY,EAAE,CAAC;AAEzE,QAAM,SAAS,aAAa,MAAM;AAClC,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,SAAO,KAAK,uBAAuB,OAAO,0BAA0B,OAAO,OAAO,GAAG;AACvF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,OAAO,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AACnF,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","z","z","z","z","z","z","z"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/logger.ts","../src/server.ts","../src/version.ts","../src/client/pagination.ts","../src/client/CloudSeeClient.ts","../src/tools/read.ts","../src/tools/format.ts","../src/tools/types.ts","../src/tools/download.ts","../src/tools/write.ts","../src/confirm.ts","../src/tools/index.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { loadConfig, type Config } from \"./config\";\r\nimport { configureLogger, logger } from \"./logger\";\r\nimport { createServer } from \"./server\";\r\nimport { VERSION } from \"./version\";\r\n\r\nfunction loadConfigOrExit(): Config {\r\n try {\r\n return loadConfig();\r\n } catch (err) {\r\n // Config errors are fatal and printed to stderr (not stdout — the transport).\r\n process.stderr.write(`\\n${err instanceof Error ? err.message : String(err)}\\n\\n`);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const config = loadConfigOrExit();\r\n // Register the secret for redaction before anything else runs.\r\n configureLogger({ level: config.logLevel, redact: [config.apiKeySecret] });\r\n\r\n const server = createServer(config);\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n\r\n logger.info(`cloudsee-drive-mcp v${VERSION} ready on stdio (base: ${config.baseUrl})`);\r\n}\r\n\r\nmain().catch((err) => {\r\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\r\n process.exit(1);\r\n});\r\n","import { z } from \"zod\";\r\nimport { CloudSeeError } from \"./errors\";\r\nimport type { LogLevel } from \"./logger\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://drive-api.cloudsee.cloud\";\r\nexport const DEFAULT_TIMEOUT_MS = 30_000;\r\n\r\nexport interface Config {\r\n apiKeyId: string;\r\n apiKeySecret: string;\r\n baseUrl: string;\r\n defaultBucket?: string;\r\n timeoutMs: number;\r\n logLevel: LogLevel;\r\n}\r\n\r\nconst envSchema = z.object({\r\n CLOUDSEE_API_KEY_ID: z.string().trim().min(1, \"is required (your CloudSee Drive API key id)\"),\r\n CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, \"is required (the matching API key secret)\"),\r\n CLOUDSEE_API_BASE_URL: z.string().url(\"must be a valid URL\").optional(),\r\n CLOUDSEE_DEFAULT_BUCKET: z.string().trim().min(1).optional(),\r\n CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),\r\n CLOUDSEE_LOG_LEVEL: z.enum([\"error\", \"warn\", \"info\", \"debug\"]).optional(),\r\n});\r\n\r\n/**\r\n * Load and validate configuration from the environment. Throws a CloudSeeError\r\n * with an actionable, secret-free message when required vars are missing.\r\n */\r\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\r\n const parsed = envSchema.safeParse(env);\r\n if (!parsed.success) {\r\n const issues = parsed.error.issues.map((i) => ` • ${i.path.join(\".\")} ${i.message}`).join(\"\\n\");\r\n throw new CloudSeeError(\r\n `CloudSee Drive MCP server is not configured:\\n${issues}\\n\\n` +\r\n `Set the required environment variables (see .env.example or the README).`,\r\n { code: \"config\" },\r\n );\r\n }\r\n const e = parsed.data;\r\n return {\r\n apiKeyId: e.CLOUDSEE_API_KEY_ID,\r\n apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,\r\n baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\r\n defaultBucket: e.CLOUDSEE_DEFAULT_BUCKET,\r\n timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,\r\n logLevel: e.CLOUDSEE_LOG_LEVEL ?? \"info\",\r\n };\r\n}\r\n","export interface CloudSeeErrorOptions {\r\n code?: string;\r\n status?: number;\r\n retryable?: boolean;\r\n}\r\n\r\n/** Error raised for any failed CloudSee API interaction. Messages are redacted\r\n * by the client before construction and must never contain the API secret. */\r\nexport class CloudSeeError extends Error {\r\n readonly code: string;\r\n readonly status?: number;\r\n readonly retryable: boolean;\r\n\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message);\r\n this.name = \"CloudSeeError\";\r\n this.code = options.code ?? \"error\";\r\n this.status = options.status;\r\n this.retryable = options.retryable ?? false;\r\n }\r\n}\r\n\r\n/** Authentication/authorization failure (HTTP 401/403). Terminal — never retried. */\r\nexport class AuthError extends CloudSeeError {\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message, { ...options, code: options.code ?? \"auth\" });\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n","// Structured logging that writes to **stderr only**. stdout is reserved for the\r\n// MCP stdio transport — anything written there corrupts the protocol stream.\r\n// All output is passed through secret redaction as a defense-in-depth backstop;\r\n// the real rule is to never hand the API secret to the logger in the first place.\r\n\r\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\";\r\n\r\nconst LEVEL_WEIGHT: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 };\r\n\r\nlet currentLevel: LogLevel = \"info\";\r\nlet secrets: string[] = [];\r\n\r\nexport function configureLogger(options: { level?: LogLevel; redact?: string[] }): void {\r\n if (options.level) currentLevel = options.level;\r\n if (options.redact) {\r\n secrets = options.redact.filter((s): s is string => typeof s === \"string\" && s.length > 0);\r\n }\r\n}\r\n\r\n/** Replace any registered secret with `***` anywhere it appears in `text`. */\r\nexport function redactSecrets(text: string): string {\r\n let out = text;\r\n for (const secret of secrets) {\r\n if (out.includes(secret)) out = out.split(secret).join(\"***\");\r\n }\r\n return out;\r\n}\r\n\r\nfunction stringify(meta: unknown): string {\r\n if (meta === undefined) return \"\";\r\n if (typeof meta === \"string\") return meta;\r\n try {\r\n return JSON.stringify(meta);\r\n } catch {\r\n return String(meta);\r\n }\r\n}\r\n\r\nfunction emit(level: LogLevel, message: string, meta?: unknown): void {\r\n if (LEVEL_WEIGHT[level] > LEVEL_WEIGHT[currentLevel]) return;\r\n const suffix = meta === undefined ? \"\" : ` ${stringify(meta)}`;\r\n process.stderr.write(`[cloudsee-drive-mcp] ${level.toUpperCase()}: ${redactSecrets(message + suffix)}\\n`);\r\n}\r\n\r\nexport const logger = {\r\n error: (message: string, meta?: unknown): void => emit(\"error\", message, meta),\r\n warn: (message: string, meta?: unknown): void => emit(\"warn\", message, meta),\r\n info: (message: string, meta?: unknown): void => emit(\"info\", message, meta),\r\n debug: (message: string, meta?: unknown): void => emit(\"debug\", message, meta),\r\n};\r\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { CloudSeeClient } from \"./client/CloudSeeClient\";\r\nimport { allTools } from \"./tools/index\";\r\nimport { logger } from \"./logger\";\r\nimport { VERSION } from \"./version\";\r\nimport type { Config } from \"./config\";\r\n\r\n/**\r\n * Build the MCP server: one CloudSeeClient shared across all tools, each tool\r\n * registered with its Zod input schema and MCP annotations. Tool handler errors\r\n * are caught and returned as `isError` results (never thrown across the\r\n * transport), and are logged to stderr with the secret redacted.\r\n */\r\nexport function createServer(config: Config): McpServer {\r\n const client = new CloudSeeClient(config);\r\n const server = new McpServer({ name: \"cloudsee-drive-mcp\", version: VERSION });\r\n\r\n for (const tool of allTools) {\r\n server.registerTool(\r\n tool.name,\r\n {\r\n title: tool.title,\r\n description: tool.description,\r\n inputSchema: tool.inputSchema,\r\n annotations: { title: tool.title, ...tool.annotations },\r\n },\r\n async (args: Record<string, unknown>) => {\r\n try {\r\n return await tool.handler(args ?? {}, { client, defaultBucket: config.defaultBucket });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n logger.error(`tool \"${tool.name}\" failed`, message);\r\n return { content: [{ type: \"text\" as const, text: `Error: ${message}` }], isError: true };\r\n }\r\n },\r\n );\r\n }\r\n\r\n logger.info(`registered ${allTools.length} tools`);\r\n return server;\r\n}\r\n","// Single source of truth for the package version, surfaced to the MCP client\r\n// handshake and the User-Agent. Kept in sync with package.json by release tooling.\r\nexport const VERSION = \"0.1.0\";\r\n","import { CloudSeeError } from \"../errors\";\r\n\r\n// The data plane uses five different pagination param names across endpoint\r\n// families (C10): `nextPage`, `marker`, `lastEvaluatedKey`, `pageToken`,\r\n// `nextToken`. Tools expose a single opaque `cursor`; this module encodes the\r\n// dialect + raw token into that cursor and decodes it back, so a cursor minted\r\n// for one operation can't be misapplied to another.\r\n\r\nexport type PaginationDialect = \"nextPage\" | \"marker\" | \"lastEvaluatedKey\" | \"pageToken\" | \"nextToken\";\r\n\r\ninterface CursorPayload {\r\n d: PaginationDialect;\r\n v: string;\r\n}\r\n\r\nexport function encodeCursor(dialect: PaginationDialect, value: string | null | undefined): string | undefined {\r\n if (value === null || value === undefined || value === \"\") return undefined;\r\n const payload: CursorPayload = { d: dialect, v: String(value) };\r\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\");\r\n}\r\n\r\nexport function decodeCursor(cursor: string, expected: PaginationDialect): string {\r\n let payload: CursorPayload;\r\n try {\r\n payload = JSON.parse(Buffer.from(cursor, \"base64url\").toString(\"utf8\")) as CursorPayload;\r\n } catch {\r\n throw new CloudSeeError(\"Invalid pagination cursor.\", { code: \"bad_cursor\" });\r\n }\r\n if (!payload || typeof payload.v !== \"string\" || payload.d !== expected) {\r\n throw new CloudSeeError(\"Pagination cursor is not valid for this operation.\", { code: \"bad_cursor\" });\r\n }\r\n return payload.v;\r\n}\r\n\r\nconst RESPONSE_FIELDS = [\"nextPage\", \"nextPageToken\", \"marker\", \"lastEvaluatedKey\", \"pageToken\", \"nextToken\"];\r\n\r\n/**\r\n * Pull the raw continuation token out of a response envelope's `data`, tolerant\r\n * of the exact field name (which varies by endpoint and isn't guaranteed by the\r\n * RPC contract). Prefers the operation's own dialect field, then known aliases.\r\n */\r\nexport function extractNextToken(data: unknown, dialect: PaginationDialect): string | undefined {\r\n if (!data || typeof data !== \"object\") return undefined;\r\n const record = data as Record<string, unknown>;\r\n for (const field of [dialect, ...RESPONSE_FIELDS]) {\r\n const value = record[field];\r\n if (typeof value === \"string\" && value !== \"\") return value;\r\n if (value && typeof value === \"object\") {\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n /* ignore non-serializable */\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n","import { AuthError, CloudSeeError } from \"../errors\";\r\nimport { logger } from \"../logger\";\r\nimport { VERSION } from \"../version\";\r\nimport type { Config } from \"../config\";\r\nimport { decodeCursor, encodeCursor, extractNextToken, type PaginationDialect } from \"./pagination\";\r\n\r\ninterface Envelope<T> {\r\n success?: boolean;\r\n data?: T;\r\n errorMessage?: string;\r\n code?: string;\r\n}\r\n\r\nexport interface RequestOptions {\r\n retries?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport interface PagedResult<T> {\r\n data: T;\r\n nextCursor?: string;\r\n}\r\n\r\ninterface ParsedResponse<T> {\r\n data: T;\r\n envelope: Record<string, unknown> | undefined;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 3;\r\nconst BASE_BACKOFF_MS = 300;\r\nconst MAX_BACKOFF_MS = 8_000;\r\n\r\n/**\r\n * Typed wrapper over the drive-bridge `/v1/*` data plane. Every endpoint is an\r\n * RPC `POST /noun/verb` returning the platform `{ success, data, errorMessage,\r\n * code }` envelope. The client:\r\n * - sets the `X-Api-Key-Id` / `X-Api-Key-Secret` auth headers from config,\r\n * - retries 429/5xx with exponential backoff honoring `Retry-After` (the\r\n * `csd-mcp` usage plan is dormant, so the server must self-throttle — C6),\r\n * - unwraps the envelope and surfaces `success:false` as a CloudSeeError,\r\n * - never logs the secret and redacts it from any error message.\r\n */\r\nexport class CloudSeeClient {\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private readonly secret: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: Config) {\r\n this.baseUrl = config.baseUrl;\r\n this.timeoutMs = config.timeoutMs;\r\n this.secret = config.apiKeySecret;\r\n this.headers = {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n \"X-Api-Key-Id\": config.apiKeyId,\r\n \"X-Api-Key-Secret\": config.apiKeySecret,\r\n \"User-Agent\": `cloudsee-drive-mcp/${VERSION}`,\r\n };\r\n }\r\n\r\n /** POST a data-plane RPC endpoint (path WITHOUT `/v1`). Returns unwrapped `data`. */\r\n async post<T = unknown>(path: string, body: Record<string, unknown> = {}, options: RequestOptions = {}): Promise<T> {\r\n return (await this.request<T>(path, body, options)).data;\r\n }\r\n\r\n /** POST a paginated endpoint. Translates the opaque `cursor` to/from the\r\n * endpoint's pagination dialect and returns a normalized `nextCursor`. The\r\n * continuation token can sit at the envelope level (e.g. `/storage/recent`\r\n * returns `nextToken` as a sibling of `data`) or inside `data` — check the\r\n * envelope first, then `data`, so a token is never silently dropped. */\r\n async postPaged<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n dialect: PaginationDialect,\r\n cursor?: string,\r\n options: RequestOptions = {},\r\n ): Promise<PagedResult<T>> {\r\n const requestBody = { ...body };\r\n if (cursor) requestBody[dialect] = decodeCursor(cursor, dialect);\r\n const { data, envelope } = await this.request<T>(path, requestBody, options);\r\n const token = extractNextToken(envelope, dialect) ?? extractNextToken(data, dialect);\r\n return { data, nextCursor: encodeCursor(dialect, token) };\r\n }\r\n\r\n /** Run the request with retry/backoff, returning the unwrapped `data` together\r\n * with the raw response envelope (needed for envelope-level pagination tokens). */\r\n private async request<T>(path: string, body: Record<string, unknown>, options: RequestOptions): Promise<ParsedResponse<T>> {\r\n const url = `${this.baseUrl}/v1${path}`;\r\n const retries = options.retries ?? DEFAULT_RETRIES;\r\n let attempt = 0;\r\n for (;;) {\r\n attempt += 1;\r\n const response = await this.fetchOnce(url, path, body, options.signal);\r\n\r\n if (response.status === 401 || response.status === 403) {\r\n throw new AuthError(\r\n \"Authentication failed (HTTP \" +\r\n response.status +\r\n \"). Check CLOUDSEE_API_KEY_ID and CLOUDSEE_API_KEY_SECRET, and that the key is active and not expired.\",\r\n { status: response.status },\r\n );\r\n }\r\n\r\n if ((response.status === 429 || response.status >= 500) && attempt <= retries) {\r\n const waitMs = retryAfterMs(response.headers.get(\"retry-after\")) ?? backoffMs(attempt);\r\n logger.warn(`HTTP ${response.status} from ${path}; retrying in ${waitMs}ms (attempt ${attempt}/${retries}).`);\r\n await sleep(waitMs);\r\n continue;\r\n }\r\n\r\n return await this.parse<T>(response, path);\r\n }\r\n }\r\n\r\n private async fetchOnce(\r\n url: string,\r\n path: string,\r\n body: Record<string, unknown>,\r\n external?: AbortSignal,\r\n ): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n const onAbort = (): void => controller.abort();\r\n if (external) external.addEventListener(\"abort\", onAbort, { once: true });\r\n try {\r\n return await fetch(url, {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } catch (err) {\r\n const reason = controller.signal.aborted ? `timed out after ${this.timeoutMs}ms` : this.redact(errMessage(err));\r\n throw new CloudSeeError(`Network error calling ${path}: ${reason}`, { code: \"network\", retryable: true });\r\n } finally {\r\n clearTimeout(timer);\r\n if (external) external.removeEventListener(\"abort\", onAbort);\r\n }\r\n }\r\n\r\n private async parse<T>(response: Response, path: string): Promise<ParsedResponse<T>> {\r\n const text = await response.text();\r\n let json: Envelope<T> | undefined;\r\n if (text) {\r\n try {\r\n json = JSON.parse(text) as Envelope<T>;\r\n } catch {\r\n throw new CloudSeeError(\r\n response.ok\r\n ? `CloudSee API returned a non-JSON response for ${path}.`\r\n : `CloudSee API returned HTTP ${response.status} for ${path}.`,\r\n { status: response.status, code: response.ok ? \"bad_response\" : \"http_error\" },\r\n );\r\n }\r\n }\r\n if (!response.ok) {\r\n const message = json?.errorMessage ?? `HTTP ${response.status}`;\r\n throw new CloudSeeError(`CloudSee API error for ${path}: ${this.redact(message)}`, {\r\n status: response.status,\r\n code: json?.code ?? \"http_error\",\r\n });\r\n }\r\n if (json && typeof json === \"object\" && json.success === false) {\r\n throw new CloudSeeError(this.redact(json.errorMessage || `Operation failed (${json.code ?? \"unknown\"}).`), {\r\n code: json.code ?? \"operation_failed\",\r\n status: response.status,\r\n });\r\n }\r\n const envelope = json && typeof json === \"object\" ? (json as Record<string, unknown>) : undefined;\r\n if (json && typeof json === \"object\" && \"data\" in json) return { data: json.data as T, envelope };\r\n return { data: (json ?? ({} as T)) as T, envelope };\r\n }\r\n\r\n private redact(text: string): string {\r\n return this.secret && text.includes(this.secret) ? text.split(this.secret).join(\"***\") : text;\r\n }\r\n}\r\n\r\nfunction errMessage(err: unknown): string {\r\n return err instanceof Error ? err.message : String(err);\r\n}\r\n\r\nfunction backoffMs(attempt: number): number {\r\n const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** (attempt - 1));\r\n // Full jitter over [ceiling/2, ceiling] to avoid thundering-herd retries.\r\n return Math.round(ceiling / 2 + Math.random() * (ceiling / 2));\r\n}\r\n\r\nfunction retryAfterMs(header: string | null): number | undefined {\r\n if (!header) return undefined;\r\n const seconds = Number(header);\r\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\r\n const date = Date.parse(header);\r\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\r\n return undefined;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize, withCursor } from \"./format\";\r\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\r\n\r\n// ---- list_buckets → POST /storage/buckets (storageBucketList, drive:read) ----\r\nconst listBuckets: ToolDef = {\r\n name: \"list_buckets\",\r\n title: \"List buckets\",\r\n description:\r\n \"List the storage buckets (Amazon S3 buckets) the authenticated CloudSee Drive account can access. Use this first to discover available buckets before browsing or searching.\",\r\n endpoint: { method: \"POST\", path: \"/storage/buckets\", scopes: [\"drive:read\"] },\r\n inputSchema: {},\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (_args, { client }) => {\r\n const data = await client.post(\"/storage/buckets\", {});\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- browse_folder → POST /storage/list (storageList, drive:read) ----\r\nconst browseSchema = z.object({\r\n bucketName: bucketField,\r\n path: z.string().optional().describe(\"Folder path / prefix within the drive to list. Empty or omitted = the drive root.\"),\r\n sortOption: z.string().optional().describe(\"Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst browseFolder: ToolDef = {\r\n name: \"browse_folder\",\r\n title: \"Browse folder\",\r\n description:\r\n \"List the files and sub-folders inside a folder of a drive (the indexed view), with sorting and pagination. Requires the drive (bucketName). To filter by keyword use 'search_files'; for a complete, recursive file listing straight from storage use 'list_files'.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: browseSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = browseSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { bucketName, dirPath: a.path ?? \"\", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- search_files → POST /storage/list with searchingKeyword (drive:read) ----\r\nconst searchSchema = z.object({\r\n bucketName: bucketField,\r\n query: z.string().min(1).describe(\"Keyword to match against file and folder names.\"),\r\n path: z.string().optional().describe(\"Folder within the drive to search under. Empty = drive root.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst searchFiles: ToolDef = {\r\n name: \"search_files\",\r\n title: \"Search files\",\r\n description:\r\n \"Search for files and folders by name keyword within a drive (the indexed view). Requires the drive (bucketName). Returns matches with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: searchSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = searchSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { bucketName, dirPath: a.path ?? \"\", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- list_files → POST /storage/bucket/files (getAllFilesInTheBucket, drive:read) ----\r\nconst listFilesSchema = z.object({\r\n bucketName: bucketField,\r\n deep: z.boolean().optional().describe(\"Recurse into sub-folders (default true).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst listFiles: ToolDef = {\r\n name: \"list_files\",\r\n title: \"List files in a drive\",\r\n description:\r\n \"List the files in a drive straight from storage, recursively by default — the most reliable way to see what a drive actually contains. Requires the drive (bucketName). Returns names, sizes, storage classes and keys, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/bucket/files\", scopes: [\"drive:read\"] },\r\n inputSchema: listFilesSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = listFilesSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/bucket/files\",\r\n { bucketName, deepQuery: a.deep ?? true },\r\n \"marker\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- recent_files → POST /storage/recent (storageRecentFiles, drive:read) ----\r\nconst recentSchema = z.object({\r\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst recentFiles: ToolDef = {\r\n name: \"recent_files\",\r\n title: \"Recent files\",\r\n description: \"List the account's most recently accessed or modified files, newest first, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/recent\", scopes: [\"drive:read\"] },\r\n inputSchema: recentSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = recentSchema.parse(args);\r\n const limit = a.limit ?? 50;\r\n // /storage/recent paginates with `nextToken` (a composite key returned at the\r\n // envelope level), not `nextPage`. It echoes that token even when the list is\r\n // exhausted, so only advertise a next page when this page came back full.\r\n const { data, nextCursor } = await client.postPaged(\"/storage/recent\", { limit }, \"nextToken\", a.cursor);\r\n const hasMore = Array.isArray(data) && data.length >= limit;\r\n return textResult(withCursor(summarize(data), hasMore ? nextCursor : undefined));\r\n },\r\n};\r\n\r\n// ---- get_file_metadata → POST /storage/object/detail (storageDetail, drive:read) ----\r\nconst metaSchema = z.object({\r\n bucketName: bucketField,\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file within the drive.\"),\r\n});\r\nconst getFileMetadata: ToolDef = {\r\n name: \"get_file_metadata\",\r\n title: \"Get file metadata\",\r\n description:\r\n \"Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key. Requires the drive (bucketName).\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/detail\", scopes: [\"drive:read\"] },\r\n inputSchema: metaSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = metaSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/detail\", { bucketName, objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- get_file_tags → POST /storage/object/tagging (getObjectTagging, drive:read) ----\r\nconst tagsSchema = z.object({\r\n bucketName: bucketField,\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file within the drive.\"),\r\n});\r\nconst getFileTags: ToolDef = {\r\n name: \"get_file_tags\",\r\n title: \"Get file tags\",\r\n description: \"Get the S3 object tags (key/value pairs) attached to a file, by object key. Requires the drive (bucketName).\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/tagging\", scopes: [\"drive:read\"] },\r\n inputSchema: tagsSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = tagsSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/tagging\", { bucketName, objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const readTools: ToolDef[] = [\r\n listBuckets,\r\n browseFolder,\r\n searchFiles,\r\n listFiles,\r\n recentFiles,\r\n getFileMetadata,\r\n getFileTags,\r\n];\r\n","// Output bounding so large listings never flood the model context (R8 / AC).\r\n// Arrays are capped, deeply, and the whole rendering is hard-capped by length.\r\n\r\nconst DEFAULT_MAX_ITEMS = 100;\r\nconst DEFAULT_MAX_CHARS = 8_000;\r\n\r\nexport function summarize(data: unknown, opts: { maxItems?: number; maxChars?: number } = {}): string {\r\n const maxItems = opts.maxItems ?? DEFAULT_MAX_ITEMS;\r\n const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;\r\n let json: string;\r\n try {\r\n json = JSON.stringify(boundArrays(data, maxItems), null, 2);\r\n } catch {\r\n json = String(data);\r\n }\r\n if (json.length > maxChars) {\r\n json = `${json.slice(0, maxChars)}\\n… (output truncated at ${maxChars} characters — narrow your query or paginate).`;\r\n }\r\n return json;\r\n}\r\n\r\nfunction boundArrays(value: unknown, maxItems: number): unknown {\r\n if (Array.isArray(value)) {\r\n const capped: unknown[] = value.slice(0, maxItems).map((v) => boundArrays(v, maxItems));\r\n if (value.length > maxItems) {\r\n capped.push(`… ${value.length - maxItems} more item(s) omitted — paginate for the rest.`);\r\n }\r\n return capped;\r\n }\r\n if (value && typeof value === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\r\n out[k] = boundArrays(v, maxItems);\r\n }\r\n return out;\r\n }\r\n return value;\r\n}\r\n\r\n/** Append a continuation hint when the API returned another page. */\r\nexport function withCursor(body: string, nextCursor?: string): string {\r\n if (!nextCursor) return body;\r\n return `${body}\\n\\n↪ More results available. Call this tool again with cursor=\"${nextCursor}\" for the next page.`;\r\n}\r\n","import { z, type ZodRawShape } from \"zod\";\r\nimport type { CloudSeeClient } from \"../client/CloudSeeClient\";\r\nimport { CloudSeeError } from \"../errors\";\r\n\r\nexport interface ToolContext {\r\n client: CloudSeeClient;\r\n /** Optional configured default drive (CLOUDSEE_DEFAULT_BUCKET); never hardcoded. */\r\n defaultBucket?: string;\r\n}\r\n\r\n/** Reusable Zod field for the drive/bucket selector. Optional in the schema so a\r\n * configured CLOUDSEE_DEFAULT_BUCKET can supply it; `resolveBucket` enforces presence. */\r\nexport const bucketField = z\r\n .string()\r\n .min(1)\r\n .optional()\r\n .describe(\r\n \"The CloudSee drive (S3 bucket) name to operate in, e.g. 'max-2778abc0' — find it in the CloudSee dashboard. Required unless CLOUDSEE_DEFAULT_BUCKET is configured on the server.\",\r\n );\r\n\r\n/** Resolve the drive/bucket from the parsed tool input, falling back to the\r\n * configured default. The value is always taken from input or config — never\r\n * hardcoded — and an actionable error is thrown when neither is present. */\r\nexport function resolveBucket(bucket: string | undefined, defaultBucket: string | undefined): string {\r\n const resolved = (bucket ?? defaultBucket ?? \"\").trim();\r\n if (!resolved) {\r\n throw new CloudSeeError(\r\n \"No CloudSee drive specified. Pass `bucketName` (your drive name, shown in the CloudSee dashboard), \" +\r\n \"or set CLOUDSEE_DEFAULT_BUCKET in the server config.\",\r\n { code: \"no_bucket\" },\r\n );\r\n }\r\n return resolved;\r\n}\r\n\r\nexport interface ToolResult {\r\n // Index signature mirrors the MCP SDK's CallToolResult (which permits extra\r\n // fields like _meta); lets ToolResult satisfy the registerTool callback type.\r\n [x: string]: unknown;\r\n content: Array<{ type: \"text\"; text: string }>;\r\n isError?: boolean;\r\n}\r\n\r\nexport interface ToolAnnotations {\r\n readOnlyHint?: boolean;\r\n destructiveHint?: boolean;\r\n idempotentHint?: boolean;\r\n openWorldHint?: boolean;\r\n}\r\n\r\n/** The real data-plane endpoint a tool wraps — asserted against the committed\r\n * contract snapshot by the drift test. `path` excludes the `/v1` prefix. */\r\nexport interface ToolEndpoint {\r\n method: \"POST\";\r\n path: string;\r\n scopes: string[];\r\n}\r\n\r\nexport interface ToolDef {\r\n name: string;\r\n title: string;\r\n description: string;\r\n endpoint: ToolEndpoint;\r\n inputSchema: ZodRawShape;\r\n annotations: ToolAnnotations;\r\n handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>;\r\n}\r\n\r\nexport function textResult(text: string): ToolResult {\r\n return { content: [{ type: \"text\", text }] };\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize } from \"./format\";\r\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\r\n\r\n// Both tools wrap POST /storage/object/download-url (getObjectUrl, drive:read +\r\n// drive:download). The API returns a short-lived pre-signed URL, never AWS\r\n// credentials. We surface the URL (a capability link, not a secret); the model\r\n// can hand it to the user. We never download bytes into the conversation.\r\n\r\n// ---- download_file ----\r\nconst downloadSchema = z.object({\r\n bucketName: bucketField,\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to download, within the drive.\"),\r\n forceDownload: z.boolean().optional().describe(\"If true, the link forces an attachment download instead of inline view.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst downloadFile: ToolDef = {\r\n name: \"download_file\",\r\n title: \"Download file\",\r\n description:\r\n \"Get a short-lived pre-signed download URL for a file. Requires the drive (bucketName). The URL is time-limited and grants read access to that one object — share it with care. Returns the URL; it does not load file bytes into the conversation.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: downloadSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = downloadSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n bucketName,\r\n filePath: a.filePath,\r\n download: a.forceDownload ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- share_link ----\r\nconst shareSchema = z.object({\r\n bucketName: bucketField,\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to share, within the drive.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst shareLink: ToolDef = {\r\n name: \"share_link\",\r\n title: \"Create share link\",\r\n description:\r\n \"Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). Requires the drive (bucketName). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: shareSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = shareSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n bucketName,\r\n filePath: a.filePath,\r\n shareableLink: true,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const downloadTools: ToolDef[] = [downloadFile, shareLink];\r\n","import { readFile, stat } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport { z } from \"zod\";\nimport { CloudSeeError } from \"../errors\";\nimport { confirmShape, confirmationPreview } from \"../confirm\";\nimport { summarize } from \"./format\";\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\n\n// Write/delete tools are present but their end-to-end success is sequenced\n// behind CSD-572 (the gateway enforces no scope/RBAC yet and the write path is\n// a Phase-1 stub). This note is appended to each tool's description so the model\n// and the user understand a gateway denial here is expected, not a tool bug.\nconst GATED =\n \" (Write access is authorized server-side; until CloudSee's public-API RBAC wiring (CSD-572) is live, the gateway may deny this — that is expected, not a tool failure.)\";\n\nconst MULTIPART_THRESHOLD = 8 * 1024 * 1024; // 8 MiB\n\nfunction pickUrl(data: unknown): string | undefined {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\") {\n const record = data as Record<string, unknown>;\n for (const key of [\"url\", \"uploadUrl\", \"signedUrl\", \"preSignedUrl\", \"presignedUrl\", \"putUrl\"]) {\n const value = record[key];\n if (typeof value === \"string\" && value) return value;\n }\n }\n return undefined;\n}\n\n// The object key that `complete` finalizes MUST come from the server's presign\n// response — never guessed — or we could register a record that doesn't match\n// the bytes actually written to the (server-chosen) S3 key.\nfunction pickKey(data: unknown): string | undefined {\n if (data && typeof data === \"object\") {\n const record = data as Record<string, unknown>;\n for (const key of [\"key\", \"objectKey\", \"Key\", \"objectPath\", \"filePath\"]) {\n const value = record[key];\n if (typeof value === \"string\" && value) return value;\n }\n const fields = record[\"fields\"];\n if (fields && typeof fields === \"object\") {\n const nested = (fields as Record<string, unknown>)[\"key\"];\n if (typeof nested === \"string\" && nested) return nested;\n }\n }\n return undefined;\n}\n\n// ---- upload_file → POST /storage/upload/url then /storage/upload/complete (drive:write) ----\nconst uploadSchema = z.object({\n bucketName: bucketField,\n localPath: z.string().min(1).describe(\"Path to the local file to upload (absolute, or relative to the server's working directory).\"),\n destinationFolder: z.string().describe(\"Destination folder/prefix in the drive. Empty = drive root.\"),\n fileName: z.string().optional().describe(\"Name to store the file as. Defaults to the local file's name.\"),\n contentType: z.string().optional().describe(\"MIME type. Defaults to application/octet-stream.\"),\n storageClass: z.string().optional().describe(\"Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).\"),\n});\nconst uploadFile: ToolDef = {\n name: \"upload_file\",\n title: \"Upload file\",\n description:\n \"Upload a local file to a folder in a drive. Requires the drive (bucketName). Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/upload/url\", scopes: [\"drive:write\"] },\n inputSchema: uploadSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = uploadSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const fileName = a.fileName ?? basename(a.localPath);\n const contentType = a.contentType ?? \"application/octet-stream\";\n\n let info;\n try {\n info = await stat(a.localPath);\n } catch {\n throw new CloudSeeError(`Local file not found: ${a.localPath}`, { code: \"file_not_found\" });\n }\n if (!info.isFile()) throw new CloudSeeError(`Not a regular file: ${a.localPath}`, { code: \"not_a_file\" });\n if (info.size > MULTIPART_THRESHOLD) {\n throw new CloudSeeError(\n `File is ${(info.size / 1048576).toFixed(1)} MiB. Multipart upload (>8 MiB) is not enabled in this release — upload a smaller file or use the CloudSee web app for large files.`,\n { code: \"too_large\" },\n );\n }\n\n const bytes = await readFile(a.localPath);\n const presign = await client.post<Record<string, unknown>>(\"/storage/upload/url\", {\n bucketName,\n dirPath: a.destinationFolder,\n fileName,\n contentType,\n storageClass: a.storageClass,\n });\n const uploadUrl = pickUrl(presign);\n if (!uploadUrl) throw new CloudSeeError(\"The API did not return a usable upload URL.\", { code: \"no_upload_url\" });\n\n // Resolve the server-assigned key BEFORE uploading any bytes — if it's\n // absent we cannot finalize safely, so we fail without writing anything.\n const key = pickKey(presign);\n if (!key) {\n throw new CloudSeeError(\n \"The upload-URL response did not include the object key, so the upload cannot be finalized safely. (No bytes were uploaded.)\",\n { code: \"no_object_key\" },\n );\n }\n\n const put = await fetch(uploadUrl, { method: \"PUT\", headers: { \"Content-Type\": contentType }, body: new Uint8Array(bytes) });\n if (!put.ok) throw new CloudSeeError(`Upload to storage failed (HTTP ${put.status}).`, { status: put.status, code: \"upload_failed\" });\n\n let complete: unknown;\n try {\n complete = await client.post(\"/storage/upload/complete\", {\n bucketName,\n dirPath: a.destinationFolder,\n objects: [{ fileName, key, contentType, size: info.size }],\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new CloudSeeError(\n `Bytes were uploaded to storage but finalization failed: ${reason}. The object may exist without an index entry — retry the upload, or remove it via the CloudSee app.`,\n { code: \"finalize_failed\" },\n );\n }\n return textResult(`Uploaded \"${fileName}\" (${info.size} bytes) to \"${a.destinationFolder || \"/\"}\".\\n\\n${summarize(complete)}`);\n },\n};\n\n// ---- create_folder → POST /storage/folder/create (createFolder, drive:write) ----\nconst createFolderSchema = z.object({\n bucketName: bucketField,\n parentPath: z.string().describe(\"Parent folder/prefix in the drive. Empty = drive root.\"),\n name: z.string().min(1).describe(\"New folder name.\"),\n});\nconst createFolder: ToolDef = {\n name: \"create_folder\",\n title: \"Create folder\",\n description: \"Create a new folder at the given path in a drive. Requires the drive (bucketName).\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/folder/create\", scopes: [\"drive:write\"] },\n inputSchema: createFolderSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = createFolderSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const data = await client.post(\"/storage/folder/create\", { bucketName, dirPath: a.parentPath, object: a.name });\n return textResult(`Created folder \"${a.name}\" in \"${a.parentPath || \"/\"}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- rename_file → POST /storage/object/rename (storageRename, drive:write) — destructive ----\nconst renameSchema = z.object({\n bucketName: bucketField,\n oldObjectId: z.string().min(1).describe(\"Current object key/id of the file or folder.\"),\n newName: z.string().min(1).describe(\"New name.\"),\n isFolder: z.boolean().optional().describe(\"Set true when renaming a folder.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst renameFile: ToolDef = {\n name: \"rename_file\",\n title: \"Rename file or folder\",\n description: \"Rename a file or folder in a drive. Requires the drive (bucketName). Destructive (changes the object's key). Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/rename\", scopes: [\"drive:write\"] },\n inputSchema: renameSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = renameSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`rename ${a.isFolder ? \"folder\" : \"file\"} to \"${a.newName}\"`, `Drive: ${bucketName}\\nTarget: ${a.oldObjectId}`);\n }\n const data = await client.post(\"/storage/object/rename\", {\n bucketName,\n oldObjectId: a.oldObjectId,\n newObjectName: a.newName,\n isFolder: a.isFolder ?? false,\n storageId: a.storageId,\n });\n return textResult(`Renamed to \"${a.newName}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- move_file → POST /storage/object/move (storageMoveObject, drive:write) — destructive unless asCopy ----\nconst moveSchema = z.object({\n bucketName: bucketField,\n sourcePath: z.string().min(1).describe(\"Source object key/prefix.\"),\n destinationPath: z.string().min(1).describe(\"Destination prefix.\"),\n asCopy: z.boolean().optional().describe(\"Copy instead of move (copy is non-destructive; the source is kept).\"),\n isFolder: z.boolean().optional().describe(\"Set true for a folder.\"),\n destinationBucket: z.string().optional().describe(\"Target drive, if different from the source drive.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst moveFile: ToolDef = {\n name: \"move_file\",\n title: \"Move or copy file\",\n description:\n \"Move (or copy, with asCopy=true) a file or folder to a new location. Requires the source drive (bucketName). A move removes the source and is destructive, so it requires confirm=true; a copy does not.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/move\", scopes: [\"drive:write\"] },\n inputSchema: moveSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = moveSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const isMove = !a.asCopy;\n if (isMove && !a.confirm) {\n return confirmationPreview(`move \"${a.sourcePath}\" → \"${a.destinationPath}\"`, \"This removes the source. Pass asCopy=true to copy instead.\");\n }\n const data = await client.post(\"/storage/object/move\", {\n bucketName,\n asCopy: a.asCopy ?? false,\n isFolder: a.isFolder ?? false,\n sourcePath: a.sourcePath,\n destinationPath: a.destinationPath,\n destinationBucket: a.destinationBucket,\n storageId: a.storageId,\n });\n return textResult(`${isMove ? \"Moved\" : \"Copied\"} \"${a.sourcePath}\" → \"${a.destinationPath}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- duplicate_file → POST /storage/object/duplicate (duplicateFile, drive:write) ----\nconst duplicateSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Source object key to duplicate.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n});\nconst duplicateFile: ToolDef = {\n name: \"duplicate_file\",\n title: \"Duplicate file\",\n description: \"Create a copy of a file in the same location. Requires the drive (bucketName).\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/duplicate\", scopes: [\"drive:write\"] },\n inputSchema: duplicateSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = duplicateSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const data = await client.post(\"/storage/object/duplicate\", { bucketName, objectKey: a.objectKey, storageId: a.storageId });\n return textResult(`Duplicated \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- delete_files → POST /storage/objects/delete (deleteObjects, drive:delete) — destructive ----\nconst deleteSchema = z.object({\n bucketName: bucketField,\n objectKeys: z.array(z.string().min(1)).min(1).describe(\"Object keys (paths) to permanently delete.\"),\n ...confirmShape,\n});\nconst deleteFiles: ToolDef = {\n name: \"delete_files\",\n title: \"Delete files\",\n description: \"Permanently delete one or more files/objects by key from a drive. Requires the drive (bucketName). Destructive and irreversible. Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/objects/delete\", scopes: [\"drive:delete\"] },\n inputSchema: deleteSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = deleteSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s) from \"${bucketName}\"`, a.objectKeys.map((k) => ` - ${k}`).join(\"\\n\"));\n }\n const data = await client.post(\"/storage/objects/delete\", { bucketName, deletedObjects: a.objectKeys });\n return textResult(`Deleted ${a.objectKeys.length} object(s).\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- update_metadata → POST /storage/object/metadata (storageUpdateObjectMetadata, drive:write) — destructive ----\nconst updateMetaSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Object key whose metadata to update.\"),\n metadata: z.record(z.string(), z.unknown()).describe(\"Metadata fields to set (key/value map). Overwrites the listed fields.\"),\n ...confirmShape,\n});\nconst updateMetadata: ToolDef = {\n name: \"update_metadata\",\n title: \"Update file metadata\",\n description: \"Update a file's metadata in a drive. Requires the drive (bucketName). Destructive (overwrites the listed metadata fields). Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/metadata\", scopes: [\"drive:write\"] },\n inputSchema: updateMetaSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = updateMetaSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`update metadata on \"${a.objectKey}\"`, `Drive: ${bucketName}\\nFields: ${Object.keys(a.metadata).join(\", \") || \"(none)\"}`);\n }\n const data = await client.post(\"/storage/object/metadata\", { bucketName, objectKey: a.objectKey, metadata: a.metadata });\n return textResult(`Updated metadata on \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- restore_archived_file → POST /storage/object/restore (restoreObject, drive:write) — Glacier un-archive ----\nconst restoreSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Object key of the archived (Glacier) object to restore.\"),\n days: z.number().int().min(1).optional().describe(\"How many days to keep the restored copy available.\"),\n retrievalTier: z.enum([\"Expedited\", \"Standard\", \"Bulk\"]).optional().describe(\"Glacier retrieval tier (default Standard).\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst restoreArchivedFile: ToolDef = {\n name: \"restore_archived_file\",\n title: \"Restore archived file\",\n description:\n \"Begin restoring an archived (S3 Glacier) object so it can be downloaded. Requires the drive (bucketName). This is Glacier un-archiving — NOT recovery of a deleted file — and may incur retrieval cost and take minutes to hours. Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/restore\", scopes: [\"drive:write\"] },\n inputSchema: restoreSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = restoreSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(\n `restore archived object \"${a.objectKey}\"`,\n `Drive: ${bucketName}\\nTier: ${a.retrievalTier ?? \"Standard\"}${a.days ? `, ${a.days} day(s)` : \"\"}. May incur retrieval cost.`,\n );\n }\n const data = await client.post(\"/storage/object/restore\", {\n bucketName,\n objectKey: a.objectKey,\n days: a.days,\n retrievalTier: a.retrievalTier,\n storageId: a.storageId,\n });\n return textResult(`Restore initiated for \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\nexport const writeTools: ToolDef[] = [\n uploadFile,\n createFolder,\n renameFile,\n moveFile,\n duplicateFile,\n deleteFiles,\n updateMetadata,\n restoreArchivedFile,\n];\n","import { z } from \"zod\";\r\nimport { textResult, type ToolResult } from \"./tools/types\";\r\n\r\n// Two-step confirmation for destructive tools. A first call WITHOUT confirm=true\r\n// returns a human-readable preview and performs no mutation; the model (or user)\r\n// must re-call with confirm=true to execute. This is UX, NOT the security\r\n// boundary — the CloudSee API authorizes every operation server-side (CSD-572).\r\n\r\nexport const confirmShape = {\r\n confirm: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n \"Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes.\",\r\n ),\r\n};\r\n\r\nconst SECURITY_NOTE =\r\n \"Note: this confirmation is a client-side safety prompt, not the security boundary — \" +\r\n \"the CloudSee API authorizes every operation server-side.\";\r\n\r\n/** The no-op preview returned when a destructive tool is called without confirm=true. */\r\nexport function confirmationPreview(action: string, details: string): ToolResult {\r\n return textResult(\r\n `⚠️ Confirmation required — no changes have been made.\\n\\n` +\r\n `About to: ${action}\\n${details}\\n\\n` +\r\n `Re-run this tool with confirm=true to proceed.\\n${SECURITY_NOTE}`,\r\n );\r\n}\r\n","import type { ToolDef } from \"./types\";\r\nimport { readTools } from \"./read\";\r\nimport { downloadTools } from \"./download\";\r\nimport { writeTools } from \"./write\";\r\n\r\n/** The complete, grounded tool set (CSD-586 §2.4). Read + download tools are\r\n * callable today; write/delete tools are present but gated on CSD-572 RBAC. */\r\nexport const allTools: ToolDef[] = [...readTools, ...downloadTools, ...writeTools];\r\n\r\nexport type { ToolDef } from \"./types\";\r\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webapper/cloudsee-drive-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Open-source MCP server for CloudSee Drive — connect your CloudSee account (Amazon S3) to Claude Desktop and any MCP client with an API key.",
5
5
  "type": "module",
6
6
  "license": "MIT",