@mastra/server 1.1.0 → 1.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/{chunk-J3EJR43M.js → chunk-32IP4IQQ.js} +436 -25
  3. package/dist/chunk-32IP4IQQ.js.map +1 -0
  4. package/dist/{chunk-H4VFL22H.cjs → chunk-33WUKZ5N.cjs} +6 -6
  5. package/dist/{chunk-H4VFL22H.cjs.map → chunk-33WUKZ5N.cjs.map} +1 -1
  6. package/dist/{chunk-WWVDNKEF.cjs → chunk-ADNT2PDV.cjs} +6 -6
  7. package/dist/{chunk-WWVDNKEF.cjs.map → chunk-ADNT2PDV.cjs.map} +1 -1
  8. package/dist/{chunk-XXBURIYX.js → chunk-BEMGUJPC.js} +3 -3
  9. package/dist/{chunk-XXBURIYX.js.map → chunk-BEMGUJPC.js.map} +1 -1
  10. package/dist/{chunk-BWINB344.js → chunk-BV24MR2R.js} +3 -3
  11. package/dist/{chunk-BWINB344.js.map → chunk-BV24MR2R.js.map} +1 -1
  12. package/dist/{chunk-JUGGEWCR.js → chunk-CRGRPMWI.js} +3 -3
  13. package/dist/{chunk-JUGGEWCR.js.map → chunk-CRGRPMWI.js.map} +1 -1
  14. package/dist/{chunk-C4NZAUUW.js → chunk-G75NUKBO.js} +14 -6
  15. package/dist/chunk-G75NUKBO.js.map +1 -0
  16. package/dist/{chunk-I3CJUT6J.cjs → chunk-JANOIUVD.cjs} +4 -4
  17. package/dist/{chunk-I3CJUT6J.cjs.map → chunk-JANOIUVD.cjs.map} +1 -1
  18. package/dist/{chunk-ZFHZRI6C.js → chunk-JG46WVHC.js} +3 -3
  19. package/dist/{chunk-ZFHZRI6C.js.map → chunk-JG46WVHC.js.map} +1 -1
  20. package/dist/{chunk-7YCFHDWX.js → chunk-KPNL6ZFK.js} +20 -11
  21. package/dist/chunk-KPNL6ZFK.js.map +1 -0
  22. package/dist/{chunk-VXPVROQJ.cjs → chunk-MGJLH2AB.cjs} +5 -5
  23. package/dist/{chunk-VXPVROQJ.cjs.map → chunk-MGJLH2AB.cjs.map} +1 -1
  24. package/dist/{chunk-GDWZ2R7I.js → chunk-OBH5ZLNK.js} +73 -4
  25. package/dist/chunk-OBH5ZLNK.js.map +1 -0
  26. package/dist/{chunk-37DO73XV.cjs → chunk-QL3KET2C.cjs} +14 -6
  27. package/dist/chunk-QL3KET2C.cjs.map +1 -0
  28. package/dist/{chunk-4UNYZYBB.cjs → chunk-W7AZENC3.cjs} +84 -3
  29. package/dist/chunk-W7AZENC3.cjs.map +1 -0
  30. package/dist/{chunk-RWV4GKEY.cjs → chunk-WK2FRQEW.cjs} +480 -62
  31. package/dist/chunk-WK2FRQEW.cjs.map +1 -0
  32. package/dist/{chunk-6DHJKQLP.cjs → chunk-XC42BH7B.cjs} +21 -11
  33. package/dist/chunk-XC42BH7B.cjs.map +1 -0
  34. package/dist/docs/README.md +1 -1
  35. package/dist/docs/SKILL.md +1 -1
  36. package/dist/docs/SOURCE_MAP.json +1 -1
  37. package/dist/server/handlers/a2a.cjs +9 -9
  38. package/dist/server/handlers/a2a.js +1 -1
  39. package/dist/server/handlers/agent-builder.cjs +16 -16
  40. package/dist/server/handlers/agent-builder.js +1 -1
  41. package/dist/server/handlers/agents.cjs +35 -31
  42. package/dist/server/handlers/agents.d.ts +8 -0
  43. package/dist/server/handlers/agents.d.ts.map +1 -1
  44. package/dist/server/handlers/agents.js +1 -1
  45. package/dist/server/handlers/memory.d.ts +1 -1
  46. package/dist/server/handlers/scores.cjs +7 -7
  47. package/dist/server/handlers/scores.js +1 -1
  48. package/dist/server/handlers/tools.cjs +6 -6
  49. package/dist/server/handlers/tools.js +1 -1
  50. package/dist/server/handlers/voice.cjs +8 -8
  51. package/dist/server/handlers/voice.js +1 -1
  52. package/dist/server/handlers/workspace.cjs +47 -19
  53. package/dist/server/handlers/workspace.d.ts +265 -0
  54. package/dist/server/handlers/workspace.d.ts.map +1 -1
  55. package/dist/server/handlers/workspace.js +1 -1
  56. package/dist/server/handlers.cjs +12 -12
  57. package/dist/server/handlers.js +6 -6
  58. package/dist/server/schemas/memory.d.ts +2 -2
  59. package/dist/server/schemas/workspace.d.ts +255 -0
  60. package/dist/server/schemas/workspace.d.ts.map +1 -1
  61. package/dist/server/server-adapter/index.cjs +71 -69
  62. package/dist/server/server-adapter/index.cjs.map +1 -1
  63. package/dist/server/server-adapter/index.js +10 -8
  64. package/dist/server/server-adapter/index.js.map +1 -1
  65. package/dist/server/server-adapter/routes/workspace.d.ts.map +1 -1
  66. package/package.json +5 -5
  67. package/dist/chunk-37DO73XV.cjs.map +0 -1
  68. package/dist/chunk-4UNYZYBB.cjs.map +0 -1
  69. package/dist/chunk-6DHJKQLP.cjs.map +0 -1
  70. package/dist/chunk-7YCFHDWX.js.map +0 -1
  71. package/dist/chunk-C4NZAUUW.js.map +0 -1
  72. package/dist/chunk-GDWZ2R7I.js.map +0 -1
  73. package/dist/chunk-J3EJR43M.js.map +0 -1
  74. package/dist/chunk-RWV4GKEY.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @mastra/server
