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.
Files changed (131) hide show
  1. package/README.md +4 -0
  2. package/dist/chunks/{app-layer-4AA4YYGK.js → app-layer-LF2HK2B2.js} +6 -3
  3. package/dist/chunks/{apps-V2JTB5MD.js → apps-ZMCMQZKX.js} +16 -12
  4. package/dist/chunks/{chunk-NVTC3XOA.js → chunk-2WMGA5FE.js} +8 -10
  5. package/dist/chunks/{chunk-6L5WJF7T.js → chunk-35PPAOWD.js} +2 -2
  6. package/dist/chunks/chunk-3VURZQHA.js +864 -0
  7. package/dist/chunks/{chunk-3DOOLZZM.js → chunk-43YI36NA.js} +1 -1
  8. package/dist/chunks/{chunk-JAS6QZ2W.js → chunk-A3CJUBA7.js} +1 -1
  9. package/dist/chunks/{chunk-NWVIUBJ3.js → chunk-BWA3COR4.js} +573 -1204
  10. package/dist/chunks/{chunk-G6WW2656.js → chunk-HLCTINEP.js} +1 -1
  11. package/dist/chunks/{chunk-65BVQIHR.js → chunk-IATK3CIP.js} +6 -6
  12. package/dist/chunks/{chunk-MSX2J653.js → chunk-ICLLW5QD.js} +1 -1
  13. package/dist/chunks/chunk-KSRWKXRB.js +12 -0
  14. package/dist/chunks/{chunk-GALAW74P.js → chunk-KT2BB4AR.js} +30 -11
  15. package/dist/chunks/{chunk-NWQTF6O4.js → chunk-LNKYEHIJ.js} +28 -3
  16. package/dist/chunks/{chunk-7IPF4X3K.js → chunk-MHBQWGCL.js} +1003 -56
  17. package/dist/chunks/chunk-MUMPWI4O.js +92 -0
  18. package/dist/chunks/{chunk-CJSD5ADV.js → chunk-N6E6YSTP.js} +2 -2
  19. package/dist/chunks/{chunk-TYYR7KWY.js → chunk-PGET7EZF.js} +7 -5
  20. package/dist/chunks/chunk-T4QXKU5Y.js +1 -0
  21. package/dist/chunks/{chunk-YLULZMWE.js → chunk-VFL2JVBZ.js} +48 -121
  22. package/dist/chunks/{chunk-MJT7EW5Q.js → chunk-WL76II47.js} +1 -1
  23. package/dist/chunks/{chunk-3JJBNVNX.js → chunk-WTA33KQQ.js} +23 -19
  24. package/dist/chunks/{chunk-LHYK6QO2.js → chunk-XEJ7XXUQ.js} +1 -0
  25. package/dist/chunks/{chunk-YRFQKUZD.js → chunk-ZPL4GZML.js} +88 -3
  26. package/dist/chunks/chunk-ZZ4ECDTP.js +668 -0
  27. package/dist/chunks/{cli-RJHEE4FA.js → cli-5NFIDCRL.js} +5 -5
  28. package/dist/chunks/cli-AS3DZ7XB.js +23 -0
  29. package/dist/chunks/{core-commands-CQSPCVC4.js → core-commands-7223BEQL.js} +7 -4
  30. package/dist/chunks/{extend-4MZ75SHV.js → extend-6N5S5KA5.js} +18 -13
  31. package/dist/chunks/extension-FJSVCG2Q.js +121 -0
  32. package/dist/chunks/{harness-YVZU42S7.js → harness-OMJXIGEW.js} +6 -3
  33. package/dist/chunks/{help-6SULYXVS.js → help-YPPROIS7.js} +2 -1
  34. package/dist/chunks/{opchat-XGS4AXHA.js → opchat-K7IYFPX6.js} +12 -7
  35. package/dist/chunks/{organizations-4GFQ3NT2.js → organizations-W3FMAWQ4.js} +8 -5
  36. package/dist/chunks/{profile-VWMBIHIE.js → profile-N4B3F7M7.js} +11 -8
  37. package/dist/chunks/{project-agent-SQ6ODALG.js → project-agent-NA6SGU25.js} +10 -7
  38. package/dist/chunks/{sandbox-command-34S2V7BN.js → sandbox-command-AKCFJKKN.js} +7 -4
  39. package/dist/chunks/{sandbox-template-VP5HMGQI.js → sandbox-template-5NKTAXCL.js} +11 -7
  40. package/dist/chunks/{src-X3R3XIJT.js → src-SCS4KKP4.js} +1997 -1247
  41. package/dist/chunks/{src-YHUMH6W6.js → src-XAVSQTVR.js} +3 -3
  42. package/dist/chunks/{teams-bot-EIVRNO5S.js → teams-bot-557NZC73.js} +6 -3
  43. package/dist/chunks/{workspaces-2N52V3BI.js → workspaces-S2H75XQB.js} +6 -2
  44. package/dist/cli.js +14 -11
  45. package/dist/index.js +27 -9
  46. package/dist/profile/local-profile-types.d.ts +1 -0
  47. package/dist/profile/profile-skills.d.ts +2 -0
  48. package/dist/web/assets/{AppDialog-j4N-gIi3.js → AppDialog-y6krB0MJ.js} +1 -1
  49. package/dist/web/assets/{AppsView-YiK3pqNb.js → AppsView-4F6oLktr.js} +1 -1
  50. package/dist/web/assets/{BrowserSidebar-BWFLEQ8j.js → BrowserSidebar-C0MHQaaD.js} +1 -1
  51. package/dist/web/assets/{CloudWorkView-3sS_1bhN.js → CloudWorkView-DwFIZoXy.js} +1 -1
  52. package/dist/web/assets/{CommandMenu-B11OrTCq.js → CommandMenu-CYGFrMZi.js} +1 -1
  53. package/dist/web/assets/{CommunityView-DXyXTWhP.js → CommunityView-C6n89YXz.js} +1 -1
  54. package/dist/web/assets/{ComposerCreateImproveStrip-3NoRiJJq.js → ComposerCreateImproveStrip-C3RwXC3_.js} +1 -1
  55. package/dist/web/assets/{GetStartedView-CPV5lgVH.js → GetStartedView-fwD0ZXo9.js} +1 -1
  56. package/dist/web/assets/LabSkillSidebar-BSuc-NJr.js +1 -0
  57. package/dist/web/assets/LabSkillSidebar-CVxtS9pd.css +1 -0
  58. package/dist/web/assets/LabsRoute-BrJkBNig.css +1 -0
  59. package/dist/web/assets/LabsRoute-D2y8dSsT.js +3 -0
  60. package/dist/web/assets/MainChatThread-Dj_Ry6WQ.js +2 -0
  61. package/dist/web/assets/{MakeAgentTutorialLearningPanel-C6F-kJQd.js → MakeAgentTutorialLearningPanel-Bz2VDaRe.js} +2 -2
  62. package/dist/web/assets/{MarkdownText-CkVkr3P1.js → MarkdownText-B6Nu_0Fz.js} +4 -4
  63. package/dist/web/assets/Messages-CH2wfv2a.js +10 -0
  64. package/dist/web/assets/NativeSkillSidebar-4iUfgwqO.js +1 -0
  65. package/dist/web/assets/{NewProjectDialog-DBMhkTvD.js → NewProjectDialog-8M5-6-h-.js} +1 -1
  66. package/dist/web/assets/{PostTrainingLearningPanel-B1Rp7iIL.js → PostTrainingLearningPanel-CdG5KHZs.js} +2 -2
  67. package/dist/web/assets/{PostTrainingStatusPill-BGZ9h5eW.js → PostTrainingStatusPill-COZ4qISQ.js} +1 -1
  68. package/dist/web/assets/{ProfileSettingsSection-2NOHEz2b.js → ProfileSettingsSection-Bo7dybgW.js} +1 -1
  69. package/dist/web/assets/RightChatPanelStack-Di8zoHJI.js +1 -0
  70. package/dist/web/assets/{SettingsView-uHhQnazW.css → SettingsView-BcYyY66X.css} +1 -1
  71. package/dist/web/assets/SettingsView-CAaKdDHt.js +6 -0
  72. package/dist/web/assets/{TeamChatView-qVscSedQ.js → TeamChatView-CzQYFBap.js} +2 -2
  73. package/dist/web/assets/{TerminalOverlay-Bkw7XqMy.js → TerminalOverlay-B56XmI-k.js} +1 -1
  74. package/dist/web/assets/{TrainingCreationPanel-COg1wvw8.js → TrainingCreationPanel-C_ntWAAP.js} +1 -1
  75. package/dist/web/assets/{TrainingDraftPanel-BNK2znmI.js → TrainingDraftPanel-3hJIxLNn.js} +1 -1
  76. package/dist/web/assets/{UsageSettingsSection-CUxBaohC.js → UsageSettingsSection-r3CqRm6Q.js} +1 -1
  77. package/dist/web/assets/WorkspaceDiffPanel-By8PWUuE.js +14 -0
  78. package/dist/web/assets/{WorkspaceEnvironmentMenu-C0-mH7oF.js → WorkspaceEnvironmentMenu-7wmC-spz.js} +1 -1
  79. package/dist/web/assets/{WorkspaceGitDialogs-VvC2px0X.js → WorkspaceGitDialogs-Hn--lsRh.js} +1 -1
  80. package/dist/web/assets/{WorkspaceMonacoEditor-CUIwoHv1.js → WorkspaceMonacoEditor-CHd22UcG.js} +3 -3
  81. package/dist/web/assets/{activity-BhqHgkfJ.js → activity-Dp5V58om.js} +1 -1
  82. package/dist/web/assets/{boxes-BpqUKFIm.js → boxes-DE7WJaEv.js} +1 -1
  83. package/dist/web/assets/{chevron-up-BTn9DVRP.js → chevron-up-BWx8gGab.js} +1 -1
  84. package/dist/web/assets/{circle-x-B9Qa8o5a.js → circle-x-DnmOfZIg.js} +1 -1
  85. package/dist/web/assets/{copy-BsYs6wBI.js → copy-BjWvaXLC.js} +1 -1
  86. package/dist/web/assets/{create-pipeline-request-vxgKoAvv.js → create-pipeline-request-C4V6-lWN.js} +1 -1
  87. package/dist/web/assets/{credit-card-BCePAwMI.js → credit-card-Bjg0Zaw5.js} +1 -1
  88. package/dist/web/assets/{cssMode-mw8RI2fR.js → cssMode-KFDYjM8T.js} +1 -1
  89. package/dist/web/assets/file-BrC1KjAS.js +1 -0
  90. package/dist/web/assets/{git-branch-C7qQYFIs.js → git-branch-DTeeARsp.js} +1 -1
  91. package/dist/web/assets/{git-commit-horizontal-CbX4pTOA.js → git-commit-horizontal-C8UFKlXy.js} +1 -1
  92. package/dist/web/assets/{htmlMode-Cc4Czh_M.js → htmlMode-NbLN3n6Q.js} +1 -1
  93. package/dist/web/assets/index-BcZetaeD.css +1 -0
  94. package/dist/web/assets/index-DQalqxbw.js +177 -0
  95. package/dist/web/assets/index-DYxF_d9i.js +1 -0
  96. package/dist/web/assets/{info-BNGbwgtq.js → info-BXBpMeVU.js} +1 -1
  97. package/dist/web/assets/{jsonMode-CbqbqGVz.js → jsonMode-Bl3r4LM_.js} +1 -1
  98. package/dist/web/assets/{lspLanguageFeatures-DKiKr5tH.js → lspLanguageFeatures-DH22w5XD.js} +1 -1
  99. package/dist/web/assets/{make-agent-tutorial-KKauJpB9.js → make-agent-tutorial-CAnTPFCK.js} +1 -1
  100. package/dist/web/assets/{monaco.contribution-D_-wu578.js → monaco.contribution-C0BsxYAz.js} +2 -2
  101. package/dist/web/assets/{monaco.contribution-CdUAX47Q.js → monaco.contribution-CzpXjm9j.js} +2 -2
  102. package/dist/web/assets/{monaco.contribution-Dxc4IxFM.js → monaco.contribution-DK-g-i00.js} +2 -2
  103. package/dist/web/assets/{monaco.contribution-CNgwz_pw.js → monaco.contribution-iAurj7TQ.js} +2 -2
  104. package/dist/web/assets/{monitor-CNdx_GTg.js → monitor-Ba2FFblh.js} +1 -1
  105. package/dist/web/assets/{play-BLRANWaV.js → play-2NDQ4-7K.js} +1 -1
  106. package/dist/web/assets/{python-DYgDMu2R.js → python-CQ0TmUW9.js} +1 -1
  107. package/dist/web/assets/{refresh-cw-BFjjwYNf.js → refresh-cw-CyermMsy.js} +1 -1
  108. package/dist/web/assets/{reply-D012cuZl.js → reply-BSExNLHM.js} +1 -1
  109. package/dist/web/assets/{rotate-cw-CTfjlR4G.js → rotate-cw-DJ-zYG9E.js} +1 -1
  110. package/dist/web/assets/{save-B1zYlRyN.js → save-Bij3cc3Q.js} +1 -1
  111. package/dist/web/assets/{toggleHighContrast-D9XFH7EA.js → toggleHighContrast-CxALivAH.js} +1 -1
  112. package/dist/web/assets/{tsMode-BU-tuKsY.js → tsMode-vH4lMS7h.js} +1 -1
  113. package/dist/web/assets/{upload-DU_81jvL.js → upload-DbJfEI3G.js} +1 -1
  114. package/dist/web/assets/{wifi-off-TPsBnbGW.js → wifi-off-BnVFBG28.js} +1 -1
  115. package/dist/web/assets/{workers-NhWySRdT.js → workers-CBihj7z6.js} +1 -1
  116. package/dist/web/assets/{yaml-DWXa_3ZL.js → yaml-Bhkoq1sy.js} +1 -1
  117. package/dist/web/index.html +2 -2
  118. package/docs/command-reference.md +15 -0
  119. package/package.json +1 -1
  120. package/dist/chunks/cli-4JA3SIDE.js +0 -19
  121. package/dist/web/assets/LabsRoute-B_nsEZXy.js +0 -3
  122. package/dist/web/assets/LabsRoute-dNN-odU0.css +0 -1
  123. package/dist/web/assets/MainChatThread-Dg7p3R2O.js +0 -2
  124. package/dist/web/assets/Messages-C7uHGoFw.js +0 -10
  125. package/dist/web/assets/NativeSkillSidebar-BPpoHjEW.js +0 -1
  126. package/dist/web/assets/RightChatPanelStack-CLK7_zKe.js +0 -1
  127. package/dist/web/assets/SettingsView-BAqea_KX.js +0 -6
  128. package/dist/web/assets/WorkspaceDiffPanel-qfFj8IRq.js +0 -14
  129. package/dist/web/assets/index-9tbdqUau.css +0 -1
  130. package/dist/web/assets/index-B_3z_lxq.js +0 -1
  131. 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-LHYK6QO2.js";
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 entries = await readdir(path.join(profileSourcePath2, PROFILE_SKILLS_DIR, directoryName), { withFileTypes: true });
132
- for (const entry of entries) {
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 fileStat = await stat(path.join(profileSourcePath2, skill.path)).catch(() => null);
189
- if (!fileStat?.isFile())
190
- continue;
191
- latest = Math.max(latest, fileStat.mtimeMs);
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 named = splitOptionalSkillName(args);
2921
+ const named2 = splitOptionalSkillName(args);
2903
2922
  return {
2904
2923
  action: "create",
2905
- objective: named.objective,
2906
- requestedName: named.name
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
- return { action: "help" };
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 are single-file profile instructions. If the workflow needs scripts, references, tools, or assets, create an agent instead."
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 skill package single-file: only SKILL.md.",
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
- "- Do not add references, scripts, assets, tool dependencies, or setup files. If those are needed, explain that this should be an agent instead.",
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 (path11, contents) => client.uploadFile((await resume()).id, path11, contents),
3547
- read: async (path11) => client.downloadFile((await resume()).id, path11),
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 (path11, input = {}) => client.deleteFile((await resume()).id, path11, input),
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 (path11) => client.statFile((await resume()).id, path11),
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, path11, contents) {
4057
- return this.uploadFileBase64(sandboxId, path11, Buffer.from(contents, "utf-8").toString("base64"));
4081
+ uploadFile(sandboxId, path13, contents) {
4082
+ return this.uploadFileBase64(sandboxId, path13, Buffer.from(contents, "utf-8").toString("base64"));
4058
4083
  }
4059
- uploadFileBase64(sandboxId, path11, contentsBase64) {
4084
+ uploadFileBase64(sandboxId, path13, contentsBase64) {
4060
4085
  return this.request(`/${encodeURIComponent(sandboxId)}/files`, {
4061
4086
  method: "POST",
4062
4087
  body: JSON.stringify({
4063
- path: path11,
4088
+ path: path13,
4064
4089
  contentsBase64
4065
4090
  })
4066
4091
  });
4067
4092
  }
4068
- async downloadFile(sandboxId, path11) {
4069
- const payload = await this.downloadFileResponse(sandboxId, path11);
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, path11, input = {}) {
4094
- const query = new URLSearchParams({ path: path11 });
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, path11) {
4102
- const query = new URLSearchParams({ stat: "1", path: path11 });
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(path11, init = {}) {
4279
- const response = await apiFetch(this.sandboxApiUrl, this.apiKey, path11, init);
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(path11, init = {}) {
4283
- const response = await apiFetch(this.apiRootUrl, this.apiKey, path11, init);
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 createHash3, randomUUID as randomUUID3 } from "node:crypto";
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 ?? randomUUID3)()}`;
5892
+ return `${base}:retry:${(input.randomId ?? randomUUID4)()}`;
5035
5893
  }
5036
5894
  function hashStableJson(value) {
5037
- return createHash3("sha256").update(stableJsonStringify(value)).digest("hex");
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 existsSync6 } from "node:fs";
5052
- import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
5053
- import path10 from "node:path";
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 = path10.join(goal.profileSourcePath, skillPath);
5065
- await mkdir2(path10.dirname(absolutePath), { recursive: true });
5066
- await writeFile3(absolutePath, renderSkillMarkdown({
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 = path10.join(goal.profileSourcePath, skillPath);
5079
- if (!existsSync6(absolutePath))
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 readFile6(absolutePath, "utf8");
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 writeFile3(absolutePath, renderSkillMarkdown({
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: path10.join(goal.profileSourceRelativePath, PROFILE_SKILLS_DIR, skillName, PROFILE_SKILL_FILE).replace(/\\/g, "/")
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) || existsSync6(path10.join(profileSourcePath2, PROFILE_SKILLS_DIR, 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 path10.join(PROFILE_SKILLS_DIR, skillName, PROFILE_SKILL_FILE).replace(/\\/g, "/");
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
- buildHostedRunIdempotencyKey,
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,