openpond 0.0.30-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -0
  4. package/RELEASE.md +20 -0
  5. package/dist/api/core.d.ts +22 -0
  6. package/dist/api/deployments.d.ts +30 -0
  7. package/dist/api/types.d.ts +624 -0
  8. package/dist/api.d.ts +90 -0
  9. package/dist/cache.d.ts +22 -0
  10. package/dist/chunks/app-layer-YKXEQGOW.js +163 -0
  11. package/dist/chunks/apps-GGSIN2ZF.js +539 -0
  12. package/dist/chunks/chunk-3CC6MH35.js +1663 -0
  13. package/dist/chunks/chunk-3Y4EL3P6.js +1090 -0
  14. package/dist/chunks/chunk-4HYJG2JE.js +3875 -0
  15. package/dist/chunks/chunk-5G4E547X.js +294 -0
  16. package/dist/chunks/chunk-74O6FGVN.js +598 -0
  17. package/dist/chunks/chunk-AUJLPKGI.js +632 -0
  18. package/dist/chunks/chunk-BYAPLT37.js +744 -0
  19. package/dist/chunks/chunk-FUP7EGT7.js +1845 -0
  20. package/dist/chunks/chunk-I6HN3ZGW.js +88 -0
  21. package/dist/chunks/chunk-KRKGHVMQ.js +294 -0
  22. package/dist/chunks/chunk-LHYK6QO2.js +1300 -0
  23. package/dist/chunks/chunk-NLBKZZEK.js +224 -0
  24. package/dist/chunks/chunk-OG75GMVO.js +1 -0
  25. package/dist/chunks/chunk-Q57SVPEH.js +11663 -0
  26. package/dist/chunks/chunk-UACZUFIA.js +47 -0
  27. package/dist/chunks/chunk-VQX33YAG.js +5135 -0
  28. package/dist/chunks/chunk-W5GKHH77.js +168 -0
  29. package/dist/chunks/chunk-WYDVQOBL.js +17581 -0
  30. package/dist/chunks/chunk-XSAXENQD.js +4167 -0
  31. package/dist/chunks/chunk-Y6G6WRTG.js +210 -0
  32. package/dist/chunks/cli-NVKNE4AZ.js +11 -0
  33. package/dist/chunks/cli-X3HPLSOV.js +19 -0
  34. package/dist/chunks/core-commands-IM2UKLU4.js +41 -0
  35. package/dist/chunks/extend-YXCBULPO.js +84 -0
  36. package/dist/chunks/harness-GMK6IY2U.js +140 -0
  37. package/dist/chunks/help-LMH5FGT2.js +65 -0
  38. package/dist/chunks/opchat-BWXZ32GX.js +169 -0
  39. package/dist/chunks/organizations-4ISQRGMQ.js +738 -0
  40. package/dist/chunks/profile-NLDZDT3L.js +1110 -0
  41. package/dist/chunks/project-agent-ZPVO25HA.js +892 -0
  42. package/dist/chunks/sandbox-command-5GFJ2U3V.js +1652 -0
  43. package/dist/chunks/sandbox-template-LZN4F55Z.js +1646 -0
  44. package/dist/chunks/src-3RBI4DNN.js +3595 -0
  45. package/dist/chunks/src-EYXQS6XD.js +73427 -0
  46. package/dist/chunks/teams-bot-WP3FHKGA.js +137 -0
  47. package/dist/cli.js +77 -0
  48. package/dist/client-types.d.ts +222 -0
  49. package/dist/client.d.ts +19 -0
  50. package/dist/config.d.ts +184 -0
  51. package/dist/hosted-chat.d.ts +162 -0
  52. package/dist/index.d.ts +5 -0
  53. package/dist/index.js +25481 -0
  54. package/dist/indicators.d.ts +38 -0
  55. package/dist/opchat-smoke.d.ts +47 -0
  56. package/dist/private-json-file.d.ts +3 -0
  57. package/dist/profile/local-profile-types.d.ts +160 -0
  58. package/dist/profile/local-profile.d.ts +69 -0
  59. package/dist/profile/profile-catalog.d.ts +14 -0
  60. package/dist/profile/profile-git.d.ts +17 -0
  61. package/dist/profile/profile-promotion.d.ts +21 -0
  62. package/dist/profile/profile-setup-gate.d.ts +22 -0
  63. package/dist/profile/profile-skills.d.ts +30 -0
  64. package/dist/profile/profile-source-upload.d.ts +29 -0
  65. package/dist/profile/source-upload-cache.d.ts +29 -0
  66. package/dist/sandbox/async-request-options.d.ts +2 -0
  67. package/dist/sandbox/client-handles.d.ts +113 -0
  68. package/dist/sandbox/client.d.ts +348 -0
  69. package/dist/sandbox/runtime-handle.d.ts +36 -0
  70. package/dist/sandbox/sandbox-instance-client.d.ts +72 -0
  71. package/dist/sandbox/smoke.d.ts +3 -0
  72. package/dist/sandbox/stream.d.ts +7 -0
  73. package/dist/sandbox/types/billing.d.ts +176 -0
  74. package/dist/sandbox/types/client.d.ts +103 -0
  75. package/dist/sandbox/types/core.d.ts +140 -0
  76. package/dist/sandbox/types/index.d.ts +14 -0
  77. package/dist/sandbox/types/io.d.ts +240 -0
  78. package/dist/sandbox/types/microsoft-teams-bot.d.ts +51 -0
  79. package/dist/sandbox/types/org-project-agent.d.ts +610 -0
  80. package/dist/sandbox/types/record.d.ts +53 -0
  81. package/dist/sandbox/types/responses.d.ts +295 -0
  82. package/dist/sandbox/types/runtime-profiles.d.ts +15 -0
  83. package/dist/sandbox/types/runtime.d.ts +152 -0
  84. package/dist/sandbox/types/schedules.d.ts +141 -0
  85. package/dist/sandbox/types/snapshots.d.ts +205 -0
  86. package/dist/sandbox/types/workflow-modes.d.ts +1 -0
  87. package/dist/sandbox/types/workload-sources.d.ts +20 -0
  88. package/dist/sandbox/url.d.ts +2 -0
  89. package/dist/sandbox-template/manifest.d.ts +304 -0
  90. package/dist/sandbox-template/manifest.js +18381 -0
  91. package/dist/skills/openpond-cli/SKILL.md +49 -0
  92. package/dist/skills/openpond-desktop-harness/SKILL.md +113 -0
  93. package/dist/skills/openpond-desktop-harness/agents/openai.yaml +4 -0
  94. package/dist/skills/openpond-taskset-authoring/SKILL.md +38 -0
  95. package/dist/skills/openpond-taskset-authoring/references/graders-and-rewards.md +11 -0
  96. package/dist/skills/openpond-taskset-authoring/references/method-selection.md +12 -0
  97. package/dist/skills/openpond-taskset-authoring/references/privacy-and-provenance.md +8 -0
  98. package/dist/skills/openpond-taskset-authoring/references/task-design.md +13 -0
  99. package/dist/stream.d.ts +20 -0
  100. package/dist/types.d.ts +57 -0
  101. package/dist/urls.d.ts +4 -0
  102. package/dist/web/assets/AppsView-Bu-Nc3Ew.js +1 -0
  103. package/dist/web/assets/AppsView-DIhprTIC.css +1 -0
  104. package/dist/web/assets/BrowserSidebar-CCguQ-DR.js +1 -0
  105. package/dist/web/assets/BrowserSidebar-D9i7gQzw.css +1 -0
  106. package/dist/web/assets/CloudWorkView-DNoPIlMj.css +1 -0
  107. package/dist/web/assets/CloudWorkView-Dr3xqLE5.js +1 -0
  108. package/dist/web/assets/CommandMenu-CnodJFHD.js +1 -0
  109. package/dist/web/assets/CommandMenu-hNQCm8mw.css +1 -0
  110. package/dist/web/assets/CommunityView-CIbjNUZr.js +1 -0
  111. package/dist/web/assets/CommunityView-DSik4sd2.css +1 -0
  112. package/dist/web/assets/GetStartedView-C3ZrnEz5.css +1 -0
  113. package/dist/web/assets/GetStartedView-C8nyH_PT.js +1 -0
  114. package/dist/web/assets/LabsExtensions-GtJzxDnK.js +1 -0
  115. package/dist/web/assets/LabsProfile-DG7rWYur.js +1 -0
  116. package/dist/web/assets/LabsRoute-D6MDHBzq.js +2 -0
  117. package/dist/web/assets/LabsRoute-TEt9j1vB.css +1 -0
  118. package/dist/web/assets/LabsSignals-BfsDN86w.js +1 -0
  119. package/dist/web/assets/LabsSignals-D6iA6Ryl.css +1 -0
  120. package/dist/web/assets/NewProjectDialog-CaKiCoVS.js +1 -0
  121. package/dist/web/assets/ProfileSettingsSection-DTQS2DVL.css +1 -0
  122. package/dist/web/assets/ProfileSettingsSection-DxEzoF3K.js +1 -0
  123. package/dist/web/assets/ProfileView-CEscW7Ru.js +1 -0
  124. package/dist/web/assets/SettingsView-DwbGu8Kj.css +1 -0
  125. package/dist/web/assets/SettingsView-JIRR_FO4.js +63 -0
  126. package/dist/web/assets/TeamChatView-B5M1ThhJ.js +3 -0
  127. package/dist/web/assets/TeamChatView-Bx44q3WV.css +1 -0
  128. package/dist/web/assets/TerminalOverlay-C1iP4OS1.js +19 -0
  129. package/dist/web/assets/TerminalOverlay-DwBiZLwk.css +1 -0
  130. package/dist/web/assets/TrainingSettingsSection-BDYvI2te.js +1 -0
  131. package/dist/web/assets/TrainingView-CoCFK6W3.js +5 -0
  132. package/dist/web/assets/WorkspaceDiffPanel-CMxKQeo7.js +14 -0
  133. package/dist/web/assets/WorkspaceEnvironmentMenu-B8JJbwIz.js +32 -0
  134. package/dist/web/assets/WorkspaceEnvironmentMenu-DGIFdG3o.css +1 -0
  135. package/dist/web/assets/WorkspaceGitDialogs-B4sp9BGn.js +1 -0
  136. package/dist/web/assets/WorkspaceMonacoEditor-BBqimG5X.js +845 -0
  137. package/dist/web/assets/WorkspaceMonacoEditor-BwKcXIZw.css +1 -0
  138. package/dist/web/assets/boxes-DAaDUabq.js +1 -0
  139. package/dist/web/assets/codicon-ngg6Pgfi.ttf +0 -0
  140. package/dist/web/assets/css.worker-DBVD8oXr.js +93 -0
  141. package/dist/web/assets/cssMode-CBF1mieJ.js +1 -0
  142. package/dist/web/assets/dockerfile-BG73LgW2.js +1 -0
  143. package/dist/web/assets/editor.worker-CdQrwHl8.js +26 -0
  144. package/dist/web/assets/git-commit-horizontal-CAickXpN.js +1 -0
  145. package/dist/web/assets/git-dialogs-BZDqS1eT.css +1 -0
  146. package/dist/web/assets/html.worker-CwpTb9lJ.js +470 -0
  147. package/dist/web/assets/htmlMode-2u8OHtWO.js +1 -0
  148. package/dist/web/assets/index-BdY6A-YN.css +1 -0
  149. package/dist/web/assets/index-CU2qhUYV.js +195 -0
  150. package/dist/web/assets/index-Dz2B3-4C.js +1 -0
  151. package/dist/web/assets/info-CQNTh78o.js +1 -0
  152. package/dist/web/assets/json.worker-BoL8UZqY.js +58 -0
  153. package/dist/web/assets/jsonMode-C4CWXViX.js +7 -0
  154. package/dist/web/assets/lspLanguageFeatures-CBs_BOlB.js +4 -0
  155. package/dist/web/assets/markdown-BFxVWTOG.js +1 -0
  156. package/dist/web/assets/monaco.contribution-C1LUxAKf.js +2 -0
  157. package/dist/web/assets/monaco.contribution-Cpf6dWsh.js +2 -0
  158. package/dist/web/assets/monaco.contribution-lOXpG2xQ.js +2 -0
  159. package/dist/web/assets/monaco.contribution-sd3cFkcN.js +2 -0
  160. package/dist/web/assets/monitor-DsIswrMV.js +1 -0
  161. package/dist/web/assets/play-CeRw21NV.js +1 -0
  162. package/dist/web/assets/python-CXYq3BUl.js +1 -0
  163. package/dist/web/assets/refresh-cw-N5b_6AP5.js +1 -0
  164. package/dist/web/assets/reply-BSKjZrJg.js +1 -0
  165. package/dist/web/assets/save-D0oHH1a3.js +1 -0
  166. package/dist/web/assets/settings-lists-BEg6LMcy.css +1 -0
  167. package/dist/web/assets/shell-DiJ1NA_G.js +1 -0
  168. package/dist/web/assets/toggleHighContrast-B7NOMXDY.css +1 -0
  169. package/dist/web/assets/toggleHighContrast-DRjlcwes.js +62 -0
  170. package/dist/web/assets/training-model-data-BPrc68VG.js +1 -0
  171. package/dist/web/assets/ts.worker-BH9nVgjN.js +67718 -0
  172. package/dist/web/assets/tsMode-x-1A7ZcB.js +11 -0
  173. package/dist/web/assets/wifi-off-Dt52ofzh.js +1 -0
  174. package/dist/web/assets/workers-Ciff6Dzi.js +1 -0
  175. package/dist/web/assets/yaml-CB67Kxnz.js +1 -0
  176. package/dist/web/connected-apps/github.svg +3 -0
  177. package/dist/web/connected-apps/google.svg +6 -0
  178. package/dist/web/connected-apps/linear.svg +3 -0
  179. package/dist/web/connected-apps/microsoft.svg +6 -0
  180. package/dist/web/connected-apps/notion.svg +3 -0
  181. package/dist/web/connected-apps/openpond-mcp.svg +4 -0
  182. package/dist/web/connected-apps/slack.svg +6 -0
  183. package/dist/web/connected-apps/x.svg +3 -0
  184. package/dist/web/favicon-16x16.png +0 -0
  185. package/dist/web/favicon-32x32.png +0 -0
  186. package/dist/web/index.html +16 -0
  187. package/dist/web/openpond-icon.png +0 -0
  188. package/dist/web/openpond-wordlogo-white.png +0 -0
  189. package/dist/web/tokens/eth.svg +1 -0
  190. package/dist/web/tokens/usdc.svg +1 -0
  191. package/docs/agent-edit-check-status-json.md +132 -0
  192. package/docs/command-reference.md +695 -0
  193. package/package.json +84 -0