2
2
 
3
+ ## 1.2.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added skills.sh proxy endpoints for browsing, searching, and installing skills from the community registry. ([#12492](https://github.com/mastra-ai/mastra/pull/12492))
8
+
9
+ **New endpoints:**
10
+ - GET /api/workspaces/:id/skills-sh/search - Search skills
11
+ - GET /api/workspaces/:id/skills-sh/popular - Browse popular skills
12
+ - GET /api/workspaces/:id/skills-sh/preview - Preview skill SKILL.md content
13
+ - POST /api/workspaces/:id/skills-sh/install - Install a skill from GitHub
14
+ - POST /api/workspaces/:id/skills-sh/update - Update installed skills
15
+ - POST /api/workspaces/:id/skills-sh/remove - Remove an installed skill
16
+
17
+ ### Patch Changes
18
+
19
+ - Fixed custom gateway provider detection in Studio. ([#11815](https://github.com/mastra-ai/mastra/pull/11815))
20
+
21
+ **What changed:**
22
+ - Studio now correctly detects connected custom gateway providers (e.g., providers registered as `acme/custom` are now found when the agent uses model `acme/custom/gpt-4o`)
23
+ - The model selector properly displays and updates models for custom gateway providers
24
+ - "Enhance prompt" feature works correctly with custom gateway providers
25
+
26
+ **Why:**
27
+ Custom gateway providers are stored with a gateway prefix (e.g., `acme/custom`), but the model router extracts just the provider part (e.g., `custom`). The lookups were failing because they only did exact matching. Now both backend and frontend use fallback logic to find providers with gateway prefixes.
28
+
29
+ Fixes #11732
30
+
31
+ - Improved workspace filesystem error handling: return 404 for not-found errors instead of 500, show user-friendly error messages in UI, and add MastraClientError class with status/body properties for better error handling ([#12533](https://github.com/mastra-ai/mastra/pull/12533))
32
+
33
+ - Updated dependencies [[`e6fc281`](https://github.com/mastra-ai/mastra/commit/e6fc281896a3584e9e06465b356a44fe7faade65), [`97be6c8`](https://github.com/mastra-ai/mastra/commit/97be6c8963130fca8a664fcf99d7b3a38e463595), [`5fe1fe0`](https://github.com/mastra-ai/mastra/commit/5fe1fe0109faf2c87db34b725d8a4571a594f80e), [`f6673b8`](https://github.com/mastra-ai/mastra/commit/f6673b893b65b7d273ad25ead42e990704cc1e17), [`cd6be8a`](https://github.com/mastra-ai/mastra/commit/cd6be8ad32741cd41cabf508355bb31b71e8a5bd), [`9eb4e8e`](https://github.com/mastra-ai/mastra/commit/9eb4e8e39efbdcfff7a40ff2ce07ce2714c65fa8), [`aa37c84`](https://github.com/mastra-ai/mastra/commit/aa37c84d29b7db68c72517337932ef486c316275), [`47eba72`](https://github.com/mastra-ai/mastra/commit/47eba72f0397d0d14fbe324b97940c3d55e5a525)]:
34
+ - @mastra/core@1.2.0-alpha.0
35
+
3
36
  ## 1.1.0
4
37
 
5
38
  ### Minor Changes
@@ -1,9 +1,25 @@
1
- import { listWorkspacesResponseSchema, workspaceInfoResponseSchema, workspaceIdPathParams, fsReadResponseSchema, fsReadQuerySchema, fsWriteResponseSchema, fsWriteBodySchema, fsListResponseSchema, fsListQuerySchema, fsDeleteResponseSchema, fsDeleteQuerySchema, fsMkdirResponseSchema, fsMkdirBodySchema, fsStatResponseSchema, fsStatQuerySchema, searchResponseSchema, searchQuerySchema, indexResponseSchema, indexBodySchema, listSkillsResponseSchema, getSkillResponseSchema, skillNamePathParams, listReferencesResponseSchema, skillReferenceResponseSchema, skillReferencePathParams, searchSkillsResponseSchema, searchSkillsQuerySchema } from './chunk-GDWZ2R7I.js';
1
+ import { listWorkspacesResponseSchema, workspaceInfoResponseSchema, workspaceIdPathParams, fsReadResponseSchema, fsReadQuerySchema, fsWriteResponseSchema, fsWriteBodySchema, fsListResponseSchema, fsListQuerySchema, fsDeleteResponseSchema, fsDeleteQuerySchema, fsMkdirResponseSchema, fsMkdirBodySchema, fsStatResponseSchema, fsStatQuerySchema, searchResponseSchema, searchQuerySchema, indexResponseSchema, indexBodySchema, listSkillsResponseSchema, getSkillResponseSchema, skillNamePathParams, listReferencesResponseSchema, skillReferenceResponseSchema, skillReferencePathParams, searchSkillsResponseSchema, searchSkillsQuerySchema, skillsShSearchResponseSchema, skillsShSearchQuerySchema, skillsShListResponseSchema, skillsShPopularQuerySchema, skillsShPreviewResponseSchema, skillsShPreviewQuerySchema, skillsShInstallResponseSchema, skillsShInstallBodySchema, skillsShRemoveResponseSchema, skillsShRemoveBodySchema, skillsShUpdateResponseSchema, skillsShUpdateBodySchema } from './chunk-OBH5ZLNK.js';
2
2
  import { handleError } from './chunk-ONN5EA7E.js';
3
3
  import { createRoute } from './chunk-D5VCL56B.js';
4
4
  import { HTTPException } from './chunk-6QWQZI4Q.js';
5
5
  import { coreFeatures } from '@mastra/core/features';
6
6
 
7
+ function isFilesystemNotFoundError(error) {
8
+ if (!error || typeof error !== "object") return false;
9
+ if ("code" in error && error.code === "ENOENT") return true;
10
+ if ("name" in error) {
11
+ const name = error.name;
12
+ if (name === "FileNotFoundError" || name === "DirectoryNotFoundError") return true;
13
+ }
14
+ return false;
15
+ }
16
+ function handleWorkspaceError(error, defaultMessage) {
17
+ if (isFilesystemNotFoundError(error)) {
18
+ const message = error instanceof Error ? error.message : "Not found";
19
+ throw new HTTPException(404, { message });
20
+ }
21
+ return handleError(error, defaultMessage);
22
+ }
7
23
  function requireWorkspaceV1Support() {
8
24
  if (!coreFeatures.has("workspaces-v1")) {
9
25
  throw new HTTPException(501, {
@@ -103,7 +119,7 @@ var LIST_WORKSPACES_ROUTE = createRoute({
103
119
  }
104
120
  return { workspaces };
105
121
  } catch (error) {
106
- return handleError(error, "Error listing workspaces");
122
+ return handleWorkspaceError(error, "Error listing workspaces");
107
123
  }
108
124
  }
109
125
  });
@@ -142,7 +158,7 @@ var GET_WORKSPACE_ROUTE = createRoute({
142
158
  }
143
159
  };
144
160
  } catch (error) {
145
- return handleError(error, "Error getting workspace info");
161
+ return handleWorkspaceError(error, "Error getting workspace info");
146
162
  }
147
163
  }
148
164
  });
@@ -179,7 +195,7 @@ var WORKSPACE_FS_READ_ROUTE = createRoute({
179
195
  type: "file"
180
196
  };
181
197
  } catch (error) {
182
- return handleError(error, "Error reading file");
198
+ return handleWorkspaceError(error, "Error reading file");
183
199
  }
184
200
  }
185
201
  });
