@skrr-ai/cli 0.1.47 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/base-command.d.ts +18 -0
- package/dist/base-command.js +61 -1
- package/dist/commands/agent-worker.d.ts +12 -0
- package/dist/commands/agent-worker.js +140 -0
- package/dist/commands/agents/chat.d.ts +21 -0
- package/dist/commands/agents/chat.js +26 -9
- package/dist/commands/balance/usage/breakdown.d.ts +14 -0
- package/dist/commands/balance/usage/breakdown.js +68 -0
- package/dist/commands/balance/usage.js +24 -0
- package/dist/commands/commerce/entitlements/explain.d.ts +22 -0
- package/dist/commands/commerce/entitlements/explain.js +40 -0
- package/dist/commands/commerce/incidents/audit.d.ts +20 -0
- package/dist/commands/commerce/incidents/audit.js +37 -0
- package/dist/commands/commerce/incidents/list.d.ts +22 -0
- package/dist/commands/commerce/incidents/list.js +49 -0
- package/dist/commands/commerce/incidents/resolve.d.ts +23 -0
- package/dist/commands/commerce/incidents/resolve.js +45 -0
- package/dist/commands/commerce/incidents/retry.d.ts +22 -0
- package/dist/commands/commerce/incidents/retry.js +45 -0
- package/dist/commands/commerce/incidents/show.d.ts +13 -0
- package/dist/commands/commerce/incidents/show.js +30 -0
- package/dist/commands/commerce/offers/approve.d.ts +17 -0
- package/dist/commands/commerce/offers/approve.js +33 -0
- package/dist/commands/commerce/offers/create.d.ts +18 -0
- package/dist/commands/commerce/offers/create.js +34 -0
- package/dist/commands/commerce/offers/publish.d.ts +13 -0
- package/dist/commands/commerce/offers/publish.js +28 -0
- package/dist/commands/commerce/offers/retire.d.ts +17 -0
- package/dist/commands/commerce/offers/retire.js +32 -0
- package/dist/commands/commerce/offers/validate.d.ts +18 -0
- package/dist/commands/commerce/offers/validate.js +33 -0
- package/dist/commands/commerce/products/create.d.ts +10 -0
- package/dist/commands/commerce/products/create.js +25 -0
- package/dist/commands/commerce/promotions/redeem.d.ts +13 -0
- package/dist/commands/commerce/promotions/redeem.js +32 -0
- package/dist/commands/commerce/terms/accept.d.ts +18 -0
- package/dist/commands/commerce/terms/accept.js +39 -0
- package/dist/commands/commerce/terms/show.d.ts +10 -0
- package/dist/commands/commerce/terms/show.js +34 -0
- package/dist/commands/commerce/trials/conversion-preview.d.ts +18 -0
- package/dist/commands/commerce/trials/conversion-preview.js +39 -0
- package/dist/commands/commerce/trials/start.d.ts +23 -0
- package/dist/commands/commerce/trials/start.js +58 -0
- package/dist/commands/commitments/accounting.d.ts +39 -0
- package/dist/commands/commitments/accounting.js +116 -0
- package/dist/commands/commitments/action-history.d.ts +1 -0
- package/dist/commands/commitments/action-history.js +8 -1
- package/dist/commands/commitments/case.d.ts +1 -0
- package/dist/commands/commitments/case.js +19 -3
- package/dist/commands/commitments/cases.d.ts +1 -0
- package/dist/commands/commitments/cases.js +11 -1
- package/dist/commands/commitments/checks.d.ts +1 -0
- package/dist/commands/commitments/checks.js +5 -1
- package/dist/commands/commitments/cost.d.ts +62 -0
- package/dist/commands/commitments/cost.js +130 -0
- package/dist/commands/commitments/create.js +1 -1
- package/dist/commands/commitments/delete.d.ts +1 -0
- package/dist/commands/commitments/delete.js +56 -6
- package/dist/commands/commitments/feedback.js +9 -1
- package/dist/commands/commitments/handover.js +16 -0
- package/dist/commands/commitments/hypotheses.d.ts +1 -0
- package/dist/commands/commitments/hypotheses.js +17 -2
- package/dist/commands/commitments/links/list.js +4 -1
- package/dist/commands/commitments/liveness.d.ts +18 -0
- package/dist/commands/commitments/liveness.js +80 -0
- package/dist/commands/commitments/metrics.d.ts +1 -0
- package/dist/commands/commitments/metrics.js +8 -1
- package/dist/commands/commitments/next-tick.d.ts +39 -0
- package/dist/commands/commitments/next-tick.js +122 -0
- package/dist/commands/commitments/observations.d.ts +1 -0
- package/dist/commands/commitments/observations.js +19 -2
- package/dist/commands/commitments/pause.js +12 -0
- package/dist/commands/commitments/receipt.d.ts +2 -0
- package/dist/commands/commitments/receipt.js +16 -2
- package/dist/commands/commitments/remediation-proposal.js +6 -2
- package/dist/commands/commitments/remediation-proposals.js +5 -1
- package/dist/commands/commitments/restore.d.ts +25 -0
- package/dist/commands/commitments/restore.js +46 -0
- package/dist/commands/commitments/show.js +32 -0
- package/dist/commands/commitments/trigger-status.js +33 -0
- package/dist/commands/commitments/update.d.ts +1 -0
- package/dist/commands/commitments/update.js +31 -5
- package/dist/commands/commitments/wake-capabilities.d.ts +0 -1
- package/dist/commands/commitments/wake-capabilities.js +8 -4
- package/dist/commands/enterprise/budgets/create.d.ts +11 -0
- package/dist/commands/enterprise/budgets/create.js +27 -0
- package/dist/commands/enterprise/budgets/evaluate.d.ts +17 -0
- package/dist/commands/enterprise/budgets/evaluate.js +32 -0
- package/dist/commands/enterprise/budgets/publish.d.ts +14 -0
- package/dist/commands/enterprise/budgets/publish.js +30 -0
- package/dist/commands/enterprise/contracts/activate.d.ts +14 -0
- package/dist/commands/enterprise/contracts/activate.js +30 -0
- package/dist/commands/enterprise/contracts/create.d.ts +14 -0
- package/dist/commands/enterprise/contracts/create.js +32 -0
- package/dist/commands/enterprise/contracts/issue.d.ts +18 -0
- package/dist/commands/enterprise/contracts/issue.js +34 -0
- package/dist/commands/enterprise/domains/begin.d.ts +20 -0
- package/dist/commands/enterprise/domains/begin.js +39 -0
- package/dist/commands/enterprise/domains/verify.d.ts +14 -0
- package/dist/commands/enterprise/domains/verify.js +32 -0
- package/dist/commands/enterprise/external-config/activate.d.ts +22 -0
- package/dist/commands/enterprise/external-config/activate.js +49 -0
- package/dist/commands/enterprise/external-config/set.d.ts +25 -0
- package/dist/commands/enterprise/external-config/set.js +123 -0
- package/dist/commands/enterprise/external-config/show.d.ts +12 -0
- package/dist/commands/enterprise/external-config/show.js +39 -0
- package/dist/commands/enterprise/external-config/suspend.d.ts +13 -0
- package/dist/commands/enterprise/external-config/suspend.js +41 -0
- package/dist/commands/enterprise/finance/close-snapshot.d.ts +11 -0
- package/dist/commands/enterprise/finance/close-snapshot.js +28 -0
- package/dist/commands/enterprise/finance/record-document.d.ts +18 -0
- package/dist/commands/enterprise/finance/record-document.js +35 -0
- package/dist/commands/enterprise/profile.d.ts +20 -0
- package/dist/commands/enterprise/profile.js +38 -0
- package/dist/commands/login.d.ts +13 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/machines/dedicated/index.js +3 -0
- package/dist/commands/machines/dedicated/service/apply.d.ts +23 -0
- package/dist/commands/machines/dedicated/service/apply.js +99 -0
- package/dist/commands/machines/dedicated/service/approve.d.ts +23 -0
- package/dist/commands/machines/dedicated/service/approve.js +52 -0
- package/dist/commands/machines/dedicated/service/create.d.ts +55 -0
- package/dist/commands/machines/dedicated/service/create.js +222 -0
- package/dist/commands/machines/dedicated/service/edit.d.ts +30 -0
- package/dist/commands/machines/dedicated/service/edit.js +146 -0
- package/dist/commands/machines/dedicated/service/exec.d.ts +26 -0
- package/dist/commands/machines/dedicated/service/exec.js +92 -0
- package/dist/commands/machines/dedicated/service/export.d.ts +29 -0
- package/dist/commands/machines/dedicated/service/export.js +106 -0
- package/dist/commands/machines/dedicated/service/index.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/index.js +49 -0
- package/dist/commands/machines/dedicated/service/logs.d.ts +33 -0
- package/dist/commands/machines/dedicated/service/logs.js +148 -0
- package/dist/commands/machines/dedicated/service/ls.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/ls.js +80 -0
- package/dist/commands/machines/dedicated/service/restart.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/restart.js +29 -0
- package/dist/commands/machines/dedicated/service/rm.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/rm.js +33 -0
- package/dist/commands/machines/dedicated/service/secret/index.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/secret/index.js +25 -0
- package/dist/commands/machines/dedicated/service/secret/rm.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/secret/rm.js +54 -0
- package/dist/commands/machines/dedicated/service/secret/set.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/secret/set.js +93 -0
- package/dist/commands/machines/dedicated/service/start.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/start.js +29 -0
- package/dist/commands/machines/dedicated/service/status.d.ts +21 -0
- package/dist/commands/machines/dedicated/service/status.js +85 -0
- package/dist/commands/machines/dedicated/service/stop.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/stop.js +35 -0
- package/dist/commands/machines/dedicated/service/top.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/top.js +120 -0
- package/dist/commands/machines/dedicated/sign-in-status.d.ts +22 -0
- package/dist/commands/machines/dedicated/sign-in-status.js +79 -0
- package/dist/commands/machines/dedicated/ssh-config.d.ts +28 -0
- package/dist/commands/machines/dedicated/ssh-config.js +66 -0
- package/dist/commands/machines/dedicated/ssh-proxy.d.ts +22 -0
- package/dist/commands/machines/dedicated/ssh-proxy.js +62 -0
- package/dist/commands/machines/dedicated/ssh.d.ts +29 -0
- package/dist/commands/machines/dedicated/ssh.js +121 -0
- package/dist/commands/native/allow-all.d.ts +14 -0
- package/dist/commands/native/allow-all.js +25 -0
- package/dist/commands/native/allow.d.ts +24 -0
- package/dist/commands/native/allow.js +45 -0
- package/dist/commands/native/deny.d.ts +24 -0
- package/dist/commands/native/deny.js +43 -0
- package/dist/commands/native/disable.d.ts +17 -0
- package/dist/commands/native/disable.js +28 -0
- package/dist/commands/native/enable.d.ts +12 -0
- package/dist/commands/native/enable.js +29 -0
- package/dist/commands/native/list-apps.d.ts +19 -0
- package/dist/commands/native/list-apps.js +30 -0
- package/dist/commands/native/restrict-to.d.ts +22 -0
- package/dist/commands/native/restrict-to.js +41 -0
- package/dist/commands/native/status.d.ts +19 -0
- package/dist/commands/native/status.js +33 -0
- package/dist/commands/native.d.ts +39 -0
- package/dist/commands/native.js +76 -0
- package/dist/commands/service/index.d.ts +14 -0
- package/dist/commands/service/index.js +31 -0
- package/dist/commands/service/logs.d.ts +18 -0
- package/dist/commands/service/logs.js +53 -0
- package/dist/commands/service/ls.d.ts +13 -0
- package/dist/commands/service/ls.js +48 -0
- package/dist/commands/service/propose.d.ts +34 -0
- package/dist/commands/service/propose.js +113 -0
- package/dist/commands/service/status.d.ts +17 -0
- package/dist/commands/service/status.js +56 -0
- package/dist/commands/spaces/auto-executor/runs.d.ts +13 -0
- package/dist/commands/spaces/auto-executor/runs.js +56 -0
- package/dist/commands/store/governance/artifacts.d.ts +25 -0
- package/dist/commands/store/governance/artifacts.js +55 -0
- package/dist/commands/store/governance/assign.d.ts +16 -0
- package/dist/commands/store/governance/assign.js +54 -0
- package/dist/commands/store/governance/create-license.d.ts +18 -0
- package/dist/commands/store/governance/create-license.js +35 -0
- package/dist/commands/store/governance/install-plan.d.ts +25 -0
- package/dist/commands/store/governance/install-plan.js +56 -0
- package/dist/commands/store/governance/license-policy.d.ts +30 -0
- package/dist/commands/store/governance/license-policy.js +69 -0
- package/dist/commands/store/governance/policy.d.ts +25 -0
- package/dist/commands/store/governance/policy.js +65 -0
- package/dist/commands/store/governance/publish-release.d.ts +17 -0
- package/dist/commands/store/governance/publish-release.js +34 -0
- package/dist/commands/tasks/deliverable/add.d.ts +15 -0
- package/dist/commands/tasks/deliverable/add.js +174 -0
- package/dist/commands/tasks/deliverable/archive.d.ts +35 -0
- package/dist/commands/tasks/deliverable/archive.js +205 -0
- package/dist/commands/tasks/labels/detach.js +3 -0
- package/dist/commands/tasks/labels/suggest.d.ts +24 -0
- package/dist/commands/tasks/labels/suggest.js +69 -0
- package/dist/commands/tasks/list.d.ts +16 -2
- package/dist/commands/tasks/list.js +100 -24
- package/dist/commands/tasks/show.d.ts +23 -0
- package/dist/commands/tasks/show.js +51 -1
- package/dist/commands/wiki/rm.d.ts +22 -1
- package/dist/commands/wiki/rm.js +52 -3
- package/dist/lib/agentic-stream.js +10 -2
- package/dist/lib/balance.d.ts +6 -1
- package/dist/lib/balance.js +15 -0
- package/dist/lib/commerce-command.d.ts +48 -0
- package/dist/lib/commerce-command.js +70 -0
- package/dist/lib/commerce.d.ts +93 -0
- package/dist/lib/commerce.js +129 -0
- package/dist/lib/commitment-product.js +1 -1
- package/dist/lib/commitments.d.ts +202 -29
- package/dist/lib/commitments.js +300 -33
- package/dist/lib/daemon-installer.d.ts +6 -5
- package/dist/lib/daemon-installer.js +4 -17
- package/dist/lib/dedicated-lease-command.js +10 -0
- package/dist/lib/dedicated-machines.d.ts +37 -0
- package/dist/lib/dedicated-machines.js +47 -0
- package/dist/lib/dedicated-service-command.d.ts +60 -0
- package/dist/lib/dedicated-service-command.js +182 -0
- package/dist/lib/dedicated-service.d.ts +342 -0
- package/dist/lib/dedicated-service.js +741 -0
- package/dist/lib/dedicated-ssh.d.ts +136 -0
- package/dist/lib/dedicated-ssh.js +387 -0
- package/dist/lib/delegated-cli.js +51 -13
- package/dist/lib/enterprise-commerce.d.ts +67 -0
- package/dist/lib/enterprise-commerce.js +112 -0
- package/dist/lib/enterprise-external-configuration.d.ts +60 -0
- package/dist/lib/enterprise-external-configuration.js +58 -0
- package/dist/lib/first-party-harness.js +17 -3
- package/dist/lib/governed-store.d.ts +110 -0
- package/dist/lib/governed-store.js +79 -0
- package/dist/lib/guest-service-command.d.ts +16 -0
- package/dist/lib/guest-service-command.js +32 -0
- package/dist/lib/guest-service.d.ts +67 -0
- package/dist/lib/guest-service.js +112 -0
- package/dist/lib/prompt.d.ts +1 -0
- package/dist/lib/prompt.js +1 -0
- package/dist/lib/task-asset-upload.d.ts +20 -0
- package/dist/lib/task-asset-upload.js +30 -8
- package/dist/lib/task-extras.d.ts +23 -0
- package/dist/lib/task-extras.js +6 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.d.ts +9 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.js +10 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +8 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +16 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +21 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.d.ts +38 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseManifest.js +66 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +24 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.d.ts +9 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.js +10 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +16 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +20 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.d.ts +38 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseManifest.js +64 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +24 -2
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5060 -4827
- package/oclif.manifest.json +35194 -29157
- package/package.json +43 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_command_1 = require("../../base-command");
|
|
5
|
+
const enterprise_commerce_1 = require("../../lib/enterprise-commerce");
|
|
6
|
+
const commerce_command_1 = require("../../lib/commerce-command");
|
|
7
|
+
/**
|
|
8
|
+
* `skrr enterprise profile <workspace-id>` — the workspace's enterprise profile.
|
|
9
|
+
*
|
|
10
|
+
* A PUT, so this replaces the profile rather than patching it. The command
|
|
11
|
+
* says so in its description instead of pretending to merge, because a
|
|
12
|
+
* partial body sent to a replacing endpoint silently drops whatever it omits.
|
|
13
|
+
*/
|
|
14
|
+
class EnterpriseProfile extends base_command_1.BaseCommand {
|
|
15
|
+
static description = "Replace a workspace's enterprise profile (admin). This is a full replace, not a merge — anything your body omits is dropped.";
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> enterprise profile <workspace-id> --file ./profile.json',
|
|
18
|
+
];
|
|
19
|
+
static args = {
|
|
20
|
+
workspace: core_1.Args.string({ description: 'Workspace id', required: true, ignoreStdin: true }),
|
|
21
|
+
};
|
|
22
|
+
static flags = { ...commerce_command_1.jsonBodyFlags };
|
|
23
|
+
async run() {
|
|
24
|
+
this.requireAuth();
|
|
25
|
+
const { args, flags } = await this.parse(EnterpriseProfile);
|
|
26
|
+
const body = await (0, commerce_command_1.jsonBodyFrom)(this, flags.file);
|
|
27
|
+
let result;
|
|
28
|
+
try {
|
|
29
|
+
result = await (0, enterprise_commerce_1.upsertEnterpriseProfile)(args.workspace, body);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
this.handleApiError(error);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.log(JSON.stringify(result, null, 2));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.default = EnterpriseProfile;
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -66,4 +66,17 @@ export default class Login extends BaseCommand {
|
|
|
66
66
|
* only when the client can locally confirm the alphabet is wrong.
|
|
67
67
|
*/
|
|
68
68
|
private runRecoveryCodeLogin;
|
|
69
|
+
/**
|
|
70
|
+
* C3 step 2 — the server accepted the recovery code and staged a
|
|
71
|
+
* Redis-backed OTP challenge, emailing the code to the account address.
|
|
72
|
+
* Prompt for it and complete the redemption; /redeem/confirm is the only
|
|
73
|
+
* endpoint that mints the token pair.
|
|
74
|
+
*/
|
|
75
|
+
private confirmRecoveryChallenge;
|
|
76
|
+
/**
|
|
77
|
+
* A credential-minting flow may persist only a response that carries a
|
|
78
|
+
* credential. Asserts the token-bearing contract before writeToBackend so
|
|
79
|
+
* a successful-but-tokenless answer can never clobber the stored bundle.
|
|
80
|
+
*/
|
|
81
|
+
private assertRedeemResult;
|
|
69
82
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -12,6 +12,7 @@ const login_1 = require("../lib/login");
|
|
|
12
12
|
const device_code_1 = require("../lib/device-code");
|
|
13
13
|
const api_fetch_1 = require("../lib/api-fetch");
|
|
14
14
|
const auth_storage_1 = require("../lib/auth-storage");
|
|
15
|
+
const prompt_1 = require("../lib/prompt");
|
|
15
16
|
/**
|
|
16
17
|
* `skrr login`
|
|
17
18
|
*
|
|
@@ -337,6 +338,18 @@ class Login extends base_command_1.BaseCommand {
|
|
|
337
338
|
}
|
|
338
339
|
this.handleApiError(err);
|
|
339
340
|
}
|
|
341
|
+
// C3 step 2 — a challenge-shaped answer means the server staged an OTP
|
|
342
|
+
// and emailed it to the account address; complete the redemption with
|
|
343
|
+
// POST /redeem/confirm, the only endpoint that mints tokens.
|
|
344
|
+
if (typeof result.challengeId === 'string' && result.challengeId.length > 0) {
|
|
345
|
+
result = await this.confirmRecoveryChallenge(result.challengeId);
|
|
346
|
+
}
|
|
347
|
+
// Persist only a response that actually carries a credential. A
|
|
348
|
+
// tokenless body (an unexpected shape, or a challenge we could not
|
|
349
|
+
// complete) must never reach writeToBackend — writing undefined token
|
|
350
|
+
// fields destroys the stored credential (Keychain delete-all on macOS,
|
|
351
|
+
// an origin-only cli-auth.json elsewhere) and redeems nothing.
|
|
352
|
+
this.assertRedeemResult(result);
|
|
340
353
|
// Persist via the CLI's canonical token-save path (Keychain on macOS, file elsewhere).
|
|
341
354
|
(0, auth_storage_1.writeToBackend)({
|
|
342
355
|
token: result.token,
|
|
@@ -351,5 +364,69 @@ class Login extends base_command_1.BaseCommand {
|
|
|
351
364
|
`${(0, config_1.describeDeployment)(this.cliConfig.baseURL)}.`);
|
|
352
365
|
this.log(`Run \`${this.config.bin} whoami\` to confirm your identity.`);
|
|
353
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* C3 step 2 — the server accepted the recovery code and staged a
|
|
369
|
+
* Redis-backed OTP challenge, emailing the code to the account address.
|
|
370
|
+
* Prompt for it and complete the redemption; /redeem/confirm is the only
|
|
371
|
+
* endpoint that mints the token pair.
|
|
372
|
+
*/
|
|
373
|
+
async confirmRecoveryChallenge(challengeId) {
|
|
374
|
+
if (!process.stdin.isTTY) {
|
|
375
|
+
this.error('This server requires a confirmation code that was emailed to the account ' +
|
|
376
|
+
'address, and there is no terminal to enter it. Run this command ' +
|
|
377
|
+
'interactively. No credentials were changed.', { exit: 1 });
|
|
378
|
+
}
|
|
379
|
+
const otp = await (0, prompt_1.prompt)('Enter the recovery confirmation code from your email: ', {
|
|
380
|
+
hidden: true,
|
|
381
|
+
});
|
|
382
|
+
if (!otp) {
|
|
383
|
+
this.error('No confirmation code entered; recovery aborted. No credentials were changed.', {
|
|
384
|
+
exit: 1,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
return await (0, api_fetch_1.apiFetch)('/api/auth/recovery/redeem/confirm', {
|
|
389
|
+
method: 'POST',
|
|
390
|
+
// Recovery redeem is public — no credential needed.
|
|
391
|
+
body: { challengeId, otp },
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
if (err instanceof api_fetch_1.ApiFetchError) {
|
|
396
|
+
if (err.status === 401) {
|
|
397
|
+
this.error('Confirmation code is incorrect. The challenge was consumed — ' +
|
|
398
|
+
'run the command again with the recovery code to receive a new one.', { exit: 1 });
|
|
399
|
+
}
|
|
400
|
+
if (err.status === 410) {
|
|
401
|
+
this.error('Recovery challenge expired. Run the command again to restart.', { exit: 1 });
|
|
402
|
+
}
|
|
403
|
+
if (err.status === 429) {
|
|
404
|
+
this.error('Too many recovery attempts. Please wait 15 minutes before trying again.', {
|
|
405
|
+
exit: 1,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
this.handleApiError(err);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* A credential-minting flow may persist only a response that carries a
|
|
414
|
+
* credential. Asserts the token-bearing contract before writeToBackend so
|
|
415
|
+
* a successful-but-tokenless answer can never clobber the stored bundle.
|
|
416
|
+
*/
|
|
417
|
+
assertRedeemResult(result) {
|
|
418
|
+
const candidate = result;
|
|
419
|
+
if (!candidate ||
|
|
420
|
+
typeof candidate.token !== 'string' ||
|
|
421
|
+
candidate.token.length === 0 ||
|
|
422
|
+
typeof candidate.refreshToken !== 'string' ||
|
|
423
|
+
candidate.refreshToken.length === 0 ||
|
|
424
|
+
!candidate.user ||
|
|
425
|
+
typeof candidate.user.email !== 'string' ||
|
|
426
|
+
candidate.user.email.length === 0) {
|
|
427
|
+
this.error('Recovery did not return a session — the server answered with an unexpected ' +
|
|
428
|
+
'response shape. No credentials were changed. Update the skrr CLI if this persists.', { exit: 1 });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
354
431
|
}
|
|
355
432
|
exports.default = Login;
|
|
@@ -28,6 +28,8 @@ class DedicatedMachines extends core_1.Command {
|
|
|
28
28
|
'<%= config.bin %> machines dedicated spending-limit <lease-id> --cents 30000',
|
|
29
29
|
'<%= config.bin %> machines dedicated health-check <lease-id>',
|
|
30
30
|
'<%= config.bin %> machines dedicated incident-close <lease-id>',
|
|
31
|
+
'<%= config.bin %> machines dedicated service create <lease-id> --name worker -- node dist/worker.js',
|
|
32
|
+
'<%= config.bin %> machines dedicated service ls <lease-id>',
|
|
31
33
|
'<%= config.bin %> machines dedicated destroy <lease-id>',
|
|
32
34
|
];
|
|
33
35
|
async run() {
|
|
@@ -51,6 +53,7 @@ class DedicatedMachines extends core_1.Command {
|
|
|
51
53
|
['spending-limit', 'change the monthly spending cap'],
|
|
52
54
|
['health-check', 'probe daemon and disk liveness'],
|
|
53
55
|
['incident-close', 'close AZ-recovery evidence hold'],
|
|
56
|
+
['service', 'declare workloads the machine keeps running'],
|
|
54
57
|
['destroy', 'stop compute; keep or purge the workspace disk'],
|
|
55
58
|
];
|
|
56
59
|
this.log('skrr Dedicated Runtime commands:');
|
|
@@ -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
|
+
}
|