@@ -0,0 +1,1646 @@
1
+ import { createRequire as __openpondCreateRequire } from "node:module"; var require = __openpondCreateRequire(import.meta.url);
2
+ import {
3
+ createSandboxFromPlan,
4
+ summarizeSandbox
5
+ } from "./chunk-AUJLPKGI.js";
6
+ import {
7
+ resolveGitBranch
8
+ } from "./chunk-74O6FGVN.js";
9
+ import "./chunk-OG75GMVO.js";
10
+ import {
11
+ OPENPOND_MANIFEST_FILE_NAME,
12
+ SANDBOX_TEMPLATE_BUILD_PLAN_FILE_NAME,
13
+ SANDBOX_TEMPLATE_BUILD_PLAN_KIND,
14
+ formatSandboxTemplateDiagnostics,
15
+ sandboxTemplateBuildMetadata,
16
+ sandboxTemplateBuildPlan,
17
+ sandboxTemplateExecutableEntries,
18
+ sandboxTemplateJsonSchema,
19
+ sandboxTemplateScaffoldFiles,
20
+ validateSandboxTemplateYaml
21
+ } from "./chunk-BYAPLT37.js";
22
+ import "./chunk-WYDVQOBL.js";
23
+ import {
24
+ createRepo,
25
+ ensureApiKey,
26
+ formatTokenizedRepoUrl,
27
+ getGitRemoteUrl,
28
+ loadConfig,
29
+ parseSandboxRuntimePromotionPolicyOption,
30
+ parseSandboxTemplateEnvOptions,
31
+ parseSandboxWorkflowModeOption,
32
+ promptConfirm,
33
+ redactToken,
34
+ resolveRepoUrl,
35
+ resolveSandboxClient,
36
+ runCommand,
37
+ runShellCommand,
38
+ warnOnRepoHostMismatch
39
+ } from "./chunk-XSAXENQD.js";
40
+ import {
41
+ resolveBaseUrl,
42
+ resolvePublicApiBaseUrl
43
+ } from "./chunk-5G4E547X.js";
44
+ import {
45
+ parseBooleanOption,
46
+ parseIntegerOption,
47
+ parseJsonOption
48
+ } from "./chunk-3Y4EL3P6.js";
49
+ import "./chunk-UACZUFIA.js";
50
+
51
+ // src/cli/sandbox-template.ts
52
+ import { promises as fs3, existsSync as existsSync2 } from "node:fs";
53
+ import path4 from "node:path";
54
+
55
+ // src/cli/sandbox-template-local.ts
56
+ import { Buffer as Buffer2 } from "node:buffer";
57
+ import { promises as fs2 } from "node:fs";
58
+ import path3 from "node:path";
59
+
60
+ // src/cli/sandbox-template-start.ts
61
+ import { Buffer } from "node:buffer";
62
+ import { promises as fs } from "node:fs";
63
+ import path2 from "node:path";
64
+
65
+ // src/cli/sandbox-template-schedules.ts
66
+ async function createSandboxTemplateStartSchedules(client, sandboxId, manifest, options) {
67
+ const selection = resolveSandboxTemplateStartScheduleSelection(
68
+ manifest,
69
+ options
70
+ );
71
+ if (!selection) {
72
+ return [];
73
+ }
74
+ const overrides = parseSandboxTemplateScheduleOverrides(manifest, options);
75
+ const projectId = typeof options.projectId === "string" && options.projectId.trim() ? options.projectId.trim() : typeof options.runtimeProjectId === "string" ? options.runtimeProjectId.trim() : "";
76
+ const teamId = typeof options.teamId === "string" ? options.teamId.trim() : "";
77
+ const created = [];
78
+ for (const schedule of selection.schedules) {
79
+ const input2 = buildSandboxTemplateStartScheduleInput({
80
+ manifest,
81
+ schedule,
82
+ override: overrides.get(schedule.name),
83
+ mode: selection.mode,
84
+ sourceSandboxId: sandboxId,
85
+ teamId,
86
+ projectId
87
+ });
88
+ const result = await client.createSchedule(input2);
89
+ created.push({
90
+ id: result.schedule.id,
91
+ name: result.schedule.name,
92
+ enabled: result.schedule.enabled,
93
+ scheduleType: result.schedule.scheduleType,
94
+ scheduleExpression: result.schedule.scheduleExpression,
95
+ syncStatus: result.schedule.syncStatus,
96
+ awsScheduleArn: result.schedule.awsScheduleArn ?? null,
97
+ target: result.schedule.target
98
+ });
99
+ }
100
+ return created;
101
+ }
102
+ function resolveSandboxTemplateStartScheduleSelection(manifest, options) {
103
+ const enableRaw = options.enableSchedules ?? options.enableSchedule;
104
+ const disableRaw = options.disableSchedules ?? options.disableSchedule;
105
+ if (enableRaw !== void 0 && disableRaw !== void 0) {
106
+ throw new Error(
107
+ "pass only one of --enable-schedules or --disable-schedules"
108
+ );
109
+ }
110
+ let mode = null;
111
+ let rawSelection;
112
+ if (enableRaw !== void 0) {
113
+ mode = "enabled";
114
+ rawSelection = enableRaw;
115
+ } else if (disableRaw !== void 0) {
116
+ mode = "disabled";
117
+ rawSelection = disableRaw;
118
+ } else if (options.schedules !== void 0) {
119
+ rawSelection = options.schedules;
120
+ const rawMode = typeof options.scheduleMode === "string" ? options.scheduleMode.trim().toLowerCase() : "disabled";
121
+ if (rawMode === "none") {
122
+ return null;
123
+ }
124
+ if (rawMode !== "enabled" && rawMode !== "disabled") {
125
+ throw new Error("--schedule-mode must be enabled, disabled, or none");
126
+ }
127
+ mode = rawMode;
128
+ }
129
+ if (!mode) {
130
+ return null;
131
+ }
132
+ if (manifest.schedules.length === 0) {
133
+ return { mode, schedules: [] };
134
+ }
135
+ const selectedNames = parseSandboxTemplateScheduleNameSelection(
136
+ rawSelection,
137
+ manifest
138
+ );
139
+ const schedules = selectedNames === null ? manifest.schedules : manifest.schedules.filter(
140
+ (schedule) => selectedNames.has(schedule.name)
141
+ );
142
+ return { mode, schedules };
143
+ }
144
+ function parseSandboxTemplateScheduleNameSelection(value, manifest) {
145
+ if (value === void 0 || value === true) {
146
+ return null;
147
+ }
148
+ const raw = String(value).trim();
149
+ if (!raw || raw === "true" || raw.toLowerCase() === "all") {
150
+ return null;
151
+ }
152
+ if (raw.toLowerCase() === "none") {
153
+ return /* @__PURE__ */ new Set();
154
+ }
155
+ const known = new Set(manifest.schedules.map((schedule) => schedule.name));
156
+ const selected = new Set(
157
+ raw.split(",").map((item) => item.trim()).filter(Boolean)
158
+ );
159
+ for (const name of selected) {
160
+ if (!known.has(name)) {
161
+ throw new Error(`manifest schedule not found: ${name}`);
162
+ }
163
+ }
164
+ return selected;
165
+ }
166
+ function parseSandboxTemplateScheduleOverrides(manifest, options) {
167
+ const raw = typeof options.scheduleOverrides === "string" ? options.scheduleOverrides : typeof options.scheduleOverride === "string" ? options.scheduleOverride : "";
168
+ if (!raw.trim()) {
169
+ return /* @__PURE__ */ new Map();
170
+ }
171
+ const parsed = parseJsonOption(raw, "schedule-overrides");
172
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
173
+ throw new Error(
174
+ "schedule-overrides must be a JSON object keyed by schedule name"
175
+ );
176
+ }
177
+ const known = new Set(manifest.schedules.map((schedule) => schedule.name));
178
+ const out = /* @__PURE__ */ new Map();
179
+ for (const [name, override] of Object.entries(parsed)) {
180
+ if (!known.has(name)) {
181
+ throw new Error(`schedule override target does not exist: ${name}`);
182
+ }
183
+ if (!override || typeof override !== "object" || Array.isArray(override)) {
184
+ throw new Error(`schedule override for ${name} must be an object`);
185
+ }
186
+ out.set(name, override);
187
+ }
188
+ return out;
189
+ }
190
+ function buildSandboxTemplateStartScheduleInput(params) {
191
+ const schedule = mergeSandboxTemplateScheduleOverride(
192
+ params.schedule,
193
+ params.override
194
+ );
195
+ const expression = sandboxTemplateScheduleExpression(schedule);
196
+ return {
197
+ sourceSandboxId: params.sourceSandboxId,
198
+ ...params.teamId ? { teamId: params.teamId } : {},
199
+ ...params.projectId ? { projectId: params.projectId } : {},
200
+ name: schedule.name,
201
+ ...schedule.description ? { description: schedule.description } : {},
202
+ ...expression,
203
+ ...schedule.timezone ? { timezone: schedule.timezone } : {},
204
+ enabled: typeof params.override?.enabled === "boolean" ? params.override.enabled : params.mode === "enabled",
205
+ ...schedule.startAt ? { startAt: schedule.startAt } : {},
206
+ ...schedule.endAt ? { endAt: schedule.endAt } : {},
207
+ ...schedule.maxRuns !== void 0 ? { maxRuns: schedule.maxRuns } : {},
208
+ runtimePolicy: schedule.runtimePolicy,
209
+ target: sandboxTemplateScheduleCommandTarget(params.manifest, schedule),
210
+ ...schedule.budget ? { budget: schedule.budget } : {},
211
+ ...schedule.resources ? { resources: schedule.resources } : {},
212
+ ...schedule.quotas ? { quotas: schedule.quotas } : {},
213
+ ...schedule.lifecycle ? { lifecycle: schedule.lifecycle } : {},
214
+ ...schedule.retentionPolicy ? { retentionPolicy: schedule.retentionPolicy } : {},
215
+ ...schedule.env ? { env: schedule.env } : {},
216
+ ...schedule.integrationLeases ? {
217
+ integrationLeases: schedule.integrationLeases
218
+ } : {},
219
+ metadata: {
220
+ ...schedule.metadata ?? {},
221
+ manifestScheduleName: schedule.name,
222
+ source: "openpond-code-sandbox-template-start"
223
+ },
224
+ managementSource: "openpond.yaml",
225
+ manifestPath: OPENPOND_MANIFEST_FILE_NAME
226
+ };
227
+ }
228
+ function mergeSandboxTemplateScheduleOverride(schedule, override) {
229
+ if (!override) {
230
+ return schedule;
231
+ }
232
+ return {
233
+ ...schedule,
234
+ ...override,
235
+ target: schedule.target || override.target ? {
236
+ ...schedule.target ?? {},
237
+ ...override.target ?? {}
238
+ } : void 0,
239
+ metadata: schedule.metadata || override.metadata ? {
240
+ ...schedule.metadata ?? {},
241
+ ...override.metadata ?? {}
242
+ } : void 0
243
+ };
244
+ }
245
+ function sandboxTemplateScheduleExpression(schedule) {
246
+ if (schedule.scheduleExpression) {
247
+ return {
248
+ scheduleType: schedule.scheduleType ?? "cron",
249
+ scheduleExpression: schedule.scheduleExpression
250
+ };
251
+ }
252
+ if (schedule.rate) {
253
+ return {
254
+ scheduleType: "rate",
255
+ scheduleExpression: /^rate\(/i.test(schedule.rate) ? schedule.rate : `rate(${schedule.rate})`
256
+ };
257
+ }
258
+ if (schedule.once) {
259
+ return {
260
+ scheduleType: "once",
261
+ scheduleExpression: /^at\(/i.test(schedule.once) ? schedule.once : `at(${schedule.once})`
262
+ };
263
+ }
264
+ return {
265
+ scheduleType: "cron",
266
+ scheduleExpression: schedule.cron ?? ""
267
+ };
268
+ }
269
+ function sandboxTemplateScheduleCommandTarget(manifest, schedule) {
270
+ const explicitCommand = schedule.command ?? schedule.target?.command ?? null;
271
+ if (explicitCommand) {
272
+ return {
273
+ kind: "command",
274
+ command: explicitCommand,
275
+ requiresStart: schedule.requiresStart ?? schedule.target?.requiresStart ?? false
276
+ };
277
+ }
278
+ const targetKind = schedule.target?.kind ?? "action";
279
+ if (targetKind === "start") {
280
+ return {
281
+ kind: "command",
282
+ command: manifest.start.command,
283
+ requiresStart: schedule.requiresStart ?? schedule.target?.requiresStart ?? manifest.start.requiresStart ?? false
284
+ };
285
+ }
286
+ if (targetKind === "service") {
287
+ const serviceName = schedule.target?.name ?? "";
288
+ const service = manifest.services.find((item) => item.name === serviceName);
289
+ if (!service) {
290
+ throw new Error(`schedule service target does not exist: ${serviceName}`);
291
+ }
292
+ return {
293
+ kind: "command",
294
+ command: service.command,
295
+ requiresStart: schedule.requiresStart ?? schedule.target?.requiresStart ?? service.requiresStart ?? false
296
+ };
297
+ }
298
+ const actionName = schedule.actionName ?? schedule.action ?? schedule.target?.actionName ?? (schedule.target?.kind === "action" ? schedule.target?.name : void 0);
299
+ const action = manifest.actions.find((item) => item.name === actionName);
300
+ if (!action) {
301
+ throw new Error(
302
+ `schedule action target does not exist: ${actionName ?? ""}`
303
+ );
304
+ }
305
+ return {
306
+ kind: "command",
307
+ command: action.command,
308
+ requiresStart: schedule.requiresStart ?? schedule.target?.requiresStart ?? action.requiresStart ?? false
309
+ };
310
+ }
311
+
312
+ // src/cli/sandbox-template-git.ts
313
+ import { existsSync } from "node:fs";
314
+ import path from "node:path";
315
+ import { stdin as input } from "node:process";
316
+ async function resolveSandboxTemplateStartRepo(manifest, options, projectPath) {
317
+ const explicitRepo = typeof options.repo === "string" ? options.repo.trim() : "";
318
+ if (explicitRepo) {
319
+ return normalizeSandboxTemplateRepoUrl(explicitRepo);
320
+ }
321
+ await ensureGitRepository(projectPath);
322
+ const config = await loadConfig();
323
+ const uiBase = resolveBaseUrl(config);
324
+ const apiBase = resolvePublicApiBaseUrl(config);
325
+ const apiKey = await ensureApiKey(config, uiBase);
326
+ let originUrl = await getGitRemoteUrl(projectPath, "origin");
327
+ if (!originUrl) {
328
+ const teamId = typeof options.teamId === "string" ? options.teamId.trim() : "";
329
+ const response = await createRepo(apiBase, apiKey, {
330
+ name: manifest.name,
331
+ description: manifest.description,
332
+ repoInit: "empty",
333
+ ...teamId ? { teamId } : {}
334
+ });
335
+ originUrl = resolveRepoUrl(response);
336
+ const remoteResult = await runCommand(
337
+ "git",
338
+ ["remote", "add", "origin", originUrl],
339
+ {
340
+ cwd: projectPath
341
+ }
342
+ );
343
+ if (remoteResult.code !== 0) {
344
+ throw new Error(
345
+ `git remote add failed: ${remoteResult.stderr.trim() || remoteResult.stdout.trim() || "unknown error"}`
346
+ );
347
+ }
348
+ }
349
+ warnOnRepoHostMismatch(originUrl);
350
+ await ensureGitCommitForSandboxTemplateStart(projectPath, options);
351
+ if (!parseBooleanOption(options.noPush)) {
352
+ const branch = await resolveSandboxTemplateStartBranch(
353
+ projectPath,
354
+ options
355
+ );
356
+ await pushGitBranchForSandboxTemplateStart(
357
+ projectPath,
358
+ originUrl,
359
+ branch,
360
+ apiKey,
361
+ options
362
+ );
363
+ }
364
+ return normalizeSandboxTemplateRepoUrl(originUrl);
365
+ }
366
+ async function ensureGitRepository(projectPath) {
367
+ const gitDir = path.join(projectPath, ".git");
368
+ if (existsSync(gitDir)) return;
369
+ const result = await runCommand("git", ["init"], { cwd: projectPath });
370
+ if (result.code !== 0) {
371
+ throw new Error(
372
+ `git init failed: ${result.stderr.trim() || result.stdout.trim() || "unknown error"}`
373
+ );
374
+ }
375
+ }
376
+ async function ensureGitCommitForSandboxTemplateStart(projectPath, options) {
377
+ const status = await runCommand("git", ["status", "--porcelain"], {
378
+ cwd: projectPath
379
+ });
380
+ if (status.code !== 0) {
381
+ throw new Error(
382
+ `git status failed: ${status.stderr.trim() || status.stdout.trim() || "unknown error"}`
383
+ );
384
+ }
385
+ const head = await runCommand("git", ["rev-parse", "--verify", "HEAD"], {
386
+ cwd: projectPath
387
+ });
388
+ const hasHead = head.code === 0;
389
+ const dirty = status.stdout.trim().length > 0;
390
+ if (!dirty && hasHead) return;
391
+ const shouldCommit = parseBooleanOption(options.commit) || parseBooleanOption(options.yes) || (input.isTTY ? await promptConfirm(
392
+ hasHead ? "Commit local changes before starting the sandbox?" : "Create the initial git commit before starting the sandbox?",
393
+ !hasHead
394
+ ) : false);
395
+ if (!shouldCommit) {
396
+ if (!hasHead) {
397
+ throw new Error(
398
+ "local repository has no commits; pass --commit or commit before starting"
399
+ );
400
+ }
401
+ if (dirty) {
402
+ console.warn(
403
+ "warning: uncommitted local changes will not be included in the sandbox"
404
+ );
405
+ }
406
+ return;
407
+ }
408
+ const add = await runCommand("git", ["add", "-A"], { cwd: projectPath });
409
+ if (add.code !== 0) {
410
+ throw new Error(
411
+ `git add failed: ${add.stderr.trim() || add.stdout.trim() || "unknown error"}`
412
+ );
413
+ }
414
+ const message = typeof options.commitMessage === "string" && options.commitMessage.trim() ? options.commitMessage.trim() : `start ${OPENPOND_MANIFEST_FILE_NAME} sandbox`;
415
+ const commit = await runCommand("git", ["commit", "-m", message], {
416
+ cwd: projectPath
417
+ });
418
+ if (commit.code !== 0) {
419
+ const output = commit.stderr.trim() || commit.stdout.trim();
420
+ if (output.includes("nothing to commit") && hasHead) return;
421
+ throw new Error(`git commit failed: ${output || "unknown error"}`);
422
+ }
423
+ }
424
+ async function resolveSandboxTemplateStartBranch(projectPath, options) {
425
+ const branchOption = typeof options.branch === "string" ? options.branch.trim() : "";
426
+ const branch = branchOption || await resolveGitBranch(projectPath);
427
+ if (!branch) {
428
+ throw new Error("unable to resolve git branch; pass --branch");
429
+ }
430
+ return branch;
431
+ }
432
+ async function pushGitBranchForSandboxTemplateStart(projectPath, originUrl, branch, apiKey, options) {
433
+ let tokenRemote;
434
+ try {
435
+ tokenRemote = formatTokenizedRepoUrl(originUrl, apiKey);
436
+ } catch {
437
+ throw new Error("origin remote must be https for tokenized pushes");
438
+ }
439
+ const keepTokenRemote = parseBooleanOption(options.keepTokenRemote) || parseBooleanOption(options.token) || parseBooleanOption(options.setRemoteToken);
440
+ const alreadyTokenized = originUrl.includes("x-access-token:");
441
+ const restoreUrl = !keepTokenRemote && !alreadyTokenized ? originUrl : null;
442
+ const previousPrompt = process.env.GIT_TERMINAL_PROMPT;
443
+ process.env.GIT_TERMINAL_PROMPT = "0";
444
+ try {
445
+ if (!alreadyTokenized) {
446
+ const setResult = await runCommand(
447
+ "git",
448
+ ["remote", "set-url", "origin", tokenRemote],
449
+ {
450
+ cwd: projectPath
451
+ }
452
+ );
453
+ if (setResult.code !== 0) {
454
+ throw new Error(
455
+ `git remote set-url failed: ${redactToken(
456
+ setResult.stderr.trim() || setResult.stdout.trim() || "unknown error"
457
+ )}`
458
+ );
459
+ }
460
+ }
461
+ const push = await runCommand("git", ["push", "-u", "origin", branch], {
462
+ cwd: projectPath,
463
+ inherit: true
464
+ });
465
+ if (push.code !== 0) {
466
+ throw new Error("git push failed");
467
+ }
468
+ } finally {
469
+ if (restoreUrl) {
470
+ await runCommand("git", ["remote", "set-url", "origin", restoreUrl], {
471
+ cwd: projectPath
472
+ }).catch(() => null);
473
+ }
474
+ if (previousPrompt === void 0) {
475
+ delete process.env.GIT_TERMINAL_PROMPT;
476
+ } else {
477
+ process.env.GIT_TERMINAL_PROMPT = previousPrompt;
478
+ }
479
+ }
480
+ }
481
+ function normalizeSandboxTemplateRepoIdentity(repoUrl) {
482
+ return normalizeSandboxTemplateRepoUrl(repoUrl).replace(/\.git$/, "");
483
+ }
484
+ function normalizeSandboxTemplateRepoUrl(repoUrl) {
485
+ const parsed = new URL(repoUrl);
486
+ parsed.username = "";
487
+ parsed.password = "";
488
+ const text = parsed.toString();
489
+ return text.endsWith(".git") ? text : `${text.replace(/\/$/, "")}.git`;
490
+ }
491
+
492
+ // src/cli/sandbox-template-start.ts
493
+ async function runSandboxTemplateStart(options) {
494
+ const filePath = resolveSandboxTemplateFilePath(options);
495
+ const projectPath = path2.dirname(filePath);
496
+ const source = await fs.readFile(filePath, "utf8");
497
+ const result = validateSandboxTemplateYaml(source);
498
+ if (!result.ok) {
499
+ console.error(formatSandboxTemplateDiagnostics(result.diagnostics));
500
+ process.exitCode = 1;
501
+ return;
502
+ }
503
+ const manifest = result.manifest;
504
+ const executable = resolveSandboxTemplateStartExecutable(manifest, options);
505
+ const input2 = await resolveSandboxTemplateStartInput(
506
+ manifest,
507
+ options,
508
+ projectPath
509
+ );
510
+ const repo = await resolveSandboxTemplateStartRepo(
511
+ manifest,
512
+ options,
513
+ projectPath
514
+ );
515
+ const client = await resolveSandboxClient(options);
516
+ const createInput = buildSandboxTemplateStartCreateInput(
517
+ manifest,
518
+ options,
519
+ repo
520
+ );
521
+ const sandbox = await createSandboxTemplateStartSandbox(
522
+ client,
523
+ createInput,
524
+ repo
525
+ );
526
+ await waitForSandboxTemplateRunnerReady(client, sandbox.id);
527
+ const setupCommands = await runSandboxTemplateSetupCommands(
528
+ client,
529
+ sandbox.id,
530
+ manifest,
531
+ options
532
+ );
533
+ const uploadedFiles = await uploadSandboxTemplateStartFiles(
534
+ client,
535
+ sandbox.id,
536
+ input2.uploadRequests
537
+ );
538
+ const commandInput = {
539
+ ...input2.scalars,
540
+ ...formatUploadedFileParams(uploadedFiles, input2.uploadRequests)
541
+ };
542
+ const execution = await runSandboxTemplateExecutable(
543
+ client,
544
+ sandbox.id,
545
+ executable,
546
+ commandInput,
547
+ sandboxTemplateRuntimeEnv(sandbox)
548
+ );
549
+ const previews = await openSandboxTemplatePorts(
550
+ client,
551
+ sandbox.id,
552
+ executable.ports
553
+ );
554
+ const schedules = await createSandboxTemplateStartSchedules(
555
+ client,
556
+ sandbox.id,
557
+ manifest,
558
+ options
559
+ );
560
+ console.log(
561
+ JSON.stringify(
562
+ {
563
+ ok: true,
564
+ file: filePath,
565
+ repo,
566
+ executable: {
567
+ kind: executable.kind,
568
+ name: executable.name,
569
+ command: executable.command
570
+ },
571
+ sandbox: summarizeSandbox(sandbox),
572
+ setupCommands,
573
+ uploadedFiles,
574
+ input: commandInput,
575
+ execution,
576
+ previews,
577
+ schedules,
578
+ expectedArtifacts: executable.artifactPaths
579
+ },
580
+ null,
581
+ 2
582
+ )
583
+ );
584
+ }
585
+ function resolveSandboxTemplateStartExecutable(manifest, options) {
586
+ const executables = sandboxTemplateExecutableEntries(manifest);
587
+ const action = typeof options.action === "string" ? options.action.trim() : "";
588
+ const service = typeof options.service === "string" ? options.service.trim() : "";
589
+ const target = typeof options.target === "string" ? options.target.trim() : "";
590
+ const entrypoint = typeof options.entrypoint === "string" ? options.entrypoint.trim() : "";
591
+ const requested = action || service || target || entrypoint || "start";
592
+ const kind = action ? "action" : service ? "service" : "";
593
+ const match = executables.find(
594
+ (candidate) => candidate.name === requested && (!kind || candidate.kind === kind)
595
+ );
596
+ if (!match) {
597
+ throw new Error(
598
+ `manifest executable not found: ${requested}. Available: ${executables.map((candidate) => `${candidate.kind}:${candidate.name}`).join(", ")}`
599
+ );
600
+ }
601
+ return match;
602
+ }
603
+ function buildSandboxTemplateStartCreateInput(manifest, options, repo) {
604
+ const budgetUsd = typeof options.budgetUsd === "string" && options.budgetUsd.trim() ? options.budgetUsd.trim() : typeof options.budget === "string" && options.budget.trim() ? options.budget.trim() : "0.05";
605
+ const maxDurationSeconds = parseIntegerOption(
606
+ options.maxDurationSeconds,
607
+ "max-duration-seconds"
608
+ );
609
+ const idleTimeoutSeconds = parseIntegerOption(
610
+ options.idleTimeoutSeconds,
611
+ "idle-timeout-seconds"
612
+ );
613
+ const teamId = typeof options.teamId === "string" ? options.teamId.trim() : "";
614
+ const requestedProjectId = typeof options.projectId === "string" ? options.projectId.trim() : "";
615
+ const sandboxRuntimeProjectId = typeof options.runtimeProjectId === "string" ? options.runtimeProjectId.trim() : "";
616
+ if (requestedProjectId && sandboxRuntimeProjectId && requestedProjectId !== sandboxRuntimeProjectId) {
617
+ throw new Error(
618
+ "project-id and runtime-project-id must match when both are set"
619
+ );
620
+ }
621
+ const projectId = requestedProjectId || sandboxRuntimeProjectId;
622
+ const requestedAgentId = typeof options.agentId === "string" ? options.agentId.trim() : "";
623
+ const sandboxRuntimeAgentId = typeof options.runtimeAgentId === "string" ? options.runtimeAgentId.trim() : "";
624
+ if (requestedAgentId && sandboxRuntimeAgentId && requestedAgentId !== sandboxRuntimeAgentId) {
625
+ throw new Error(
626
+ "agent-id and runtime-agent-id must match when both are set"
627
+ );
628
+ }
629
+ const agentId = requestedAgentId || sandboxRuntimeAgentId;
630
+ const sandboxWorkflowMode = parseSandboxWorkflowModeOption(options.workflowMode);
631
+ const sandboxRuntimePromotionPolicy = parseSandboxRuntimePromotionPolicyOption(options.runtimePromotionPolicy);
632
+ const sandboxRuntimeBaseBranch = typeof options.runtimeBaseBranch === "string" && options.runtimeBaseBranch.trim() ? options.runtimeBaseBranch.trim() : "";
633
+ const sandboxRuntimeBaseSha = typeof options.runtimeBaseSha === "string" && options.runtimeBaseSha.trim() ? options.runtimeBaseSha.trim() : "";
634
+ const runtimeId = typeof options.runtimeId === "string" && options.runtimeId.trim() ? options.runtimeId.trim() : "";
635
+ const sandboxRuntimeRequested = Boolean(
636
+ sandboxWorkflowMode || sandboxRuntimePromotionPolicy || sandboxRuntimeBaseBranch || sandboxRuntimeBaseSha || runtimeId || sandboxRuntimeProjectId || sandboxRuntimeAgentId
637
+ );
638
+ const env = parseSandboxTemplateEnvOptions(manifest, options);
639
+ const workloadSource = sandboxWorkloadSourceFromManifest(manifest);
640
+ const sandbox = {
641
+ repo,
642
+ ...teamId ? { teamId } : {},
643
+ ...projectId ? { projectId } : {},
644
+ ...agentId ? { agentId } : {},
645
+ resources: manifest.resources ?? {},
646
+ networkPolicy: {
647
+ internetEgress: sandboxTemplateInternetEgressPolicy(
648
+ manifest.network.egress
649
+ )
650
+ },
651
+ budget: { maxUsd: budgetUsd },
652
+ quotas: {
653
+ maxSpendUsd: budgetUsd,
654
+ ...maxDurationSeconds !== void 0 ? { maxDurationSeconds } : {},
655
+ ...idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds } : {}
656
+ },
657
+ ...env.length > 0 ? { env } : {},
658
+ ...workloadSource ? { workloadSource } : {},
659
+ volumes: manifest.volumes,
660
+ metadata: {
661
+ source: "openpond-code-sandbox-template-start",
662
+ manifestFile: OPENPOND_MANIFEST_FILE_NAME,
663
+ template: {
664
+ name: manifest.name,
665
+ version: manifest.version,
666
+ useCase: manifest.useCase
667
+ }
668
+ }
669
+ };
670
+ return {
671
+ sandbox,
672
+ ...sandboxRuntimeRequested && runtimeId ? { runtimeId } : {},
673
+ ...sandboxRuntimeRequested && !runtimeId ? {
674
+ sandboxRuntime: {
675
+ ...teamId ? { teamId } : {},
676
+ ...sandboxWorkflowMode ? { workflowMode: sandboxWorkflowMode } : {},
677
+ ...projectId ? { projectId } : {},
678
+ ...agentId ? { agentId } : {},
679
+ baseBranch: sandboxRuntimeBaseBranch || "master",
680
+ ...sandboxRuntimeBaseSha ? { baseSha: sandboxRuntimeBaseSha } : {},
681
+ ...sandboxRuntimePromotionPolicy ? { promotionPolicy: sandboxRuntimePromotionPolicy } : {}
682
+ }
683
+ } : {}
684
+ };
685
+ }
686
+ function sandboxWorkloadSourceFromManifest(manifest) {
687
+ if (manifest.runtime.image) {
688
+ return { image: manifest.runtime.image };
689
+ }
690
+ if (manifest.runtime.dockerfile) {
691
+ return { dockerfile: manifest.runtime.dockerfile };
692
+ }
693
+ return void 0;
694
+ }
695
+ function sandboxTemplateInternetEgressPolicy(egress) {
696
+ return egress === "allow" ? "allow" : "block";
697
+ }
698
+ async function createSandboxTemplateStartSandbox(client, plan, repo) {
699
+ const requestedAt = Date.now();
700
+ try {
701
+ return (await createSandboxFromPlan(client, plan)).sandbox;
702
+ } catch (error) {
703
+ if (!isLikelySandboxCreateTimeout(error)) {
704
+ throw error;
705
+ }
706
+ console.warn(
707
+ "warning: sandbox create timed out; checking for the created sandbox record"
708
+ );
709
+ return recoverTimedOutSandboxCreate(
710
+ client,
711
+ plan.sandbox,
712
+ repo,
713
+ requestedAt
714
+ );
715
+ }
716
+ }
717
+ function isLikelySandboxCreateTimeout(error) {
718
+ return error instanceof Error && /\b(504|timed out|timeout)\b/i.test(error.message);
719
+ }
720
+ async function recoverTimedOutSandboxCreate(client, input2, repo, requestedAt) {
721
+ const timeoutMs = 12 * 6e4;
722
+ const pollMs = 3e3;
723
+ const deadline = Date.now() + timeoutMs;
724
+ const repoIdentity = normalizeSandboxTemplateRepoIdentity(repo);
725
+ const metadata = input2.metadata ?? {};
726
+ while (Date.now() < deadline) {
727
+ const sandboxes = await client.list({
728
+ ...input2.teamId ? { teamId: input2.teamId } : {},
729
+ ...input2.projectId ? { projectId: input2.projectId } : {},
730
+ ...input2.agentId ? { agentId: input2.agentId } : {}
731
+ });
732
+ const match = sandboxes.filter((sandbox) => {
733
+ if (!sandbox.repo) return false;
734
+ if (normalizeSandboxTemplateRepoIdentity(sandbox.repo) !== repoIdentity)
735
+ return false;
736
+ const createdAt = Date.parse(sandbox.createdAt);
737
+ if (Number.isFinite(createdAt) && createdAt < requestedAt - 3e4)
738
+ return false;
739
+ if (metadata.source && sandbox.metadata?.source !== metadata.source)
740
+ return false;
741
+ return true;
742
+ }).sort(
743
+ (left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt)
744
+ )[0];
745
+ if (match?.state === "running" || match?.state === "stopped") {
746
+ return match;
747
+ }
748
+ if (match?.state === "error") {
749
+ throw new Error(
750
+ `sandbox create failed after timeout: ${match.id}
751
+ ${match.logs.join(
752
+ "\n"
753
+ )}`
754
+ );
755
+ }
756
+ await sleep(pollMs);
757
+ }
758
+ throw new Error(
759
+ "sandbox create timed out and no matching created sandbox reached running state"
760
+ );
761
+ }
762
+ async function waitForSandboxTemplateRunnerReady(client, sandboxId) {
763
+ const timeoutMs = 12e4;
764
+ const pollMs = 3e3;
765
+ const deadline = Date.now() + timeoutMs;
766
+ let lastError = null;
767
+ while (Date.now() < deadline) {
768
+ try {
769
+ const result = await client.exec(sandboxId, {
770
+ command: "true",
771
+ timeoutSeconds: 30
772
+ });
773
+ if (result.command.status === "succeeded" && result.command.exitCode === 0) {
774
+ return;
775
+ }
776
+ lastError = new Error(
777
+ `readiness command ${result.command.status} with exit code ${String(
778
+ result.command.exitCode
779
+ )}`
780
+ );
781
+ } catch (error) {
782
+ lastError = error;
783
+ if (!isRetryableSandboxRunnerReadyError(error)) {
784
+ throw error;
785
+ }
786
+ }
787
+ await sleep(pollMs);
788
+ }
789
+ throw new Error(
790
+ `sandbox runner was not ready after create: ${formatUnknownError(
791
+ lastError
792
+ )}`
793
+ );
794
+ }
795
+ function isRetryableSandboxRunnerReadyError(error) {
796
+ return error instanceof Error && /\b(502|503|504|timed out|timeout|sandbox_not_found|sandbox_not_ready|sandbox_runner_failed)\b/i.test(
797
+ error.message
798
+ );
799
+ }
800
+ function formatUnknownError(error) {
801
+ if (error instanceof Error) return error.message;
802
+ if (typeof error === "string") return error;
803
+ return "unknown error";
804
+ }
805
+ function sleep(ms) {
806
+ return new Promise((resolve) => setTimeout(resolve, ms));
807
+ }
808
+ async function resolveSandboxTemplateStartInput(manifest, options, projectPath) {
809
+ const scalars = parseSandboxTemplateScalarInputs(options);
810
+ const uploadSpecs = collectSandboxTemplateUploadSpecs(manifest);
811
+ const uploadRequests = [];
812
+ const fileInputs = parseSandboxTemplateFileInputOptions(options);
813
+ for (const [inputName, rawValue] of Object.entries(fileInputs)) {
814
+ const spec = uploadSpecs.get(inputName);
815
+ if (!spec) {
816
+ throw new Error(
817
+ `${inputName} is not declared as a file upload input in ${OPENPOND_MANIFEST_FILE_NAME}`
818
+ );
819
+ }
820
+ const localPaths = await expandSandboxTemplateUploadPaths(
821
+ rawValue,
822
+ projectPath
823
+ );
824
+ if (!spec.multiple && localPaths.length > 1) {
825
+ throw new Error(
826
+ `${inputName} accepts one file, got ${localPaths.length}`
827
+ );
828
+ }
829
+ uploadRequests.push({ inputName, localPaths, spec });
830
+ }
831
+ const requiredInputs = Array.isArray(manifest.inputs.schema.required) ? manifest.inputs.schema.required.filter(
832
+ (value) => typeof value === "string"
833
+ ) : [];
834
+ for (const inputName of requiredInputs) {
835
+ if (uploadSpecs.has(inputName) && !uploadRequests.some((request) => request.inputName === inputName)) {
836
+ throw new Error(
837
+ `${inputName} is required; pass --input-file ${inputName}=<path> or --input-files ${inputName}=<glob>`
838
+ );
839
+ }
840
+ }
841
+ return { scalars, uploadRequests };
842
+ }
843
+ function parseSandboxTemplateScalarInputs(options) {
844
+ const rawInputs = typeof options.inputs === "string" ? options.inputs : typeof options.inputJson === "string" ? options.inputJson : typeof options.params === "string" ? options.params : "";
845
+ const scalars = rawInputs ? parseJsonOption(rawInputs, "inputs") : {};
846
+ if (!scalars || typeof scalars !== "object" || Array.isArray(scalars)) {
847
+ throw new Error("inputs must be a JSON object");
848
+ }
849
+ const rawInput = typeof options.input === "string" ? options.input.trim() : "";
850
+ if (rawInput) {
851
+ const [name, value] = parseKeyValueOption(rawInput, "input");
852
+ scalars[name] = value;
853
+ }
854
+ return scalars;
855
+ }
856
+ function parseSandboxTemplateFileInputOptions(options) {
857
+ const out = {};
858
+ const rawFile = typeof options.inputFile === "string" ? options.inputFile.trim() : "";
859
+ if (rawFile) {
860
+ const [name, value] = parseKeyValueOption(rawFile, "input-file");
861
+ out[name] = value;
862
+ }
863
+ const rawFiles = typeof options.inputFiles === "string" ? options.inputFiles.trim() : "";
864
+ if (rawFiles) {
865
+ const [name, value] = parseKeyValueOption(rawFiles, "input-files");
866
+ out[name] = value;
867
+ }
868
+ return out;
869
+ }
870
+ function parseKeyValueOption(value, label) {
871
+ const index = value.indexOf("=");
872
+ if (index <= 0) {
873
+ throw new Error(`${label} must be formatted as name=value`);
874
+ }
875
+ const key = value.slice(0, index).trim();
876
+ const raw = value.slice(index + 1).trim();
877
+ if (!key || !raw) {
878
+ throw new Error(`${label} must be formatted as name=value`);
879
+ }
880
+ return [key, raw];
881
+ }
882
+ function collectSandboxTemplateUploadSpecs(manifest) {
883
+ const properties = asPlainRecord(manifest.inputs.schema.properties) ?? {};
884
+ const specs = /* @__PURE__ */ new Map();
885
+ for (const [inputName, rawProperty] of Object.entries(properties)) {
886
+ const property = asPlainRecord(rawProperty);
887
+ if (!property) continue;
888
+ const upload = asPlainRecord(
889
+ property["x-openpond-upload"] ?? property.xOpenPondUpload
890
+ );
891
+ if (!upload) continue;
892
+ const targetPath = typeof upload.targetPath === "string" && upload.targetPath.trim() ? normalizeSandboxUploadTargetPath(upload.targetPath) : "";
893
+ if (!targetPath) {
894
+ throw new Error(`${inputName} upload metadata is missing targetPath`);
895
+ }
896
+ const multiple = upload.multiple === true || property.type === "array" || asPlainRecord(property.items)?.format === "file";
897
+ specs.set(inputName, { inputName, multiple, targetPath });
898
+ }
899
+ return specs;
900
+ }
901
+ function asPlainRecord(value) {
902
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
903
+ return value;
904
+ }
905
+ function normalizeSandboxUploadTargetPath(value) {
906
+ const normalized = value.trim().replace(/\\/g, "/").replace(/^\/workspace\//, "").replace(/^\.\//, "").replace(/^\/+/, "");
907
+ if (!normalized || normalized.split("/").some((segment) => segment === ".." || segment === "")) {
908
+ throw new Error(`invalid upload target path: ${value}`);
909
+ }
910
+ if (normalized.split("/").some(isSandboxEnvFileName)) {
911
+ throw new Error(
912
+ "sandbox template uploads cannot target .env* files; create sandbox secrets and pass refs with --env-ref"
913
+ );
914
+ }
915
+ return normalized;
916
+ }
917
+ async function expandSandboxTemplateUploadPaths(rawValue, projectPath) {
918
+ const values = rawValue.split(",").map((item) => item.trim()).filter(Boolean);
919
+ const paths = (await Promise.all(
920
+ values.map((value) => expandSandboxTemplateUploadPath(value, projectPath))
921
+ )).flat();
922
+ if (paths.length === 0) {
923
+ throw new Error(`no files matched ${rawValue}`);
924
+ }
925
+ return paths;
926
+ }
927
+ async function expandSandboxTemplateUploadPath(rawValue, projectPath) {
928
+ const absolute = path2.resolve(projectPath, rawValue);
929
+ if (!rawValue.includes("*")) {
930
+ assertSandboxTemplateUploadPathAllowed(absolute);
931
+ const stats = await fs.stat(absolute);
932
+ if (!stats.isFile()) {
933
+ throw new Error(`upload path is not a file: ${absolute}`);
934
+ }
935
+ return [absolute];
936
+ }
937
+ const directory = path2.dirname(absolute);
938
+ const basename = path2.basename(absolute);
939
+ const regex = globBasenameToRegExp(basename);
940
+ const entries = await fs.readdir(directory, { withFileTypes: true });
941
+ const matchingEntries = entries.filter((entry) => entry.isFile() && regex.test(entry.name)).sort((left, right) => left.name.localeCompare(right.name));
942
+ const blocked = matchingEntries.find(
943
+ (entry) => isSandboxEnvFileName(entry.name)
944
+ );
945
+ if (blocked) {
946
+ throw new Error(
947
+ "sandbox template uploads cannot include .env* files; create sandbox secrets and pass refs with --env-ref"
948
+ );
949
+ }
950
+ return matchingEntries.map((entry) => path2.join(directory, entry.name)).sort((left, right) => left.localeCompare(right));
951
+ }
952
+ function assertSandboxTemplateUploadPathAllowed(localPath) {
953
+ if (isSandboxEnvFileName(path2.basename(localPath))) {
954
+ throw new Error(
955
+ "sandbox template uploads cannot include .env* files; create sandbox secrets and pass refs with --env-ref"
956
+ );
957
+ }
958
+ }
959
+ function isSandboxEnvFileName(name) {
960
+ return name === ".env" || name.startsWith(".env.");
961
+ }
962
+ function globBasenameToRegExp(pattern) {
963
+ const escaped = pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
964
+ return new RegExp(`^${escaped}$`);
965
+ }
966
+ async function runSandboxTemplateSetupCommands(client, sandboxId, manifest, options) {
967
+ const timeoutSeconds = parseIntegerOption(options.setupTimeoutSeconds, "setup-timeout-seconds") ?? 900;
968
+ const results = [];
969
+ for (const command of manifest.setup.commands) {
970
+ const result = await runSandboxTemplateShellCommand(
971
+ client,
972
+ sandboxId,
973
+ command,
974
+ timeoutSeconds
975
+ );
976
+ results.push({
977
+ command,
978
+ status: result.status,
979
+ exitCode: result.exitCode
980
+ });
981
+ if (result.status !== "succeeded" || result.exitCode !== 0) {
982
+ throw new Error(`setup command failed: ${command}
983
+ ${result.output}`);
984
+ }
985
+ }
986
+ return results;
987
+ }
988
+ async function uploadSandboxTemplateStartFiles(client, sandboxId, requests) {
989
+ const uploaded = [];
990
+ for (const request of requests) {
991
+ for (const localPath of request.localPaths) {
992
+ const contents = await fs.readFile(localPath);
993
+ const sandboxPath = joinSandboxUploadPath(
994
+ request.spec.targetPath,
995
+ path2.basename(localPath)
996
+ );
997
+ await client.uploadFileBase64(
998
+ sandboxId,
999
+ sandboxPath,
1000
+ contents.toString("base64")
1001
+ );
1002
+ uploaded.push({
1003
+ inputName: request.inputName,
1004
+ localPath,
1005
+ sandboxPath,
1006
+ sizeBytes: contents.byteLength
1007
+ });
1008
+ }
1009
+ }
1010
+ return uploaded;
1011
+ }
1012
+ function joinSandboxUploadPath(targetPath, basename) {
1013
+ return `${targetPath.replace(/\/+$/, "")}/${basename.replace(/^\/+/, "")}`;
1014
+ }
1015
+ function formatUploadedFileParams(uploadedFiles, requests) {
1016
+ const out = {};
1017
+ for (const request of requests) {
1018
+ const files = uploadedFiles.filter((file) => file.inputName === request.inputName).map((file) => file.sandboxPath);
1019
+ out[request.inputName] = request.spec.multiple ? files : files[0] ?? "";
1020
+ }
1021
+ return out;
1022
+ }
1023
+ async function runSandboxTemplateExecutable(client, sandboxId, executable, input2, env = {}) {
1024
+ await uploadSandboxTemplateReplayParams(client, sandboxId, input2);
1025
+ const command = formatSandboxTemplateCommand(executable, env);
1026
+ const timeoutSeconds = executable.timeoutSeconds;
1027
+ if (executable.kind === "service") {
1028
+ const result2 = await client.startProcess(sandboxId, {
1029
+ command,
1030
+ ...timeoutSeconds !== void 0 ? { timeoutSeconds } : {}
1031
+ });
1032
+ return { kind: "service", process: result2.process };
1033
+ }
1034
+ const result = await runSandboxTemplateProcessToCompletion(
1035
+ client,
1036
+ sandboxId,
1037
+ command,
1038
+ timeoutSeconds ?? 900
1039
+ );
1040
+ if (result.status !== "succeeded" || result.exitCode !== 0) {
1041
+ throw new Error(
1042
+ `template command failed: ${executable.name}
1043
+ ${result.output}`
1044
+ );
1045
+ }
1046
+ return { kind: executable.kind, process: result };
1047
+ }
1048
+ async function runSandboxTemplateShellCommand(client, sandboxId, command, timeoutSeconds) {
1049
+ return runSandboxTemplateProcessToCompletion(
1050
+ client,
1051
+ sandboxId,
1052
+ command,
1053
+ timeoutSeconds ?? 900
1054
+ );
1055
+ }
1056
+ async function uploadSandboxTemplateReplayParams(client, sandboxId, input2) {
1057
+ const paramsJson = `${JSON.stringify({ input: input2 }, null, 2)}
1058
+ `;
1059
+ await client.uploadFileBase64(
1060
+ sandboxId,
1061
+ "openpond-replay-params.json",
1062
+ Buffer.from(paramsJson, "utf8").toString("base64")
1063
+ );
1064
+ }
1065
+ async function runSandboxTemplateProcessToCompletion(client, sandboxId, command, timeoutSeconds) {
1066
+ const started = await client.startProcess(sandboxId, {
1067
+ command,
1068
+ timeoutSeconds
1069
+ });
1070
+ let current = started.process;
1071
+ const deadline = Date.now() + timeoutSeconds * 1e3 + 3e4;
1072
+ while (current.status === "running" && Date.now() < deadline) {
1073
+ await sleep(3e3);
1074
+ const polled = await client.getProcess(sandboxId, current.id);
1075
+ current = polled.process;
1076
+ }
1077
+ return {
1078
+ command: current.command,
1079
+ status: current.status,
1080
+ output: current.output,
1081
+ exitCode: current.exitCode,
1082
+ processId: current.id
1083
+ };
1084
+ }
1085
+ function sandboxTemplateRuntimeEnv(sandbox) {
1086
+ return {
1087
+ OPENPOND_SANDBOX_ID: sandbox.id,
1088
+ ...sandbox.runtimeId ? { OPENPOND_SANDBOX_RUNTIME_ID: sandbox.runtimeId } : {}
1089
+ };
1090
+ }
1091
+ function formatSandboxTemplateCommand(executable, env = {}) {
1092
+ const paramsPath = quoteShellArg(replayParamsPathForExecutable(executable));
1093
+ const envPrefix = `OPENPOND_REPLAY_PARAMS_BASE64="$(base64 -w0 ${paramsPath} 2>/dev/null || base64 ${paramsPath} | tr -d '\\n')"`;
1094
+ const runtimeEnvPrefix = Object.entries(env).filter(([, value]) => value.length > 0).map(([name, value]) => `${name}=${quoteShellArg(value)}`).join(" ");
1095
+ const command = [runtimeEnvPrefix, envPrefix, executable.command].filter(Boolean).join(" ");
1096
+ if (!executable.cwd) return command;
1097
+ return `cd ${quoteShellArg(executable.cwd)} && ${command}`;
1098
+ }
1099
+ function replayParamsPathForExecutable(executable) {
1100
+ if (!executable.cwd) return "openpond-replay-params.json";
1101
+ const cwd = executable.cwd.replace(/\\/g, "/").replace(/\/+$/, "") || ".";
1102
+ return path2.posix.relative(cwd, "openpond-replay-params.json") || "openpond-replay-params.json";
1103
+ }
1104
+ function quoteShellArg(value) {
1105
+ return `'${value.replace(/'/g, "'\\''")}'`;
1106
+ }
1107
+ async function openSandboxTemplatePorts(client, sandboxId, ports) {
1108
+ const previews = [];
1109
+ for (const port of ports) {
1110
+ const result = await client.openPort(sandboxId, {
1111
+ port: port.port,
1112
+ ...port.label ? { label: port.label } : {},
1113
+ access: port.access,
1114
+ autoStart: false
1115
+ });
1116
+ previews.push(result.preview);
1117
+ }
1118
+ return previews;
1119
+ }
1120
+ function resolveSandboxTemplateFilePath(options) {
1121
+ const rawFile = typeof options.file === "string" && options.file.trim().length > 0 ? options.file.trim() : typeof options.manifest === "string" && options.manifest.trim().length > 0 ? options.manifest.trim() : OPENPOND_MANIFEST_FILE_NAME;
1122
+ return path2.resolve(process.cwd(), rawFile);
1123
+ }
1124
+ function resolveSandboxTemplateScaffoldPath(options) {
1125
+ const rawPath = typeof options.path === "string" && options.path.trim().length > 0 ? options.path.trim() : typeof options.dir === "string" && options.dir.trim().length > 0 ? options.dir.trim() : process.cwd();
1126
+ return path2.resolve(process.cwd(), rawPath);
1127
+ }
1128
+
1129
+ // src/cli/sandbox-template-local.ts
1130
+ async function runSandboxTemplateLocal(options, mode) {
1131
+ const { plan, filePath, projectPath } = await loadSandboxTemplateBuildPlan(
1132
+ options
1133
+ );
1134
+ const executable = resolveSandboxTemplateLocalExecutable(
1135
+ plan.manifest,
1136
+ options,
1137
+ mode
1138
+ );
1139
+ const input2 = await resolveSandboxTemplateStartInput(
1140
+ plan.manifest,
1141
+ options,
1142
+ projectPath
1143
+ );
1144
+ const volumes = await prepareLocalSandboxTemplateVolumes(
1145
+ plan.manifest,
1146
+ projectPath
1147
+ );
1148
+ const setupCommands = await runLocalSandboxTemplateSetupCommands(
1149
+ plan.manifest,
1150
+ projectPath,
1151
+ options
1152
+ );
1153
+ const uploadedFiles = await prepareLocalSandboxTemplateUploads(
1154
+ input2.uploadRequests,
1155
+ projectPath
1156
+ );
1157
+ const commandInput = {
1158
+ ...input2.scalars,
1159
+ ...formatUploadedFileParams(uploadedFiles, input2.uploadRequests)
1160
+ };
1161
+ const replay = await writeLocalSandboxTemplateReplayParams(
1162
+ projectPath,
1163
+ executable,
1164
+ commandInput
1165
+ );
1166
+ const previews = localSandboxTemplatePreviews(executable);
1167
+ if (mode === "dev" || executable.kind === "service") {
1168
+ console.log(
1169
+ JSON.stringify(
1170
+ {
1171
+ ok: true,
1172
+ mode,
1173
+ file: filePath,
1174
+ template: plan.manifest.name,
1175
+ executable: summarizeSandboxTemplateExecutable(executable),
1176
+ volumes,
1177
+ setupCommands,
1178
+ uploadedFiles,
1179
+ input: commandInput,
1180
+ replayParamsPath: replay.paramsPath,
1181
+ previews,
1182
+ status: "starting"
1183
+ },
1184
+ null,
1185
+ 2
1186
+ )
1187
+ );
1188
+ const result = await runLocalSandboxTemplateCommand(
1189
+ projectPath,
1190
+ executable,
1191
+ replay,
1192
+ {
1193
+ inherit: true,
1194
+ timeoutSeconds: executable.timeoutSeconds
1195
+ }
1196
+ );
1197
+ if (result.status !== "succeeded") process.exitCode = 1;
1198
+ return;
1199
+ }
1200
+ const execution = await runLocalSandboxTemplateCommand(
1201
+ projectPath,
1202
+ executable,
1203
+ replay,
1204
+ {
1205
+ timeoutSeconds: executable.timeoutSeconds ?? 900
1206
+ }
1207
+ );
1208
+ const artifacts = await collectLocalSandboxTemplateArtifacts(
1209
+ projectPath,
1210
+ executable.artifactPaths
1211
+ );
1212
+ if (execution.status !== "succeeded") process.exitCode = 1;
1213
+ console.log(
1214
+ JSON.stringify(
1215
+ {
1216
+ ok: execution.status === "succeeded",
1217
+ mode,
1218
+ file: filePath,
1219
+ template: plan.manifest.name,
1220
+ executable: summarizeSandboxTemplateExecutable(executable),
1221
+ volumes,
1222
+ setupCommands,
1223
+ uploadedFiles,
1224
+ input: commandInput,
1225
+ replayParamsPath: replay.paramsPath,
1226
+ execution,
1227
+ previews,
1228
+ artifacts
1229
+ },
1230
+ null,
1231
+ 2
1232
+ )
1233
+ );
1234
+ }
1235
+ async function runSandboxTemplateExistingSandboxAction(options, rest) {
1236
+ const sandboxId = rest[0]?.trim() || (typeof options.sandboxId === "string" ? options.sandboxId.trim() : "");
1237
+ const actionName = rest[1]?.trim() || (typeof options.action === "string" ? options.action.trim() : "") || (typeof options.target === "string" ? options.target.trim() : "");
1238
+ if (!sandboxId || !actionName) {
1239
+ throw new Error(
1240
+ `usage: sandbox-template action <sandboxId> <actionName> [--file ${OPENPOND_MANIFEST_FILE_NAME}]`
1241
+ );
1242
+ }
1243
+ const { plan, filePath, projectPath } = await loadSandboxTemplateBuildPlan(
1244
+ options
1245
+ );
1246
+ const executable = resolveSandboxTemplateActionExecutable(
1247
+ plan.manifest,
1248
+ actionName
1249
+ );
1250
+ const input2 = await resolveSandboxTemplateStartInput(
1251
+ plan.manifest,
1252
+ options,
1253
+ projectPath
1254
+ );
1255
+ const client = await resolveSandboxClient(options);
1256
+ const uploadedFiles = await uploadSandboxTemplateStartFiles(
1257
+ client,
1258
+ sandboxId,
1259
+ input2.uploadRequests
1260
+ );
1261
+ const commandInput = {
1262
+ ...input2.scalars,
1263
+ ...formatUploadedFileParams(uploadedFiles, input2.uploadRequests)
1264
+ };
1265
+ const execution = await runSandboxTemplateExecutable(
1266
+ client,
1267
+ sandboxId,
1268
+ executable,
1269
+ commandInput
1270
+ );
1271
+ console.log(
1272
+ JSON.stringify(
1273
+ {
1274
+ ok: true,
1275
+ file: filePath,
1276
+ sandboxId,
1277
+ executable: summarizeSandboxTemplateExecutable(executable),
1278
+ uploadedFiles,
1279
+ input: commandInput,
1280
+ execution,
1281
+ expectedArtifacts: executable.artifactPaths
1282
+ },
1283
+ null,
1284
+ 2
1285
+ )
1286
+ );
1287
+ }
1288
+ function resolveSandboxTemplateLocalExecutable(manifest, options, mode) {
1289
+ if (mode === "dev" && typeof options.target !== "string" && typeof options.action !== "string" && typeof options.service !== "string" && typeof options.entrypoint !== "string") {
1290
+ const firstService = sandboxTemplateExecutableEntries(manifest).find(
1291
+ (candidate) => candidate.kind === "service"
1292
+ );
1293
+ if (firstService) return firstService;
1294
+ }
1295
+ return resolveSandboxTemplateStartExecutable(manifest, options);
1296
+ }
1297
+ function resolveSandboxTemplateActionExecutable(manifest, actionName) {
1298
+ const match = sandboxTemplateExecutableEntries(manifest).find(
1299
+ (candidate) => candidate.kind === "action" && candidate.name === actionName
1300
+ );
1301
+ if (!match) {
1302
+ const actions = manifest.actions.map((action) => action.name).join(", ") || "(none)";
1303
+ throw new Error(
1304
+ `manifest action not found: ${actionName}. Available actions: ${actions}`
1305
+ );
1306
+ }
1307
+ return match;
1308
+ }
1309
+ function summarizeSandboxTemplateExecutable(executable) {
1310
+ return {
1311
+ kind: executable.kind,
1312
+ name: executable.name,
1313
+ command: executable.command,
1314
+ cwd: executable.cwd ?? null,
1315
+ timeoutSeconds: executable.timeoutSeconds ?? null,
1316
+ ports: executable.ports,
1317
+ artifactPaths: executable.artifactPaths
1318
+ };
1319
+ }
1320
+ async function prepareLocalSandboxTemplateVolumes(manifest, projectPath) {
1321
+ const volumes = [];
1322
+ for (const volume of manifest.volumes) {
1323
+ const mountPath = typeof volume.mountPath === "string" && volume.mountPath.trim() ? volume.mountPath.trim() : volume.name ? `/workspace/volumes/${volume.name}` : "";
1324
+ if (!mountPath) continue;
1325
+ const localPath = path3.resolve(
1326
+ projectPath,
1327
+ normalizeLocalWorkspacePath(mountPath)
1328
+ );
1329
+ await fs2.mkdir(localPath, { recursive: true });
1330
+ volumes.push({
1331
+ name: volume.name ?? null,
1332
+ mountPath,
1333
+ localPath
1334
+ });
1335
+ }
1336
+ return volumes;
1337
+ }
1338
+ async function runLocalSandboxTemplateSetupCommands(manifest, projectPath, options) {
1339
+ const timeoutSeconds = parseIntegerOption(options.setupTimeoutSeconds, "setup-timeout-seconds") ?? 900;
1340
+ const results = [];
1341
+ for (const command of manifest.setup.commands) {
1342
+ const result = await runLocalShellCommandResult(command, projectPath, {
1343
+ timeoutSeconds
1344
+ });
1345
+ results.push(result);
1346
+ if (result.status !== "succeeded") {
1347
+ throw new Error(
1348
+ `local setup command failed: ${command}
1349
+ ${result.output}`
1350
+ );
1351
+ }
1352
+ }
1353
+ return results;
1354
+ }
1355
+ async function prepareLocalSandboxTemplateUploads(requests, projectPath) {
1356
+ const uploaded = [];
1357
+ for (const request of requests) {
1358
+ for (const localPath of request.localPaths) {
1359
+ const contents = await fs2.readFile(localPath);
1360
+ const sandboxPath = joinSandboxUploadPath(
1361
+ request.spec.targetPath,
1362
+ path3.basename(localPath)
1363
+ );
1364
+ const destination = path3.resolve(
1365
+ projectPath,
1366
+ normalizeLocalWorkspacePath(sandboxPath)
1367
+ );
1368
+ await fs2.mkdir(path3.dirname(destination), { recursive: true });
1369
+ await fs2.copyFile(localPath, destination);
1370
+ uploaded.push({
1371
+ inputName: request.inputName,
1372
+ localPath,
1373
+ sandboxPath,
1374
+ sizeBytes: contents.byteLength
1375
+ });
1376
+ }
1377
+ }
1378
+ return uploaded;
1379
+ }
1380
+ async function writeLocalSandboxTemplateReplayParams(projectPath, executable, input2) {
1381
+ const paramsJson = `${JSON.stringify({ input: input2 }, null, 2)}
1382
+ `;
1383
+ const encoded = Buffer2.from(paramsJson, "utf8").toString("base64");
1384
+ const paramsPath = path3.join(projectPath, "openpond-replay-params.json");
1385
+ await fs2.writeFile(paramsPath, paramsJson, "utf8");
1386
+ if (executable.cwd) {
1387
+ const cwdParamsPath = path3.join(
1388
+ projectPath,
1389
+ executable.cwd,
1390
+ "openpond-replay-params.json"
1391
+ );
1392
+ if (cwdParamsPath !== paramsPath) {
1393
+ await fs2.mkdir(path3.dirname(cwdParamsPath), { recursive: true });
1394
+ await fs2.writeFile(cwdParamsPath, paramsJson, "utf8");
1395
+ }
1396
+ }
1397
+ return { paramsPath, encoded };
1398
+ }
1399
+ async function runLocalSandboxTemplateCommand(projectPath, executable, replay, options = {}) {
1400
+ const cwd = executable.cwd ? path3.resolve(projectPath, executable.cwd) : projectPath;
1401
+ return runLocalShellCommandResult(executable.command, cwd, {
1402
+ env: {
1403
+ OPENPOND_REPLAY_PARAMS_BASE64: replay.encoded
1404
+ },
1405
+ timeoutSeconds: options.timeoutSeconds,
1406
+ inherit: options.inherit
1407
+ });
1408
+ }
1409
+ async function runLocalShellCommandResult(command, cwd, options = {}) {
1410
+ const result = await runShellCommand(command, {
1411
+ cwd,
1412
+ env: options.env,
1413
+ timeoutSeconds: options.timeoutSeconds,
1414
+ inherit: options.inherit
1415
+ });
1416
+ const output = [result.stdout, result.stderr].filter(Boolean).join("");
1417
+ return {
1418
+ command,
1419
+ cwd,
1420
+ status: result.timedOut ? "timed_out" : result.code === 0 ? "succeeded" : "failed",
1421
+ output,
1422
+ exitCode: result.code
1423
+ };
1424
+ }
1425
+ function localSandboxTemplatePreviews(executable) {
1426
+ return executable.ports.map((port) => ({
1427
+ port: port.port,
1428
+ label: port.label ?? null,
1429
+ access: port.access,
1430
+ url: `http://127.0.0.1:${port.port}${port.path}`
1431
+ }));
1432
+ }
1433
+ async function collectLocalSandboxTemplateArtifacts(projectPath, artifactPaths) {
1434
+ const artifacts = [];
1435
+ for (const artifactPath of artifactPaths) {
1436
+ const localPath = path3.resolve(
1437
+ projectPath,
1438
+ normalizeLocalWorkspacePath(artifactPath)
1439
+ );
1440
+ try {
1441
+ const stat = await fs2.stat(localPath);
1442
+ artifacts.push({
1443
+ path: artifactPath,
1444
+ exists: stat.isFile(),
1445
+ sizeBytes: stat.size
1446
+ });
1447
+ } catch {
1448
+ artifacts.push({ path: artifactPath, exists: false, sizeBytes: null });
1449
+ }
1450
+ }
1451
+ return artifacts;
1452
+ }
1453
+ function normalizeLocalWorkspacePath(value) {
1454
+ const normalized = value.trim().replace(/\\/g, "/").replace(/^\/workspace\//, "").replace(/^workspace\//, "").replace(/^\.\//, "").replace(/^\/+/, "");
1455
+ if (!normalized || normalized.split("/").some((segment) => segment === ".." || segment === "")) {
1456
+ throw new Error(`invalid workspace path: ${value}`);
1457
+ }
1458
+ return normalized;
1459
+ }
1460
+
1461
+ // src/cli/sandbox-template.ts
1462
+ async function runSandboxTemplateCommand(options, rest) {
1463
+ const subcommand = rest[0] || "validate";
1464
+ if (subcommand === "validate") {
1465
+ const filePath = resolveSandboxTemplateFilePath(options);
1466
+ const source = await fs3.readFile(filePath, "utf8");
1467
+ const result = validateSandboxTemplateYaml(source);
1468
+ if (!result.ok) {
1469
+ console.error(formatSandboxTemplateDiagnostics(result.diagnostics));
1470
+ process.exitCode = 1;
1471
+ return;
1472
+ }
1473
+ console.log(
1474
+ JSON.stringify(
1475
+ {
1476
+ ok: true,
1477
+ file: filePath,
1478
+ name: result.manifest.name,
1479
+ version: result.manifest.version,
1480
+ start: result.manifest.start.command,
1481
+ actions: result.manifest.actions.map((action) => action.name),
1482
+ services: result.manifest.services.map((service) => service.name),
1483
+ schedules: result.manifest.schedules.map((schedule) => schedule.name)
1484
+ },
1485
+ null,
1486
+ 2
1487
+ )
1488
+ );
1489
+ return;
1490
+ }
1491
+ if (subcommand === "print-schema") {
1492
+ console.log(JSON.stringify(sandboxTemplateJsonSchema(), null, 2));
1493
+ return;
1494
+ }
1495
+ if (subcommand === "scaffold") {
1496
+ const outputPath = resolveSandboxTemplateScaffoldPath(options);
1497
+ const rawName = typeof options.name === "string" && options.name.trim().length > 0 ? options.name.trim() : path4.basename(outputPath);
1498
+ const description = typeof options.description === "string" && options.description.trim().length > 0 ? options.description.trim() : void 0;
1499
+ const files = sandboxTemplateScaffoldFiles({ name: rawName, description });
1500
+ const manifestPath = path4.join(outputPath, OPENPOND_MANIFEST_FILE_NAME);
1501
+ if (existsSync2(manifestPath)) {
1502
+ throw new Error(
1503
+ `${OPENPOND_MANIFEST_FILE_NAME} already exists at ${manifestPath}`
1504
+ );
1505
+ }
1506
+ await fs3.mkdir(outputPath, { recursive: true });
1507
+ for (const [relativePath, contents] of Object.entries(files)) {
1508
+ const filePath = path4.join(outputPath, relativePath);
1509
+ await fs3.mkdir(path4.dirname(filePath), { recursive: true });
1510
+ await fs3.writeFile(filePath, contents, "utf8");
1511
+ }
1512
+ console.log(
1513
+ JSON.stringify(
1514
+ {
1515
+ ok: true,
1516
+ path: outputPath,
1517
+ files: Object.keys(files).sort(
1518
+ (left, right) => left.localeCompare(right)
1519
+ )
1520
+ },
1521
+ null,
1522
+ 2
1523
+ )
1524
+ );
1525
+ return;
1526
+ }
1527
+ if (subcommand === "build") {
1528
+ await runSandboxTemplateBuild(options);
1529
+ return;
1530
+ }
1531
+ if (subcommand === "run") {
1532
+ await runSandboxTemplateLocal(options, "run");
1533
+ return;
1534
+ }
1535
+ if (subcommand === "dev") {
1536
+ await runSandboxTemplateLocal(options, "dev");
1537
+ return;
1538
+ }
1539
+ if (subcommand === "action") {
1540
+ await runSandboxTemplateExistingSandboxAction(options, rest.slice(1));
1541
+ return;
1542
+ }
1543
+ if (subcommand === "start") {
1544
+ await runSandboxTemplateStart(options);
1545
+ return;
1546
+ }
1547
+ throw new Error(
1548
+ `usage: sandbox-template <validate|print-schema|scaffold|build|run|dev|start|action> [--file ${OPENPOND_MANIFEST_FILE_NAME}] [--path <dir>] [--name <name>]`
1549
+ );
1550
+ }
1551
+ async function runSandboxTemplateBuild(options) {
1552
+ const context = await loadSandboxTemplateManifestContext(options);
1553
+ const outputPath = resolveSandboxTemplateBuildOutputPath(
1554
+ options,
1555
+ context.projectPath
1556
+ );
1557
+ const plan = sandboxTemplateBuildPlan({
1558
+ manifest: context.manifest,
1559
+ manifestFile: path4.relative(context.projectPath, context.filePath) || OPENPOND_MANIFEST_FILE_NAME,
1560
+ projectRoot: path4.relative(path4.dirname(outputPath), context.projectPath) || "."
1561
+ });
1562
+ const shouldWrite = !parseBooleanOption(options.noWrite);
1563
+ if (shouldWrite) {
1564
+ await fs3.mkdir(path4.dirname(outputPath), { recursive: true });
1565
+ await fs3.writeFile(
1566
+ outputPath,
1567
+ `${JSON.stringify(plan, null, 2)}
1568
+ `,
1569
+ "utf8"
1570
+ );
1571
+ }
1572
+ const metadata = sandboxTemplateBuildMetadata(context.manifest);
1573
+ const executables = sandboxTemplateExecutableEntries(context.manifest);
1574
+ console.log(
1575
+ JSON.stringify(
1576
+ {
1577
+ ok: true,
1578
+ file: context.filePath,
1579
+ output: shouldWrite ? outputPath : null,
1580
+ ...metadata,
1581
+ startCommand: executables[0]?.command ?? null,
1582
+ plan
1583
+ },
1584
+ null,
1585
+ 2
1586
+ )
1587
+ );
1588
+ }
1589
+ async function loadSandboxTemplateManifestContext(options) {
1590
+ const filePath = resolveSandboxTemplateFilePath(options);
1591
+ const source = await fs3.readFile(filePath, "utf8");
1592
+ const result = validateSandboxTemplateYaml(source);
1593
+ if (!result.ok) {
1594
+ process.exitCode = 1;
1595
+ throw new Error(formatSandboxTemplateDiagnostics(result.diagnostics));
1596
+ }
1597
+ return {
1598
+ filePath,
1599
+ projectPath: path4.dirname(filePath),
1600
+ manifest: result.manifest
1601
+ };
1602
+ }
1603
+ function resolveSandboxTemplateBuildOutputPath(options, projectPath) {
1604
+ const rawOutput = typeof options.output === "string" && options.output.trim() ? options.output.trim() : typeof options.out === "string" && options.out.trim() ? options.out.trim() : "";
1605
+ if (rawOutput) return path4.resolve(process.cwd(), rawOutput);
1606
+ const rawOutputDir = typeof options.outputDir === "string" && options.outputDir.trim() ? options.outputDir.trim() : typeof options.outDir === "string" && options.outDir.trim() ? options.outDir.trim() : "dist";
1607
+ return path4.resolve(
1608
+ projectPath,
1609
+ rawOutputDir,
1610
+ SANDBOX_TEMPLATE_BUILD_PLAN_FILE_NAME
1611
+ );
1612
+ }
1613
+ async function loadSandboxTemplateBuildPlan(options) {
1614
+ const rawBuild = typeof options.build === "string" && options.build.trim() ? options.build.trim() : typeof options.plan === "string" && options.plan.trim() ? options.plan.trim() : "";
1615
+ if (rawBuild) {
1616
+ const filePath = path4.resolve(process.cwd(), rawBuild);
1617
+ const parsed = JSON.parse(
1618
+ await fs3.readFile(filePath, "utf8")
1619
+ );
1620
+ if (parsed.kind !== SANDBOX_TEMPLATE_BUILD_PLAN_KIND || parsed.schemaVersion !== 1) {
1621
+ throw new Error(`invalid sandbox template build plan: ${filePath}`);
1622
+ }
1623
+ return {
1624
+ plan: parsed,
1625
+ filePath,
1626
+ projectPath: path4.resolve(path4.dirname(filePath), parsed.projectRoot)
1627
+ };
1628
+ }
1629
+ const context = await loadSandboxTemplateManifestContext(options);
1630
+ return {
1631
+ plan: sandboxTemplateBuildPlan({
1632
+ manifest: context.manifest,
1633
+ manifestFile: path4.relative(context.projectPath, context.filePath) || OPENPOND_MANIFEST_FILE_NAME,
1634
+ projectRoot: context.projectPath
1635
+ }),
1636
+ filePath: context.filePath,
1637
+ projectPath: context.projectPath
1638
+ };
1639
+ }
1640
+ export {
1641
+ loadSandboxTemplateBuildPlan,
1642
+ loadSandboxTemplateManifestContext,
1643
+ resolveSandboxTemplateBuildOutputPath,
1644
+ runSandboxTemplateBuild,
1645
+ runSandboxTemplateCommand
1646
+ };