@@ -217,7 +233,7 @@ var WORKSPACE_FS_WRITE_ROUTE = createRoute({
217
233
  path: decodedPath
218
234
  };
219
235
  } catch (error) {
220
- return handleError(error, "Error writing file");
236
+ return handleWorkspaceError(error, "Error writing file");
221
237
  }
222
238
  }
223
239
  });
@@ -259,7 +275,7 @@ var WORKSPACE_FS_LIST_ROUTE = createRoute({
259
275
  }))
260
276
  };
261
277
  } catch (error) {
262
- return handleError(error, "Error listing directory");
278
+ return handleWorkspaceError(error, "Error listing directory");
263
279
  }
264
280
  }
265
281
  });
@@ -303,7 +319,7 @@ var WORKSPACE_FS_DELETE_ROUTE = createRoute({
303
319
  path: decodedPath
304
320
  };
305
321
  } catch (error) {
306
- return handleError(error, "Error deleting path");
322
+ return handleWorkspaceError(error, "Error deleting path");
307
323
  }
308
324
  }
309
325
  });
@@ -337,7 +353,7 @@ var WORKSPACE_FS_MKDIR_ROUTE = createRoute({
337
353
  path: decodedPath
338
354
  };
339
355
  } catch (error) {
340
- return handleError(error, "Error creating directory");
356
+ return handleWorkspaceError(error, "Error creating directory");
341
357
  }
