@skrr-ai/cli 0.1.47 → 0.1.48

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 (274) hide show
  1. package/README.md +1 -1
  2. package/dist/base-command.d.ts +18 -0
  3. package/dist/base-command.js +61 -1
  4. package/dist/commands/agents/chat.d.ts +21 -0
  5. package/dist/commands/agents/chat.js +26 -9
  6. package/dist/commands/balance/usage/breakdown.d.ts +14 -0
  7. package/dist/commands/balance/usage/breakdown.js +68 -0
  8. package/dist/commands/balance/usage.js +24 -0
  9. package/dist/commands/commerce/entitlements/explain.d.ts +22 -0
  10. package/dist/commands/commerce/entitlements/explain.js +40 -0
  11. package/dist/commands/commerce/incidents/audit.d.ts +20 -0
  12. package/dist/commands/commerce/incidents/audit.js +37 -0
  13. package/dist/commands/commerce/incidents/list.d.ts +22 -0
  14. package/dist/commands/commerce/incidents/list.js +49 -0
  15. package/dist/commands/commerce/incidents/resolve.d.ts +23 -0
  16. package/dist/commands/commerce/incidents/resolve.js +45 -0
  17. package/dist/commands/commerce/incidents/retry.d.ts +22 -0
  18. package/dist/commands/commerce/incidents/retry.js +45 -0
  19. package/dist/commands/commerce/incidents/show.d.ts +13 -0
  20. package/dist/commands/commerce/incidents/show.js +30 -0
  21. package/dist/commands/commerce/offers/approve.d.ts +17 -0
  22. package/dist/commands/commerce/offers/approve.js +33 -0
  23. package/dist/commands/commerce/offers/create.d.ts +18 -0
  24. package/dist/commands/commerce/offers/create.js +34 -0
  25. package/dist/commands/commerce/offers/publish.d.ts +13 -0
  26. package/dist/commands/commerce/offers/publish.js +28 -0
  27. package/dist/commands/commerce/offers/retire.d.ts +17 -0
  28. package/dist/commands/commerce/offers/retire.js +32 -0
  29. package/dist/commands/commerce/offers/validate.d.ts +18 -0
  30. package/dist/commands/commerce/offers/validate.js +33 -0
  31. package/dist/commands/commerce/products/create.d.ts +10 -0
  32. package/dist/commands/commerce/products/create.js +25 -0
  33. package/dist/commands/commerce/promotions/redeem.d.ts +13 -0
  34. package/dist/commands/commerce/promotions/redeem.js +32 -0
  35. package/dist/commands/commerce/terms/accept.d.ts +18 -0
  36. package/dist/commands/commerce/terms/accept.js +39 -0
  37. package/dist/commands/commerce/terms/show.d.ts +10 -0
  38. package/dist/commands/commerce/terms/show.js +34 -0
  39. package/dist/commands/commerce/trials/conversion-preview.d.ts +18 -0
  40. package/dist/commands/commerce/trials/conversion-preview.js +39 -0
  41. package/dist/commands/commerce/trials/start.d.ts +23 -0
  42. package/dist/commands/commerce/trials/start.js +58 -0
  43. package/dist/commands/commitments/accounting.d.ts +39 -0
  44. package/dist/commands/commitments/accounting.js +116 -0
  45. package/dist/commands/commitments/action-history.d.ts +1 -0
  46. package/dist/commands/commitments/action-history.js +8 -1
  47. package/dist/commands/commitments/case.d.ts +1 -0
  48. package/dist/commands/commitments/case.js +19 -3
  49. package/dist/commands/commitments/cases.d.ts +1 -0
  50. package/dist/commands/commitments/cases.js +11 -1
  51. package/dist/commands/commitments/checks.d.ts +1 -0
  52. package/dist/commands/commitments/checks.js +5 -1
  53. package/dist/commands/commitments/cost.d.ts +62 -0
  54. package/dist/commands/commitments/cost.js +130 -0
  55. package/dist/commands/commitments/create.js +1 -1
  56. package/dist/commands/commitments/delete.d.ts +1 -0
  57. package/dist/commands/commitments/delete.js +56 -6
  58. package/dist/commands/commitments/feedback.js +9 -1
  59. package/dist/commands/commitments/handover.js +16 -0
  60. package/dist/commands/commitments/hypotheses.d.ts +1 -0
  61. package/dist/commands/commitments/hypotheses.js +17 -2
  62. package/dist/commands/commitments/links/list.js +4 -1
  63. package/dist/commands/commitments/liveness.d.ts +18 -0
  64. package/dist/commands/commitments/liveness.js +80 -0
  65. package/dist/commands/commitments/metrics.d.ts +1 -0
  66. package/dist/commands/commitments/metrics.js +8 -1
  67. package/dist/commands/commitments/next-tick.d.ts +39 -0
  68. package/dist/commands/commitments/next-tick.js +122 -0
  69. package/dist/commands/commitments/observations.d.ts +1 -0
  70. package/dist/commands/commitments/observations.js +19 -2
  71. package/dist/commands/commitments/pause.js +12 -0
  72. package/dist/commands/commitments/receipt.d.ts +2 -0
  73. package/dist/commands/commitments/receipt.js +16 -2
  74. package/dist/commands/commitments/remediation-proposal.js +6 -2
  75. package/dist/commands/commitments/remediation-proposals.js +5 -1
  76. package/dist/commands/commitments/restore.d.ts +25 -0
  77. package/dist/commands/commitments/restore.js +46 -0
  78. package/dist/commands/commitments/show.js +32 -0
  79. package/dist/commands/commitments/trigger-status.js +33 -0
  80. package/dist/commands/commitments/update.d.ts +1 -0
  81. package/dist/commands/commitments/update.js +31 -5
  82. package/dist/commands/commitments/wake-capabilities.d.ts +0 -1
  83. package/dist/commands/commitments/wake-capabilities.js +8 -4
  84. package/dist/commands/enterprise/budgets/create.d.ts +11 -0
  85. package/dist/commands/enterprise/budgets/create.js +27 -0
  86. package/dist/commands/enterprise/budgets/evaluate.d.ts +17 -0
  87. package/dist/commands/enterprise/budgets/evaluate.js +32 -0
  88. package/dist/commands/enterprise/budgets/publish.d.ts +14 -0
  89. package/dist/commands/enterprise/budgets/publish.js +30 -0
  90. package/dist/commands/enterprise/contracts/activate.d.ts +14 -0
  91. package/dist/commands/enterprise/contracts/activate.js +30 -0
  92. package/dist/commands/enterprise/contracts/create.d.ts +14 -0
  93. package/dist/commands/enterprise/contracts/create.js +32 -0
  94. package/dist/commands/enterprise/contracts/issue.d.ts +18 -0
  95. package/dist/commands/enterprise/contracts/issue.js +34 -0
  96. package/dist/commands/enterprise/domains/begin.d.ts +20 -0
  97. package/dist/commands/enterprise/domains/begin.js +39 -0
  98. package/dist/commands/enterprise/domains/verify.d.ts +14 -0
  99. package/dist/commands/enterprise/domains/verify.js +32 -0
  100. package/dist/commands/enterprise/external-config/activate.d.ts +22 -0
  101. package/dist/commands/enterprise/external-config/activate.js +49 -0
  102. package/dist/commands/enterprise/external-config/set.d.ts +25 -0
  103. package/dist/commands/enterprise/external-config/set.js +123 -0
  104. package/dist/commands/enterprise/external-config/show.d.ts +12 -0
  105. package/dist/commands/enterprise/external-config/show.js +39 -0
  106. package/dist/commands/enterprise/external-config/suspend.d.ts +13 -0
  107. package/dist/commands/enterprise/external-config/suspend.js +41 -0
  108. package/dist/commands/enterprise/finance/close-snapshot.d.ts +11 -0
  109. package/dist/commands/enterprise/finance/close-snapshot.js +28 -0
  110. package/dist/commands/enterprise/finance/record-document.d.ts +18 -0
  111. package/dist/commands/enterprise/finance/record-document.js +35 -0
  112. package/dist/commands/enterprise/profile.d.ts +20 -0
  113. package/dist/commands/enterprise/profile.js +38 -0
  114. package/dist/commands/login.d.ts +13 -0
  115. package/dist/commands/login.js +77 -0
  116. package/dist/commands/machines/dedicated/index.js +3 -0
  117. package/dist/commands/machines/dedicated/service/apply.d.ts +23 -0
  118. package/dist/commands/machines/dedicated/service/apply.js +99 -0
  119. package/dist/commands/machines/dedicated/service/approve.d.ts +23 -0
  120. package/dist/commands/machines/dedicated/service/approve.js +52 -0
  121. package/dist/commands/machines/dedicated/service/create.d.ts +55 -0
  122. package/dist/commands/machines/dedicated/service/create.js +222 -0
  123. package/dist/commands/machines/dedicated/service/edit.d.ts +30 -0
  124. package/dist/commands/machines/dedicated/service/edit.js +146 -0
  125. package/dist/commands/machines/dedicated/service/exec.d.ts +26 -0
  126. package/dist/commands/machines/dedicated/service/exec.js +92 -0
  127. package/dist/commands/machines/dedicated/service/export.d.ts +29 -0
  128. package/dist/commands/machines/dedicated/service/export.js +106 -0
  129. package/dist/commands/machines/dedicated/service/index.d.ts +14 -0
  130. package/dist/commands/machines/dedicated/service/index.js +49 -0
  131. package/dist/commands/machines/dedicated/service/logs.d.ts +33 -0
  132. package/dist/commands/machines/dedicated/service/logs.js +148 -0
  133. package/dist/commands/machines/dedicated/service/ls.d.ts +20 -0
  134. package/dist/commands/machines/dedicated/service/ls.js +80 -0
  135. package/dist/commands/machines/dedicated/service/restart.d.ts +14 -0
  136. package/dist/commands/machines/dedicated/service/restart.js +29 -0
  137. package/dist/commands/machines/dedicated/service/rm.d.ts +20 -0
  138. package/dist/commands/machines/dedicated/service/rm.js +33 -0
  139. package/dist/commands/machines/dedicated/service/secret/index.d.ts +14 -0
  140. package/dist/commands/machines/dedicated/service/secret/index.js +25 -0
  141. package/dist/commands/machines/dedicated/service/secret/rm.d.ts +22 -0
  142. package/dist/commands/machines/dedicated/service/secret/rm.js +54 -0
  143. package/dist/commands/machines/dedicated/service/secret/set.d.ts +22 -0
  144. package/dist/commands/machines/dedicated/service/secret/set.js +93 -0
  145. package/dist/commands/machines/dedicated/service/start.d.ts +14 -0
  146. package/dist/commands/machines/dedicated/service/start.js +29 -0
  147. package/dist/commands/machines/dedicated/service/status.d.ts +21 -0
  148. package/dist/commands/machines/dedicated/service/status.js +85 -0
  149. package/dist/commands/machines/dedicated/service/stop.d.ts +20 -0
  150. package/dist/commands/machines/dedicated/service/stop.js +35 -0
  151. package/dist/commands/machines/dedicated/service/top.d.ts +22 -0
  152. package/dist/commands/machines/dedicated/service/top.js +120 -0
  153. package/dist/commands/machines/dedicated/sign-in-status.d.ts +22 -0
  154. package/dist/commands/machines/dedicated/sign-in-status.js +79 -0
  155. package/dist/commands/machines/dedicated/ssh-config.d.ts +28 -0
  156. package/dist/commands/machines/dedicated/ssh-config.js +66 -0
  157. package/dist/commands/machines/dedicated/ssh-proxy.d.ts +22 -0
  158. package/dist/commands/machines/dedicated/ssh-proxy.js +62 -0
  159. package/dist/commands/machines/dedicated/ssh.d.ts +29 -0
  160. package/dist/commands/machines/dedicated/ssh.js +121 -0
  161. package/dist/commands/native/allow-all.d.ts +14 -0
  162. package/dist/commands/native/allow-all.js +25 -0
  163. package/dist/commands/native/allow.d.ts +24 -0
  164. package/dist/commands/native/allow.js +45 -0
  165. package/dist/commands/native/deny.d.ts +24 -0
  166. package/dist/commands/native/deny.js +43 -0
  167. package/dist/commands/native/disable.d.ts +17 -0
  168. package/dist/commands/native/disable.js +28 -0
  169. package/dist/commands/native/enable.d.ts +12 -0
  170. package/dist/commands/native/enable.js +29 -0
  171. package/dist/commands/native/list-apps.d.ts +19 -0
  172. package/dist/commands/native/list-apps.js +30 -0
  173. package/dist/commands/native/restrict-to.d.ts +22 -0
  174. package/dist/commands/native/restrict-to.js +41 -0
  175. package/dist/commands/native/status.d.ts +19 -0
  176. package/dist/commands/native/status.js +33 -0
  177. package/dist/commands/native.d.ts +39 -0
  178. package/dist/commands/native.js +76 -0
  179. package/dist/commands/service/index.d.ts +14 -0
  180. package/dist/commands/service/index.js +31 -0
  181. package/dist/commands/service/logs.d.ts +18 -0
  182. package/dist/commands/service/logs.js +53 -0
  183. package/dist/commands/service/ls.d.ts +13 -0
  184. package/dist/commands/service/ls.js +48 -0
  185. package/dist/commands/service/propose.d.ts +34 -0
  186. package/dist/commands/service/propose.js +113 -0
  187. package/dist/commands/service/status.d.ts +17 -0
  188. package/dist/commands/service/status.js +56 -0
  189. package/dist/commands/spaces/auto-executor/runs.d.ts +13 -0
  190. package/dist/commands/spaces/auto-executor/runs.js +56 -0
  191. package/dist/commands/store/governance/artifacts.d.ts +25 -0
  192. package/dist/commands/store/governance/artifacts.js +55 -0
  193. package/dist/commands/store/governance/assign.d.ts +16 -0
  194. package/dist/commands/store/governance/assign.js +54 -0
  195. package/dist/commands/store/governance/create-license.d.ts +18 -0
  196. package/dist/commands/store/governance/create-license.js +35 -0
  197. package/dist/commands/store/governance/install-plan.d.ts +25 -0
  198. package/dist/commands/store/governance/install-plan.js +56 -0
  199. package/dist/commands/store/governance/license-policy.d.ts +30 -0
  200. package/dist/commands/store/governance/license-policy.js +69 -0
  201. package/dist/commands/store/governance/policy.d.ts +25 -0
  202. package/dist/commands/store/governance/policy.js +65 -0
  203. package/dist/commands/store/governance/publish-release.d.ts +17 -0
  204. package/dist/commands/store/governance/publish-release.js +34 -0
  205. package/dist/commands/tasks/deliverable/add.d.ts +15 -0
  206. package/dist/commands/tasks/deliverable/add.js +174 -0
  207. package/dist/commands/tasks/deliverable/archive.d.ts +35 -0
  208. package/dist/commands/tasks/deliverable/archive.js +205 -0
  209. package/dist/commands/tasks/labels/detach.js +3 -0
  210. package/dist/commands/tasks/labels/suggest.d.ts +24 -0
  211. package/dist/commands/tasks/labels/suggest.js +69 -0
  212. package/dist/commands/tasks/list.d.ts +16 -2
  213. package/dist/commands/tasks/list.js +100 -24
  214. package/dist/commands/tasks/show.d.ts +23 -0
  215. package/dist/commands/tasks/show.js +51 -1
  216. package/dist/commands/wiki/rm.d.ts +22 -1
  217. package/dist/commands/wiki/rm.js +52 -3
  218. package/dist/lib/agentic-stream.js +10 -2
  219. package/dist/lib/balance.d.ts +6 -1
  220. package/dist/lib/balance.js +15 -0
  221. package/dist/lib/commerce-command.d.ts +48 -0
  222. package/dist/lib/commerce-command.js +70 -0
  223. package/dist/lib/commerce.d.ts +93 -0
  224. package/dist/lib/commerce.js +129 -0
  225. package/dist/lib/commitment-product.js +1 -1
  226. package/dist/lib/commitments.d.ts +202 -29
  227. package/dist/lib/commitments.js +300 -33
  228. package/dist/lib/dedicated-lease-command.js +10 -0
  229. package/dist/lib/dedicated-machines.d.ts +37 -0
  230. package/dist/lib/dedicated-machines.js +47 -0
  231. package/dist/lib/dedicated-service-command.d.ts +60 -0
  232. package/dist/lib/dedicated-service-command.js +182 -0
  233. package/dist/lib/dedicated-service.d.ts +342 -0
  234. package/dist/lib/dedicated-service.js +741 -0
  235. package/dist/lib/dedicated-ssh.d.ts +136 -0
  236. package/dist/lib/dedicated-ssh.js +387 -0
  237. package/dist/lib/delegated-cli.js +51 -13
  238. package/dist/lib/enterprise-commerce.d.ts +67 -0
  239. package/dist/lib/enterprise-commerce.js +112 -0
  240. package/dist/lib/enterprise-external-configuration.d.ts +60 -0
  241. package/dist/lib/enterprise-external-configuration.js +58 -0
  242. package/dist/lib/first-party-harness.js +17 -3
  243. package/dist/lib/governed-store.d.ts +110 -0
  244. package/dist/lib/governed-store.js +79 -0
  245. package/dist/lib/guest-service-command.d.ts +16 -0
  246. package/dist/lib/guest-service-command.js +32 -0
  247. package/dist/lib/guest-service.d.ts +67 -0
  248. package/dist/lib/guest-service.js +112 -0
  249. package/dist/lib/prompt.d.ts +1 -0
  250. package/dist/lib/prompt.js +1 -0
  251. package/dist/lib/task-asset-upload.d.ts +20 -0
  252. package/dist/lib/task-asset-upload.js +30 -8
  253. package/dist/lib/task-extras.d.ts +23 -0
  254. package/dist/lib/task-extras.js +6 -0
  255. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.d.ts +9 -3
  256. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.js +10 -3
  257. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  258. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +6 -5
  259. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +16 -0
  260. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +21 -4
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +1 -1
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +24 -2
  263. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.d.ts +9 -3
  264. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.js +10 -3
  265. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  266. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  267. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +16 -0
  268. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +20 -4
  269. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +1 -1
  270. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +24 -2
  271. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  272. package/dist/node_modules/@skrr-ai/data-provider/index.js +5060 -4827
  273. package/oclif.manifest.json +37416 -31416
  274. package/package.json +43 -1
