@skrr-ai/cli 0.1.89 → 0.1.90
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/dist/commands/machines/dedicated/exec.js +60 -14
- package/dist/commands/machines/dedicated/index.js +2 -0
- package/dist/commands/machines/dedicated/power/explain.d.ts +13 -0
- package/dist/commands/machines/dedicated/power/explain.js +36 -0
- package/dist/commands/machines/dedicated/power/index.d.ts +10 -0
- package/dist/commands/machines/dedicated/power/index.js +24 -0
- package/dist/commands/machines/dedicated/power/keep-awake.d.ts +20 -0
- package/dist/commands/machines/dedicated/power/keep-awake.js +69 -0
- package/dist/commands/machines/dedicated/power/set.d.ts +23 -0
- package/dist/commands/machines/dedicated/power/set.js +106 -0
- package/dist/commands/machines/dedicated/power/show.d.ts +13 -0
- package/dist/commands/machines/dedicated/power/show.js +36 -0
- package/dist/lib/commitments.d.ts +1 -1
- package/dist/lib/commitments.js +4 -2
- package/dist/lib/dedicated-keep-awake.d.ts +19 -0
- package/dist/lib/dedicated-keep-awake.js +64 -0
- package/dist/lib/dedicated-power.d.ts +61 -0
- package/dist/lib/dedicated-power.js +243 -0
- package/dist/lib/format.d.ts +13 -0
- package/dist/lib/format.js +51 -0
- package/dist/lib/keychain.js +3 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +27 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeBusyWire.d.ts +107 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeBusyWire.js +151 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeWakeWire.d.ts +115 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dedicatedRuntimeWakeWire.js +117 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +8 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +31 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +149 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +24 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeBusyWire.d.ts +107 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeBusyWire.js +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeWakeWire.d.ts +115 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dedicatedRuntimeWakeWire.js +113 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +31 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +144 -8
- package/dist/node_modules/@skrr-ai/auth-core/package.json +21 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6393 -6278
- package/oclif.manifest.json +30493 -30127
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.resolveDedicatedExecCommand = resolveDedicatedExecCommand;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
|
+
const api_fetch_1 = require("../../../lib/api-fetch");
|
|
6
7
|
const shell_quote_1 = require("../../../lib/shell-quote");
|
|
7
8
|
const hosted_machines_1 = require("../../../lib/hosted-machines");
|
|
8
9
|
const dedicated_machines_1 = require("../../../lib/dedicated-machines");
|
|
@@ -144,23 +145,68 @@ class DedicatedMachinesExec extends base_command_1.BaseCommand {
|
|
|
144
145
|
if (timeoutMs !== undefined && timeoutMs > dedicated_machines_1.DEDICATED_EXEC_MAX_TIMEOUT_MS) {
|
|
145
146
|
this.error(`--timeout is at most ${dedicated_machines_1.DEDICATED_EXEC_MAX_TIMEOUT_MS / 60000}m. For longer work, start it in the background (nohup … &) or give it to an agent.`, { exit: 2 });
|
|
146
147
|
}
|
|
148
|
+
// A stopped machine that this command woke answers "starting" until it is ready. That is a normal
|
|
149
|
+
// moment, not a failure: say so once, wait it out (bounded), and run the command.
|
|
150
|
+
const WAKE_CODE_STARTING = 'DEDICATED_RUNTIME_STARTING';
|
|
151
|
+
const WAKE_WAIT_BOUND_MS = 180_000;
|
|
152
|
+
const wakeStartedAt = Date.now();
|
|
153
|
+
let announcedWake = false;
|
|
147
154
|
let result;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
for (;;) {
|
|
156
|
+
try {
|
|
157
|
+
result = await (0, dedicated_exec_1.runDedicatedExec)(id, {
|
|
158
|
+
command,
|
|
159
|
+
...(flags.cwd ? { cwd: flags.cwd } : {}),
|
|
160
|
+
...(timeoutMs ? { timeoutMs } : {}),
|
|
161
|
+
}, { workspaceId: flags.workspace, ...(requestId ? { requestId } : {}) });
|
|
162
|
+
break;
|
|
156
163
|
}
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
catch (err) {
|
|
165
|
+
const refusal = (0, base_command_1.apiRefusal)(err);
|
|
166
|
+
if (refusal.code === WAKE_CODE_STARTING) {
|
|
167
|
+
if (Date.now() - wakeStartedAt > WAKE_WAIT_BOUND_MS) {
|
|
168
|
+
this.error(`${id} is still starting after ${Math.round(WAKE_WAIT_BOUND_MS / 1000)}s. Nothing was run. Check it with \`${this.config.bin} machines dedicated show ${id}\` and run the command again.`, { exit: 1, code: WAKE_CODE_STARTING });
|
|
169
|
+
}
|
|
170
|
+
if (!announcedWake && !flags.json) {
|
|
171
|
+
// The API masks a 503's `message`; the sentence lives in details.wake.detail.
|
|
172
|
+
const wake = refusal.body?.details
|
|
173
|
+
?.wake;
|
|
174
|
+
const sentence = typeof wake?.detail === 'string' && wake.detail ? wake.detail : `${id} is starting.`;
|
|
175
|
+
process.stderr.write(`${sentence} Waiting for it, then running your command…\n`);
|
|
176
|
+
announcedWake = true;
|
|
177
|
+
}
|
|
178
|
+
const retryAfter = Number(refusal.body?.retryAfterSeconds);
|
|
179
|
+
const waitMs = Math.min(Math.max(Number.isFinite(retryAfter) ? retryAfter * 1000 : 5000, 2000), 10_000);
|
|
180
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (err instanceof dedicated_exec_1.DedicatedExecContactLostError ||
|
|
184
|
+
err instanceof dedicated_exec_1.DedicatedExecStartUnconfirmedError ||
|
|
185
|
+
err instanceof dedicated_exec_1.DedicatedExecIdempotencyUnsupportedError) {
|
|
186
|
+
this.error(err.message, { exit: 1, code: err.code });
|
|
187
|
+
}
|
|
188
|
+
// The other wake outcomes (stopping, refused, timed out) carry their sentence as `message` and no
|
|
189
|
+
// `error`, which the generic handler does not read ("An unexpected error occurred"). Print it, and
|
|
190
|
+
// keep the status and the retry decision with it.
|
|
191
|
+
if (refusal.code?.startsWith('DEDICATED_RUNTIME_') &&
|
|
192
|
+
refusal.message &&
|
|
193
|
+
!refusal.body?.error) {
|
|
194
|
+
this.failWithCliError({
|
|
195
|
+
message: refusal.message,
|
|
196
|
+
code: refusal.code,
|
|
197
|
+
status: err instanceof api_fetch_1.ApiFetchError ? err.status : undefined,
|
|
198
|
+
exit: 1,
|
|
199
|
+
retryable: refusal.body?.retryable === true,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Keep the API status, request id and explicit retry decision together.
|
|
203
|
+
if ((0, base_command_1.apiRefusal)(err).body?.retryable === true)
|
|
204
|
+
this.handleApiError(err);
|
|
205
|
+
const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
|
|
206
|
+
if (message)
|
|
207
|
+
this.error(message, { exit: 1, code: (0, dedicated_machines_1.dedicatedRuntimeApiErrorCode)(err) });
|
|
159
208
|
this.handleApiError(err);
|
|
160
|
-
|
|
161
|
-
if (message)
|
|
162
|
-
this.error(message, { exit: 1, code: (0, dedicated_machines_1.dedicatedRuntimeApiErrorCode)(err) });
|
|
163
|
-
this.handleApiError(err);
|
|
209
|
+
}
|
|
164
210
|
}
|
|
165
211
|
if (flags.json) {
|
|
166
212
|
this.log(JSON.stringify(result, null, 2));
|
|
@@ -33,6 +33,7 @@ class DedicatedMachines extends core_1.Command {
|
|
|
33
33
|
'<%= config.bin %> machines dedicated snapshot <lease-id> --wait',
|
|
34
34
|
'<%= config.bin %> machines dedicated grow <lease-id> --storage-gb 200',
|
|
35
35
|
'<%= config.bin %> machines dedicated spending-limit <lease-id> --cents 30000',
|
|
36
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --policy manual',
|
|
36
37
|
'<%= config.bin %> machines dedicated health-check <lease-id>',
|
|
37
38
|
'<%= config.bin %> machines dedicated incident-close <lease-id>',
|
|
38
39
|
'<%= config.bin %> machines dedicated service create <lease-id> --name worker -- node dist/worker.js',
|
|
@@ -41,6 +42,7 @@ class DedicatedMachines extends core_1.Command {
|
|
|
41
42
|
];
|
|
42
43
|
async run() {
|
|
43
44
|
const rows = [
|
|
45
|
+
['power', 'when the machine may stop and start by itself (set)'],
|
|
44
46
|
['catalog', 'sizes, storage bounds and prices create accepts'],
|
|
45
47
|
['create', 'request a Dedicated Runtime'],
|
|
46
48
|
['list', 'list Dedicated Runtime leases'],
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DedicatedLeaseCommand } from '../../../../lib/dedicated-lease-command';
|
|
2
|
+
/** `skrr machines dedicated power explain` — the latest PowerDecision, with the fix last. */
|
|
3
|
+
export default class DedicatedMachinesPowerExplain extends DedicatedLeaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const dedicated_lease_command_1 = require("../../../../lib/dedicated-lease-command");
|
|
5
|
+
const dedicated_power_1 = require("../../../../lib/dedicated-power");
|
|
6
|
+
/** `skrr machines dedicated power explain` — the latest PowerDecision, with the fix last. */
|
|
7
|
+
class DedicatedMachinesPowerExplain extends dedicated_lease_command_1.DedicatedLeaseCommand {
|
|
8
|
+
static description = 'Explain why a Dedicated Runtime stayed on, stopped or woke: the latest decision, its gate and reason, and what you can do next. Read-only.';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> machines dedicated power explain <lease-id>',
|
|
11
|
+
'<%= config.bin %> machines dedicated power explain self --json',
|
|
12
|
+
];
|
|
13
|
+
static args = {
|
|
14
|
+
lease: core_1.Args.string({
|
|
15
|
+
description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
|
|
16
|
+
required: true,
|
|
17
|
+
ignoreStdin: true,
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
static flags = {
|
|
21
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
this.requireAuth();
|
|
25
|
+
const { args, flags } = await this.parse(DedicatedMachinesPowerExplain);
|
|
26
|
+
const leaseId = await this.resolveLeaseArg(args.lease);
|
|
27
|
+
const summary = await this.callDedicated(() => (0, dedicated_power_1.getDedicatedPowerSummary)(leaseId, this.scopeFrom(flags)));
|
|
28
|
+
if (flags.json) {
|
|
29
|
+
this.log(JSON.stringify(summary, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const line of (0, dedicated_power_1.describePowerExplanation)(summary))
|
|
33
|
+
this.log(line);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = DedicatedMachinesPowerExplain;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr machines dedicated power` — when a Dedicated Runtime may stop and start
|
|
4
|
+
* on its own. `set` is the standing policy; more verbs arrive with the power card.
|
|
5
|
+
*/
|
|
6
|
+
export default class DedicatedMachinesPower extends Command {
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
/**
|
|
5
|
+
* `skrr machines dedicated power` — when a Dedicated Runtime may stop and start
|
|
6
|
+
* on its own. `set` is the standing policy; more verbs arrive with the power card.
|
|
7
|
+
*/
|
|
8
|
+
class DedicatedMachinesPower extends core_1.Command {
|
|
9
|
+
static description = 'Power policy of a Dedicated Runtime — when it may stop and start on its own. Files are preserved across stops.\n\nSubcommands:\n show state, policy, holders, usage, next wake\n explain why it stayed on, stopped or woke, and the fix\n set always_on | on_demand | manual (+ --idle-after, --budget-hours)\n keep-awake --for <duration> | --until <iso> | --clear';
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --policy manual',
|
|
12
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --policy on_demand --idle-after 60',
|
|
13
|
+
];
|
|
14
|
+
async run() {
|
|
15
|
+
this.log('skrr machines dedicated power commands:');
|
|
16
|
+
this.log(' skrr machines dedicated power show state, policy, holders, usage, next wake');
|
|
17
|
+
this.log(' skrr machines dedicated power explain why it stayed on, stopped or woke, and the fix');
|
|
18
|
+
this.log(' skrr machines dedicated power set always_on | on_demand | manual (+ --idle-after)');
|
|
19
|
+
this.log(' skrr machines dedicated power keep-awake --for <duration> | --until <iso> | --clear');
|
|
20
|
+
this.log('');
|
|
21
|
+
this.log('Every command takes a lease id (or `self` on the machine) and --json.');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.default = DedicatedMachinesPower;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DedicatedLeaseCommand } from '../../../../lib/dedicated-lease-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr machines dedicated power keep-awake` — a temporary override that forbids
|
|
4
|
+
* AUTOMATIC stop. It is not a policy, it expires by itself, and it never starts a
|
|
5
|
+
* machine (on a stopped machine it means "do not auto-stop once running").
|
|
6
|
+
*/
|
|
7
|
+
export default class DedicatedMachinesPowerKeepAwake extends DedicatedLeaseCommand {
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static args: {
|
|
11
|
+
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
static flags: {
|
|
14
|
+
for: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
until: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
clear: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const dedicated_lease_command_1 = require("../../../../lib/dedicated-lease-command");
|
|
5
|
+
const dedicated_keep_awake_1 = require("../../../../lib/dedicated-keep-awake");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr machines dedicated power keep-awake` — a temporary override that forbids
|
|
8
|
+
* AUTOMATIC stop. It is not a policy, it expires by itself, and it never starts a
|
|
9
|
+
* machine (on a stopped machine it means "do not auto-stop once running").
|
|
10
|
+
*/
|
|
11
|
+
class DedicatedMachinesPowerKeepAwake extends dedicated_lease_command_1.DedicatedLeaseCommand {
|
|
12
|
+
static description = 'Keep a Dedicated Runtime from stopping on its own for a while: --for 2h, --until <iso> or --clear. It expires by itself, never starts the machine and does not change the power policy. Files are preserved across stops.';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> machines dedicated power keep-awake <lease-id> --for 2h',
|
|
15
|
+
'<%= config.bin %> machines dedicated power keep-awake <lease-id> --until 2026-10-10T18:00:00Z',
|
|
16
|
+
'<%= config.bin %> machines dedicated power keep-awake <lease-id> --clear --json',
|
|
17
|
+
];
|
|
18
|
+
static args = {
|
|
19
|
+
lease: core_1.Args.string({
|
|
20
|
+
description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
|
|
21
|
+
required: true,
|
|
22
|
+
ignoreStdin: true,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
static flags = {
|
|
26
|
+
for: core_1.Flags.string({
|
|
27
|
+
description: 'How long, such as 90m, 2h or 3d (5 minutes to 7 days). A bare number is minutes.',
|
|
28
|
+
exclusive: ['until', 'clear'],
|
|
29
|
+
}),
|
|
30
|
+
until: core_1.Flags.string({
|
|
31
|
+
description: 'Keep awake until this ISO 8601 date (5 minutes to 7 days from now).',
|
|
32
|
+
exclusive: ['for', 'clear'],
|
|
33
|
+
}),
|
|
34
|
+
clear: core_1.Flags.boolean({
|
|
35
|
+
description: 'Cancel keep-awake so the machine follows its power policy again.',
|
|
36
|
+
exclusive: ['for', 'until'],
|
|
37
|
+
}),
|
|
38
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
39
|
+
};
|
|
40
|
+
async run() {
|
|
41
|
+
this.requireAuth();
|
|
42
|
+
const { args, flags } = await this.parse(DedicatedMachinesPowerKeepAwake);
|
|
43
|
+
if (!flags.for && !flags.until && !flags.clear) {
|
|
44
|
+
this.error('Choose --for <duration>, --until <iso-date> or --clear.', { exit: 2 });
|
|
45
|
+
}
|
|
46
|
+
let request = null;
|
|
47
|
+
try {
|
|
48
|
+
if (flags.for)
|
|
49
|
+
request = { forMinutes: (0, dedicated_keep_awake_1.parseKeepAwakeDuration)(flags.for) };
|
|
50
|
+
else if (flags.until)
|
|
51
|
+
request = { until: (0, dedicated_keep_awake_1.parseKeepAwakeUntil)(flags.until) };
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
this.error(err.message, { exit: 2 });
|
|
55
|
+
}
|
|
56
|
+
const leaseId = await this.resolveLeaseArg(args.lease);
|
|
57
|
+
const scope = this.scopeFrom(flags);
|
|
58
|
+
const response = await this.callDedicated(() => request
|
|
59
|
+
? (0, dedicated_keep_awake_1.setDedicatedKeepAwake)(leaseId, request, scope)
|
|
60
|
+
: (0, dedicated_keep_awake_1.clearDedicatedKeepAwake)(leaseId, scope));
|
|
61
|
+
if (flags.json) {
|
|
62
|
+
this.log(JSON.stringify({ ok: true, ...response }, null, 2));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.log(`Dedicated Runtime ${response.leaseId} ${(0, dedicated_keep_awake_1.describeKeepAwake)(response.keepAwake)}.`);
|
|
66
|
+
this.log('Nothing was started or stopped.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.default = DedicatedMachinesPowerKeepAwake;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DedicatedLeaseCommand } from '../../../../lib/dedicated-lease-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr machines dedicated power set` — the standing power policy.
|
|
4
|
+
*
|
|
5
|
+
* Changing the policy starts and stops nothing; it records what the platform may
|
|
6
|
+
* do on its own from now on. Machines that never had a policy set stay
|
|
7
|
+
* `always_on`.
|
|
8
|
+
*/
|
|
9
|
+
export default class DedicatedMachinesPowerSet extends DedicatedLeaseCommand {
|
|
10
|
+
static description: string;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
static args: {
|
|
13
|
+
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
static flags: {
|
|
16
|
+
policy: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
+
'idle-after': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
+
'budget-hours': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
'no-budget': 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,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const dedicated_lease_command_1 = require("../../../../lib/dedicated-lease-command");
|
|
6
|
+
const dedicated_power_1 = require("../../../../lib/dedicated-power");
|
|
7
|
+
/**
|
|
8
|
+
* `skrr machines dedicated power set` — the standing power policy.
|
|
9
|
+
*
|
|
10
|
+
* Changing the policy starts and stops nothing; it records what the platform may
|
|
11
|
+
* do on its own from now on. Machines that never had a policy set stay
|
|
12
|
+
* `always_on`.
|
|
13
|
+
*/
|
|
14
|
+
class DedicatedMachinesPowerSet extends dedicated_lease_command_1.DedicatedLeaseCommand {
|
|
15
|
+
static description = 'Set when a Dedicated Runtime may stop and start on its own: always_on, on_demand or manual. Changing the policy starts and stops nothing. Files are preserved across stops.';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --policy manual',
|
|
18
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --policy on_demand --idle-after 60',
|
|
19
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --budget-hours 200',
|
|
20
|
+
'<%= config.bin %> machines dedicated power set <lease-id> --no-budget',
|
|
21
|
+
'<%= config.bin %> machines dedicated power set self --policy always_on --json',
|
|
22
|
+
];
|
|
23
|
+
static args = {
|
|
24
|
+
lease: core_1.Args.string({
|
|
25
|
+
description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
|
|
26
|
+
required: true,
|
|
27
|
+
ignoreStdin: true,
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
static flags = {
|
|
31
|
+
policy: core_1.Flags.string({
|
|
32
|
+
description: 'always_on: never stops by itself (default). on_demand: stops when idle, starts when work arrives. manual: only you start and stop it.',
|
|
33
|
+
options: [...data_provider_1.DEDICATED_POWER_POLICIES],
|
|
34
|
+
}),
|
|
35
|
+
'idle-after': core_1.Flags.integer({
|
|
36
|
+
description: `Minutes idle before an on_demand machine stops (${data_provider_1.DEDICATED_POWER_IDLE_AFTER_MINUTES_MIN}-${data_provider_1.DEDICATED_POWER_IDLE_AFTER_MINUTES_MAX}). on_demand only.`,
|
|
37
|
+
min: data_provider_1.DEDICATED_POWER_IDLE_AFTER_MINUTES_MIN,
|
|
38
|
+
max: data_provider_1.DEDICATED_POWER_IDLE_AFTER_MINUTES_MAX,
|
|
39
|
+
}),
|
|
40
|
+
'budget-hours': core_1.Flags.string({
|
|
41
|
+
description: `Hard monthly compute-hour budget (greater than 0, at most ${data_provider_1.DEDICATED_POWER_BUDGET_HOURS_MAX}). At the budget, automatic starts are refused until the month resets or you raise it. A running machine is never stopped by it, and your own start is never blocked.`,
|
|
42
|
+
exclusive: ['no-budget'],
|
|
43
|
+
}),
|
|
44
|
+
'no-budget': core_1.Flags.boolean({
|
|
45
|
+
description: 'Remove the monthly compute-hour budget (no limit).',
|
|
46
|
+
exclusive: ['budget-hours'],
|
|
47
|
+
}),
|
|
48
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
49
|
+
};
|
|
50
|
+
async run() {
|
|
51
|
+
this.requireAuth();
|
|
52
|
+
const { args, flags } = await this.parse(DedicatedMachinesPowerSet);
|
|
53
|
+
let budget;
|
|
54
|
+
if (flags['no-budget'])
|
|
55
|
+
budget = null;
|
|
56
|
+
else if (flags['budget-hours'] !== undefined) {
|
|
57
|
+
const raw = flags['budget-hours'].trim();
|
|
58
|
+
budget = /^\d+(\.\d+)?$/.test(raw) ? Number(raw) : Number.NaN;
|
|
59
|
+
if (!(budget > 0) || budget > data_provider_1.DEDICATED_POWER_BUDGET_HOURS_MAX) {
|
|
60
|
+
this.error(`--budget-hours must be a number greater than 0 and at most ${data_provider_1.DEDICATED_POWER_BUDGET_HOURS_MAX}. Use --no-budget to remove the budget.`, { exit: 2 });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (flags.policy === undefined && flags['idle-after'] === undefined && budget === undefined) {
|
|
64
|
+
this.error('Nothing to change. Pass --policy <always_on|on_demand|manual>, --idle-after <minutes> (on_demand only), or --budget-hours <n> / --no-budget.', { exit: 2 });
|
|
65
|
+
}
|
|
66
|
+
if (flags['idle-after'] !== undefined &&
|
|
67
|
+
flags.policy !== undefined &&
|
|
68
|
+
flags.policy !== 'on_demand') {
|
|
69
|
+
this.error('--idle-after applies to --policy on_demand only.', { exit: 2 });
|
|
70
|
+
}
|
|
71
|
+
const leaseId = await this.resolveLeaseArg(args.lease);
|
|
72
|
+
const outcome = await this.callDedicated(() => (0, dedicated_power_1.setDedicatedPowerPolicy)(leaseId, {
|
|
73
|
+
...(flags.policy ? { policy: flags.policy } : {}),
|
|
74
|
+
...(flags['idle-after'] !== undefined ? { idleAfterMinutes: flags['idle-after'] } : {}),
|
|
75
|
+
...(budget !== undefined ? { budgetHoursPerMonth: budget } : {}),
|
|
76
|
+
}, this.scopeFrom(flags)));
|
|
77
|
+
if (!outcome.ok) {
|
|
78
|
+
const { refusal } = outcome;
|
|
79
|
+
if (flags.json) {
|
|
80
|
+
this.log(JSON.stringify({ ok: false, ...refusal }, null, 2));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.log(`on_demand is not available for ${leaseId}: ${refusal.reason}`);
|
|
84
|
+
this.log(` ${refusal.detail}`);
|
|
85
|
+
this.log(` Fix: ${refusal.fix}`);
|
|
86
|
+
this.log(' The power policy was not changed.');
|
|
87
|
+
}
|
|
88
|
+
this.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const { response } = outcome;
|
|
91
|
+
if (flags.json) {
|
|
92
|
+
this.log(JSON.stringify({ ok: true, ...response }, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const verb = response.changed ? 'set to' : 'already';
|
|
96
|
+
// Whether anything will actually stop the machine depends on the platform, not on this write: ask, so
|
|
97
|
+
// the sentence below is true. Best effort: a failed read leaves the neutral wording.
|
|
98
|
+
const autoStopMode = await (0, dedicated_power_1.getDedicatedPowerSummary)(leaseId, this.scopeFrom(flags))
|
|
99
|
+
.then((summary) => summary.autoStopMode)
|
|
100
|
+
.catch(() => undefined);
|
|
101
|
+
this.log(`Dedicated Runtime ${response.leaseId} power policy ${verb} ${(0, dedicated_power_1.describePowerPolicy)(response.power, autoStopMode)}.`);
|
|
102
|
+
this.log((0, dedicated_power_1.describeBudget)(response.power));
|
|
103
|
+
this.log('Nothing was started or stopped.');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.default = DedicatedMachinesPowerSet;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DedicatedLeaseCommand } from '../../../../lib/dedicated-lease-command';
|
|
2
|
+
/** `skrr machines dedicated power show` — the read-only power card. */
|
|
3
|
+
export default class DedicatedMachinesPowerShow extends DedicatedLeaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const dedicated_lease_command_1 = require("../../../../lib/dedicated-lease-command");
|
|
5
|
+
const dedicated_power_1 = require("../../../../lib/dedicated-power");
|
|
6
|
+
/** `skrr machines dedicated power show` — the read-only power card. */
|
|
7
|
+
class DedicatedMachinesPowerShow extends dedicated_lease_command_1.DedicatedLeaseCommand {
|
|
8
|
+
static description = 'Show a Dedicated Runtime power state: policy, why it is on or off, who is holding it, this month hours and cost, and the next expected wake. Read-only.';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> machines dedicated power show <lease-id>',
|
|
11
|
+
'<%= config.bin %> machines dedicated power show self --json',
|
|
12
|
+
];
|
|
13
|
+
static args = {
|
|
14
|
+
lease: core_1.Args.string({
|
|
15
|
+
description: dedicated_lease_command_1.DEDICATED_LEASE_ARG_DESCRIPTION,
|
|
16
|
+
required: true,
|
|
17
|
+
ignoreStdin: true,
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
static flags = {
|
|
21
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
this.requireAuth();
|
|
25
|
+
const { args, flags } = await this.parse(DedicatedMachinesPowerShow);
|
|
26
|
+
const leaseId = await this.resolveLeaseArg(args.lease);
|
|
27
|
+
const summary = await this.callDedicated(() => (0, dedicated_power_1.getDedicatedPowerSummary)(leaseId, this.scopeFrom(flags)));
|
|
28
|
+
if (flags.json) {
|
|
29
|
+
this.log(JSON.stringify(summary, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const line of (0, dedicated_power_1.describePowerSummary)(summary))
|
|
33
|
+
this.log(line);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = DedicatedMachinesPowerShow;
|
|
@@ -744,7 +744,7 @@ export declare function describeAutonomyLine(commitment: CommitmentView, latestC
|
|
|
744
744
|
} | null): string;
|
|
745
745
|
/**
|
|
746
746
|
* One-line rendering of `commitment.executorBlocked` — the B4/A7 signal that
|
|
747
|
-
* the executor's targets are all unusable: `{reason:'quota'|'offline', until?,
|
|
747
|
+
* the executor's targets are all unusable: `{reason:'quota'|'offline'|'waiting_for_machine', until?,
|
|
748
748
|
* provider?}`.
|
|
749
749
|
*
|
|
750
750
|
* Defensive by contract: the field is being added server-side, so its absence
|
package/dist/lib/commitments.js
CHANGED
|
@@ -874,7 +874,7 @@ function describeAutonomyLine(commitment, latestCheck) {
|
|
|
874
874
|
}
|
|
875
875
|
/**
|
|
876
876
|
* One-line rendering of `commitment.executorBlocked` — the B4/A7 signal that
|
|
877
|
-
* the executor's targets are all unusable: `{reason:'quota'|'offline', until?,
|
|
877
|
+
* the executor's targets are all unusable: `{reason:'quota'|'offline'|'waiting_for_machine', until?,
|
|
878
878
|
* provider?}`.
|
|
879
879
|
*
|
|
880
880
|
* Defensive by contract: the field is being added server-side, so its absence
|
|
@@ -893,7 +893,9 @@ function describeExecutorBlocked(commitment) {
|
|
|
893
893
|
? `quota exhausted${provider ? ` on ${provider}` : ''}`
|
|
894
894
|
: reason === 'offline'
|
|
895
895
|
? 'executor runtime offline'
|
|
896
|
-
: reason
|
|
896
|
+
: reason === 'waiting_for_machine'
|
|
897
|
+
? 'executor machine is stopped and will be started for the work'
|
|
898
|
+
: reason || 'blocked';
|
|
897
899
|
return `${label}${until ? ` until ${until}` : ''}`;
|
|
898
900
|
}
|
|
899
901
|
function renderCommitmentSummary(commitment, log,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type DedicatedRuntimeKeepAwakeResponse, type DedicatedRuntimeKeepAwakeState, type SetDedicatedRuntimeKeepAwakeRequest } from '@skrr-ai/data-provider';
|
|
2
|
+
import { type WhenOptions } from './format';
|
|
3
|
+
import { type DedicatedRuntimeScope } from './dedicated-machines';
|
|
4
|
+
/**
|
|
5
|
+
* Domain lib for `skrr machines dedicated power keep-awake` (K1). Keep-awake is a
|
|
6
|
+
* temporary override that forbids AUTOMATIC stop; it never starts the machine.
|
|
7
|
+
* The web calls the same routes through `dataService.setDedicatedRuntimeKeepAwake`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function setDedicatedKeepAwake(leaseId: string, request: SetDedicatedRuntimeKeepAwakeRequest, scope?: DedicatedRuntimeScope): Promise<DedicatedRuntimeKeepAwakeResponse>;
|
|
10
|
+
export declare function clearDedicatedKeepAwake(leaseId: string, scope?: DedicatedRuntimeScope): Promise<DedicatedRuntimeKeepAwakeResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* `--for` takes a whole number with a unit: `90m`, `2h`, `3d`. A bare number is
|
|
13
|
+
* minutes (the unit the API speaks). Returns minutes, or throws a plain message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseKeepAwakeDuration(value: string): number;
|
|
16
|
+
export declare function assertKeepAwakeMinutes(minutes: number): void;
|
|
17
|
+
/** `--until` takes any ISO 8601 date. The server re-checks the bounds. */
|
|
18
|
+
export declare function parseKeepAwakeUntil(value: string): string;
|
|
19
|
+
export declare function describeKeepAwake(state: DedicatedRuntimeKeepAwakeState, when?: WhenOptions): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setDedicatedKeepAwake = setDedicatedKeepAwake;
|
|
4
|
+
exports.clearDedicatedKeepAwake = clearDedicatedKeepAwake;
|
|
5
|
+
exports.parseKeepAwakeDuration = parseKeepAwakeDuration;
|
|
6
|
+
exports.assertKeepAwakeMinutes = assertKeepAwakeMinutes;
|
|
7
|
+
exports.parseKeepAwakeUntil = parseKeepAwakeUntil;
|
|
8
|
+
exports.describeKeepAwake = describeKeepAwake;
|
|
9
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
10
|
+
const api_fetch_1 = require("./api-fetch");
|
|
11
|
+
const format_1 = require("./format");
|
|
12
|
+
const dedicated_machines_1 = require("./dedicated-machines");
|
|
13
|
+
/**
|
|
14
|
+
* Domain lib for `skrr machines dedicated power keep-awake` (K1). Keep-awake is a
|
|
15
|
+
* temporary override that forbids AUTOMATIC stop; it never starts the machine.
|
|
16
|
+
* The web calls the same routes through `dataService.setDedicatedRuntimeKeepAwake`.
|
|
17
|
+
*/
|
|
18
|
+
function setDedicatedKeepAwake(leaseId, request, scope = {}) {
|
|
19
|
+
return (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power/keep-awake`, scope), {
|
|
20
|
+
method: 'PUT',
|
|
21
|
+
body: request,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function clearDedicatedKeepAwake(leaseId, scope = {}) {
|
|
25
|
+
return (0, api_fetch_1.apiFetch)((0, dedicated_machines_1.withDedicatedScope)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/power/keep-awake`, scope), {
|
|
26
|
+
method: 'DELETE',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `--for` takes a whole number with a unit: `90m`, `2h`, `3d`. A bare number is
|
|
31
|
+
* minutes (the unit the API speaks). Returns minutes, or throws a plain message.
|
|
32
|
+
*/
|
|
33
|
+
function parseKeepAwakeDuration(value) {
|
|
34
|
+
const match = value.trim().match(/^(\d+)\s*(m|h|d)?$/i);
|
|
35
|
+
if (!match) {
|
|
36
|
+
throw new Error(`Invalid --for value "${value}". Use a number with m, h or d, such as 90m, 2h or 3d.`);
|
|
37
|
+
}
|
|
38
|
+
const amount = Number(match[1]);
|
|
39
|
+
const unit = (match[2] || 'm').toLowerCase();
|
|
40
|
+
const minutes = unit === 'd' ? amount * 1440 : unit === 'h' ? amount * 60 : amount;
|
|
41
|
+
assertKeepAwakeMinutes(minutes);
|
|
42
|
+
return minutes;
|
|
43
|
+
}
|
|
44
|
+
function assertKeepAwakeMinutes(minutes) {
|
|
45
|
+
if (minutes < data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MIN) {
|
|
46
|
+
throw new Error(`Keep-awake must last at least ${data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MIN} minutes.`);
|
|
47
|
+
}
|
|
48
|
+
if (minutes > data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MAX) {
|
|
49
|
+
throw new Error(`Keep-awake can last at most ${data_provider_1.DEDICATED_KEEP_AWAKE_MINUTES_MAX / 1440} days. For longer, use \`power set --policy always_on\`.`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** `--until` takes any ISO 8601 date. The server re-checks the bounds. */
|
|
53
|
+
function parseKeepAwakeUntil(value) {
|
|
54
|
+
const at = new Date(value);
|
|
55
|
+
if (Number.isNaN(at.getTime())) {
|
|
56
|
+
throw new Error(`Invalid --until value "${value}". Use an ISO date such as 2026-10-10T18:00:00Z.`);
|
|
57
|
+
}
|
|
58
|
+
return at.toISOString();
|
|
59
|
+
}
|
|
60
|
+
function describeKeepAwake(state, when = {}) {
|
|
61
|
+
return state.active && state.until
|
|
62
|
+
? `is kept awake until ${(0, format_1.formatWhen)(state.until, when)}`
|
|
63
|
+
: 'has no keep-awake; it follows its power policy';
|
|
64
|
+
}
|