342
358
  }
343
359
  });
@@ -375,7 +391,7 @@ var WORKSPACE_FS_STAT_ROUTE = createRoute({
375
391
  mimeType: stat.mimeType
376
392
  };
377
393
  } catch (error) {
378
- return handleError(error, "Error getting file info");
394
+ return handleWorkspaceError(error, "Error getting file info");
379
395
  }
380
396
  }
381
397
  });
@@ -438,7 +454,7 @@ var WORKSPACE_SEARCH_ROUTE = createRoute({
438
454
  mode: searchMode
439
455
  };
440
456
  } catch (error) {
441
- return handleError(error, "Error searching workspace");
457
+ return handleWorkspaceError(error, "Error searching workspace");
442
458
  }
443
459
  }
444
460
  });
@@ -472,7 +488,7 @@ var WORKSPACE_INDEX_ROUTE = createRoute({
472
488
  path
473
489
  };
474
490
  } catch (error) {
475
- return handleError(error, "Error indexing content");
491
+ return handleWorkspaceError(error, "Error indexing content");
476
492
  }
477
493
  }
478
494
  });
@@ -494,18 +510,30 @@ var WORKSPACE_LIST_SKILLS_ROUTE = createRoute({
494
510
  }
495
511
  await skills.maybeRefresh({ requestContext });
496
512
  const skillsList = await skills.list();
513
+ const skillsWithPath = await Promise.all(
514
+ skillsList.map(async (skillMeta) => {
515
+ let path = "";
516
+ try {
517
+ const fullSkill = await skills.get(skillMeta.name);
518
+ path = fullSkill?.path ?? "";
519
+ } catch {
520
+ }
521
+ return {
522
+ name: skillMeta.name,
523
+ description: skillMeta.description,
524
+ license: skillMeta.license,
525
+ compatibility: skillMeta.compatibility,
526
+ metadata: skillMeta.metadata,
527
+ path
528
+ };
529
+ })
530
+ );
497
531
  return {
498
- skills: skillsList.map((skill) => ({
499
- name: skill.name,
500
- description: skill.description,
501
- license: skill.license,
502
- compatibility: skill.compatibility,
503
- metadata: skill.metadata
504
- })),
532
+ skills: skillsWithPath,
505
533
  isSkillsConfigured: true
506
534
  };
507
535
  } catch (error) {
508
- return handleError(error, "Error listing skills");
536
+ return handleWorkspaceError(error, "Error listing skills");
509
537
  }
510
538
  }
511
539
  });
@@ -547,7 +575,7 @@ var WORKSPACE_GET_SKILL_ROUTE = createRoute({
547
575
  assets: skill.assets
548
576
  };
549
577
  } catch (error) {
550
- return handleError(error, "Error getting skill");
578
+ return handleWorkspaceError(error, "Error getting skill");
551
579
  }
552
580
  }
553
581
  });
@@ -581,7 +609,7 @@ var WORKSPACE_LIST_SKILL_REFERENCES_ROUTE = createRoute({
581
609
  references
582
610
  };
583
611
  } catch (error) {
584
- return handleError(error, "Error listing skill references");
612
+ return handleWorkspaceError(error, "Error listing skill references");
585
613
  }
586
614
  }
587
615
  });
@@ -616,7 +644,7 @@ var WORKSPACE_GET_SKILL_REFERENCE_ROUTE = createRoute({
616
644
  content
617
645
  };
618
646
  } catch (error) {
619
- return handleError(error, "Error getting skill reference");
647
+ return handleWorkspaceError(error, "Error getting skill reference");
620
648
  }
621
649
  }
622
650
  });