@@ -0,0 +1,23 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated service apply` — the git-ops verb: make the
4
+ * machine's declaration match the file.
5
+ *
6
+ * `create` refuses a name that exists; `apply` is idempotent — it reads the
7
+ * current spec (for its CAS `revision`), then updates or creates so a
8
+ * checked-in `service.json` can be re-applied any number of times. There is
9
+ * deliberately no watcher: the file is input, not a live mirror.
10
+ */
11
+ export default class DedicatedServiceApply extends DedicatedServiceCommand {
12
+ static summary: string;
13
+ static description: string;
14
+ static examples: string[];
15
+ static args: {
16
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ static flags: {
19
+ file: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ };
22
+ run(): Promise<void>;
23
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_fs_1 = require("node:fs");
4
+ const core_1 = require("@oclif/core");
5
+ const prompt_1 = require("../../../../lib/prompt");
6
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
7
+ const dedicated_service_1 = require("../../../../lib/dedicated-service");
8
+ /**
9
+ * `skrr machines dedicated service apply` — the git-ops verb: make the
10
+ * machine's declaration match the file.
11
+ *
12
+ * `create` refuses a name that exists; `apply` is idempotent — it reads the
13
+ * current spec (for its CAS `revision`), then updates or creates so a
14
+ * checked-in `service.json` can be re-applied any number of times. There is
15
+ * deliberately no watcher: the file is input, not a live mirror.
16
+ */
17
+ class DedicatedServiceApply extends dedicated_service_command_1.DedicatedServiceCommand {
18
+ static summary = 'Create or update a service from a spec file, idempotently';
19
+ static description = 'Applies service.json to the machine: creates the service when absent, updates it ' +
20
+ 'against the stored spec’s revision when present (CAS — a spec that changed since the ' +
21
+ 'read is reported as a conflict, never silently merged). Re-running it is safe.';
22
+ static examples = [
23
+ '<%= config.bin %> machines dedicated service apply <lease-id> --file service.json',
24
+ ];
25
+ static args = {
26
+ lease: core_1.Args.string({
27
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
28
+ required: true,
29
+ ignoreStdin: true,
30
+ }),
31
+ };
32
+ static flags = {
33
+ file: core_1.Flags.string({
34
+ description: 'The service.json to apply',
35
+ required: true,
36
+ }),
37
+ json: core_1.Flags.boolean({ description: 'Output the result as JSON' }),
38
+ };
39
+ async run() {
40
+ this.requireAuth();
41
+ const { args, flags } = await this.parse(DedicatedServiceApply);
42
+ const leaseId = await this.resolveLeaseArg(args.lease);
43
+ let raw;
44
+ try {
45
+ raw = flags.file === '-' ? await (0, prompt_1.readStdin)() : (0, node_fs_1.readFileSync)(flags.file, 'utf8');
46
+ }
47
+ catch (err) {
48
+ this.error(`Cannot read ${flags.file}: ${err instanceof Error ? err.message : err}`, {
49
+ exit: 2,
50
+ });
51
+ }
52
+ let spec;
53
+ try {
54
+ spec = (0, dedicated_service_1.parseServiceSpecDocument)(raw, flags.file);
55
+ }
56
+ catch (err) {
57
+ this.reportServiceError(err);
58
+ }
59
+ // Upsert: read the current spec for its revision, then choose create or
60
+ // CAS update. `get` failing with not_found is the create path — read the
61
+ // raw ack here rather than through callService, which exits on refusals.
62
+ let verb = 'create';
63
+ let baseRevision;
64
+ try {
65
+ const channel = await this.serviceChannel();
66
+ const { result } = await channel.call('get', {
67
+ leaseId,
68
+ name: spec.name,
69
+ ...this.scopeFrom(flags),
70
+ });
71
+ if (result?.spec) {
72
+ verb = 'update';
73
+ baseRevision = typeof result.spec.revision === 'number' ? result.spec.revision : undefined;
74
+ }
75
+ }
76
+ catch (err) {
77
+ if (!(err instanceof dedicated_service_1.DedicatedServiceError && err.code === 'not_found')) {
78
+ this.reportServiceError(err);
79
+ }
80
+ }
81
+ const payload = verb === 'update'
82
+ ? {
83
+ leaseId,
84
+ name: spec.name,
85
+ patch: (0, dedicated_service_1.specPayload)(spec),
86
+ baseRevision,
87
+ ...this.scopeFrom(flags),
88
+ }
89
+ : { leaseId, spec: (0, dedicated_service_1.specPayload)(spec), ...this.scopeFrom(flags) };
90
+ const result = await this.callService(verb, payload);
91
+ this.closeServiceChannel();
92
+ if (flags.json) {
93
+ this.log(JSON.stringify(result ?? { name: spec.name, applied: verb }, null, 2));
94
+ return;
95
+ }
96
+ this.log(`Service ${spec.name} ${verb === 'create' ? 'created' : 'updated'} on ${leaseId}.`);
97
+ }
98
+ }
99
+ exports.default = DedicatedServiceApply;
@@ -0,0 +1,23 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated service approve` — the owner's answer to an
4
+ * agent-proposed service.
5
+ *
6
+ * Agents on the machine propose services through `skrr service propose`; a
7
+ * proposal is inert — nothing runs — until the owner answers it here.
8
+ * Approving turns the proposal into a live declaration with the agent's
9
+ * provenance preserved; rejecting drops it.
10
+ */
11
+ export default class DedicatedServiceApprove extends DedicatedServiceCommand {
12
+ static summary: string;
13
+ static examples: string[];
14
+ static args: {
15
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
16
+ name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ static flags: {
19
+ reject: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ };
22
+ run(): Promise<void>;
23
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
5
+ /**
6
+ * `skrr machines dedicated service approve` — the owner's answer to an
7
+ * agent-proposed service.
8
+ *
9
+ * Agents on the machine propose services through `skrr service propose`; a
10
+ * proposal is inert — nothing runs — until the owner answers it here.
11
+ * Approving turns the proposal into a live declaration with the agent's
12
+ * provenance preserved; rejecting drops it.
13
+ */
14
+ class DedicatedServiceApprove extends dedicated_service_command_1.DedicatedServiceCommand {
15
+ static summary = 'Approve or reject an agent-proposed service';
16
+ static examples = [
17
+ '<%= config.bin %> machines dedicated service approve <lease-id> <name>',
18
+ '<%= config.bin %> machines dedicated service approve <lease-id> <name> --reject',
19
+ ];
20
+ static args = {
21
+ lease: core_1.Args.string({
22
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
23
+ required: true,
24
+ ignoreStdin: true,
25
+ }),
26
+ name: core_1.Args.string({ description: 'Proposed service name', required: true, ignoreStdin: true }),
27
+ };
28
+ static flags = {
29
+ reject: core_1.Flags.boolean({ description: 'Reject the proposal instead of approving it' }),
30
+ json: core_1.Flags.boolean({ description: 'Output the result as JSON' }),
31
+ };
32
+ async run() {
33
+ this.requireAuth();
34
+ const { args, flags } = await this.parse(DedicatedServiceApprove);
35
+ const leaseId = await this.resolveLeaseArg(args.lease);
36
+ const result = await this.callService('approve', {
37
+ leaseId,
38
+ name: args.name,
39
+ approve: !flags.reject,
40
+ ...this.scopeFrom(flags),
41
+ });
42
+ this.closeServiceChannel();
43
+ if (flags.json) {
44
+ this.log(JSON.stringify(result ?? { name: args.name, approved: !flags.reject }, null, 2));
45
+ return;
46
+ }
47
+ this.log(flags.reject
48
+ ? `Proposal ${args.name} rejected — nothing runs.`
49
+ : `Proposal ${args.name} approved — the service is now declared and starts.`);
50
+ }
51
+ }
52
+ exports.default = DedicatedServiceApprove;
@@ -0,0 +1,55 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated service create` — declare a workload the machine
4
+ * keeps running.
5
+ *
6
+ * The spec arrives one of three ways, never mixed:
7
+ *
8
+ * argv `service create <lease> --name devin -- <argv…>`
9
+ * file `service create <lease> --file service.json`
10
+ * template `service create <lease> --template worker --param repo=x`
11
+ *
12
+ * A template is a spec with `${param}` holes resolved locally
13
+ * (`~/.skrr/templates/<name>.json`, then the API gallery) — what reaches the
14
+ * daemon is always a concrete spec, so nothing downstream ever learns that
15
+ * templates exist. `--print` shows that merged spec instead of creating.
16
+ */
17
+ export default class DedicatedServiceCreate extends DedicatedServiceCommand {
18
+ static summary: string;
19
+ static description: string;
20
+ static examples: string[];
21
+ static usage: string;
22
+ static args: {
23
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
24
+ command: import("@oclif/core/lib/interfaces").Arg<string[] | undefined, Record<string, unknown>>;
25
+ };
26
+ static flags: {
27
+ file: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
28
+ template: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
29
+ param: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
30
+ print: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
32
+ name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
+ cwd: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
+ env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
+ restart: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
36
+ notify: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
37
+ 'stop-timeout': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
+ 'limit-memory': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
39
+ 'limit-cpu': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
40
+ 'limit-pids': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
41
+ };
42
+ run(): Promise<void>;
43
+ /**
44
+ * The one spec this invocation means — flags+argv, --file, or --template.
45
+ * Mixing sources is refused rather than merged: "which spec won" is a
46
+ * question only the caller can answer.
47
+ */
48
+ private resolveSpec;
49
+ /**
50
+ * `~/.skrr/templates/<name>.json` first, then the API gallery. A missing
51
+ * local file is a fall-through, a broken one is an error; a gallery miss
52
+ * names both places looked.
53
+ */
54
+ private resolveTemplate;
55
+ }
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_fs_1 = require("node:fs");
4
+ const core_1 = require("@oclif/core");
5
+ const prompt_1 = require("../../../../lib/prompt");
6
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
7
+ const dedicated_service_1 = require("../../../../lib/dedicated-service");
8
+ const exec_1 = require("../../hosted/exec");
9
+ /**
10
+ * `skrr machines dedicated service create` — declare a workload the machine
11
+ * keeps running.
12
+ *
13
+ * The spec arrives one of three ways, never mixed:
14
+ *
15
+ * argv `service create <lease> --name devin -- <argv…>`
16
+ * file `service create <lease> --file service.json`
17
+ * template `service create <lease> --template worker --param repo=x`
18
+ *
19
+ * A template is a spec with `${param}` holes resolved locally
20
+ * (`~/.skrr/templates/<name>.json`, then the API gallery) — what reaches the
21
+ * daemon is always a concrete spec, so nothing downstream ever learns that
22
+ * templates exist. `--print` shows that merged spec instead of creating.
23
+ */
24
+ class DedicatedServiceCreate extends dedicated_service_command_1.DedicatedServiceCommand {
25
+ static summary = 'Declare an always-on service on a Dedicated Runtime';
26
+ static description = 'Declares a service the machine keeps running: it restarts on crash, on daemon restart, ' +
27
+ 'after a checkpoint, and on a replacement machine. Give the command after -- as argv, ' +
28
+ 'or a spec with --file service.json, or a --template with --param values. ' +
29
+ 'Combine none of the three — each is a complete spec.';
30
+ static examples = [
31
+ '<%= config.bin %> machines dedicated service create <lease-id> --name devin --cwd /workspace/repos/devin -- node dist/worker.js',
32
+ '<%= config.bin %> machines dedicated service create <lease-id> --file service.json',
33
+ '<%= config.bin %> machines dedicated service create <lease-id> --name worker --restart on-failure --limit-memory 2G -- node dist/worker.js',
34
+ '<%= config.bin %> machines dedicated service create <lease-id> --template devin-worker --param repo=/workspace/repos/app',
35
+ '<%= config.bin %> machines dedicated service create <lease-id> --template devin-worker --param repo=x --print',
36
+ ];
37
+ static usage = 'machines dedicated service create <lease-id> [flags] -- <command...>';
38
+ static args = {
39
+ lease: core_1.Args.string({
40
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
41
+ required: true,
42
+ ignoreStdin: true,
43
+ }),
44
+ command: core_1.Args.string({
45
+ description: 'The service command and its arguments, after --',
46
+ multiple: true,
47
+ required: false,
48
+ ignoreStdin: true,
49
+ }),
50
+ };
51
+ static flags = {
52
+ ...dedicated_service_command_1.serviceSpecFlags,
53
+ file: core_1.Flags.string({ description: 'Read the spec from a service.json file instead of flags' }),
54
+ template: core_1.Flags.string({
55
+ description: 'Build the spec from a template (~/.skrr/templates/<name>.json, then the API gallery)',
56
+ }),
57
+ param: core_1.Flags.string({
58
+ description: 'Template parameter as name=value; repeat for several',
59
+ multiple: true,
60
+ }),
61
+ print: core_1.Flags.boolean({
62
+ description: 'Print the spec that would be created without creating it',
63
+ }),
64
+ json: core_1.Flags.boolean({ description: 'Output the created service as JSON' }),
65
+ };
66
+ async run() {
67
+ this.requireAuth();
68
+ const { head, tail } = (0, exec_1.splitExecArgv)(this.argv);
69
+ const { args, flags } = await this.parse(DedicatedServiceCreate, head);
70
+ const leaseId = await this.resolveLeaseArg(args.lease);
71
+ let spec;
72
+ try {
73
+ spec = await this.resolveSpec(flags, tail ?? args.command ?? []);
74
+ }
75
+ catch (err) {
76
+ this.reportServiceError(err);
77
+ }
78
+ const errors = (0, dedicated_service_1.validateServiceSpec)(spec);
79
+ if (errors.length > 0) {
80
+ this.error(`The service spec is not valid:\n${errors.map((e) => ` - ${e}`).join('\n')}`, {
81
+ exit: 2,
82
+ });
83
+ }
84
+ if (flags.print) {
85
+ this.log(JSON.stringify((0, dedicated_service_1.specPayload)(spec), null, 2));
86
+ return;
87
+ }
88
+ const result = await this.callService('create', {
89
+ leaseId,
90
+ spec: (0, dedicated_service_1.specPayload)(spec),
91
+ ...this.scopeFrom(flags),
92
+ });
93
+ this.closeServiceChannel();
94
+ if (flags.json) {
95
+ this.log(JSON.stringify(result ?? { name: spec.name }, null, 2));
96
+ return;
97
+ }
98
+ const name = spec.name ?? result?.name ?? 'service';
99
+ this.log(`Service ${name} declared on ${leaseId}. It starts now and restarts across crashes, ` +
100
+ `daemon updates, checkpoints and machine replacement.`);
101
+ this.log(`Watch it: \`${this.config.bin} machines dedicated service status ${leaseId} ${name}\`, ` +
102
+ `logs: \`${this.config.bin} machines dedicated service logs ${leaseId} ${name} -f\`.`);
103
+ }
104
+ /**
105
+ * The one spec this invocation means — flags+argv, --file, or --template.
106
+ * Mixing sources is refused rather than merged: "which spec won" is a
107
+ * question only the caller can answer.
108
+ */
109
+ async resolveSpec(flags, argv) {
110
+ const file = flags.file;
111
+ const templateName = flags.template;
112
+ const flagSpecUsed = Boolean(flags.name ||
113
+ flags.cwd ||
114
+ flags.env?.length ||
115
+ flags.restart ||
116
+ flags.notify !== undefined ||
117
+ flags['stop-timeout'] !== undefined ||
118
+ flags['limit-memory'] ||
119
+ flags['limit-cpu'] !== undefined ||
120
+ flags['limit-pids'] !== undefined);
121
+ if (file && (templateName || flagSpecUsed || argv.length > 0)) {
122
+ this.error('--file is the whole spec — it cannot be combined with --template, spec flags, or a command after --.', { exit: 2 });
123
+ }
124
+ if (templateName && argv.length > 0) {
125
+ this.error('--template already carries the command — nothing may follow --.', { exit: 2 });
126
+ }
127
+ if (file) {
128
+ // `-` is the repo's stdin convention — a spec is small enough to pipe.
129
+ let raw;
130
+ try {
131
+ raw = file === '-' ? await (0, prompt_1.readStdin)() : (0, node_fs_1.readFileSync)(file, 'utf8');
132
+ }
133
+ catch (err) {
134
+ this.error(`Cannot read ${file}: ${err instanceof Error ? err.message : err}`, {
135
+ exit: 2,
136
+ });
137
+ }
138
+ return (0, dedicated_service_1.parseServiceSpecDocument)(raw, file === '-' ? '<stdin>' : file);
139
+ }
140
+ if (templateName) {
141
+ const given = (0, dedicated_service_1.parseParamEntries)(flags.param);
142
+ const template = await this.resolveTemplate(templateName, flags);
143
+ const spec = (0, dedicated_service_1.mergeServiceTemplate)(template, given, `template ${templateName}`);
144
+ // Flag overrides sit over the merged spec — a template sets defaults,
145
+ // the invocation decides.
146
+ const fromFlags = (0, dedicated_service_1.specFromFlags)({
147
+ name: flags.name,
148
+ cwd: flags.cwd,
149
+ env: (0, dedicated_service_1.parseEnvEntries)(flags.env),
150
+ restart: flags.restart,
151
+ notify: flags.notify,
152
+ stopTimeoutSeconds: flags['stop-timeout'],
153
+ limits: {
154
+ memoryMax: flags['limit-memory'],
155
+ cpuQuotaPercent: flags['limit-cpu'],
156
+ pidsMax: flags['limit-pids'],
157
+ },
158
+ });
159
+ return { ...spec, ...stripUndefined(fromFlags) };
160
+ }
161
+ return (0, dedicated_service_1.specFromFlags)({
162
+ name: flags.name,
163
+ argv,
164
+ cwd: flags.cwd,
165
+ env: (0, dedicated_service_1.parseEnvEntries)(flags.env),
166
+ restart: flags.restart,
167
+ notify: flags.notify,
168
+ stopTimeoutSeconds: flags['stop-timeout'],
169
+ limits: {
170
+ memoryMax: flags['limit-memory'],
171
+ cpuQuotaPercent: flags['limit-cpu'],
172
+ pidsMax: flags['limit-pids'],
173
+ },
174
+ });
175
+ }
176
+ /**
177
+ * `~/.skrr/templates/<name>.json` first, then the API gallery. A missing
178
+ * local file is a fall-through, a broken one is an error; a gallery miss
179
+ * names both places looked.
180
+ */
181
+ async resolveTemplate(name, flags) {
182
+ const local = (0, dedicated_service_1.loadLocalServiceTemplate)(name);
183
+ if (local)
184
+ return local;
185
+ try {
186
+ const channel = await this.serviceChannel();
187
+ const { result } = await channel.callTemplates('get', {
188
+ name,
189
+ ...this.scopeFrom(flags),
190
+ });
191
+ const template = result?.template ?? result;
192
+ if (template && typeof template === 'object' && template.spec) {
193
+ return template;
194
+ }
195
+ this.error(`Template ${name} was not found in ~/.skrr/templates/ or the gallery.`, {
196
+ exit: 1,
197
+ });
198
+ }
199
+ catch (err) {
200
+ // A timeout here is the API not answering `dedicated:templates:*` at
201
+ // all — the gallery is not served yet; name THAT, not "daemon offline".
202
+ if (err instanceof dedicated_service_1.DedicatedServiceError && err.code === 'SERVICE_CALL_TIMEOUT') {
203
+ this.error(`The template gallery is not available — ${name} was not in ~/.skrr/templates/ and this API does not serve dedicated:templates yet.`, { exit: 1 });
204
+ }
205
+ if (err instanceof dedicated_service_1.DedicatedServiceError)
206
+ this.reportServiceError(err);
207
+ this.handleApiError(err);
208
+ }
209
+ }
210
+ }
211
+ exports.default = DedicatedServiceCreate;
212
+ function stripUndefined(spec) {
213
+ const out = {};
214
+ for (const [key, value] of Object.entries(spec)) {
215
+ if (value === undefined)
216
+ continue;
217
+ if (key === 'limits' && value && typeof value === 'object' && Object.keys(value).length === 0)
218
+ continue;
219
+ out[key] = value;
220
+ }
221
+ return out;
222
+ }
@@ -0,0 +1,30 @@
1
+ import { DedicatedServiceCommand } from '../../../../lib/dedicated-service-command';
2
+ /**
3
+ * `skrr machines dedicated service edit` — change a running declaration in
4
+ * $EDITOR, applied as a compare-and-swap update.
5
+ *
6
+ * The round-trip is honest about the race it cannot hide: the daemon owns the
7
+ * spec and increments `revision` on every write, so an edit made against a
8
+ * stale read is refused `conflict`, never merged. When that happens your edit
9
+ * is saved to a named file, the editor re-opens on the CURRENT spec, and you
10
+ * re-apply — three rounds, then the command gives up with the file named.
11
+ */
12
+ export default class DedicatedServiceEdit extends DedicatedServiceCommand {
13
+ static summary: string;
14
+ static examples: string[];
15
+ static args: {
16
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
18
+ };
19
+ static flags: {
20
+ editor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
22
+ };
23
+ run(): Promise<void>;
24
+ /**
25
+ * Run the editor over the temp file. Returns the new contents, or null when
26
+ * the file is byte-identical — an unopened or untouched edit applies
27
+ * nothing, which is the honest answer to "I looked and left".
28
+ */
29
+ private editFile;
30
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = require("node:fs");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_child_process_1 = require("node:child_process");
10
+ const core_1 = require("@oclif/core");
11
+ const dedicated_service_command_1 = require("../../../../lib/dedicated-service-command");
12
+ const dedicated_service_1 = require("../../../../lib/dedicated-service");
13
+ /** How many times a CAS conflict re-seeds the editor before giving up. */
14
+ const EDIT_CONFLICT_ROUNDS = 3;
15
+ /**
16
+ * `skrr machines dedicated service edit` — change a running declaration in
17
+ * $EDITOR, applied as a compare-and-swap update.
18
+ *
19
+ * The round-trip is honest about the race it cannot hide: the daemon owns the
20
+ * spec and increments `revision` on every write, so an edit made against a
21
+ * stale read is refused `conflict`, never merged. When that happens your edit
22
+ * is saved to a named file, the editor re-opens on the CURRENT spec, and you
23
+ * re-apply — three rounds, then the command gives up with the file named.
24
+ */
25
+ class DedicatedServiceEdit extends dedicated_service_command_1.DedicatedServiceCommand {
26
+ static summary = 'Edit a service spec in $EDITOR and apply it (CAS update)';
27
+ static examples = [
28
+ '<%= config.bin %> machines dedicated service edit <lease-id> <name>',
29
+ ];
30
+ static args = {
31
+ lease: core_1.Args.string({
32
+ description: dedicated_service_command_1.SERVICE_LEASE_ARG_DESCRIPTION,
33
+ required: true,
34
+ ignoreStdin: true,
35
+ }),
36
+ name: core_1.Args.string({ description: 'Service name', required: true, ignoreStdin: true }),
37
+ };
38
+ static flags = {
39
+ editor: core_1.Flags.string({ description: 'Editor to use (default $VISUAL, $EDITOR, then vi)' }),
40
+ json: core_1.Flags.boolean({ description: 'Output the updated spec as JSON' }),
41
+ };
42
+ async run() {
43
+ this.requireAuth();
44
+ const { args, flags } = await this.parse(DedicatedServiceEdit);
45
+ const leaseId = await this.resolveLeaseArg(args.lease);
46
+ const editor = flags.editor ?? process.env.VISUAL ?? process.env.EDITOR ?? 'vi';
47
+ const channel = await this.serviceChannel();
48
+ const dir = (0, node_fs_1.mkdtempSync)(node_path_1.default.join((0, node_os_1.tmpdir)(), 'skrr-svc-edit-'));
49
+ const file = node_path_1.default.join(dir, `${args.name}.json`);
50
+ // Set on every path that NAMES the temp file for the user — an error that
51
+ // says "your edit is at <file>" must leave the file standing.
52
+ let keepDir = false;
53
+ try {
54
+ for (let round = 1; round <= EDIT_CONFLICT_ROUNDS; round += 1) {
55
+ const { result } = await channel.call('get', {
56
+ leaseId,
57
+ name: args.name,
58
+ ...this.scopeFrom(flags),
59
+ });
60
+ const spec = result?.spec;
61
+ if (!spec) {
62
+ this.error(`No service named ${args.name} on ${leaseId}.`, { exit: 1 });
63
+ }
64
+ const baseRevision = typeof spec.revision === 'number' ? spec.revision : undefined;
65
+ (0, node_fs_1.writeFileSync)(file, `${JSON.stringify(spec, null, 2)}\n`);
66
+ const edited = await this.editFile(file, editor);
67
+ if (edited === null) {
68
+ this.log('Unchanged — nothing applied.');
69
+ return;
70
+ }
71
+ let next;
72
+ try {
73
+ next = (0, dedicated_service_1.parseServiceSpecDocument)(edited, file);
74
+ }
75
+ catch (err) {
76
+ keepDir = true;
77
+ // Keep the file so the person can fix it by hand and apply it.
78
+ this.error(`${err instanceof Error ? err.message : err}\nYour edit is still at ${file} — fix it and run \`${this.config.bin} machines dedicated service apply ${leaseId} --file ${file}\`, or re-run edit.`, { exit: 2 });
79
+ }
80
+ try {
81
+ const { result: updated } = await channel.call('update', {
82
+ leaseId,
83
+ name: args.name,
84
+ patch: (0, dedicated_service_1.specPayload)(next),
85
+ baseRevision,
86
+ ...this.scopeFrom(flags),
87
+ });
88
+ this.closeServiceChannel();
89
+ if (flags.json) {
90
+ this.log(JSON.stringify(updated ?? (0, dedicated_service_1.specPayload)(next), null, 2));
91
+ return;
92
+ }
93
+ this.log(`Service ${args.name} updated on ${leaseId}.`);
94
+ return;
95
+ }
96
+ catch (err) {
97
+ if (!(err instanceof dedicated_service_1.DedicatedServiceError && err.code === 'conflict')) {
98
+ this.reportServiceError(err);
99
+ }
100
+ // CAS lost: preserve the edit, say where, and re-seed the editor
101
+ // with the spec as it actually is now.
102
+ const rejected = `${file}.rejected-${round}.json`;
103
+ (0, node_fs_1.writeFileSync)(rejected, edited);
104
+ if (round === EDIT_CONFLICT_ROUNDS) {
105
+ keepDir = true;
106
+ this.error(`The spec changed underneath the edit ${EDIT_CONFLICT_ROUNDS} times in a row. ` +
107
+ `Your last edit is at ${rejected} — apply it by hand with \`${this.config.bin} machines dedicated service apply ${leaseId} --file ${rejected}\` once the machine settles.`, { exit: 1 });
108
+ }
109
+ keepDir = true;
110
+ process.stderr.write(`[edit] the spec changed since you opened it; your edit is saved at ${rejected} and the editor re-opens on the current spec.\n`);
111
+ }
112
+ }
113
+ }
114
+ finally {
115
+ if (!keepDir) {
116
+ try {
117
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
118
+ }
119
+ catch {
120
+ /* a file the user still needs is worth more than a clean tmpdir */
121
+ }
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Run the editor over the temp file. Returns the new contents, or null when
127
+ * the file is byte-identical — an unopened or untouched edit applies
128
+ * nothing, which is the honest answer to "I looked and left".
129
+ */
130
+ async editFile(file, editor) {
131
+ const before = (0, node_fs_1.readFileSync)(file, 'utf8');
132
+ // sh -c so "code --wait" and friends work as one flag value.
133
+ const spawned = (0, node_child_process_1.spawnSync)('sh', ['-c', `${editor} "$1"`, 'sh', file], {
134
+ stdio: 'inherit',
135
+ });
136
+ if (spawned.error) {
137
+ this.error(`Could not run ${editor}: ${spawned.error.message}`, { exit: 1 });
138
+ }
139
+ if (spawned.status !== 0) {
140
+ this.error(`${editor} exited ${spawned.status}; nothing was applied.`, { exit: 1 });
141
+ }
142
+ const after = (0, node_fs_1.readFileSync)(file, 'utf8');
143
+ return after === before ? null : after;
144
+ }
145
+ }
146
+ exports.default = DedicatedServiceEdit;