@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.
- package/CHANGELOG.md +33 -0
- package/dist/{chunk-J3EJR43M.js → chunk-32IP4IQQ.js} +436 -25
- package/dist/chunk-32IP4IQQ.js.map +1 -0
- package/dist/{chunk-H4VFL22H.cjs → chunk-33WUKZ5N.cjs} +6 -6
- package/dist/{chunk-H4VFL22H.cjs.map → chunk-33WUKZ5N.cjs.map} +1 -1
- package/dist/{chunk-WWVDNKEF.cjs → chunk-ADNT2PDV.cjs} +6 -6
- package/dist/{chunk-WWVDNKEF.cjs.map → chunk-ADNT2PDV.cjs.map} +1 -1
- package/dist/{chunk-XXBURIYX.js → chunk-BEMGUJPC.js} +3 -3
- package/dist/{chunk-XXBURIYX.js.map → chunk-BEMGUJPC.js.map} +1 -1
- package/dist/{chunk-BWINB344.js → chunk-BV24MR2R.js} +3 -3
- package/dist/{chunk-BWINB344.js.map → chunk-BV24MR2R.js.map} +1 -1
- package/dist/{chunk-JUGGEWCR.js → chunk-CRGRPMWI.js} +3 -3
- package/dist/{chunk-JUGGEWCR.js.map → chunk-CRGRPMWI.js.map} +1 -1
- package/dist/{chunk-C4NZAUUW.js → chunk-G75NUKBO.js} +14 -6
- package/dist/chunk-G75NUKBO.js.map +1 -0
- package/dist/{chunk-I3CJUT6J.cjs → chunk-JANOIUVD.cjs} +4 -4
- package/dist/{chunk-I3CJUT6J.cjs.map → chunk-JANOIUVD.cjs.map} +1 -1
- package/dist/{chunk-ZFHZRI6C.js → chunk-JG46WVHC.js} +3 -3
- package/dist/{chunk-ZFHZRI6C.js.map → chunk-JG46WVHC.js.map} +1 -1
- package/dist/{chunk-7YCFHDWX.js → chunk-KPNL6ZFK.js} +20 -11
- package/dist/chunk-KPNL6ZFK.js.map +1 -0
- package/dist/{chunk-VXPVROQJ.cjs → chunk-MGJLH2AB.cjs} +5 -5
- package/dist/{chunk-VXPVROQJ.cjs.map → chunk-MGJLH2AB.cjs.map} +1 -1
- package/dist/{chunk-GDWZ2R7I.js → chunk-OBH5ZLNK.js} +73 -4
- package/dist/chunk-OBH5ZLNK.js.map +1 -0
- package/dist/{chunk-37DO73XV.cjs → chunk-QL3KET2C.cjs} +14 -6
- package/dist/chunk-QL3KET2C.cjs.map +1 -0
- package/dist/{chunk-4UNYZYBB.cjs → chunk-W7AZENC3.cjs} +84 -3
- package/dist/chunk-W7AZENC3.cjs.map +1 -0
- package/dist/{chunk-RWV4GKEY.cjs → chunk-WK2FRQEW.cjs} +480 -62
- package/dist/chunk-WK2FRQEW.cjs.map +1 -0
- package/dist/{chunk-6DHJKQLP.cjs → chunk-XC42BH7B.cjs} +21 -11
- package/dist/chunk-XC42BH7B.cjs.map +1 -0
- package/dist/docs/README.md +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/SOURCE_MAP.json +1 -1
- package/dist/server/handlers/a2a.cjs +9 -9
- package/dist/server/handlers/a2a.js +1 -1
- package/dist/server/handlers/agent-builder.cjs +16 -16
- package/dist/server/handlers/agent-builder.js +1 -1
- package/dist/server/handlers/agents.cjs +35 -31
- package/dist/server/handlers/agents.d.ts +8 -0
- package/dist/server/handlers/agents.d.ts.map +1 -1
- package/dist/server/handlers/agents.js +1 -1
- package/dist/server/handlers/memory.d.ts +1 -1
- package/dist/server/handlers/scores.cjs +7 -7
- package/dist/server/handlers/scores.js +1 -1
- package/dist/server/handlers/tools.cjs +6 -6
- package/dist/server/handlers/tools.js +1 -1
- package/dist/server/handlers/voice.cjs +8 -8
- package/dist/server/handlers/voice.js +1 -1
- package/dist/server/handlers/workspace.cjs +47 -19
- package/dist/server/handlers/workspace.d.ts +265 -0
- package/dist/server/handlers/workspace.d.ts.map +1 -1
- package/dist/server/handlers/workspace.js +1 -1
- package/dist/server/handlers.cjs +12 -12
- package/dist/server/handlers.js +6 -6
- package/dist/server/schemas/memory.d.ts +2 -2
- package/dist/server/schemas/workspace.d.ts +255 -0
- package/dist/server/schemas/workspace.d.ts.map +1 -1
- package/dist/server/server-adapter/index.cjs +71 -69
- package/dist/server/server-adapter/index.cjs.map +1 -1
- package/dist/server/server-adapter/index.js +10 -8
- package/dist/server/server-adapter/index.js.map +1 -1
- package/dist/server/server-adapter/routes/workspace.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-37DO73XV.cjs.map +0 -1
- package/dist/chunk-4UNYZYBB.cjs.map +0 -1
- package/dist/chunk-6DHJKQLP.cjs.map +0 -1
- package/dist/chunk-7YCFHDWX.js.map +0 -1
- package/dist/chunk-C4NZAUUW.js.map +0 -1
- package/dist/chunk-GDWZ2R7I.js.map +0 -1
- package/dist/chunk-J3EJR43M.js.map +0 -1
- 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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
689
|
-
//# sourceMappingURL=chunk-
|
|
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
|