@@ -663,10 +691,393 @@ var WORKSPACE_SEARCH_SKILLS_ROUTE = createRoute({
663
691
  query
664
692
  };
665
693
  } catch (error) {
694
+ return handleWorkspaceError(error, "Error searching skills");
695
+ }
696
+ }
697
+ });
698
+ var SKILLS_SH_API_URL = "https://skills-api-production.up.railway.app";
699
+ var WORKSPACE_SKILLS_SH_SEARCH_ROUTE = createRoute({
700
+ method: "GET",
701
+ path: "/workspaces/:workspaceId/skills-sh/search",
702
+ responseType: "json",
703
+ pathParamSchema: workspaceIdPathParams,
704
+ queryParamSchema: skillsShSearchQuerySchema,
705
+ responseSchema: skillsShSearchResponseSchema,
706
+ summary: "Search skills on skills.sh",
707
+ description: "Proxies search requests to skills.sh API to avoid CORS issues",
708
+ tags: ["Workspace", "Skills"],
709
+ handler: async ({ q, limit }) => {
710
+ try {
711
+ const controller = new AbortController();
712
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
713
+ const url = `${SKILLS_SH_API_URL}/api/skills?query=${encodeURIComponent(q)}&pageSize=${limit}`;
714
+ const response = await fetch(url, { signal: controller.signal });
715
+ clearTimeout(timeoutId);
716
+ if (!response.ok) {
717
+ throw new HTTPException(502, {
718
+ message: `Skills API error: ${response.status} ${response.statusText}`
719
+ });
720
+ }
721
+ const data = await response.json();
722
+ return {
723
+ query: q,
724
+ searchType: "query",
725
+ skills: data.skills.map((s) => ({ id: s.skillId, name: s.name, installs: s.installs, topSource: s.source })),
726
+ count: data.total
727
+ };
728
+ } catch (error) {
729
+ if (error instanceof HTTPException) {
730
+ throw error;
731
+ }
666
732
  return handleError(error, "Error searching skills");
667
733
  }
668
734
  }
669
735
  });
