openpond 0.0.36 → 0.0.37
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 +4 -0
- package/dist/chunks/{app-layer-4AA4YYGK.js → app-layer-LF2HK2B2.js} +6 -3
- package/dist/chunks/{apps-V2JTB5MD.js → apps-ZMCMQZKX.js} +16 -12
- package/dist/chunks/{chunk-NVTC3XOA.js → chunk-2WMGA5FE.js} +8 -10
- package/dist/chunks/{chunk-6L5WJF7T.js → chunk-35PPAOWD.js} +2 -2
- package/dist/chunks/chunk-3VURZQHA.js +864 -0
- package/dist/chunks/{chunk-3DOOLZZM.js → chunk-43YI36NA.js} +1 -1
- package/dist/chunks/{chunk-JAS6QZ2W.js → chunk-A3CJUBA7.js} +1 -1
- package/dist/chunks/{chunk-NWVIUBJ3.js → chunk-BWA3COR4.js} +573 -1204
- package/dist/chunks/{chunk-G6WW2656.js → chunk-HLCTINEP.js} +1 -1
- package/dist/chunks/{chunk-65BVQIHR.js → chunk-IATK3CIP.js} +6 -6
- package/dist/chunks/{chunk-MSX2J653.js → chunk-ICLLW5QD.js} +1 -1
- package/dist/chunks/chunk-KSRWKXRB.js +12 -0
- package/dist/chunks/{chunk-GALAW74P.js → chunk-KT2BB4AR.js} +30 -11
- package/dist/chunks/{chunk-NWQTF6O4.js → chunk-LNKYEHIJ.js} +28 -3
- package/dist/chunks/{chunk-7IPF4X3K.js → chunk-MHBQWGCL.js} +1003 -56
- package/dist/chunks/chunk-MUMPWI4O.js +92 -0
- package/dist/chunks/{chunk-CJSD5ADV.js → chunk-N6E6YSTP.js} +2 -2
- package/dist/chunks/{chunk-TYYR7KWY.js → chunk-PGET7EZF.js} +7 -5
- package/dist/chunks/chunk-T4QXKU5Y.js +1 -0
- package/dist/chunks/{chunk-YLULZMWE.js → chunk-VFL2JVBZ.js} +48 -121
- package/dist/chunks/{chunk-MJT7EW5Q.js → chunk-WL76II47.js} +1 -1
- package/dist/chunks/{chunk-3JJBNVNX.js → chunk-WTA33KQQ.js} +23 -19
- package/dist/chunks/{chunk-LHYK6QO2.js → chunk-XEJ7XXUQ.js} +1 -0
- package/dist/chunks/{chunk-YRFQKUZD.js → chunk-ZPL4GZML.js} +88 -3
- package/dist/chunks/chunk-ZZ4ECDTP.js +668 -0
- package/dist/chunks/{cli-RJHEE4FA.js → cli-5NFIDCRL.js} +5 -5
- package/dist/chunks/cli-AS3DZ7XB.js +23 -0
- package/dist/chunks/{core-commands-CQSPCVC4.js → core-commands-7223BEQL.js} +7 -4
- package/dist/chunks/{extend-4MZ75SHV.js → extend-6N5S5KA5.js} +18 -13
- package/dist/chunks/extension-FJSVCG2Q.js +121 -0
- package/dist/chunks/{harness-YVZU42S7.js → harness-OMJXIGEW.js} +6 -3
- package/dist/chunks/{help-6SULYXVS.js → help-YPPROIS7.js} +2 -1
- package/dist/chunks/{opchat-XGS4AXHA.js → opchat-K7IYFPX6.js} +12 -7
- package/dist/chunks/{organizations-4GFQ3NT2.js → organizations-W3FMAWQ4.js} +8 -5
- package/dist/chunks/{profile-VWMBIHIE.js → profile-N4B3F7M7.js} +11 -8
- package/dist/chunks/{project-agent-SQ6ODALG.js → project-agent-NA6SGU25.js} +10 -7
- package/dist/chunks/{sandbox-command-34S2V7BN.js → sandbox-command-AKCFJKKN.js} +7 -4
- package/dist/chunks/{sandbox-template-VP5HMGQI.js → sandbox-template-5NKTAXCL.js} +11 -7
- package/dist/chunks/{src-X3R3XIJT.js → src-SCS4KKP4.js} +1997 -1247
- package/dist/chunks/{src-YHUMH6W6.js → src-XAVSQTVR.js} +3 -3
- package/dist/chunks/{teams-bot-EIVRNO5S.js → teams-bot-557NZC73.js} +6 -3
- package/dist/chunks/{workspaces-2N52V3BI.js → workspaces-S2H75XQB.js} +6 -2
- package/dist/cli.js +14 -11
- package/dist/index.js +27 -9
- package/dist/profile/local-profile-types.d.ts +1 -0
- package/dist/profile/profile-skills.d.ts +2 -0
- package/dist/web/assets/{AppDialog-j4N-gIi3.js → AppDialog-y6krB0MJ.js} +1 -1
- package/dist/web/assets/{AppsView-YiK3pqNb.js → AppsView-4F6oLktr.js} +1 -1
- package/dist/web/assets/{BrowserSidebar-BWFLEQ8j.js → BrowserSidebar-C0MHQaaD.js} +1 -1
- package/dist/web/assets/{CloudWorkView-3sS_1bhN.js → CloudWorkView-DwFIZoXy.js} +1 -1
- package/dist/web/assets/{CommandMenu-B11OrTCq.js → CommandMenu-CYGFrMZi.js} +1 -1
- package/dist/web/assets/{CommunityView-DXyXTWhP.js → CommunityView-C6n89YXz.js} +1 -1
- package/dist/web/assets/{ComposerCreateImproveStrip-3NoRiJJq.js → ComposerCreateImproveStrip-C3RwXC3_.js} +1 -1
- package/dist/web/assets/{GetStartedView-CPV5lgVH.js → GetStartedView-fwD0ZXo9.js} +1 -1
- package/dist/web/assets/LabSkillSidebar-BSuc-NJr.js +1 -0
- package/dist/web/assets/LabSkillSidebar-CVxtS9pd.css +1 -0
- package/dist/web/assets/LabsRoute-BrJkBNig.css +1 -0
- package/dist/web/assets/LabsRoute-D2y8dSsT.js +3 -0
- package/dist/web/assets/MainChatThread-Dj_Ry6WQ.js +2 -0
- package/dist/web/assets/{MakeAgentTutorialLearningPanel-C6F-kJQd.js → MakeAgentTutorialLearningPanel-Bz2VDaRe.js} +2 -2
- package/dist/web/assets/{MarkdownText-CkVkr3P1.js → MarkdownText-B6Nu_0Fz.js} +4 -4
- package/dist/web/assets/Messages-CH2wfv2a.js +10 -0
- package/dist/web/assets/NativeSkillSidebar-4iUfgwqO.js +1 -0
- package/dist/web/assets/{NewProjectDialog-DBMhkTvD.js → NewProjectDialog-8M5-6-h-.js} +1 -1
- package/dist/web/assets/{PostTrainingLearningPanel-B1Rp7iIL.js → PostTrainingLearningPanel-CdG5KHZs.js} +2 -2
- package/dist/web/assets/{PostTrainingStatusPill-BGZ9h5eW.js → PostTrainingStatusPill-COZ4qISQ.js} +1 -1
- package/dist/web/assets/{ProfileSettingsSection-2NOHEz2b.js → ProfileSettingsSection-Bo7dybgW.js} +1 -1
- package/dist/web/assets/RightChatPanelStack-Di8zoHJI.js +1 -0
- package/dist/web/assets/{SettingsView-uHhQnazW.css → SettingsView-BcYyY66X.css} +1 -1
- package/dist/web/assets/SettingsView-CAaKdDHt.js +6 -0
- package/dist/web/assets/{TeamChatView-qVscSedQ.js → TeamChatView-CzQYFBap.js} +2 -2
- package/dist/web/assets/{TerminalOverlay-Bkw7XqMy.js → TerminalOverlay-B56XmI-k.js} +1 -1
- package/dist/web/assets/{TrainingCreationPanel-COg1wvw8.js → TrainingCreationPanel-C_ntWAAP.js} +1 -1
- package/dist/web/assets/{TrainingDraftPanel-BNK2znmI.js → TrainingDraftPanel-3hJIxLNn.js} +1 -1
- package/dist/web/assets/{UsageSettingsSection-CUxBaohC.js → UsageSettingsSection-r3CqRm6Q.js} +1 -1
- package/dist/web/assets/WorkspaceDiffPanel-By8PWUuE.js +14 -0
- package/dist/web/assets/{WorkspaceEnvironmentMenu-C0-mH7oF.js → WorkspaceEnvironmentMenu-7wmC-spz.js} +1 -1
- package/dist/web/assets/{WorkspaceGitDialogs-VvC2px0X.js → WorkspaceGitDialogs-Hn--lsRh.js} +1 -1
- package/dist/web/assets/{WorkspaceMonacoEditor-CUIwoHv1.js → WorkspaceMonacoEditor-CHd22UcG.js} +3 -3
- package/dist/web/assets/{activity-BhqHgkfJ.js → activity-Dp5V58om.js} +1 -1
- package/dist/web/assets/{boxes-BpqUKFIm.js → boxes-DE7WJaEv.js} +1 -1
- package/dist/web/assets/{chevron-up-BTn9DVRP.js → chevron-up-BWx8gGab.js} +1 -1
- package/dist/web/assets/{circle-x-B9Qa8o5a.js → circle-x-DnmOfZIg.js} +1 -1
- package/dist/web/assets/{copy-BsYs6wBI.js → copy-BjWvaXLC.js} +1 -1
- package/dist/web/assets/{create-pipeline-request-vxgKoAvv.js → create-pipeline-request-C4V6-lWN.js} +1 -1
- package/dist/web/assets/{credit-card-BCePAwMI.js → credit-card-Bjg0Zaw5.js} +1 -1
- package/dist/web/assets/{cssMode-mw8RI2fR.js → cssMode-KFDYjM8T.js} +1 -1
- package/dist/web/assets/file-BrC1KjAS.js +1 -0
- package/dist/web/assets/{git-branch-C7qQYFIs.js → git-branch-DTeeARsp.js} +1 -1
- package/dist/web/assets/{git-commit-horizontal-CbX4pTOA.js → git-commit-horizontal-C8UFKlXy.js} +1 -1
- package/dist/web/assets/{htmlMode-Cc4Czh_M.js → htmlMode-NbLN3n6Q.js} +1 -1
- package/dist/web/assets/index-BcZetaeD.css +1 -0
- package/dist/web/assets/index-DQalqxbw.js +177 -0
- package/dist/web/assets/index-DYxF_d9i.js +1 -0
- package/dist/web/assets/{info-BNGbwgtq.js → info-BXBpMeVU.js} +1 -1
- package/dist/web/assets/{jsonMode-CbqbqGVz.js → jsonMode-Bl3r4LM_.js} +1 -1
- package/dist/web/assets/{lspLanguageFeatures-DKiKr5tH.js → lspLanguageFeatures-DH22w5XD.js} +1 -1
- package/dist/web/assets/{make-agent-tutorial-KKauJpB9.js → make-agent-tutorial-CAnTPFCK.js} +1 -1
- package/dist/web/assets/{monaco.contribution-D_-wu578.js → monaco.contribution-C0BsxYAz.js} +2 -2
- package/dist/web/assets/{monaco.contribution-CdUAX47Q.js → monaco.contribution-CzpXjm9j.js} +2 -2
- package/dist/web/assets/{monaco.contribution-Dxc4IxFM.js → monaco.contribution-DK-g-i00.js} +2 -2
- package/dist/web/assets/{monaco.contribution-CNgwz_pw.js → monaco.contribution-iAurj7TQ.js} +2 -2
- package/dist/web/assets/{monitor-CNdx_GTg.js → monitor-Ba2FFblh.js} +1 -1
- package/dist/web/assets/{play-BLRANWaV.js → play-2NDQ4-7K.js} +1 -1
- package/dist/web/assets/{python-DYgDMu2R.js → python-CQ0TmUW9.js} +1 -1
- package/dist/web/assets/{refresh-cw-BFjjwYNf.js → refresh-cw-CyermMsy.js} +1 -1
- package/dist/web/assets/{reply-D012cuZl.js → reply-BSExNLHM.js} +1 -1
- package/dist/web/assets/{rotate-cw-CTfjlR4G.js → rotate-cw-DJ-zYG9E.js} +1 -1
- package/dist/web/assets/{save-B1zYlRyN.js → save-Bij3cc3Q.js} +1 -1
- package/dist/web/assets/{toggleHighContrast-D9XFH7EA.js → toggleHighContrast-CxALivAH.js} +1 -1
- package/dist/web/assets/{tsMode-BU-tuKsY.js → tsMode-vH4lMS7h.js} +1 -1
- package/dist/web/assets/{upload-DU_81jvL.js → upload-DbJfEI3G.js} +1 -1
- package/dist/web/assets/{wifi-off-TPsBnbGW.js → wifi-off-BnVFBG28.js} +1 -1
- package/dist/web/assets/{workers-NhWySRdT.js → workers-CBihj7z6.js} +1 -1
- package/dist/web/assets/{yaml-DWXa_3ZL.js → yaml-Bhkoq1sy.js} +1 -1
- package/dist/web/index.html +2 -2
- package/docs/command-reference.md +15 -0
- package/package.json +1 -1
- package/dist/chunks/cli-4JA3SIDE.js +0 -19
- package/dist/web/assets/LabsRoute-B_nsEZXy.js +0 -3
- package/dist/web/assets/LabsRoute-dNN-odU0.css +0 -1
- package/dist/web/assets/MainChatThread-Dg7p3R2O.js +0 -2
- package/dist/web/assets/Messages-C7uHGoFw.js +0 -10
- package/dist/web/assets/NativeSkillSidebar-BPpoHjEW.js +0 -1
- package/dist/web/assets/RightChatPanelStack-CLK7_zKe.js +0 -1
- package/dist/web/assets/SettingsView-BAqea_KX.js +0 -6
- package/dist/web/assets/WorkspaceDiffPanel-qfFj8IRq.js +0 -14
- package/dist/web/assets/index-9tbdqUau.css +0 -1
- package/dist/web/assets/index-B_3z_lxq.js +0 -1
- package/dist/web/assets/index-Cr_RZ7GD.js +0 -177
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SandboxTemplateManifestSchema,
|
|
6
6
|
SandboxTemplateValidationDiagnosticSchema,
|
|
7
7
|
validateConnectedAppToolCallRequest
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XEJ7XXUQ.js";
|
|
9
9
|
import {
|
|
10
10
|
external_exports,
|
|
11
11
|
yaml
|
|
@@ -113,7 +113,9 @@ async function readProfileSkill(input) {
|
|
|
113
113
|
path: skill.path,
|
|
114
114
|
sourcePath: skill.sourcePath,
|
|
115
115
|
sourceHash: skill.sourceHash,
|
|
116
|
-
charCount: skill.charCount
|
|
116
|
+
charCount: skill.charCount,
|
|
117
|
+
packagePath: path.dirname(absolutePath),
|
|
118
|
+
resourceFiles: skill.resourceFiles
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
function emptyProfileSkillCatalogState(input = {}) {
|
|
@@ -128,12 +130,8 @@ async function loadProfileSkillDirectory(profileSourcePath2, directoryName) {
|
|
|
128
130
|
const relativePath = path.join(PROFILE_SKILLS_DIR, directoryName, PROFILE_SKILL_FILE).replace(/\\/g, "/");
|
|
129
131
|
const absolutePath = path.join(profileSourcePath2, relativePath);
|
|
130
132
|
const messages = [];
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
if (entry.name === PROFILE_SKILL_FILE && entry.isFile())
|
|
134
|
-
continue;
|
|
135
|
-
messages.push(`Profile skills only support ${PROFILE_SKILL_FILE}; unsupported entry: ${entry.name}`);
|
|
136
|
-
}
|
|
133
|
+
const packageRoot = path.join(profileSourcePath2, PROFILE_SKILLS_DIR, directoryName);
|
|
134
|
+
const packageFiles = await listPackageFiles(packageRoot);
|
|
137
135
|
let contents = "";
|
|
138
136
|
if (!existsSync(absolutePath)) {
|
|
139
137
|
messages.push(`Missing ${PROFILE_SKILL_FILE}.`);
|
|
@@ -163,9 +161,26 @@ async function loadProfileSkillDirectory(profileSourcePath2, directoryName) {
|
|
|
163
161
|
charCount: contents.length,
|
|
164
162
|
sourceHash: sha256Hex(contents),
|
|
165
163
|
validationStatus: messages.length > 0 ? "error" : "valid",
|
|
166
|
-
validationMessages: messages
|
|
164
|
+
validationMessages: messages,
|
|
165
|
+
resourceFiles: packageFiles.filter((file) => file !== PROFILE_SKILL_FILE)
|
|
167
166
|
};
|
|
168
167
|
}
|
|
168
|
+
async function listPackageFiles(root) {
|
|
169
|
+
const files = [];
|
|
170
|
+
async function visit(directory, prefix) {
|
|
171
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
172
|
+
await Promise.all(entries.map(async (entry) => {
|
|
173
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
174
|
+
if (entry.isDirectory()) {
|
|
175
|
+
await visit(path.join(directory, entry.name), relativePath);
|
|
176
|
+
} else if (entry.isFile()) {
|
|
177
|
+
files.push(relativePath);
|
|
178
|
+
}
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
await visit(root, "");
|
|
182
|
+
return files.sort();
|
|
183
|
+
}
|
|
169
184
|
function markDuplicateSkillNames(skills) {
|
|
170
185
|
const counts = /* @__PURE__ */ new Map();
|
|
171
186
|
for (const skill of skills) {
|
|
@@ -185,10 +200,14 @@ function markDuplicateSkillNames(skills) {
|
|
|
185
200
|
async function latestSkillMtime(profileSourcePath2, skills) {
|
|
186
201
|
let latest = 0;
|
|
187
202
|
for (const skill of skills) {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
const packageRoot = path.dirname(path.join(profileSourcePath2, skill.path));
|
|
204
|
+
const packageFiles = [PROFILE_SKILL_FILE, ...skill.resourceFiles];
|
|
205
|
+
for (const file of packageFiles) {
|
|
206
|
+
const fileStat = await stat(path.join(packageRoot, file)).catch(() => null);
|
|
207
|
+
if (!fileStat?.isFile())
|
|
208
|
+
continue;
|
|
209
|
+
latest = Math.max(latest, fileStat.mtimeMs);
|
|
210
|
+
}
|
|
192
211
|
}
|
|
193
212
|
return latest > 0 ? new Date(latest).toISOString() : null;
|
|
194
213
|
}
|
|
@@ -2899,11 +2918,11 @@ function parseProfileSkillCommand(prompt) {
|
|
|
2899
2918
|
if (subcommand === "create") {
|
|
2900
2919
|
if (!args)
|
|
2901
2920
|
return { action: "help" };
|
|
2902
|
-
const
|
|
2921
|
+
const named2 = splitOptionalSkillName(args);
|
|
2903
2922
|
return {
|
|
2904
2923
|
action: "create",
|
|
2905
|
-
objective:
|
|
2906
|
-
requestedName:
|
|
2924
|
+
objective: named2.objective,
|
|
2925
|
+
requestedName: named2.name
|
|
2907
2926
|
};
|
|
2908
2927
|
}
|
|
2909
2928
|
if (subcommand === "edit") {
|
|
@@ -2914,7 +2933,12 @@ function parseProfileSkillCommand(prompt) {
|
|
|
2914
2933
|
return { action: "help" };
|
|
2915
2934
|
return { action: "edit", name, changeRequest };
|
|
2916
2935
|
}
|
|
2917
|
-
|
|
2936
|
+
const named = splitOptionalSkillName(rest);
|
|
2937
|
+
return {
|
|
2938
|
+
action: "create",
|
|
2939
|
+
objective: named.objective,
|
|
2940
|
+
requestedName: named.name
|
|
2941
|
+
};
|
|
2918
2942
|
}
|
|
2919
2943
|
return null;
|
|
2920
2944
|
}
|
|
@@ -2931,7 +2955,7 @@ function profileSkillHelp(profile) {
|
|
|
2931
2955
|
"- /skill create --name <skill-name> <what the skill should help with>",
|
|
2932
2956
|
"- /skill edit <skill-name> <change request>",
|
|
2933
2957
|
"",
|
|
2934
|
-
"Create/edit starts a profile-skill goal in the active profile repo. Skills
|
|
2958
|
+
"Create/edit starts a profile-skill goal in the active profile repo. Skills may include SKILL.md plus focused scripts, references, and assets. Use an agent when the workflow needs durable autonomy, schedules, identity, or its own model loop."
|
|
2935
2959
|
].join("\n")
|
|
2936
2960
|
};
|
|
2937
2961
|
}
|
|
@@ -3053,10 +3077,11 @@ function profileSkillGoalPrompt(goal) {
|
|
|
3053
3077
|
"",
|
|
3054
3078
|
"Rules:",
|
|
3055
3079
|
modeLine,
|
|
3056
|
-
"- Keep the
|
|
3080
|
+
"- Keep SKILL.md as the required entry point. Add only scripts, references, assets, or agents/openai.yaml that directly support the workflow.",
|
|
3057
3081
|
"- SKILL.md must include YAML frontmatter with required name and description fields, followed by a markdown body.",
|
|
3058
3082
|
"- The body should describe the reusable workflow clearly enough for future chats to apply it.",
|
|
3059
|
-
"-
|
|
3083
|
+
"- Use scripts for deterministic repeated operations, references for detailed knowledge, and assets for files consumed by outputs.",
|
|
3084
|
+
"- Do not add README, changelog, installation guide, or other auxiliary documentation.",
|
|
3060
3085
|
"- Validate the resulting file by reading it back and checking name, description, body, and path consistency.",
|
|
3061
3086
|
"- Report the final skill name, path, validation status, and explicit invocation such as $skill-name only after the file is written or updated.",
|
|
3062
3087
|
"- Ask only for missing details that materially change the skill.",
|
|
@@ -3543,13 +3568,13 @@ function createSandboxRuntimeHandle(client, runtimeId, initial = null) {
|
|
|
3543
3568
|
keepAlive,
|
|
3544
3569
|
checkpoint: (input) => client.checkpointSandboxRuntime(runtimeId, input),
|
|
3545
3570
|
files: {
|
|
3546
|
-
write: async (
|
|
3547
|
-
read: async (
|
|
3571
|
+
write: async (path13, contents) => client.uploadFile((await resume()).id, path13, contents),
|
|
3572
|
+
read: async (path13) => client.downloadFile((await resume()).id, path13),
|
|
3548
3573
|
readResponse: async (input) => client.downloadFileResponse((await resume()).id, input),
|
|
3549
3574
|
list: async (input = {}) => client.listFiles((await resume()).id, input),
|
|
3550
|
-
delete: async (
|
|
3575
|
+
delete: async (path13, input = {}) => client.deleteFile((await resume()).id, path13, input),
|
|
3551
3576
|
search: async (input) => client.searchFiles((await resume()).id, input),
|
|
3552
|
-
stat: async (
|
|
3577
|
+
stat: async (path13) => client.statFile((await resume()).id, path13),
|
|
3553
3578
|
mkdir: async (input) => client.mkdir((await resume()).id, input),
|
|
3554
3579
|
move: async (input) => client.moveFile((await resume()).id, input)
|
|
3555
3580
|
},
|
|
@@ -4053,20 +4078,20 @@ var OpenPondSandboxInstanceClient = class {
|
|
|
4053
4078
|
path: `/${encodeURIComponent(sandboxId)}/pty/${encodeURIComponent(ptyId)}/stream${query.size > 0 ? `?${query.toString()}` : ""}`
|
|
4054
4079
|
});
|
|
4055
4080
|
}
|
|
4056
|
-
uploadFile(sandboxId,
|
|
4057
|
-
return this.uploadFileBase64(sandboxId,
|
|
4081
|
+
uploadFile(sandboxId, path13, contents) {
|
|
4082
|
+
return this.uploadFileBase64(sandboxId, path13, Buffer.from(contents, "utf-8").toString("base64"));
|
|
4058
4083
|
}
|
|
4059
|
-
uploadFileBase64(sandboxId,
|
|
4084
|
+
uploadFileBase64(sandboxId, path13, contentsBase64) {
|
|
4060
4085
|
return this.request(`/${encodeURIComponent(sandboxId)}/files`, {
|
|
4061
4086
|
method: "POST",
|
|
4062
4087
|
body: JSON.stringify({
|
|
4063
|
-
path:
|
|
4088
|
+
path: path13,
|
|
4064
4089
|
contentsBase64
|
|
4065
4090
|
})
|
|
4066
4091
|
});
|
|
4067
4092
|
}
|
|
4068
|
-
async downloadFile(sandboxId,
|
|
4069
|
-
const payload = await this.downloadFileResponse(sandboxId,
|
|
4093
|
+
async downloadFile(sandboxId, path13) {
|
|
4094
|
+
const payload = await this.downloadFileResponse(sandboxId, path13);
|
|
4070
4095
|
return Buffer.from(payload.file.contentsBase64, "base64").toString("utf-8");
|
|
4071
4096
|
}
|
|
4072
4097
|
downloadFileResponse(sandboxId, input) {
|
|
@@ -4090,16 +4115,16 @@ var OpenPondSandboxInstanceClient = class {
|
|
|
4090
4115
|
query.set("maxEntries", String(input.maxEntries));
|
|
4091
4116
|
return this.request(`/${encodeURIComponent(sandboxId)}/files?${query.toString()}`);
|
|
4092
4117
|
}
|
|
4093
|
-
deleteFile(sandboxId,
|
|
4094
|
-
const query = new URLSearchParams({ path:
|
|
4118
|
+
deleteFile(sandboxId, path13, input = {}) {
|
|
4119
|
+
const query = new URLSearchParams({ path: path13 });
|
|
4095
4120
|
if (input.recursive !== void 0)
|
|
4096
4121
|
query.set("recursive", String(input.recursive));
|
|
4097
4122
|
return this.request(`/${encodeURIComponent(sandboxId)}/files?${query.toString()}`, {
|
|
4098
4123
|
method: "DELETE"
|
|
4099
4124
|
});
|
|
4100
4125
|
}
|
|
4101
|
-
statFile(sandboxId,
|
|
4102
|
-
const query = new URLSearchParams({ stat: "1", path:
|
|
4126
|
+
statFile(sandboxId, path13) {
|
|
4127
|
+
const query = new URLSearchParams({ stat: "1", path: path13 });
|
|
4103
4128
|
return this.request(`/${encodeURIComponent(sandboxId)}/files?${query.toString()}`);
|
|
4104
4129
|
}
|
|
4105
4130
|
mkdir(sandboxId, input) {
|
|
@@ -4275,12 +4300,12 @@ var OpenPondSandboxInstanceClient = class {
|
|
|
4275
4300
|
body: JSON.stringify({ leaseId })
|
|
4276
4301
|
});
|
|
4277
4302
|
}
|
|
4278
|
-
async request(
|
|
4279
|
-
const response = await apiFetch(this.sandboxApiUrl, this.apiKey,
|
|
4303
|
+
async request(path13, init = {}) {
|
|
4304
|
+
const response = await apiFetch(this.sandboxApiUrl, this.apiKey, path13, init);
|
|
4280
4305
|
return readApiJson(response, "Sandbox request");
|
|
4281
4306
|
}
|
|
4282
|
-
async requestApiRoot(
|
|
4283
|
-
const response = await apiFetch(this.apiRootUrl, this.apiKey,
|
|
4307
|
+
async requestApiRoot(path13, init = {}) {
|
|
4308
|
+
const response = await apiFetch(this.apiRootUrl, this.apiKey, path13, init);
|
|
4284
4309
|
return readApiJson(response, "OpenPond API request");
|
|
4285
4310
|
}
|
|
4286
4311
|
};
|
|
@@ -5019,8 +5044,841 @@ function responsePayloadData(payload) {
|
|
|
5019
5044
|
return null;
|
|
5020
5045
|
}
|
|
5021
5046
|
|
|
5047
|
+
// ../../packages/cloud/dist/extensions/types.js
|
|
5048
|
+
var GithubExtensionError = class extends Error {
|
|
5049
|
+
code;
|
|
5050
|
+
status;
|
|
5051
|
+
constructor(message, options) {
|
|
5052
|
+
super(message);
|
|
5053
|
+
this.name = "GithubExtensionError";
|
|
5054
|
+
this.code = options.code;
|
|
5055
|
+
this.status = options.status ?? 400;
|
|
5056
|
+
}
|
|
5057
|
+
};
|
|
5058
|
+
|
|
5059
|
+
// ../../packages/cloud/dist/extensions/github-source.js
|
|
5060
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
5061
|
+
import path10 from "node:path";
|
|
5062
|
+
var DEFAULT_REF = "HEAD";
|
|
5063
|
+
var MAX_EXTENSION_SKILLS = 64;
|
|
5064
|
+
var MAX_EXTENSION_FILES = 512;
|
|
5065
|
+
var MAX_EXTENSION_BYTES = 20 * 1024 * 1024;
|
|
5066
|
+
var MAX_EXTENSION_FILE_BYTES = 5 * 1024 * 1024;
|
|
5067
|
+
var DOWNLOAD_CONCURRENCY = 8;
|
|
5068
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
5069
|
+
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
5070
|
+
var ROOT_SKILL_RESOURCE_DIRECTORIES = /* @__PURE__ */ new Set(["agents", "assets", "references", "scripts"]);
|
|
5071
|
+
function createGithubSourceClient(options = {}) {
|
|
5072
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
5073
|
+
const githubToken = options.githubToken?.trim() || process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || null;
|
|
5074
|
+
return {
|
|
5075
|
+
async resolve(request) {
|
|
5076
|
+
const source = await resolveSource(request, fetchImpl, githubToken);
|
|
5077
|
+
const preview = await buildPreview(source, fetchImpl, githubToken);
|
|
5078
|
+
return {
|
|
5079
|
+
preview,
|
|
5080
|
+
files: filesToDownload(source)
|
|
5081
|
+
};
|
|
5082
|
+
},
|
|
5083
|
+
async download(resolution, writeFile5) {
|
|
5084
|
+
await downloadFiles({
|
|
5085
|
+
resolution,
|
|
5086
|
+
fetchImpl,
|
|
5087
|
+
githubToken,
|
|
5088
|
+
writeFile: writeFile5
|
|
5089
|
+
});
|
|
5090
|
+
}
|
|
5091
|
+
};
|
|
5092
|
+
}
|
|
5093
|
+
function parseGithubExtensionSource(source) {
|
|
5094
|
+
const trimmed = source.trim();
|
|
5095
|
+
let owner = "";
|
|
5096
|
+
let repo = "";
|
|
5097
|
+
if (/^(?:https?:\/\/)?github\.com\//i.test(trimmed)) {
|
|
5098
|
+
const url = new URL(trimmed.startsWith("http") ? trimmed : `https://${trimmed}`);
|
|
5099
|
+
if (url.hostname.toLowerCase() !== "github.com") {
|
|
5100
|
+
throw invalidSource(source);
|
|
5101
|
+
}
|
|
5102
|
+
const segments = url.pathname.replace(/^\/+|\/+$/g, "").split("/");
|
|
5103
|
+
if (segments.length !== 2)
|
|
5104
|
+
throw invalidSource(source);
|
|
5105
|
+
[owner = "", repo = ""] = segments;
|
|
5106
|
+
} else {
|
|
5107
|
+
const normalized = trimmed.replace(/^github:/i, "").replace(/^\/+|\/+$/g, "");
|
|
5108
|
+
const segments = normalized.split("/");
|
|
5109
|
+
if (segments.length !== 2)
|
|
5110
|
+
throw invalidSource(source);
|
|
5111
|
+
[owner = "", repo = ""] = segments;
|
|
5112
|
+
}
|
|
5113
|
+
repo = repo.replace(/\.git$/i, "");
|
|
5114
|
+
if (!GITHUB_REPOSITORY_PATTERN.test(owner) || !GITHUB_REPOSITORY_PATTERN.test(repo)) {
|
|
5115
|
+
throw invalidSource(source);
|
|
5116
|
+
}
|
|
5117
|
+
const normalizedOwner = owner.toLowerCase();
|
|
5118
|
+
const normalizedRepo = repo.toLowerCase();
|
|
5119
|
+
return {
|
|
5120
|
+
id: `github:${normalizedOwner}/${normalizedRepo}`,
|
|
5121
|
+
owner: normalizedOwner,
|
|
5122
|
+
repo: normalizedRepo,
|
|
5123
|
+
repositoryUrl: `https://github.com/${normalizedOwner}/${normalizedRepo}`
|
|
5124
|
+
};
|
|
5125
|
+
}
|
|
5126
|
+
async function resolveSource(request, fetchImpl, githubToken) {
|
|
5127
|
+
const identity = parseGithubExtensionSource(request.source);
|
|
5128
|
+
const requestedRef = request.ref?.trim() || DEFAULT_REF;
|
|
5129
|
+
const repository = await githubJson(fetchImpl, githubToken, `/repos/${identity.owner}/${identity.repo}`);
|
|
5130
|
+
const resolvedRef = requestedRef === DEFAULT_REF ? repository.default_branch?.trim() || "main" : requestedRef;
|
|
5131
|
+
const commit = await githubJson(fetchImpl, githubToken, `/repos/${identity.owner}/${identity.repo}/commits/${encodeURIComponent(resolvedRef)}`);
|
|
5132
|
+
const resolvedCommit = commit.sha?.trim();
|
|
5133
|
+
const treeSha = commit.commit?.tree?.sha?.trim();
|
|
5134
|
+
if (!resolvedCommit || !treeSha) {
|
|
5135
|
+
throw new GithubExtensionError("GitHub returned an incomplete commit response.", {
|
|
5136
|
+
code: "github_invalid_commit",
|
|
5137
|
+
status: 502
|
|
5138
|
+
});
|
|
5139
|
+
}
|
|
5140
|
+
const treePayload = await githubJson(fetchImpl, githubToken, `/repos/${identity.owner}/${identity.repo}/git/trees/${treeSha}?recursive=1`);
|
|
5141
|
+
if (treePayload.truncated) {
|
|
5142
|
+
throw new GithubExtensionError("The GitHub repository tree is too large to install safely.", {
|
|
5143
|
+
code: "github_tree_truncated"
|
|
5144
|
+
});
|
|
5145
|
+
}
|
|
5146
|
+
const tree = (treePayload.tree ?? []).filter(validTreeEntry);
|
|
5147
|
+
const skillPackages = discoverSkillPackages(tree);
|
|
5148
|
+
if (skillPackages.length === 0) {
|
|
5149
|
+
throw new GithubExtensionError("No skills found. Add SKILL.md at the repository root or use a standard agent skills directory.", { code: "extension_no_skills" });
|
|
5150
|
+
}
|
|
5151
|
+
if (skillPackages.length > MAX_EXTENSION_SKILLS) {
|
|
5152
|
+
throw new GithubExtensionError(`Extension contains more than ${MAX_EXTENSION_SKILLS} skills.`, {
|
|
5153
|
+
code: "extension_too_many_skills"
|
|
5154
|
+
});
|
|
5155
|
+
}
|
|
5156
|
+
validateDownloadLimits(skillPackages);
|
|
5157
|
+
const readme = tree.find((entry) => entry.type === "blob" && /^readme(?:\.[^/]+)?$/i.test(entry.path)) ?? null;
|
|
5158
|
+
return { identity, requestedRef, resolvedCommit, tree, skillPackages, readme };
|
|
5159
|
+
}
|
|
5160
|
+
async function buildPreview(source, fetchImpl, githubToken) {
|
|
5161
|
+
const skills = await mapConcurrent(source.skillPackages, DOWNLOAD_CONCURRENCY, async (skillPackage) => {
|
|
5162
|
+
const contents = await downloadText(source, skillPackage.entry, fetchImpl, githubToken);
|
|
5163
|
+
return previewSkill(skillPackage, contents);
|
|
5164
|
+
});
|
|
5165
|
+
const duplicateNames = duplicateValues(skills.map((skill) => skill.name));
|
|
5166
|
+
for (const skill of skills) {
|
|
5167
|
+
if (!duplicateNames.has(skill.name))
|
|
5168
|
+
continue;
|
|
5169
|
+
skill.validationStatus = "error";
|
|
5170
|
+
skill.validationMessages = [...skill.validationMessages, `Duplicate skill name: ${skill.name}`];
|
|
5171
|
+
}
|
|
5172
|
+
const validationMessages = skills.flatMap((skill) => skill.validationMessages.map((message) => `${skill.relativePath}: ${message}`));
|
|
5173
|
+
const packageFiles = filesToDownload(source);
|
|
5174
|
+
return {
|
|
5175
|
+
...source.identity,
|
|
5176
|
+
source: "github",
|
|
5177
|
+
requestedRef: source.requestedRef,
|
|
5178
|
+
resolvedCommit: source.resolvedCommit,
|
|
5179
|
+
sourcePath: null,
|
|
5180
|
+
readmePath: source.readme?.path ?? null,
|
|
5181
|
+
packageHash: hashTree(packageFiles),
|
|
5182
|
+
skills: skills.sort((left, right) => left.name.localeCompare(right.name)),
|
|
5183
|
+
validationStatus: validationMessages.length === 0 ? "valid" : "error",
|
|
5184
|
+
validationMessages
|
|
5185
|
+
};
|
|
5186
|
+
}
|
|
5187
|
+
function previewSkill(skillPackage, contents) {
|
|
5188
|
+
const parsed = parseProfileSkillMarkdown(contents);
|
|
5189
|
+
const directoryName = skillPackage.root === "." ? null : path10.posix.basename(skillPackage.root);
|
|
5190
|
+
const name = parsed.name ?? directoryName ?? "invalid-skill";
|
|
5191
|
+
const messages = [...parsed.messages];
|
|
5192
|
+
if (!SKILL_NAME_PATTERN.test(name))
|
|
5193
|
+
messages.push("Skill name must be lowercase kebab-case.");
|
|
5194
|
+
if (directoryName && name !== directoryName) {
|
|
5195
|
+
messages.push(`Skill name must match its directory name (${directoryName}).`);
|
|
5196
|
+
}
|
|
5197
|
+
const resourceFiles = skillPackage.files.filter((file) => file.path !== skillPackage.entry.path).map((file) => relativeSkillPath(skillPackage.root, file.path)).sort();
|
|
5198
|
+
return {
|
|
5199
|
+
name,
|
|
5200
|
+
description: parsed.description ?? "",
|
|
5201
|
+
relativePath: skillPackage.root === "." ? "SKILL.md" : `${skillPackage.root}/SKILL.md`,
|
|
5202
|
+
sourcePath: "",
|
|
5203
|
+
charCount: contents.length,
|
|
5204
|
+
sourceHash: createHash3("sha256").update(contents).digest("hex"),
|
|
5205
|
+
resourceFiles,
|
|
5206
|
+
validationStatus: messages.length === 0 ? "valid" : "error",
|
|
5207
|
+
validationMessages: [...new Set(messages)]
|
|
5208
|
+
};
|
|
5209
|
+
}
|
|
5210
|
+
function discoverSkillPackages(tree) {
|
|
5211
|
+
const entries = [];
|
|
5212
|
+
const rootEntry = tree.find((entry) => entry.type === "blob" && entry.path === "SKILL.md");
|
|
5213
|
+
if (rootEntry)
|
|
5214
|
+
entries.push({ root: ".", entry: rootEntry });
|
|
5215
|
+
const skillEntries = tree.filter((entry) => entry.type === "blob" && entry.path.endsWith("/SKILL.md"));
|
|
5216
|
+
const standardEntries = skillEntries.filter((entry) => isStandardAgentSkillsPath(entry.path));
|
|
5217
|
+
const discoveredEntries = standardEntries.length > 0 ? standardEntries : skillEntries;
|
|
5218
|
+
const roots = new Set(discoveredEntries.map((entry) => path10.posix.dirname(entry.path)));
|
|
5219
|
+
for (const entry of discoveredEntries) {
|
|
5220
|
+
const root = path10.posix.dirname(entry.path);
|
|
5221
|
+
if ([...roots].some((candidate) => candidate !== root && root.startsWith(`${candidate}/`)))
|
|
5222
|
+
continue;
|
|
5223
|
+
entries.push({ root, entry });
|
|
5224
|
+
}
|
|
5225
|
+
return entries.map(({ root, entry }) => {
|
|
5226
|
+
const files = tree.filter((candidate) => {
|
|
5227
|
+
if (candidate.type !== "blob")
|
|
5228
|
+
return false;
|
|
5229
|
+
if (root !== ".")
|
|
5230
|
+
return candidate.path.startsWith(`${root}/`);
|
|
5231
|
+
if (candidate.path === "SKILL.md")
|
|
5232
|
+
return true;
|
|
5233
|
+
const firstSegment = candidate.path.split("/", 1)[0] ?? "";
|
|
5234
|
+
return ROOT_SKILL_RESOURCE_DIRECTORIES.has(firstSegment);
|
|
5235
|
+
});
|
|
5236
|
+
const unsafe = tree.find((candidate) => candidate.type !== "blob" && candidate.type !== "tree" && (root === "." ? candidate.path === "SKILL.md" || ROOT_SKILL_RESOURCE_DIRECTORIES.has(candidate.path.split("/", 1)[0] ?? "") : candidate.path.startsWith(`${root}/`)));
|
|
5237
|
+
const symlink2 = files.find((candidate) => candidate.mode === "120000");
|
|
5238
|
+
if (unsafe || symlink2) {
|
|
5239
|
+
throw new GithubExtensionError(`Skill package ${root} contains a symlink or submodule.`, {
|
|
5240
|
+
code: "extension_unsafe_entry"
|
|
5241
|
+
});
|
|
5242
|
+
}
|
|
5243
|
+
return { root, entry, files };
|
|
5244
|
+
});
|
|
5245
|
+
}
|
|
5246
|
+
function isStandardAgentSkillsPath(skillPath) {
|
|
5247
|
+
const segments = skillPath.split("/");
|
|
5248
|
+
if (segments.at(-1) !== "SKILL.md")
|
|
5249
|
+
return false;
|
|
5250
|
+
const skillDirectorySegments = segments.slice(0, -1);
|
|
5251
|
+
if (skillDirectorySegments.length < 2)
|
|
5252
|
+
return false;
|
|
5253
|
+
const skillsIndex = skillDirectorySegments.lastIndexOf("skills");
|
|
5254
|
+
if (skillsIndex < 0 || skillsIndex > 2)
|
|
5255
|
+
return false;
|
|
5256
|
+
const containerPrefix = skillDirectorySegments.slice(0, skillsIndex);
|
|
5257
|
+
const nestedSkillPath = skillDirectorySegments.slice(skillsIndex + 1);
|
|
5258
|
+
if (nestedSkillPath.length < 1 || nestedSkillPath.length > 2)
|
|
5259
|
+
return false;
|
|
5260
|
+
if (skillsIndex === 0)
|
|
5261
|
+
return true;
|
|
5262
|
+
const first = containerPrefix[0] ?? "";
|
|
5263
|
+
return first.startsWith(".") || first === "agent" || first === "data";
|
|
5264
|
+
}
|
|
5265
|
+
function filesToDownload(source) {
|
|
5266
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5267
|
+
if (source.readme)
|
|
5268
|
+
byPath.set(source.readme.path, source.readme);
|
|
5269
|
+
for (const skillPackage of source.skillPackages) {
|
|
5270
|
+
for (const file of skillPackage.files)
|
|
5271
|
+
byPath.set(file.path, file);
|
|
5272
|
+
}
|
|
5273
|
+
return [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
5274
|
+
}
|
|
5275
|
+
async function downloadFiles(input) {
|
|
5276
|
+
let downloadedBytes = 0;
|
|
5277
|
+
await mapConcurrent(input.resolution.files, DOWNLOAD_CONCURRENCY, async (file) => {
|
|
5278
|
+
const bytes = await downloadBytes({
|
|
5279
|
+
owner: input.resolution.preview.owner,
|
|
5280
|
+
repo: input.resolution.preview.repo,
|
|
5281
|
+
commit: input.resolution.preview.resolvedCommit,
|
|
5282
|
+
file,
|
|
5283
|
+
fetchImpl: input.fetchImpl,
|
|
5284
|
+
githubToken: input.githubToken
|
|
5285
|
+
});
|
|
5286
|
+
downloadedBytes += bytes.byteLength;
|
|
5287
|
+
if (downloadedBytes > MAX_EXTENSION_BYTES) {
|
|
5288
|
+
throw new GithubExtensionError("Extension download exceeds the maximum allowed size.", {
|
|
5289
|
+
code: "extension_too_large"
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5292
|
+
await input.writeFile(file.path, bytes, file.mode === "100755");
|
|
5293
|
+
});
|
|
5294
|
+
}
|
|
5295
|
+
async function downloadText(source, file, fetchImpl, githubToken) {
|
|
5296
|
+
const bytes = await downloadBytes({
|
|
5297
|
+
owner: source.identity.owner,
|
|
5298
|
+
repo: source.identity.repo,
|
|
5299
|
+
commit: source.resolvedCommit,
|
|
5300
|
+
file,
|
|
5301
|
+
fetchImpl,
|
|
5302
|
+
githubToken
|
|
5303
|
+
});
|
|
5304
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
5305
|
+
}
|
|
5306
|
+
async function downloadBytes(input) {
|
|
5307
|
+
const encodedPath = input.file.path.split("/").map(encodeURIComponent).join("/");
|
|
5308
|
+
const url = `https://raw.githubusercontent.com/${input.owner}/${input.repo}/${input.commit}/${encodedPath}`;
|
|
5309
|
+
const response = await input.fetchImpl(url, {
|
|
5310
|
+
headers: githubHeaders(input.githubToken, "application/vnd.github.raw"),
|
|
5311
|
+
redirect: "follow"
|
|
5312
|
+
});
|
|
5313
|
+
if (!response.ok) {
|
|
5314
|
+
throw new GithubExtensionError(`GitHub file download failed (${response.status}): ${input.file.path}`, {
|
|
5315
|
+
code: "github_download_failed",
|
|
5316
|
+
status: response.status
|
|
5317
|
+
});
|
|
5318
|
+
}
|
|
5319
|
+
const contentLength = Number(response.headers.get("content-length"));
|
|
5320
|
+
if (Number.isFinite(contentLength) && contentLength > MAX_EXTENSION_FILE_BYTES) {
|
|
5321
|
+
throw new GithubExtensionError(`Extension file is too large: ${input.file.path}`, {
|
|
5322
|
+
code: "extension_file_too_large"
|
|
5323
|
+
});
|
|
5324
|
+
}
|
|
5325
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
5326
|
+
if (bytes.byteLength > MAX_EXTENSION_FILE_BYTES) {
|
|
5327
|
+
throw new GithubExtensionError(`Extension file is too large: ${input.file.path}`, {
|
|
5328
|
+
code: "extension_file_too_large"
|
|
5329
|
+
});
|
|
5330
|
+
}
|
|
5331
|
+
return bytes;
|
|
5332
|
+
}
|
|
5333
|
+
async function githubJson(fetchImpl, githubToken, pathname) {
|
|
5334
|
+
const response = await fetchImpl(`https://api.github.com${pathname}`, {
|
|
5335
|
+
headers: githubHeaders(githubToken, "application/vnd.github+json")
|
|
5336
|
+
});
|
|
5337
|
+
if (!response.ok) {
|
|
5338
|
+
const details = await response.json().catch(() => null);
|
|
5339
|
+
const detail = typeof details?.message === "string" ? `: ${details.message}` : "";
|
|
5340
|
+
throw new GithubExtensionError(`GitHub request failed (${response.status})${detail}`, {
|
|
5341
|
+
code: response.status === 404 ? "github_repository_not_found" : "github_request_failed",
|
|
5342
|
+
status: response.status
|
|
5343
|
+
});
|
|
5344
|
+
}
|
|
5345
|
+
return await response.json();
|
|
5346
|
+
}
|
|
5347
|
+
function githubHeaders(token, accept) {
|
|
5348
|
+
const headers = new Headers({
|
|
5349
|
+
Accept: accept,
|
|
5350
|
+
"User-Agent": "openpond-extension-installer",
|
|
5351
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
5352
|
+
});
|
|
5353
|
+
if (token)
|
|
5354
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
5355
|
+
return headers;
|
|
5356
|
+
}
|
|
5357
|
+
function validateDownloadLimits(skillPackages) {
|
|
5358
|
+
const files = /* @__PURE__ */ new Map();
|
|
5359
|
+
for (const skillPackage of skillPackages) {
|
|
5360
|
+
for (const file of skillPackage.files)
|
|
5361
|
+
files.set(file.path, file);
|
|
5362
|
+
}
|
|
5363
|
+
if (files.size > MAX_EXTENSION_FILES) {
|
|
5364
|
+
throw new GithubExtensionError(`Extension contains more than ${MAX_EXTENSION_FILES} skill files.`, {
|
|
5365
|
+
code: "extension_too_many_files"
|
|
5366
|
+
});
|
|
5367
|
+
}
|
|
5368
|
+
let totalBytes = 0;
|
|
5369
|
+
for (const file of files.values()) {
|
|
5370
|
+
if ((file.size ?? 0) > MAX_EXTENSION_FILE_BYTES) {
|
|
5371
|
+
throw new GithubExtensionError(`Extension file is too large: ${file.path}`, {
|
|
5372
|
+
code: "extension_file_too_large"
|
|
5373
|
+
});
|
|
5374
|
+
}
|
|
5375
|
+
totalBytes += file.size ?? 0;
|
|
5376
|
+
}
|
|
5377
|
+
if (totalBytes > MAX_EXTENSION_BYTES) {
|
|
5378
|
+
throw new GithubExtensionError("Extension exceeds the maximum allowed size.", {
|
|
5379
|
+
code: "extension_too_large"
|
|
5380
|
+
});
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
function validTreeEntry(value) {
|
|
5384
|
+
if (!value || typeof value !== "object")
|
|
5385
|
+
return false;
|
|
5386
|
+
const entry = value;
|
|
5387
|
+
return typeof entry.path === "string" && !entry.path.startsWith("/") && !entry.path.split("/").includes("..") && typeof entry.mode === "string" && (entry.type === "blob" || entry.type === "tree" || entry.type === "commit") && typeof entry.sha === "string";
|
|
5388
|
+
}
|
|
5389
|
+
function hashTree(files) {
|
|
5390
|
+
const hash = createHash3("sha256");
|
|
5391
|
+
for (const file of files)
|
|
5392
|
+
hash.update(`${file.path}\0${file.sha}\0${file.size ?? ""}
|
|
5393
|
+
`);
|
|
5394
|
+
return hash.digest("hex");
|
|
5395
|
+
}
|
|
5396
|
+
function relativeSkillPath(root, filePath) {
|
|
5397
|
+
return root === "." ? filePath : filePath.slice(root.length + 1);
|
|
5398
|
+
}
|
|
5399
|
+
function duplicateValues(values) {
|
|
5400
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5401
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5402
|
+
for (const value of values) {
|
|
5403
|
+
if (seen.has(value))
|
|
5404
|
+
duplicates.add(value);
|
|
5405
|
+
seen.add(value);
|
|
5406
|
+
}
|
|
5407
|
+
return duplicates;
|
|
5408
|
+
}
|
|
5409
|
+
async function mapConcurrent(values, concurrency, mapper) {
|
|
5410
|
+
const results = new Array(values.length);
|
|
5411
|
+
let nextIndex = 0;
|
|
5412
|
+
async function worker() {
|
|
5413
|
+
while (nextIndex < values.length) {
|
|
5414
|
+
const index = nextIndex;
|
|
5415
|
+
nextIndex += 1;
|
|
5416
|
+
results[index] = await mapper(values[index]);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, worker));
|
|
5420
|
+
return results;
|
|
5421
|
+
}
|
|
5422
|
+
function invalidSource(source) {
|
|
5423
|
+
return new GithubExtensionError(`Invalid GitHub extension source: ${source}. Use owner/repo or https://github.com/owner/repo.`, { code: "github_invalid_source" });
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5426
|
+
// ../../packages/cloud/dist/extensions/extension-manager.js
|
|
5427
|
+
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
5428
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
5429
|
+
import { mkdir as mkdir2, readFile as readFile6, rename, rm, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5430
|
+
import path11 from "node:path";
|
|
5431
|
+
var REGISTRY_SCHEMA_VERSION = 1;
|
|
5432
|
+
var OPERATION_LOCK_STALE_MS = 2 * 60 * 1e3;
|
|
5433
|
+
var OPERATION_LOCK_TIMEOUT_MS = 15e3;
|
|
5434
|
+
function createGithubExtensionManager(options = {}) {
|
|
5435
|
+
const rootPath = path11.resolve(options.rootPath ?? path11.join(openPondConfigDirectory(), "extensions"));
|
|
5436
|
+
const registryPath = path11.join(rootPath, "registry.json");
|
|
5437
|
+
const github = createGithubSourceClient({
|
|
5438
|
+
fetch: options.fetch,
|
|
5439
|
+
githubToken: options.githubToken
|
|
5440
|
+
});
|
|
5441
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
5442
|
+
async function list() {
|
|
5443
|
+
try {
|
|
5444
|
+
const registry = await readRegistry(registryPath);
|
|
5445
|
+
const extensions = await Promise.all(registry.extensions.map((extension) => hydrateExtension(rootPath, extension)));
|
|
5446
|
+
markInstalledSkillConflicts(extensions, await reservedSkillNames(options.reservedSkillNames));
|
|
5447
|
+
return {
|
|
5448
|
+
rootPath,
|
|
5449
|
+
registryPath,
|
|
5450
|
+
extensions: extensions.sort(extensionSort),
|
|
5451
|
+
error: null
|
|
5452
|
+
};
|
|
5453
|
+
} catch (error) {
|
|
5454
|
+
return {
|
|
5455
|
+
rootPath,
|
|
5456
|
+
registryPath,
|
|
5457
|
+
extensions: [],
|
|
5458
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5459
|
+
};
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
async function preview(request) {
|
|
5463
|
+
const resolution = await github.resolve(request);
|
|
5464
|
+
const registry = await readRegistry(registryPath);
|
|
5465
|
+
const existing = registry.extensions.find((extension) => extension.id === resolution.preview.id) ?? null;
|
|
5466
|
+
assertSkillNamesAvailable(resolution.preview, registry.extensions.filter((extension) => extension.id !== existing?.id), await reservedSkillNames(options.reservedSkillNames));
|
|
5467
|
+
return resolution.preview;
|
|
5468
|
+
}
|
|
5469
|
+
async function add(request) {
|
|
5470
|
+
const identity = parseGithubExtensionSource(request.source);
|
|
5471
|
+
return withExtensionLock(rootPath, identity.id, async () => {
|
|
5472
|
+
const registry = await readRegistry(registryPath);
|
|
5473
|
+
if (registry.extensions.some((extension) => extension.id === identity.id)) {
|
|
5474
|
+
throw new GithubExtensionError(`${identity.owner}/${identity.repo} is already installed.`, {
|
|
5475
|
+
code: "extension_already_installed",
|
|
5476
|
+
status: 409
|
|
5477
|
+
});
|
|
5478
|
+
}
|
|
5479
|
+
const resolution = await github.resolve(request);
|
|
5480
|
+
assertInstallable(resolution.preview);
|
|
5481
|
+
assertSkillNamesAvailable(resolution.preview, registry.extensions, await reservedSkillNames(options.reservedSkillNames));
|
|
5482
|
+
const timestamp = now().toISOString();
|
|
5483
|
+
const installed = await materializeExtension({
|
|
5484
|
+
rootPath,
|
|
5485
|
+
resolution,
|
|
5486
|
+
installedAt: timestamp,
|
|
5487
|
+
updatedAt: timestamp,
|
|
5488
|
+
github
|
|
5489
|
+
});
|
|
5490
|
+
try {
|
|
5491
|
+
const reserved = await reservedSkillNames(options.reservedSkillNames);
|
|
5492
|
+
await writeRegistry(registryPath, now, (current) => ({
|
|
5493
|
+
...current,
|
|
5494
|
+
extensions: appendInstalledExtension(current, installed, reserved)
|
|
5495
|
+
}));
|
|
5496
|
+
await finalizeMaterialization(rootPath, installed.id);
|
|
5497
|
+
return installed;
|
|
5498
|
+
} catch (error) {
|
|
5499
|
+
await rollbackMaterialization(rootPath, installed.id);
|
|
5500
|
+
throw error;
|
|
5501
|
+
}
|
|
5502
|
+
});
|
|
5503
|
+
}
|
|
5504
|
+
async function update(request) {
|
|
5505
|
+
const identity = parseGithubExtensionSource(request.source);
|
|
5506
|
+
return withExtensionLock(rootPath, identity.id, async () => {
|
|
5507
|
+
const registry = await readRegistry(registryPath);
|
|
5508
|
+
const existing = requireInstalled(registry, identity.id);
|
|
5509
|
+
const resolution = await github.resolve({
|
|
5510
|
+
source: existing.repositoryUrl,
|
|
5511
|
+
ref: request.ref?.trim() || existing.requestedRef
|
|
5512
|
+
});
|
|
5513
|
+
assertInstallable(resolution.preview);
|
|
5514
|
+
assertSkillNamesAvailable(resolution.preview, registry.extensions.filter((extension) => extension.id !== existing.id), await reservedSkillNames(options.reservedSkillNames));
|
|
5515
|
+
if (existing.resolvedCommit === resolution.preview.resolvedCommit && existing.packageHash === resolution.preview.packageHash && existing.requestedRef === resolution.preview.requestedRef) {
|
|
5516
|
+
return hydrateExtension(rootPath, existing);
|
|
5517
|
+
}
|
|
5518
|
+
const installed = await materializeExtension({
|
|
5519
|
+
rootPath,
|
|
5520
|
+
resolution,
|
|
5521
|
+
installedAt: existing.installedAt,
|
|
5522
|
+
updatedAt: now().toISOString(),
|
|
5523
|
+
github
|
|
5524
|
+
});
|
|
5525
|
+
try {
|
|
5526
|
+
const reserved = await reservedSkillNames(options.reservedSkillNames);
|
|
5527
|
+
await writeRegistry(registryPath, now, (current) => ({
|
|
5528
|
+
...current,
|
|
5529
|
+
extensions: replaceInstalledExtension(current, installed, reserved)
|
|
5530
|
+
}));
|
|
5531
|
+
await finalizeMaterialization(rootPath, installed.id);
|
|
5532
|
+
return installed;
|
|
5533
|
+
} catch (error) {
|
|
5534
|
+
await rollbackMaterialization(rootPath, installed.id);
|
|
5535
|
+
throw error;
|
|
5536
|
+
}
|
|
5537
|
+
});
|
|
5538
|
+
}
|
|
5539
|
+
async function updateAll() {
|
|
5540
|
+
const catalog = await list();
|
|
5541
|
+
if (catalog.error)
|
|
5542
|
+
throw new GithubExtensionError(catalog.error, { code: "extension_registry_error" });
|
|
5543
|
+
const result = { updated: [], unchanged: [], failed: [] };
|
|
5544
|
+
for (const extension of catalog.extensions) {
|
|
5545
|
+
try {
|
|
5546
|
+
const next = await update({ source: extension.repositoryUrl, ref: extension.requestedRef });
|
|
5547
|
+
if (next.resolvedCommit === extension.resolvedCommit && next.packageHash === extension.packageHash) {
|
|
5548
|
+
result.unchanged.push(next);
|
|
5549
|
+
} else {
|
|
5550
|
+
result.updated.push(next);
|
|
5551
|
+
}
|
|
5552
|
+
} catch (error) {
|
|
5553
|
+
result.failed.push({
|
|
5554
|
+
id: extension.id,
|
|
5555
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5556
|
+
});
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
return result;
|
|
5560
|
+
}
|
|
5561
|
+
async function inspect(source) {
|
|
5562
|
+
const identity = parseGithubExtensionSource(source);
|
|
5563
|
+
const registry = await readRegistry(registryPath);
|
|
5564
|
+
return hydrateExtension(rootPath, requireInstalled(registry, identity.id));
|
|
5565
|
+
}
|
|
5566
|
+
async function remove(source) {
|
|
5567
|
+
const identity = parseGithubExtensionSource(source);
|
|
5568
|
+
return withExtensionLock(rootPath, identity.id, async () => {
|
|
5569
|
+
const registry = await readRegistry(registryPath);
|
|
5570
|
+
const existing = requireInstalled(registry, identity.id);
|
|
5571
|
+
const target = extensionDirectory(rootPath, identity.owner, identity.repo);
|
|
5572
|
+
const trash = `${target}.${randomUUID3()}.remove`;
|
|
5573
|
+
if (existsSync6(target))
|
|
5574
|
+
await rename(target, trash);
|
|
5575
|
+
try {
|
|
5576
|
+
await writeRegistry(registryPath, now, (current) => ({
|
|
5577
|
+
...current,
|
|
5578
|
+
extensions: current.extensions.filter((extension) => extension.id !== existing.id)
|
|
5579
|
+
}));
|
|
5580
|
+
await rm(trash, { recursive: true, force: true });
|
|
5581
|
+
return existing;
|
|
5582
|
+
} catch (error) {
|
|
5583
|
+
if (existsSync6(trash) && !existsSync6(target))
|
|
5584
|
+
await rename(trash, target);
|
|
5585
|
+
throw error;
|
|
5586
|
+
}
|
|
5587
|
+
});
|
|
5588
|
+
}
|
|
5589
|
+
async function readSkill(nameInput) {
|
|
5590
|
+
const name = nameInput.trim();
|
|
5591
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
5592
|
+
throw new GithubExtensionError("Extension skill name must be lowercase kebab-case.", {
|
|
5593
|
+
code: "extension_skill_invalid_name"
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5596
|
+
const catalog = await list();
|
|
5597
|
+
if (catalog.error) {
|
|
5598
|
+
throw new GithubExtensionError(catalog.error, { code: "extension_registry_error" });
|
|
5599
|
+
}
|
|
5600
|
+
const matches = catalog.extensions.flatMap((extension) => extension.skills.filter((skill) => skill.name === name).map((skill) => ({ extension, skill })));
|
|
5601
|
+
if (matches.length === 0) {
|
|
5602
|
+
throw new GithubExtensionError(`Extension skill not found: ${name}`, {
|
|
5603
|
+
code: "extension_skill_not_found",
|
|
5604
|
+
status: 404
|
|
5605
|
+
});
|
|
5606
|
+
}
|
|
5607
|
+
if (matches.length > 1) {
|
|
5608
|
+
throw new GithubExtensionError(`Extension skill name is ambiguous: ${name}`, {
|
|
5609
|
+
code: "extension_skill_conflict",
|
|
5610
|
+
status: 409
|
|
5611
|
+
});
|
|
5612
|
+
}
|
|
5613
|
+
const match = matches[0];
|
|
5614
|
+
if (match.extension.validationStatus !== "valid" || match.skill.validationStatus !== "valid") {
|
|
5615
|
+
throw new GithubExtensionError(`Extension skill ${name} is not valid.`, {
|
|
5616
|
+
code: "extension_skill_invalid"
|
|
5617
|
+
});
|
|
5618
|
+
}
|
|
5619
|
+
const absolutePath = safeDestination(match.extension.sourcePath, match.skill.relativePath);
|
|
5620
|
+
const markdown = await readFile6(absolutePath, "utf8");
|
|
5621
|
+
const sourceHash = createHash4("sha256").update(markdown).digest("hex");
|
|
5622
|
+
if (sourceHash !== match.skill.sourceHash) {
|
|
5623
|
+
throw new GithubExtensionError(`Extension skill ${name} changed on disk. Update or reinstall its extension before using it.`, { code: "extension_skill_modified" });
|
|
5624
|
+
}
|
|
5625
|
+
const parsed = parseProfileSkillMarkdown(markdown);
|
|
5626
|
+
if (parsed.messages.length > 0 || parsed.name !== name || !parsed.description) {
|
|
5627
|
+
throw new GithubExtensionError(`Extension skill ${name} is not valid. ${parsed.messages.join(" ")}`.trim(), { code: "extension_skill_invalid" });
|
|
5628
|
+
}
|
|
5629
|
+
return {
|
|
5630
|
+
name,
|
|
5631
|
+
description: parsed.description,
|
|
5632
|
+
body: parsed.body,
|
|
5633
|
+
path: match.skill.relativePath,
|
|
5634
|
+
sourcePath: match.extension.sourcePath,
|
|
5635
|
+
sourceHash,
|
|
5636
|
+
charCount: markdown.length,
|
|
5637
|
+
packagePath: path11.dirname(absolutePath),
|
|
5638
|
+
resourceFiles: match.skill.resourceFiles
|
|
5639
|
+
};
|
|
5640
|
+
}
|
|
5641
|
+
return { rootPath, registryPath, list, preview, add, update, updateAll, inspect, remove, readSkill };
|
|
5642
|
+
}
|
|
5643
|
+
async function materializeExtension(input) {
|
|
5644
|
+
const preview = input.resolution.preview;
|
|
5645
|
+
const target = extensionDirectory(input.rootPath, preview.owner, preview.repo);
|
|
5646
|
+
const temporary = `${target}.${randomUUID3()}.install`;
|
|
5647
|
+
const backup = backupDirectory(input.rootPath, preview.id);
|
|
5648
|
+
const current = path11.join(temporary, "current");
|
|
5649
|
+
await rm(temporary, { recursive: true, force: true });
|
|
5650
|
+
await rm(backup, { recursive: true, force: true });
|
|
5651
|
+
await mkdir2(current, { recursive: true, mode: 448 });
|
|
5652
|
+
try {
|
|
5653
|
+
await input.github.download(input.resolution, async (relativePath, bytes, executable) => {
|
|
5654
|
+
const destination = safeDestination(current, relativePath);
|
|
5655
|
+
await mkdir2(path11.dirname(destination), { recursive: true, mode: 448 });
|
|
5656
|
+
await writeFile3(destination, bytes, { mode: executable ? 448 : 384 });
|
|
5657
|
+
});
|
|
5658
|
+
const extension = installedExtension(preview, target, input.installedAt, input.updatedAt);
|
|
5659
|
+
await writeFile3(path11.join(temporary, "extension.json"), `${JSON.stringify(extension, null, 2)}
|
|
5660
|
+
`, { mode: 384 });
|
|
5661
|
+
await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
|
|
5662
|
+
if (existsSync6(target))
|
|
5663
|
+
await rename(target, backup);
|
|
5664
|
+
await rename(temporary, target);
|
|
5665
|
+
return extension;
|
|
5666
|
+
} catch (error) {
|
|
5667
|
+
await rm(temporary, { recursive: true, force: true });
|
|
5668
|
+
if (existsSync6(backup) && !existsSync6(target))
|
|
5669
|
+
await rename(backup, target);
|
|
5670
|
+
throw error;
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
function installedExtension(preview, target, installedAt, updatedAt) {
|
|
5674
|
+
const sourcePath = path11.join(target, "current");
|
|
5675
|
+
return {
|
|
5676
|
+
...preview,
|
|
5677
|
+
sourcePath,
|
|
5678
|
+
readmePath: preview.readmePath ? path11.join(sourcePath, preview.readmePath) : null,
|
|
5679
|
+
installedAt,
|
|
5680
|
+
updatedAt,
|
|
5681
|
+
skills: preview.skills.map((skill) => ({
|
|
5682
|
+
...skill,
|
|
5683
|
+
sourcePath: path11.join(sourcePath, skill.relativePath)
|
|
5684
|
+
}))
|
|
5685
|
+
};
|
|
5686
|
+
}
|
|
5687
|
+
async function finalizeMaterialization(rootPath, id) {
|
|
5688
|
+
await rm(backupDirectory(rootPath, id), { recursive: true, force: true });
|
|
5689
|
+
}
|
|
5690
|
+
async function rollbackMaterialization(rootPath, id) {
|
|
5691
|
+
const identity = parseGithubExtensionSource(id.replace(/^github:/, ""));
|
|
5692
|
+
const target = extensionDirectory(rootPath, identity.owner, identity.repo);
|
|
5693
|
+
const backup = backupDirectory(rootPath, id);
|
|
5694
|
+
await rm(target, { recursive: true, force: true });
|
|
5695
|
+
if (existsSync6(backup))
|
|
5696
|
+
await rename(backup, target);
|
|
5697
|
+
}
|
|
5698
|
+
async function hydrateExtension(rootPath, extension) {
|
|
5699
|
+
const target = extensionDirectory(rootPath, extension.owner, extension.repo);
|
|
5700
|
+
const sourcePath = path11.join(target, "current");
|
|
5701
|
+
const present = (await stat4(path11.join(target, "extension.json")).catch(() => null))?.isFile() ?? false;
|
|
5702
|
+
const validationMessages = extension.validationMessages.filter((message) => message !== "Installed files are missing.");
|
|
5703
|
+
if (!present)
|
|
5704
|
+
validationMessages.push("Installed files are missing.");
|
|
5705
|
+
return {
|
|
5706
|
+
...extension,
|
|
5707
|
+
sourcePath,
|
|
5708
|
+
readmePath: extension.readmePath ? path11.join(sourcePath, path11.basename(extension.readmePath)) : null,
|
|
5709
|
+
skills: extension.skills.map((skill) => ({
|
|
5710
|
+
...skill,
|
|
5711
|
+
sourcePath: path11.join(sourcePath, skill.relativePath)
|
|
5712
|
+
})),
|
|
5713
|
+
validationStatus: validationMessages.length === 0 ? "valid" : "error",
|
|
5714
|
+
validationMessages
|
|
5715
|
+
};
|
|
5716
|
+
}
|
|
5717
|
+
function assertInstallable(preview) {
|
|
5718
|
+
if (preview.validationStatus === "valid")
|
|
5719
|
+
return;
|
|
5720
|
+
throw new GithubExtensionError(`Extension contains invalid skills: ${preview.validationMessages.join(" ")}`, { code: "extension_invalid" });
|
|
5721
|
+
}
|
|
5722
|
+
function assertSkillNamesAvailable(preview, installed, reserved) {
|
|
5723
|
+
const owners = /* @__PURE__ */ new Map();
|
|
5724
|
+
for (const name of reserved)
|
|
5725
|
+
owners.set(name, "a shipped OpenPond skill");
|
|
5726
|
+
for (const extension of installed) {
|
|
5727
|
+
for (const skill of extension.skills)
|
|
5728
|
+
owners.set(skill.name, extension.id);
|
|
5729
|
+
}
|
|
5730
|
+
const conflicts = preview.skills.map((skill) => ({ name: skill.name, owner: owners.get(skill.name) })).filter((entry) => Boolean(entry.owner));
|
|
5731
|
+
if (conflicts.length === 0)
|
|
5732
|
+
return;
|
|
5733
|
+
throw new GithubExtensionError(`Skill name conflict: ${conflicts.map((entry) => `${entry.name} (${entry.owner})`).join(", ")}.`, { code: "extension_skill_conflict", status: 409 });
|
|
5734
|
+
}
|
|
5735
|
+
function appendInstalledExtension(registry, installed, reserved) {
|
|
5736
|
+
if (registry.extensions.some((extension) => extension.id === installed.id)) {
|
|
5737
|
+
throw new GithubExtensionError(`${installed.owner}/${installed.repo} is already installed.`, {
|
|
5738
|
+
code: "extension_already_installed",
|
|
5739
|
+
status: 409
|
|
5740
|
+
});
|
|
5741
|
+
}
|
|
5742
|
+
assertSkillNamesAvailable(installed, registry.extensions, reserved);
|
|
5743
|
+
return [...registry.extensions, installed].sort(extensionSort);
|
|
5744
|
+
}
|
|
5745
|
+
function replaceInstalledExtension(registry, installed, reserved) {
|
|
5746
|
+
requireInstalled(registry, installed.id);
|
|
5747
|
+
assertSkillNamesAvailable(installed, registry.extensions.filter((extension) => extension.id !== installed.id), reserved);
|
|
5748
|
+
return registry.extensions.map((extension) => extension.id === installed.id ? installed : extension).sort(extensionSort);
|
|
5749
|
+
}
|
|
5750
|
+
function markInstalledSkillConflicts(extensions, reserved) {
|
|
5751
|
+
const owners = /* @__PURE__ */ new Map();
|
|
5752
|
+
for (const name of reserved)
|
|
5753
|
+
owners.set(name, "built-in");
|
|
5754
|
+
for (const extension of extensions) {
|
|
5755
|
+
for (const skill of extension.skills) {
|
|
5756
|
+
const owner = owners.get(skill.name);
|
|
5757
|
+
if (!owner) {
|
|
5758
|
+
owners.set(skill.name, extension.id);
|
|
5759
|
+
continue;
|
|
5760
|
+
}
|
|
5761
|
+
const message = `Skill name conflicts with ${owner}: ${skill.name}`;
|
|
5762
|
+
skill.validationStatus = "error";
|
|
5763
|
+
skill.validationMessages = [.../* @__PURE__ */ new Set([...skill.validationMessages, message])];
|
|
5764
|
+
extension.validationStatus = "error";
|
|
5765
|
+
extension.validationMessages = [.../* @__PURE__ */ new Set([...extension.validationMessages, message])];
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
}
|
|
5769
|
+
async function reservedSkillNames(source) {
|
|
5770
|
+
const values = typeof source === "function" ? await source() : source ?? [];
|
|
5771
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
5772
|
+
}
|
|
5773
|
+
function requireInstalled(registry, id) {
|
|
5774
|
+
const extension = registry.extensions.find((candidate) => candidate.id === id);
|
|
5775
|
+
if (extension)
|
|
5776
|
+
return extension;
|
|
5777
|
+
throw new GithubExtensionError(`${id.replace(/^github:/, "")} is not installed.`, {
|
|
5778
|
+
code: "extension_not_installed",
|
|
5779
|
+
status: 404
|
|
5780
|
+
});
|
|
5781
|
+
}
|
|
5782
|
+
async function readRegistry(registryPath) {
|
|
5783
|
+
try {
|
|
5784
|
+
const parsed = JSON.parse(await readFile6(registryPath, "utf8"));
|
|
5785
|
+
if (parsed.schemaVersion !== REGISTRY_SCHEMA_VERSION || !Array.isArray(parsed.extensions)) {
|
|
5786
|
+
throw new Error("Extension registry has an unsupported format.");
|
|
5787
|
+
}
|
|
5788
|
+
return {
|
|
5789
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
5790
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : null,
|
|
5791
|
+
extensions: parsed.extensions.filter(isStoredExtension)
|
|
5792
|
+
};
|
|
5793
|
+
} catch (error) {
|
|
5794
|
+
if (error.code === "ENOENT")
|
|
5795
|
+
return emptyRegistry();
|
|
5796
|
+
throw error;
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
async function writeRegistry(registryPath, now, update) {
|
|
5800
|
+
await updatePrivateJsonFile(registryPath, emptyRegistry, (current) => ({
|
|
5801
|
+
...update(normalizeRegistry(current)),
|
|
5802
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
5803
|
+
updatedAt: now().toISOString()
|
|
5804
|
+
}));
|
|
5805
|
+
}
|
|
5806
|
+
function normalizeRegistry(value) {
|
|
5807
|
+
return {
|
|
5808
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
5809
|
+
updatedAt: typeof value?.updatedAt === "string" ? value.updatedAt : null,
|
|
5810
|
+
extensions: Array.isArray(value?.extensions) ? value.extensions.filter(isStoredExtension) : []
|
|
5811
|
+
};
|
|
5812
|
+
}
|
|
5813
|
+
function emptyRegistry() {
|
|
5814
|
+
return { schemaVersion: REGISTRY_SCHEMA_VERSION, updatedAt: null, extensions: [] };
|
|
5815
|
+
}
|
|
5816
|
+
function isStoredExtension(value) {
|
|
5817
|
+
if (!value || typeof value !== "object")
|
|
5818
|
+
return false;
|
|
5819
|
+
const extension = value;
|
|
5820
|
+
return extension.source === "github" && typeof extension.id === "string" && typeof extension.owner === "string" && typeof extension.repo === "string" && typeof extension.repositoryUrl === "string" && typeof extension.requestedRef === "string" && typeof extension.resolvedCommit === "string" && Array.isArray(extension.skills);
|
|
5821
|
+
}
|
|
5822
|
+
function safeDestination(root, relativePath) {
|
|
5823
|
+
if (path11.isAbsolute(relativePath) || relativePath.split(/[\\/]/).includes("..")) {
|
|
5824
|
+
throw new GithubExtensionError(`Unsafe extension path: ${relativePath}`, {
|
|
5825
|
+
code: "extension_unsafe_path"
|
|
5826
|
+
});
|
|
5827
|
+
}
|
|
5828
|
+
const destination = path11.resolve(root, ...relativePath.split("/"));
|
|
5829
|
+
const prefix = `${path11.resolve(root)}${path11.sep}`;
|
|
5830
|
+
if (!destination.startsWith(prefix)) {
|
|
5831
|
+
throw new GithubExtensionError(`Unsafe extension path: ${relativePath}`, {
|
|
5832
|
+
code: "extension_unsafe_path"
|
|
5833
|
+
});
|
|
5834
|
+
}
|
|
5835
|
+
return destination;
|
|
5836
|
+
}
|
|
5837
|
+
function extensionDirectory(rootPath, owner, repo) {
|
|
5838
|
+
return path11.join(rootPath, "github", owner, repo);
|
|
5839
|
+
}
|
|
5840
|
+
function backupDirectory(rootPath, id) {
|
|
5841
|
+
const identity = parseGithubExtensionSource(id.replace(/^github:/, ""));
|
|
5842
|
+
return `${extensionDirectory(rootPath, identity.owner, identity.repo)}.previous`;
|
|
5843
|
+
}
|
|
5844
|
+
async function withExtensionLock(rootPath, id, operation) {
|
|
5845
|
+
const safeId = id.replace(/[^a-z0-9.-]+/gi, "-");
|
|
5846
|
+
const lockPath = path11.join(rootPath, ".locks", `${safeId}.lock`);
|
|
5847
|
+
await mkdir2(path11.dirname(lockPath), { recursive: true, mode: 448 });
|
|
5848
|
+
const deadline = Date.now() + OPERATION_LOCK_TIMEOUT_MS;
|
|
5849
|
+
while (true) {
|
|
5850
|
+
try {
|
|
5851
|
+
await mkdir2(lockPath, { mode: 448 });
|
|
5852
|
+
break;
|
|
5853
|
+
} catch (error) {
|
|
5854
|
+
if (error.code !== "EEXIST")
|
|
5855
|
+
throw error;
|
|
5856
|
+
const lockStat = await stat4(lockPath).catch(() => null);
|
|
5857
|
+
if (lockStat && Date.now() - lockStat.mtimeMs > OPERATION_LOCK_STALE_MS) {
|
|
5858
|
+
await rm(lockPath, { recursive: true, force: true });
|
|
5859
|
+
continue;
|
|
5860
|
+
}
|
|
5861
|
+
if (Date.now() >= deadline) {
|
|
5862
|
+
throw new GithubExtensionError(`Timed out waiting for extension operation: ${id}`, {
|
|
5863
|
+
code: "extension_operation_busy",
|
|
5864
|
+
status: 409
|
|
5865
|
+
});
|
|
5866
|
+
}
|
|
5867
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
try {
|
|
5871
|
+
return await operation();
|
|
5872
|
+
} finally {
|
|
5873
|
+
await rm(lockPath, { recursive: true, force: true });
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
function extensionSort(left, right) {
|
|
5877
|
+
return left.id.localeCompare(right.id);
|
|
5878
|
+
}
|
|
5879
|
+
|
|
5022
5880
|
// ../../packages/cloud/dist/profile/hosted-run-idempotency.js
|
|
5023
|
-
import { createHash as
|
|
5881
|
+
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
5024
5882
|
function buildHostedRunIdempotencyKey(input) {
|
|
5025
5883
|
const explicit = input.explicitKey?.trim();
|
|
5026
5884
|
if (explicit)
|
|
@@ -5031,10 +5889,10 @@ function buildHostedRunIdempotencyKey(input) {
|
|
|
5031
5889
|
const base = `profile-push-run:${input.localHead}:${sourceHead}:${input.runtimeAgentId}:${targetProjectId}:${inputHash}`;
|
|
5032
5890
|
if (!input.retry)
|
|
5033
5891
|
return base;
|
|
5034
|
-
return `${base}:retry:${(input.randomId ??
|
|
5892
|
+
return `${base}:retry:${(input.randomId ?? randomUUID4)()}`;
|
|
5035
5893
|
}
|
|
5036
5894
|
function hashStableJson(value) {
|
|
5037
|
-
return
|
|
5895
|
+
return createHash5("sha256").update(stableJsonStringify(value)).digest("hex");
|
|
5038
5896
|
}
|
|
5039
5897
|
function stableJsonStringify(value) {
|
|
5040
5898
|
if (value === void 0)
|
|
@@ -5048,9 +5906,9 @@ function stableJsonStringify(value) {
|
|
|
5048
5906
|
}
|
|
5049
5907
|
|
|
5050
5908
|
// ../../packages/cloud/dist/profile/profile-skill-goal-executor.js
|
|
5051
|
-
import { existsSync as
|
|
5052
|
-
import { mkdir as
|
|
5053
|
-
import
|
|
5909
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
5910
|
+
import { mkdir as mkdir3, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
|
|
5911
|
+
import path12 from "node:path";
|
|
5054
5912
|
async function executeProfileSkillGoalRequest(goal) {
|
|
5055
5913
|
if (goal.operation === "edit")
|
|
5056
5914
|
return executeProfileSkillEdit(goal);
|
|
@@ -5061,9 +5919,9 @@ async function executeProfileSkillCreate(goal) {
|
|
|
5061
5919
|
const existingNames = new Set(existing.skills.map((skill) => skill.name));
|
|
5062
5920
|
const skillName = uniqueSkillName(goal.targetSkillName ?? goal.requestedName ?? inferSkillName(goal.userObjective), existingNames, goal.profileSourcePath);
|
|
5063
5921
|
const skillPath = profileSkillPath(skillName);
|
|
5064
|
-
const absolutePath =
|
|
5065
|
-
await
|
|
5066
|
-
await
|
|
5922
|
+
const absolutePath = path12.join(goal.profileSourcePath, skillPath);
|
|
5923
|
+
await mkdir3(path12.dirname(absolutePath), { recursive: true });
|
|
5924
|
+
await writeFile4(absolutePath, renderSkillMarkdown({
|
|
5067
5925
|
name: skillName,
|
|
5068
5926
|
description: descriptionForObjective(goal.userObjective),
|
|
5069
5927
|
body: bodyForObjective(goal.userObjective)
|
|
@@ -5075,10 +5933,10 @@ async function executeProfileSkillEdit(goal) {
|
|
|
5075
5933
|
if (!skillName)
|
|
5076
5934
|
throw new Error("Profile skill edit requires skillName.");
|
|
5077
5935
|
const skillPath = profileSkillPath(skillName);
|
|
5078
|
-
const absolutePath =
|
|
5079
|
-
if (!
|
|
5936
|
+
const absolutePath = path12.join(goal.profileSourcePath, skillPath);
|
|
5937
|
+
if (!existsSync7(absolutePath))
|
|
5080
5938
|
throw new Error(`Profile skill not found: ${skillName}`);
|
|
5081
|
-
const current = await
|
|
5939
|
+
const current = await readFile7(absolutePath, "utf8");
|
|
5082
5940
|
const parsed = parseProfileSkillMarkdown(current);
|
|
5083
5941
|
if (parsed.messages.length > 0 || !parsed.name || !parsed.description) {
|
|
5084
5942
|
throw new Error(`Profile skill ${skillName} is not valid. ${parsed.messages.join(" ")}`);
|
|
@@ -5092,7 +5950,7 @@ async function executeProfileSkillEdit(goal) {
|
|
|
5092
5950
|
change,
|
|
5093
5951
|
""
|
|
5094
5952
|
].join("\n");
|
|
5095
|
-
await
|
|
5953
|
+
await writeFile4(absolutePath, renderSkillMarkdown({
|
|
5096
5954
|
name: parsed.name,
|
|
5097
5955
|
description: parsed.description,
|
|
5098
5956
|
body
|
|
@@ -5108,7 +5966,7 @@ async function validateExecutedGoal(goal, skillName) {
|
|
|
5108
5966
|
...goal,
|
|
5109
5967
|
status: "completed",
|
|
5110
5968
|
targetSkillName: skillName,
|
|
5111
|
-
targetSkillPath:
|
|
5969
|
+
targetSkillPath: path12.join(goal.profileSourceRelativePath, PROFILE_SKILLS_DIR, skillName, PROFILE_SKILL_FILE).replace(/\\/g, "/")
|
|
5112
5970
|
};
|
|
5113
5971
|
const validationStatus = skill.validationStatus === "valid" ? "valid" : "error";
|
|
5114
5972
|
if (validationStatus !== "valid") {
|
|
@@ -5241,14 +6099,14 @@ function uniqueSkillName(baseName, existingNames, profileSourcePath2) {
|
|
|
5241
6099
|
const base = normalizeSkillName2(baseName) ?? "profile-skill";
|
|
5242
6100
|
let candidate = base;
|
|
5243
6101
|
let suffix = 2;
|
|
5244
|
-
while (existingNames.has(candidate) ||
|
|
6102
|
+
while (existingNames.has(candidate) || existsSync7(path12.join(profileSourcePath2, PROFILE_SKILLS_DIR, candidate))) {
|
|
5245
6103
|
candidate = `${base}-${suffix}`;
|
|
5246
6104
|
suffix += 1;
|
|
5247
6105
|
}
|
|
5248
6106
|
return candidate;
|
|
5249
6107
|
}
|
|
5250
6108
|
function profileSkillPath(skillName) {
|
|
5251
|
-
return
|
|
6109
|
+
return path12.join(PROFILE_SKILLS_DIR, skillName, PROFILE_SKILL_FILE).replace(/\\/g, "/");
|
|
5252
6110
|
}
|
|
5253
6111
|
function normalizeSkillName2(value) {
|
|
5254
6112
|
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
|
|
@@ -8100,7 +8958,8 @@ var OpenPondProfileSkillSchema = external_exports.object({
|
|
|
8100
8958
|
charCount: external_exports.number(),
|
|
8101
8959
|
sourceHash: external_exports.string(),
|
|
8102
8960
|
validationStatus: external_exports.enum(["valid", "warning", "error"]),
|
|
8103
|
-
validationMessages: external_exports.array(external_exports.string())
|
|
8961
|
+
validationMessages: external_exports.array(external_exports.string()),
|
|
8962
|
+
resourceFiles: external_exports.array(external_exports.string()).optional().default([])
|
|
8104
8963
|
});
|
|
8105
8964
|
var OpenPondProfileEvalSchema = external_exports.object({
|
|
8106
8965
|
id: external_exports.string(),
|
|
@@ -8371,6 +9230,82 @@ function emptyOpenPondProfileState() {
|
|
|
8371
9230
|
};
|
|
8372
9231
|
}
|
|
8373
9232
|
|
|
9233
|
+
// ../../packages/contracts/dist/skills.js
|
|
9234
|
+
var CodexPersonalSkillSchema = external_exports.object({
|
|
9235
|
+
name: external_exports.string(),
|
|
9236
|
+
description: external_exports.string(),
|
|
9237
|
+
path: external_exports.string(),
|
|
9238
|
+
sourcePath: external_exports.string(),
|
|
9239
|
+
enabled: external_exports.boolean(),
|
|
9240
|
+
charCount: external_exports.number().int().nonnegative(),
|
|
9241
|
+
sourceHash: external_exports.string(),
|
|
9242
|
+
validationStatus: external_exports.enum(["valid", "error"]),
|
|
9243
|
+
validationMessages: external_exports.array(external_exports.string()),
|
|
9244
|
+
resourceFiles: external_exports.array(external_exports.string()),
|
|
9245
|
+
updatedAt: external_exports.string().nullable()
|
|
9246
|
+
});
|
|
9247
|
+
var SkillSourceScopeSchema = external_exports.enum(["codex", "profile", "extension"]);
|
|
9248
|
+
var SkillSourceFileSchema = external_exports.object({
|
|
9249
|
+
skillName: external_exports.string(),
|
|
9250
|
+
scope: SkillSourceScopeSchema,
|
|
9251
|
+
path: external_exports.string(),
|
|
9252
|
+
byteSize: external_exports.number().int().nonnegative(),
|
|
9253
|
+
isBinary: external_exports.boolean(),
|
|
9254
|
+
content: external_exports.string().nullable()
|
|
9255
|
+
});
|
|
9256
|
+
|
|
9257
|
+
// ../../packages/contracts/dist/extensions.js
|
|
9258
|
+
var SHIPPED_OPENPOND_SKILL_NAMES = [
|
|
9259
|
+
"openpond-cli",
|
|
9260
|
+
"openpond-desktop-harness",
|
|
9261
|
+
"openpond-taskset-authoring"
|
|
9262
|
+
];
|
|
9263
|
+
var OpenPondExtensionSkillSchema = external_exports.object({
|
|
9264
|
+
name: external_exports.string(),
|
|
9265
|
+
description: external_exports.string(),
|
|
9266
|
+
relativePath: external_exports.string(),
|
|
9267
|
+
sourcePath: external_exports.string(),
|
|
9268
|
+
charCount: external_exports.number().int().nonnegative(),
|
|
9269
|
+
sourceHash: external_exports.string(),
|
|
9270
|
+
resourceFiles: external_exports.array(external_exports.string()),
|
|
9271
|
+
validationStatus: external_exports.enum(["valid", "error"]),
|
|
9272
|
+
validationMessages: external_exports.array(external_exports.string())
|
|
9273
|
+
});
|
|
9274
|
+
var OpenPondExtensionSchema = external_exports.object({
|
|
9275
|
+
id: external_exports.string(),
|
|
9276
|
+
source: external_exports.literal("github"),
|
|
9277
|
+
owner: external_exports.string(),
|
|
9278
|
+
repo: external_exports.string(),
|
|
9279
|
+
repositoryUrl: external_exports.string().url(),
|
|
9280
|
+
requestedRef: external_exports.string(),
|
|
9281
|
+
resolvedCommit: external_exports.string(),
|
|
9282
|
+
sourcePath: external_exports.string(),
|
|
9283
|
+
readmePath: external_exports.string().nullable(),
|
|
9284
|
+
installedAt: external_exports.string(),
|
|
9285
|
+
updatedAt: external_exports.string(),
|
|
9286
|
+
packageHash: external_exports.string(),
|
|
9287
|
+
skills: external_exports.array(OpenPondExtensionSkillSchema),
|
|
9288
|
+
validationStatus: external_exports.enum(["valid", "error"]),
|
|
9289
|
+
validationMessages: external_exports.array(external_exports.string())
|
|
9290
|
+
});
|
|
9291
|
+
var OpenPondExtensionPreviewSchema = OpenPondExtensionSchema.omit({
|
|
9292
|
+
installedAt: true,
|
|
9293
|
+
updatedAt: true,
|
|
9294
|
+
sourcePath: true
|
|
9295
|
+
}).extend({
|
|
9296
|
+
sourcePath: external_exports.null()
|
|
9297
|
+
});
|
|
9298
|
+
var OpenPondExtensionCatalogSchema = external_exports.object({
|
|
9299
|
+
rootPath: external_exports.string(),
|
|
9300
|
+
registryPath: external_exports.string(),
|
|
9301
|
+
extensions: external_exports.array(OpenPondExtensionSchema),
|
|
9302
|
+
error: external_exports.string().nullable()
|
|
9303
|
+
});
|
|
9304
|
+
var OpenPondExtensionSourceRequestSchema = external_exports.object({
|
|
9305
|
+
source: external_exports.string().trim().min(1),
|
|
9306
|
+
ref: external_exports.string().trim().min(1).optional()
|
|
9307
|
+
});
|
|
9308
|
+
|
|
8374
9309
|
// ../../packages/contracts/dist/bootstrap.js
|
|
8375
9310
|
var BootstrapEventWindowSchema = external_exports.object({
|
|
8376
9311
|
latestSequence: external_exports.number().int().nonnegative(),
|
|
@@ -8394,6 +9329,13 @@ var BootstrapPayloadSchema = external_exports.object({
|
|
|
8394
9329
|
localProjects: external_exports.array(LocalProjectSchema).optional().default([]),
|
|
8395
9330
|
cloudProjects: external_exports.array(CloudProjectSchema).optional().default([]),
|
|
8396
9331
|
profile: OpenPondProfileStateSchema.optional().default(emptyOpenPondProfileState),
|
|
9332
|
+
codexPersonalSkills: external_exports.array(CodexPersonalSkillSchema).optional().default([]),
|
|
9333
|
+
extensionCatalog: OpenPondExtensionCatalogSchema.optional().default({
|
|
9334
|
+
rootPath: "",
|
|
9335
|
+
registryPath: "",
|
|
9336
|
+
extensions: [],
|
|
9337
|
+
error: null
|
|
9338
|
+
}),
|
|
8397
9339
|
codexHistorySessions: external_exports.array(SessionSchema).optional().default([]),
|
|
8398
9340
|
sidebarAppPreferences: SidebarAppPreferencesSchema,
|
|
8399
9341
|
sidebarFileBookmarks: external_exports.array(SidebarFileBookmarkSchema).optional().default([]),
|
|
@@ -11594,8 +12536,11 @@ export {
|
|
|
11594
12536
|
normalizeSandboxApiUrl,
|
|
11595
12537
|
createOpenPondSandboxClient,
|
|
11596
12538
|
executeConnectedAppToolCall,
|
|
11597
|
-
|
|
12539
|
+
GithubExtensionError,
|
|
12540
|
+
parseProfileSkillMarkdown,
|
|
11598
12541
|
readProfileSkill,
|
|
12542
|
+
createGithubExtensionManager,
|
|
12543
|
+
buildHostedRunIdempotencyKey,
|
|
11599
12544
|
buildOpenPondProfileSetupGate,
|
|
11600
12545
|
formatOpenPondProfileSetupRequirement,
|
|
11601
12546
|
readSourceUploadCache,
|
|
@@ -11731,6 +12676,8 @@ export {
|
|
|
11731
12676
|
SwitchOpenPondAccountRequestSchema,
|
|
11732
12677
|
SaveOpenPondAccountRequestSchema,
|
|
11733
12678
|
UpdateOpenPondAccountConfigRequestSchema,
|
|
12679
|
+
SHIPPED_OPENPOND_SKILL_NAMES,
|
|
12680
|
+
OpenPondExtensionSourceRequestSchema,
|
|
11734
12681
|
BootstrapPayloadSchema,
|
|
11735
12682
|
WorkspaceTemplateConfigViewSchema,
|
|
11736
12683
|
WorkspaceToolRequestSchema,
|