736
+ var WORKSPACE_SKILLS_SH_POPULAR_ROUTE = createRoute({
737
+ method: "GET",
738
+ path: "/workspaces/:workspaceId/skills-sh/popular",
739
+ responseType: "json",
740
+ pathParamSchema: workspaceIdPathParams,
741
+ queryParamSchema: skillsShPopularQuerySchema,
742
+ responseSchema: skillsShListResponseSchema,
743
+ summary: "Get popular skills from skills.sh",
744
+ description: "Proxies popular skills requests to skills.sh API to avoid CORS issues",
745
+ tags: ["Workspace", "Skills"],
746
+ handler: async ({ limit, offset }) => {
747
+ try {
748
+ const controller = new AbortController();
749
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
750
+ const page = offset > 0 ? Math.floor(offset / limit) + 1 : 1;
751
+ const url = `${SKILLS_SH_API_URL}/api/skills/top?pageSize=${limit}&page=${page}`;
752
+ const response = await fetch(url, { signal: controller.signal });
753
+ clearTimeout(timeoutId);
754
+ if (!response.ok) {
755
+ throw new HTTPException(502, {
756
+ message: `Skills API error: ${response.status} ${response.statusText}`
757
+ });
758
+ }
759
+ const data = await response.json();
760
+ return {
761
+ skills: data.skills.map((s) => ({ id: s.skillId, name: s.name, installs: s.installs, topSource: s.source })),
762
+ count: data.total,
763
+ limit,
764
+ offset
765
+ };
766
+ } catch (error) {
767
+ if (error instanceof HTTPException) {
768
+ throw error;
769
+ }
770
+ return handleError(error, "Error fetching popular skills");
771
+ }
772
+ }
773
+ });
774
+ var SKILL_NAME_REGEX = /^[a-z0-9][a-z0-9-_]*$/i;
775
+ function assertSafeSkillName(name) {
776
+ if (!SKILL_NAME_REGEX.test(name)) {
777
+ throw new HTTPException(400, {
778
+ message: `Invalid skill name "${name}". Names must start with alphanumeric and contain only letters, numbers, hyphens, and underscores.`
779
+ });
780
+ }
781
+ return name;
782
+ }
783
+ function assertSafeFilePath(filePath) {
784
+ if (filePath.startsWith("/") || /^[a-zA-Z]:/.test(filePath)) {
785
+ throw new HTTPException(400, {
786
+ message: `Invalid file path "${filePath}". Absolute paths are not allowed.`
787
+ });
788
+ }
789
+ const segments = filePath.split("/");
790
+ for (const segment of segments) {
791
+ if (segment === ".." || segment === ".") {
792
+ throw new HTTPException(400, {
793
+ message: `Invalid file path "${filePath}". Path traversal is not allowed.`
794
+ });
795
+ }
796
+ }
797
+ return filePath;
798
+ }
799
+ async function fetchSkillFiles(owner, repo, skillName) {
800
+ const controller = new AbortController();
801
+ const timeoutId = setTimeout(() => controller.abort(), 3e4);
802
+ const url = `${SKILLS_SH_API_URL}/api/skills/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(skillName)}/files`;
803
+ const response = await fetch(url, { signal: controller.signal });
804
+ clearTimeout(timeoutId);
805
+ if (!response.ok) {
806
+ if (response.status === 404) {
807
+ return null;
808
+ }
809
+ throw new Error(`Skills API error: ${response.status} ${response.statusText}`);
810
+ }
811
+ return await response.json();
812
+ }
813
+ var WORKSPACE_SKILLS_SH_PREVIEW_ROUTE = createRoute({
814
+ method: "GET",
815
+ path: "/workspaces/:workspaceId/skills-sh/preview",
816
+ responseType: "json",
817
+ pathParamSchema: workspaceIdPathParams,
818
+ queryParamSchema: skillsShPreviewQuerySchema,
819
+ responseSchema: skillsShPreviewResponseSchema,
820
+ summary: "Preview skill content",
821
+ description: "Fetches the skill content from the Skills API.",
822
+ tags: ["Workspace", "Skills"],
823
+ handler: async ({ owner, repo, path: skillName }) => {
824
+ try {
825
+ const controller = new AbortController();
826
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
827
+ const url = `${SKILLS_SH_API_URL}/api/skills/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(skillName)}/content`;
828
+ const response = await fetch(url, { signal: controller.signal });
829
+ clearTimeout(timeoutId);
830
+ if (!response.ok) {
831
+ throw new HTTPException(404, {
832
+ message: `Could not find skill "${skillName}" for ${owner}/${repo}`
833
+ });
834
+ }
835
+ const data = await response.json();
836
+ const content = data.instructions || data.raw || "";
837
+ if (!content) {
838
+ throw new HTTPException(404, {
839
+ message: `No content available for skill "${skillName}"`
840
+ });
841
+ }
842
+ return { content };
843
+ } catch (error) {
844
+ if (error instanceof HTTPException) {
845
+ throw error;
846
+ }
847
+ return handleError(error, "Error fetching skill preview");
848
+ }
849
+ }
850
+ });
851
+ var WORKSPACE_SKILLS_SH_INSTALL_ROUTE = createRoute({
852
+ method: "POST",
853
+ path: "/workspaces/:workspaceId/skills-sh/install",
854
+ responseType: "json",
855
+ pathParamSchema: workspaceIdPathParams,
856
+ bodySchema: skillsShInstallBodySchema,
857
+ responseSchema: skillsShInstallResponseSchema,
858
+ summary: "Install skill from Skills API",
859
+ description: "Installs a skill by fetching files from the Skills API and writing to workspace filesystem.",
860
+ tags: ["Workspace", "Skills"],
861
+ handler: async ({ mastra, workspaceId, owner, repo, skillName }) => {
862
+ try {
863
+ requireWorkspaceV1Support();
864
+ const workspace = await getWorkspaceById(mastra, workspaceId);
865
+ if (!workspace) {
866
+ throw new HTTPException(404, { message: "Workspace not found" });
867
+ }
868
+ if (!workspace.filesystem) {
869
+ throw new HTTPException(400, { message: "Workspace filesystem not available" });
870
+ }
871
+ if (workspace.filesystem.readOnly) {
872
+ throw new HTTPException(403, { message: "Workspace is read-only" });
873
+ }
874
+ const result = await fetchSkillFiles(owner, repo, skillName);
875
+ if (!result || result.files.length === 0) {
876
+ throw new HTTPException(404, {
877
+ message: `Could not find skill "${skillName}" in ${owner}/${repo}.`
878
+ });
879
+ }
880
+ const safeSkillId = assertSafeSkillName(result.skillId);
881
+ const installPath = `.agents/skills/${safeSkillId}`;
882
+ try {
883
+ await workspace.filesystem.mkdir(installPath, { recursive: true });
884
+ } catch {
885
+ }
886
+ let filesWritten = 0;
887
+ for (const file of result.files) {
888
+ const safePath = assertSafeFilePath(file.path);
889
+ const filePath = `${installPath}/${safePath}`;
890
+ if (safePath.includes("/")) {
891
+ const dirPath = filePath.substring(0, filePath.lastIndexOf("/"));
892
+ try {
893
+ await workspace.filesystem.mkdir(dirPath, { recursive: true });
894
+ } catch {
895
+ }
896
+ }
897
+ const content = file.encoding === "base64" ? Buffer.from(file.content, "base64") : file.content;
898
+ await workspace.filesystem.writeFile(filePath, content);
899
+ filesWritten++;
900
+ }
901
+ const metadata = {
902
+ skillName: result.skillId,
903
+ owner: result.owner,
904
+ repo: result.repo,
905
+ branch: result.branch,
906
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
907
+ };
908
+ await workspace.filesystem.writeFile(`${installPath}/.meta.json`, JSON.stringify(metadata, null, 2));
909
+ filesWritten++;
910
+ return {
911
+ success: true,
912
+ skillName: result.skillId,
913
+ installedPath: installPath,
914
+ filesWritten
915
+ };
916
+ } catch (error) {
917
+ if (error instanceof HTTPException) {
918
+ throw error;
919
+ }
920
+ return handleError(error, "Error installing skill");
921
+ }
922
+ }
923
+ });
924
+ var WORKSPACE_SKILLS_SH_REMOVE_ROUTE = createRoute({
925
+ method: "POST",
926
+ path: "/workspaces/:workspaceId/skills-sh/remove",
927
+ responseType: "json",
928
+ pathParamSchema: workspaceIdPathParams,
929
+ bodySchema: skillsShRemoveBodySchema,
930
+ responseSchema: skillsShRemoveResponseSchema,
931
+ summary: "Remove an installed skill",
932
+ description: "Removes an installed skill by deleting its directory. Does not require sandbox.",
933
+ tags: ["Workspace", "Skills"],
934
+ handler: async ({ mastra, workspaceId, skillName }) => {
935
+ try {
936
+ requireWorkspaceV1Support();
937
+ const workspace = await getWorkspaceById(mastra, workspaceId);
938
+ if (!workspace) {
939
+ throw new HTTPException(404, { message: "Workspace not found" });
940
+ }
941
+ if (!workspace.filesystem) {
942
+ throw new HTTPException(400, { message: "Workspace filesystem not available" });
943
+ }
944
+ if (workspace.filesystem.readOnly) {
945
+ throw new HTTPException(403, { message: "Workspace is read-only" });
946
+ }
947
+ const safeSkillName = assertSafeSkillName(skillName);
948
+ const skillPath = `.agents/skills/${safeSkillName}`;
949
+ try {
950
+ await workspace.filesystem.stat(skillPath);
951
+ } catch {
952
+ throw new HTTPException(404, { message: `Skill "${skillName}" not found at ${skillPath}` });
953
+ }
954
+ await workspace.filesystem.rmdir(skillPath, { recursive: true });
955
+ return {
956
+ success: true,
957
+ skillName,
958
+ removedPath: skillPath
959
+ };
960
+ } catch (error) {
961
+ if (error instanceof HTTPException) {
962
+ throw error;
963
+ }
964
+ return handleError(error, "Error removing skill");
965
+ }
966
+ }
967
+ });
968
+ var WORKSPACE_SKILLS_SH_UPDATE_ROUTE = createRoute({
969
+ method: "POST",
970
+ path: "/workspaces/:workspaceId/skills-sh/update",
971
+ responseType: "json",
972
+ pathParamSchema: workspaceIdPathParams,
973
+ bodySchema: skillsShUpdateBodySchema,
974
+ responseSchema: skillsShUpdateResponseSchema,
975
+ summary: "Update installed skills",
976
+ description: "Updates installed skills by re-fetching from GitHub. Specify skillName to update one, or omit to update all.",
977
+ tags: ["Workspace", "Skills"],
978
+ handler: async ({ mastra, workspaceId, skillName }) => {
979
+ try {
980
+ requireWorkspaceV1Support();
981
+ const workspace = await getWorkspaceById(mastra, workspaceId);
982
+ if (!workspace) {
983
+ throw new HTTPException(404, { message: "Workspace not found" });
984
+ }
985
+ if (!workspace.filesystem) {
986
+ throw new HTTPException(400, { message: "Workspace filesystem not available" });
987
+ }
988
+ if (workspace.filesystem.readOnly) {
989
+ throw new HTTPException(403, { message: "Workspace is read-only" });
990
+ }
991
+ const skillsPath = ".agents/skills";
992
+ const results = [];
993
+ let skillsToUpdate;
994
+ if (skillName) {
995
+ skillsToUpdate = [assertSafeSkillName(skillName)];
996
+ } else {
997
+ try {
998
+ const entries = await workspace?.filesystem?.readdir(skillsPath);
999
+ skillsToUpdate = entries?.filter((e) => e.type === "directory").map((e) => e.name) ?? [];
1000
+ } catch {
1001
+ return { updated: [] };
1002
+ }
1003
+ }
1004
+ for (const skill of skillsToUpdate) {
1005
+ try {
1006
+ assertSafeSkillName(skill);
1007
+ } catch {
1008
+ results.push({
1009
+ skillName: skill,
1010
+ success: false,
1011
+ error: "Invalid skill name"
1012
+ });
1013
+ continue;
1014
+ }
1015
+ const metaPath = `${skillsPath}/${skill}/.meta.json`;
1016
+ try {
1017
+ const metaContent = await workspace?.filesystem?.readFile(metaPath, { encoding: "utf-8" });
1018
+ const meta = JSON.parse(metaContent);
1019
+ const fetchResult = await fetchSkillFiles(meta.owner, meta.repo, meta.skillName);
1020
+ if (!fetchResult || fetchResult.files.length === 0) {
1021
+ results.push({
1022
+ skillName: skill,
1023
+ success: false,
1024
+ error: "No files found in skill directory"
1025
+ });
1026
+ continue;
1027
+ }
1028
+ const installPath = `${skillsPath}/${skill}`;
1029
+ let filesWritten = 0;
1030
+ for (const file of fetchResult.files) {
1031
+ const safePath = assertSafeFilePath(file.path);
1032
+ const filePath = `${installPath}/${safePath}`;
1033
+ if (safePath.includes("/")) {
1034
+ const dirPath = filePath.substring(0, filePath.lastIndexOf("/"));
1035
+ try {
1036
+ await workspace.filesystem.mkdir(dirPath, { recursive: true });
1037
+ } catch {
1038
+ }
1039
+ }
1040
+ const content = file.encoding === "base64" ? Buffer.from(file.content, "base64") : file.content;
1041
+ await workspace.filesystem.writeFile(filePath, content);
1042
+ filesWritten++;
1043
+ }
1044
+ const updatedMeta = {
1045
+ ...meta,
1046
+ branch: fetchResult.branch,
1047
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1048
+ };
1049
+ await workspace.filesystem.writeFile(metaPath, JSON.stringify(updatedMeta, null, 2));
1050
+ filesWritten++;
1051
+ results.push({
1052
+ skillName: skill,
1053
+ success: true,
1054
+ filesWritten
1055
+ });
1056
+ } catch (error) {
1057
+ results.push({
1058
+ skillName: skill,
1059
+ success: false,
1060
+ error: error instanceof Error ? error.message : "Unknown error"
1061
+ });
1062
+ }
1063
+ }
1064
+ return { updated: results };
1065
+ } catch (error) {
1066
+ if (error instanceof HTTPException) {
1067
+ throw error;
1068
+ }
1069
+ return handleError(error, "Error updating skills");
1070
+ }
1071
+ }
1072
+ });
1073
+ var WORKSPACE_SKILLS_SH_ROUTES = [
1074
+ WORKSPACE_SKILLS_SH_SEARCH_ROUTE,
1075
+ WORKSPACE_SKILLS_SH_POPULAR_ROUTE,
1076
+ WORKSPACE_SKILLS_SH_PREVIEW_ROUTE,
1077
+ WORKSPACE_SKILLS_SH_INSTALL_ROUTE,
1078
+ WORKSPACE_SKILLS_SH_REMOVE_ROUTE,
1079
+ WORKSPACE_SKILLS_SH_UPDATE_ROUTE
1080
+ ];
670
1081
  var WORKSPACE_FS_ROUTES = [
671
1082
  WORKSPACE_FS_READ_ROUTE,
672
1083
  WORKSPACE_FS_WRITE_ROUTE,
@@ -684,6 +1095,6 @@ var WORKSPACE_SKILLS_ROUTES = [
684
1095
  WORKSPACE_GET_SKILL_REFERENCE_ROUTE
685
1096
  ];
686
1097
 
687
- export { GET_WORKSPACE_ROUTE, LIST_WORKSPACES_ROUTE, WORKSPACE_FS_DELETE_ROUTE, WORKSPACE_FS_LIST_ROUTE, WORKSPACE_FS_MKDIR_ROUTE, WORKSPACE_FS_READ_ROUTE, WORKSPACE_FS_ROUTES, WORKSPACE_FS_STAT_ROUTE, WORKSPACE_FS_WRITE_ROUTE, WORKSPACE_GET_SKILL_REFERENCE_ROUTE, WORKSPACE_GET_SKILL_ROUTE, WORKSPACE_INDEX_ROUTE, WORKSPACE_LIST_SKILLS_ROUTE, WORKSPACE_LIST_SKILL_REFERENCES_ROUTE, WORKSPACE_SEARCH_ROUTE, WORKSPACE_SEARCH_ROUTES, WORKSPACE_SEARCH_SKILLS_ROUTE, WORKSPACE_SKILLS_ROUTES };
688
- //# sourceMappingURL=chunk-J3EJR43M.js.map
689
- //# sourceMappingURL=chunk-J3EJR43M.js.map
1098
+ export { GET_WORKSPACE_ROUTE, LIST_WORKSPACES_ROUTE, WORKSPACE_FS_DELETE_ROUTE, WORKSPACE_FS_LIST_ROUTE, WORKSPACE_FS_MKDIR_ROUTE, WORKSPACE_FS_READ_ROUTE, WORKSPACE_FS_ROUTES, WORKSPACE_FS_STAT_ROUTE, WORKSPACE_FS_WRITE_ROUTE, WORKSPACE_GET_SKILL_REFERENCE_ROUTE, WORKSPACE_GET_SKILL_ROUTE, WORKSPACE_INDEX_ROUTE, WORKSPACE_LIST_SKILLS_ROUTE, WORKSPACE_LIST_SKILL_REFERENCES_ROUTE, WORKSPACE_SEARCH_ROUTE, WORKSPACE_SEARCH_ROUTES, WORKSPACE_SEARCH_SKILLS_ROUTE, WORKSPACE_SKILLS_ROUTES, WORKSPACE_SKILLS_SH_INSTALL_ROUTE, WORKSPACE_SKILLS_SH_POPULAR_ROUTE, WORKSPACE_SKILLS_SH_PREVIEW_ROUTE, WORKSPACE_SKILLS_SH_REMOVE_ROUTE, WORKSPACE_SKILLS_SH_ROUTES, WORKSPACE_SKILLS_SH_SEARCH_ROUTE, WORKSPACE_SKILLS_SH_UPDATE_ROUTE };
1099
+ //# sourceMappingURL=chunk-32IP4IQQ.js.map
1100
+ //# sourceMappingURL=chunk-32IP4IQQ.js.map