@skrr-ai/cli 0.1.27 → 0.1.29
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/base-command.d.ts +15 -0
- package/dist/base-command.js +49 -0
- package/dist/commands/agents/chat.d.ts +2 -0
- package/dist/commands/agents/chat.js +40 -2
- package/dist/commands/balance/show.d.ts +2 -3
- package/dist/commands/balance/show.js +2 -3
- package/dist/commands/balance/usage.d.ts +3 -11
- package/dist/commands/balance/usage.js +19 -72
- package/dist/commands/code/index.d.ts +4 -0
- package/dist/commands/code/index.js +9 -0
- package/dist/commands/code/install.d.ts +10 -2
- package/dist/commands/code/install.js +38 -28
- package/dist/commands/harnesses/leases/show.js +7 -4
- package/dist/commands/inbox/index.d.ts +15 -0
- package/dist/commands/inbox/index.js +52 -20
- package/dist/commands/instructions/install.d.ts +12 -0
- package/dist/commands/instructions/install.js +59 -14
- package/dist/commands/login.js +6 -0
- package/dist/commands/machines/dedicated/attach.js +1 -1
- package/dist/commands/machines/dedicated/cp.d.ts +1 -0
- package/dist/commands/machines/dedicated/cp.js +63 -9
- package/dist/commands/machines/dedicated/create.d.ts +1 -1
- package/dist/commands/machines/dedicated/create.js +7 -2
- package/dist/commands/machines/dedicated/exec.d.ts +23 -1
- package/dist/commands/machines/dedicated/exec.js +67 -7
- package/dist/commands/machines/dedicated/index.js +2 -0
- package/dist/commands/machines/dedicated/restore.d.ts +6 -0
- package/dist/commands/machines/dedicated/restore.js +7 -1
- package/dist/commands/machines/dedicated/sign-in.d.ts +4 -3
- package/dist/commands/machines/dedicated/sign-in.js +4 -3
- package/dist/commands/machines/dedicated/terminal.js +1 -1
- package/dist/commands/machines/dedicated/update-image.d.ts +15 -0
- package/dist/commands/machines/dedicated/update-image.js +38 -0
- package/dist/lib/balance.d.ts +2 -2
- package/dist/lib/balance.js +6 -4
- package/dist/lib/daemon-target.d.ts +103 -0
- package/dist/lib/daemon-target.js +110 -0
- package/dist/lib/dedicated-copy.d.ts +92 -2
- package/dist/lib/dedicated-copy.js +223 -18
- package/dist/lib/dedicated-lease-command.d.ts +7 -1
- package/dist/lib/dedicated-lease-command.js +16 -3
- package/dist/lib/dedicated-machines.d.ts +130 -8
- package/dist/lib/dedicated-machines.js +277 -16
- package/dist/lib/dedicated-terminal.d.ts +5 -25
- package/dist/lib/dedicated-terminal.js +45 -73
- package/dist/lib/dedicated-wait.d.ts +10 -0
- package/dist/lib/dedicated-wait.js +52 -0
- package/dist/lib/device-code.d.ts +12 -1
- package/dist/lib/device-code.js +44 -9
- package/dist/lib/harnesses.d.ts +13 -0
- package/dist/lib/harnesses.js +24 -0
- package/dist/lib/login.js +8 -7
- package/dist/lib/sky-code-broker.d.ts +46 -5
- package/dist/lib/sky-code-broker.js +96 -26
- package/dist/lib/sky-code.d.ts +33 -0
- package/dist/lib/sky-code.js +56 -8
- package/dist/lib/task-instruction-offer.js +12 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.d.ts +67 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refresh.js +124 -12
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.d.ts +67 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refresh.js +123 -11
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3061 -2876
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/index.js +3 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/loopback-server.js +64 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.d.ts +30 -0
- package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +43 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/loopback-server.js +64 -1
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.d.ts +30 -0
- package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +41 -0
- package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
- package/oclif.manifest.json +3113 -3011
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -367,6 +367,21 @@ export declare function withSubject(subject: string | undefined, message: string
|
|
|
367
367
|
* Exported for tests.
|
|
368
368
|
*/
|
|
369
369
|
export declare function describeHarnessUnavailable(body: Record<string, unknown> | null, bin?: string): string;
|
|
370
|
+
/**
|
|
371
|
+
* Frame a typed `GITHUB_UNAVAILABLE` failure as GitHub being unreachable rather
|
|
372
|
+
* than as a control-plane outage.
|
|
373
|
+
*
|
|
374
|
+
* The CLI's own sentence, keyed on the code, because the server's cannot be
|
|
375
|
+
* relied on: in production the 5xx redaction boundary keeps `code` and replaces
|
|
376
|
+
* the message with "An unexpected error occurred." Same meaning as the web's
|
|
377
|
+
* copy for this code (`hostedJobTaskFailureMessage`) — GitHub, not skrr, and
|
|
378
|
+
* waiting is the fix.
|
|
379
|
+
*
|
|
380
|
+
* Exported for tests.
|
|
381
|
+
*/
|
|
382
|
+
export declare function describeGitHubUnavailable(): string;
|
|
383
|
+
/** A deployment setting cannot recover through a client retry. */
|
|
384
|
+
export declare function describeGitHubAppNotConfigured(): string;
|
|
370
385
|
export declare function describeResourceNotFound(serverMessage: string | undefined, bin?: string, baseURL?: string): string | null;
|
|
371
386
|
export declare function describeAssigneeNotInSpace(serverMessage: string | undefined, bin?: string): string;
|
|
372
387
|
export declare function describeForbidden(bodyMessage?: string | null, method?: string | null): string;
|
package/dist/base-command.js
CHANGED
|
@@ -5,6 +5,8 @@ exports.assigneePayloadNeedsResolution = assigneePayloadNeedsResolution;
|
|
|
5
5
|
exports.parseGlobalFlags = parseGlobalFlags;
|
|
6
6
|
exports.withSubject = withSubject;
|
|
7
7
|
exports.describeHarnessUnavailable = describeHarnessUnavailable;
|
|
8
|
+
exports.describeGitHubUnavailable = describeGitHubUnavailable;
|
|
9
|
+
exports.describeGitHubAppNotConfigured = describeGitHubAppNotConfigured;
|
|
8
10
|
exports.describeResourceNotFound = describeResourceNotFound;
|
|
9
11
|
exports.describeAssigneeNotInSpace = describeAssigneeNotInSpace;
|
|
10
12
|
exports.describeForbidden = describeForbidden;
|
|
@@ -851,6 +853,32 @@ class BaseCommand extends core_1.Command {
|
|
|
851
853
|
details: body ?? undefined,
|
|
852
854
|
});
|
|
853
855
|
}
|
|
856
|
+
else if (bodyCode === 'GITHUB_APP_NOT_CONFIGURED') {
|
|
857
|
+
this.failWithCliError({
|
|
858
|
+
message: say(describeGitHubAppNotConfigured()),
|
|
859
|
+
code: bodyCode,
|
|
860
|
+
status: e.status,
|
|
861
|
+
exit: 2,
|
|
862
|
+
retryable: false,
|
|
863
|
+
details: body ?? undefined,
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
else if (bodyCode === 'GITHUB_UNAVAILABLE') {
|
|
867
|
+
// GitHub could not be reached — also a 503, also not a platform outage:
|
|
868
|
+
// the API answered, with a typed code. The body has no `cause` or
|
|
869
|
+
// `retryable: false`, so without this it reached the outage branch and
|
|
870
|
+
// told the user skrr was down, and recorded an incident saying so.
|
|
871
|
+
// Unlike an offline runtime, retrying IS the remedy, so the outage
|
|
872
|
+
// branch's exit code and verdict stay.
|
|
873
|
+
this.failWithCliError({
|
|
874
|
+
message: say(describeGitHubUnavailable()),
|
|
875
|
+
code: bodyCode,
|
|
876
|
+
status: e.status,
|
|
877
|
+
exit: 5,
|
|
878
|
+
retryable: true,
|
|
879
|
+
details: body ?? undefined,
|
|
880
|
+
});
|
|
881
|
+
}
|
|
854
882
|
else if (bodyCode === 'ASSIGNEE_NOT_IN_SPACE') {
|
|
855
883
|
// Code-specific enrichment takes precedence over the status heuristic:
|
|
856
884
|
// the server rejects a non-member assignee by id with no next step, so
|
|
@@ -1325,6 +1353,27 @@ function describeHarnessUnavailable(body, bin = 'skrr') {
|
|
|
1325
1353
|
`\`${bin} agents attach-harness\`.`;
|
|
1326
1354
|
return `${lead}\n → ${fix}`;
|
|
1327
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Frame a typed `GITHUB_UNAVAILABLE` failure as GitHub being unreachable rather
|
|
1358
|
+
* than as a control-plane outage.
|
|
1359
|
+
*
|
|
1360
|
+
* The CLI's own sentence, keyed on the code, because the server's cannot be
|
|
1361
|
+
* relied on: in production the 5xx redaction boundary keeps `code` and replaces
|
|
1362
|
+
* the message with "An unexpected error occurred." Same meaning as the web's
|
|
1363
|
+
* copy for this code (`hostedJobTaskFailureMessage`) — GitHub, not skrr, and
|
|
1364
|
+
* waiting is the fix.
|
|
1365
|
+
*
|
|
1366
|
+
* Exported for tests.
|
|
1367
|
+
*/
|
|
1368
|
+
function describeGitHubUnavailable() {
|
|
1369
|
+
return ('GitHub is temporarily unreachable, so this request could not be completed. ' +
|
|
1370
|
+
'skrr itself answered, so this is not a skrr outage — try again shortly.');
|
|
1371
|
+
}
|
|
1372
|
+
/** A deployment setting cannot recover through a client retry. */
|
|
1373
|
+
function describeGitHubAppNotConfigured() {
|
|
1374
|
+
return ('The GitHub App is not configured on this deployment. ' +
|
|
1375
|
+
'Retrying will not help — ask the deployment operator to configure it.');
|
|
1376
|
+
}
|
|
1328
1377
|
/**
|
|
1329
1378
|
* Enrich the server's `ASSIGNEE_NOT_IN_SPACE` rejection with the same
|
|
1330
1379
|
* copy-pasteable remedy the by-name resolution failure already gives (OSK-4864).
|
|
@@ -43,6 +43,8 @@ export declare const STREAM_LOST_EXIT_CODE = 75;
|
|
|
43
43
|
export declare function interruptedDetachMessage(input: {
|
|
44
44
|
sessionId: string;
|
|
45
45
|
conversationId?: string | null;
|
|
46
|
+
/** The second Ctrl-C: the CLI stopped waiting rather than running out the grace period. */
|
|
47
|
+
forced?: boolean;
|
|
46
48
|
}): string;
|
|
47
49
|
/**
|
|
48
50
|
* What the durable record says about a turn, read from its messages.
|
|
@@ -7,6 +7,7 @@ exports.inspectTurnCommand = inspectTurnCommand;
|
|
|
7
7
|
exports.noOutputFailureMessage = noOutputFailureMessage;
|
|
8
8
|
exports.noStreamJsonPayload = noStreamJsonPayload;
|
|
9
9
|
exports.chatStreamHeaderLines = chatStreamHeaderLines;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
10
11
|
const core_1 = require("@oclif/core");
|
|
11
12
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
12
13
|
const base_command_1 = require("../../base-command");
|
|
@@ -53,8 +54,12 @@ exports.STREAM_LOST_EXIT_CODE = 75;
|
|
|
53
54
|
* cancelled. This says what was asked, what is not guaranteed, and how to look.
|
|
54
55
|
*/
|
|
55
56
|
function interruptedDetachMessage(input) {
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const waited = input.forced
|
|
58
|
+
? `Cancel requested for run ${input.sessionId}; stopped waiting for it on the second Ctrl-C. `
|
|
59
|
+
: `Cancel requested for run ${input.sessionId}, but no terminal event arrived before the ` +
|
|
60
|
+
'grace period ended. ';
|
|
61
|
+
return (waited +
|
|
62
|
+
'The runtime MAY STILL BE EXECUTING — a tool call already running on ' +
|
|
58
63
|
'your machine is not guaranteed to stop. ' +
|
|
59
64
|
`Check what it did with: ${inspectTurnCommand(input.conversationId)}` +
|
|
60
65
|
(process.platform === 'win32'
|
|
@@ -454,12 +459,20 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
454
459
|
// event, and if it does not arrive detach explicitly while naming the
|
|
455
460
|
// session so the run can be re-attached or cancelled. A second Ctrl-C
|
|
456
461
|
// detaches immediately rather than being a blind kill.
|
|
462
|
+
//
|
|
463
|
+
// None of this ran until OSK-8708: auth-core's lock handler, installed
|
|
464
|
+
// before any command, exited the process on the FIRST Ctrl-C. It now defers
|
|
465
|
+
// to this listener — and exits the process itself on a SECOND signal, as
|
|
466
|
+
// soon as that signal's listeners have run. So the second Ctrl-C's detach
|
|
467
|
+
// notice is written here, synchronously, straight to the descriptor: the
|
|
468
|
+
// probe and the message the detach path prints after it would never happen.
|
|
457
469
|
const CANCEL_GRACE_MS = 10_000;
|
|
458
470
|
let detach = null;
|
|
459
471
|
const detached = new Promise((resolve) => {
|
|
460
472
|
detach = () => resolve('detached');
|
|
461
473
|
});
|
|
462
474
|
let interrupts = 0;
|
|
475
|
+
let forcedDetachAnnounced = false;
|
|
463
476
|
let graceTimer = null;
|
|
464
477
|
const onInterrupt = () => {
|
|
465
478
|
interrupts += 1;
|
|
@@ -474,6 +487,27 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
474
487
|
graceTimer.unref?.();
|
|
475
488
|
return;
|
|
476
489
|
}
|
|
490
|
+
if (!forcedDetachAnnounced) {
|
|
491
|
+
forcedDetachAnnounced = true;
|
|
492
|
+
const message = interruptedDetachMessage({
|
|
493
|
+
sessionId: started.sessionId,
|
|
494
|
+
conversationId: started.conversationId,
|
|
495
|
+
forced: true,
|
|
496
|
+
});
|
|
497
|
+
if (flags.jsonl) {
|
|
498
|
+
(0, node_fs_1.writeSync)(process.stdout.fd, `${JSON.stringify({
|
|
499
|
+
type: 'run.detached',
|
|
500
|
+
sessionId: started.sessionId,
|
|
501
|
+
conversationId: started.conversationId,
|
|
502
|
+
interrupted: true,
|
|
503
|
+
message,
|
|
504
|
+
})}\n`);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
(0, node_fs_1.writeSync)(process.stderr.fd, `\n${message}\n`);
|
|
508
|
+
}
|
|
509
|
+
process.exitCode = exports.DETACH_EXIT_CODE;
|
|
510
|
+
}
|
|
477
511
|
detach?.();
|
|
478
512
|
};
|
|
479
513
|
process.on('SIGINT', onInterrupt);
|
|
@@ -495,6 +529,10 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
495
529
|
// turns the server had already finalised as errors — "it may still be
|
|
496
530
|
// executing" is the most expensive thing to be wrong about here, because
|
|
497
531
|
// it is what stops a caller retrying or alerting (OSK-7595, OSK-7660).
|
|
532
|
+
if (outcome === 'detached' && forcedDetachAnnounced) {
|
|
533
|
+
// Already said, and the exit code already set, on the second Ctrl-C.
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
498
536
|
let reconciled = null;
|
|
499
537
|
if (outcome === 'detached') {
|
|
500
538
|
if (await client.probeTerminal('detach'))
|
|
@@ -3,9 +3,8 @@ import { type RuntimeMonthlySpend } from '../../lib/harnesses';
|
|
|
3
3
|
/**
|
|
4
4
|
* The hosted-runtime monthly cap rows, as lines.
|
|
5
5
|
*
|
|
6
|
-
* Pure
|
|
7
|
-
*
|
|
8
|
-
* fix, so the wording is what gets pinned.
|
|
6
|
+
* Pure because this screen must stop a user reading headroom off the wrong cap;
|
|
7
|
+
* the wording is part of the observable contract and is pinned directly.
|
|
9
8
|
*/
|
|
10
9
|
export declare function renderRuntimeSpend(spend: RuntimeMonthlySpend[], log: (line: string) => void): void;
|
|
11
10
|
/**
|
|
@@ -10,9 +10,8 @@ const usd = (cents) => (typeof cents === 'number' ? `$${(cents / 100).toFixed(2)
|
|
|
10
10
|
/**
|
|
11
11
|
* The hosted-runtime monthly cap rows, as lines.
|
|
12
12
|
*
|
|
13
|
-
* Pure
|
|
14
|
-
*
|
|
15
|
-
* fix, so the wording is what gets pinned.
|
|
13
|
+
* Pure because this screen must stop a user reading headroom off the wrong cap;
|
|
14
|
+
* the wording is part of the observable contract and is pinned directly.
|
|
16
15
|
*/
|
|
17
16
|
function renderRuntimeSpend(spend, log) {
|
|
18
17
|
const capped = spend.filter((row) => row.hasLimit);
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import type { TSkyCodeUsageSnapshot } from '@skrr-ai/data-provider';
|
|
1
2
|
import { BaseCommand } from '../../base-command';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The shape is owned server-side by `getUsageSummary` and is not a stable
|
|
6
|
-
* contract this command should reshape, so the default output pretty-prints
|
|
7
|
-
* what the server sent rather than inventing a table over fields that may move.
|
|
4
|
+
* Sky Code's versioned monthly and five-hour usage snapshot.
|
|
8
5
|
*/
|
|
9
6
|
export default class BalanceUsage extends BaseCommand {
|
|
10
7
|
static description: string;
|
|
@@ -14,9 +11,4 @@ export default class BalanceUsage extends BaseCommand {
|
|
|
14
11
|
};
|
|
15
12
|
run(): Promise<void>;
|
|
16
13
|
}
|
|
17
|
-
export declare function
|
|
18
|
-
/**
|
|
19
|
-
* Why `--workspace` is refused here rather than silently dropped, and where a
|
|
20
|
-
* workspace's figures actually live.
|
|
21
|
-
*/
|
|
22
|
-
export declare function usageNotWorkspaceScopedMessage(workspaceId: string, bin: string): string;
|
|
14
|
+
export declare function renderSkyCodeUsage(usage: TSkyCodeUsageSnapshot, log: (line: string) => void): void;
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.usageNotWorkspaceScopedMessage = usageNotWorkspaceScopedMessage;
|
|
3
|
+
exports.renderSkyCodeUsage = renderSkyCodeUsage;
|
|
5
4
|
const core_1 = require("@oclif/core");
|
|
6
5
|
const base_command_1 = require("../../base-command");
|
|
7
6
|
const balance_1 = require("../../lib/balance");
|
|
8
7
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* The shape is owned server-side by `getUsageSummary` and is not a stable
|
|
12
|
-
* contract this command should reshape, so the default output pretty-prints
|
|
13
|
-
* what the server sent rather than inventing a table over fields that may move.
|
|
8
|
+
* Sky Code's versioned monthly and five-hour usage snapshot.
|
|
14
9
|
*/
|
|
15
10
|
class BalanceUsage extends base_command_1.BaseCommand {
|
|
16
11
|
static description = 'Usage summary against your hosted usage balance';
|
|
@@ -19,24 +14,10 @@ class BalanceUsage extends base_command_1.BaseCommand {
|
|
|
19
14
|
async run() {
|
|
20
15
|
this.requireAuth();
|
|
21
16
|
const { flags } = await this.parse(BalanceUsage);
|
|
22
|
-
// `--workspace` is a global flag, so it parsed here and was then ignored:
|
|
23
|
-
// `balance usage --workspace <id>` printed the caller's PERSONAL summary
|
|
24
|
-
// with exit 0, which a member following a workspace refusal read as the
|
|
25
|
-
// workspace's (OSK-4820). There is no workspace-scoped usage read behind
|
|
26
|
-
// this command, so the honest answer is a refusal that names the reads
|
|
27
|
-
// that do exist.
|
|
28
17
|
const workspaceId = flags.workspace;
|
|
29
|
-
if (workspaceId) {
|
|
30
|
-
this.failWithCliError({
|
|
31
|
-
message: usageNotWorkspaceScopedMessage(workspaceId, this.config.bin),
|
|
32
|
-
code: 'BALANCE_USAGE_NOT_WORKSPACE_SCOPED',
|
|
33
|
-
exit: 2,
|
|
34
|
-
retryable: false,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
18
|
let summary;
|
|
38
19
|
try {
|
|
39
|
-
summary = await (0, balance_1.
|
|
20
|
+
summary = await (0, balance_1.getSkyCodeUsage)(workspaceId);
|
|
40
21
|
}
|
|
41
22
|
catch (err) {
|
|
42
23
|
this.handleApiError(err);
|
|
@@ -49,58 +30,24 @@ class BalanceUsage extends base_command_1.BaseCommand {
|
|
|
49
30
|
this.log('No usage recorded.');
|
|
50
31
|
return;
|
|
51
32
|
}
|
|
52
|
-
|
|
33
|
+
renderSkyCodeUsage(summary, (line) => this.log(line));
|
|
53
34
|
}
|
|
54
35
|
}
|
|
55
36
|
exports.default = BalanceUsage;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
projectedPeriodUsage: 'Projected period usage',
|
|
72
|
-
};
|
|
73
|
-
function renderUsageSummary(summary, log) {
|
|
74
|
-
const label = (key) => (MONEY_FIELDS[key] ?? key).padEnd(24);
|
|
75
|
-
for (const [key, value] of Object.entries(summary)) {
|
|
76
|
-
if (key === 'runRateNudge' && value != null && typeof value === 'object') {
|
|
77
|
-
log(' Run-rate nudge');
|
|
78
|
-
for (const [k, v] of Object.entries(value)) {
|
|
79
|
-
const rendered = k === 'projectedPeriodUsage' && typeof v === 'number'
|
|
80
|
-
? (0, balance_1.formatCreditsWithUsd)(v)
|
|
81
|
-
: String(v ?? '-');
|
|
82
|
-
log(` ${k.padEnd(22)} ${rendered}`);
|
|
83
|
-
}
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (key in MONEY_FIELDS && typeof value === 'number') {
|
|
87
|
-
log(` ${label(key)} ${(0, balance_1.formatCreditsWithUsd)(value)}`);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
const rendered = value != null && typeof value === 'object' ? JSON.stringify(value) : String(value ?? '-');
|
|
91
|
-
log(` ${label(key)} ${rendered}`);
|
|
37
|
+
function renderSkyCodeUsage(usage, log) {
|
|
38
|
+
log(`Sky Code ${usage.plan ?? 'current'} plan · ${usage.ownerType} ${usage.ownerId}`);
|
|
39
|
+
log(` Monthly used ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.usedUsageMicros)}`);
|
|
40
|
+
log(` Monthly reserved ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.reservedUsageMicros)}`);
|
|
41
|
+
log(` Monthly remaining ${(0, balance_1.formatCreditsWithUsd)(usage.monthly.remainingUsageMicros)}`);
|
|
42
|
+
log(` Monthly reset ${usage.monthly.resetsAt ?? 'unavailable'}`);
|
|
43
|
+
log(` 5-hour used ${(0, balance_1.formatCreditsWithUsd)(usage.fiveHour.usedUsageMicros)}`);
|
|
44
|
+
log(` 5-hour reserved ${(0, balance_1.formatCreditsWithUsd)(usage.fiveHour.reservedUsageMicros)}`);
|
|
45
|
+
log(` 5-hour limit ${(0, balance_1.formatCreditsWithUsd)(usage.fiveHour.limitUsageMicros)}`);
|
|
46
|
+
log(` 5-hour reset ${usage.fiveHour.resetsAt ?? 'unavailable'}`);
|
|
47
|
+
log(` Concurrent sessions ${usage.fiveHour.activeSessions}/${usage.fiveHour.maxConcurrentSessions}`);
|
|
48
|
+
log(` Available overage ${(0, balance_1.formatCreditsWithUsd)(usage.balance.availableMicros)}`);
|
|
49
|
+
log(` Overage consent ${usage.overageEnabled ? 'enabled' : 'disabled'}`);
|
|
50
|
+
if (usage.dataStatus === 'partial') {
|
|
51
|
+
log(` Reconciliation partial (${usage.unresolvedUsageCount} unresolved)`);
|
|
92
52
|
}
|
|
93
|
-
log('');
|
|
94
|
-
log('Figures are in credits, with the dollar value alongside.');
|
|
95
|
-
log('`skrr balance show` reports the authoritative available balance used to admit a turn (holds excluded).');
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Why `--workspace` is refused here rather than silently dropped, and where a
|
|
99
|
-
* workspace's figures actually live.
|
|
100
|
-
*/
|
|
101
|
-
function usageNotWorkspaceScopedMessage(workspaceId, bin) {
|
|
102
|
-
return (`\`${bin} balance usage\` summarises your PERSONAL account and cannot be scoped to ` +
|
|
103
|
-
`workspace ${workspaceId} — it would have shown your own figures under that id. ` +
|
|
104
|
-
`For the workspace, use \`${bin} balance show --workspace ${workspaceId}\` (any member) ` +
|
|
105
|
-
`or \`${bin} balance statement --workspace ${workspaceId}\` (owner).`);
|
|
106
53
|
}
|
|
@@ -25,6 +25,10 @@ export default class Code extends Command {
|
|
|
25
25
|
* engine exit into a generic CLI failure would make "the task failed" and "the
|
|
26
26
|
* CLI could not start it" indistinguishable in CI.
|
|
27
27
|
*
|
|
28
|
+
* So the codes this function owns are the ones where no engine ran: `127`, the
|
|
29
|
+
* engine is not installed, and `77`, the account was refused before the engine
|
|
30
|
+
* could start (OSK-8822).
|
|
31
|
+
*
|
|
28
32
|
* ── Why `managed` is opt-IN per command ────────────────────────────────────
|
|
29
33
|
*
|
|
30
34
|
* `skrr code acp` must NOT take the managed path: the daemon that drives it already
|
|
@@ -63,6 +63,10 @@ exports.default = Code;
|
|
|
63
63
|
* engine exit into a generic CLI failure would make "the task failed" and "the
|
|
64
64
|
* CLI could not start it" indistinguishable in CI.
|
|
65
65
|
*
|
|
66
|
+
* So the codes this function owns are the ones where no engine ran: `127`, the
|
|
67
|
+
* engine is not installed, and `77`, the account was refused before the engine
|
|
68
|
+
* could start (OSK-8822).
|
|
69
|
+
*
|
|
66
70
|
* ── Why `managed` is opt-IN per command ────────────────────────────────────
|
|
67
71
|
*
|
|
68
72
|
* `skrr code acp` must NOT take the managed path: the daemon that drives it already
|
|
@@ -107,6 +111,11 @@ async function forwardToEngine(cmd, subcommand, passthrough, options = {}) {
|
|
|
107
111
|
cmd.exit(code);
|
|
108
112
|
}
|
|
109
113
|
catch (err) {
|
|
114
|
+
if (err instanceof sky_code_1.EngineStartRefusedError) {
|
|
115
|
+
// The engine never ran, so there is no engine code to pass through (OSK-8822).
|
|
116
|
+
// Silent here: the hook that refused already said why, once, on stderr.
|
|
117
|
+
cmd.exit(sky_code_1.ENGINE_START_REFUSED_EXIT_CODE);
|
|
118
|
+
}
|
|
110
119
|
if (err instanceof Error && err.message === 'sky-code-not-installed') {
|
|
111
120
|
cmd.log((0, sky_code_1.notInstalledMessage)());
|
|
112
121
|
cmd.exit(127); // conventional "command not found"
|
|
@@ -31,15 +31,23 @@ export default class CodeInstall extends BaseCommand {
|
|
|
31
31
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
32
32
|
};
|
|
33
33
|
run(): Promise<void>;
|
|
34
|
+
/** Which daemon the install targets, by the rule every daemon-picking command shares. */
|
|
35
|
+
private resolveTarget;
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
37
|
+
* The daemon id to install through, or the refusal.
|
|
36
38
|
*
|
|
37
39
|
* Refuses rather than guessing when several are active. Picking one would mean
|
|
38
40
|
* installing a hundred megabytes onto a machine the user was not thinking
|
|
39
41
|
* about, and the list this reads is the same one `skrr list-daemons` prints —
|
|
40
42
|
* so the error can name the exact values that work.
|
|
43
|
+
*
|
|
44
|
+
* A Dedicated Runtime's own machine is refused here, before any request. The
|
|
45
|
+
* daemon and the API refuse it too; saying so locally is what keeps the answer
|
|
46
|
+
* the same whether or not this machine's daemon is reachable, and whether or
|
|
47
|
+
* not it has published its id. Another of the owner's machines, named with
|
|
48
|
+
* `--daemon`, is not this machine and goes to the API like anywhere else.
|
|
41
49
|
*/
|
|
42
|
-
private
|
|
50
|
+
private installTarget;
|
|
43
51
|
/**
|
|
44
52
|
* What the daemon says about this provider, when it started no job.
|
|
45
53
|
*
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const base_command_1 = require("../../base-command");
|
|
5
5
|
const api_fetch_1 = require("../../lib/api-fetch");
|
|
6
6
|
const daemon_ref_1 = require("../../lib/daemon-ref");
|
|
7
|
+
const daemon_target_1 = require("../../lib/daemon-target");
|
|
7
8
|
const sky_code_1 = require("../../lib/sky-code");
|
|
8
9
|
/**
|
|
9
10
|
* `skrr code install` — install the engine from the terminal.
|
|
@@ -37,7 +38,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
37
38
|
];
|
|
38
39
|
static flags = {
|
|
39
40
|
daemon: core_1.Flags.string({
|
|
40
|
-
description: 'Which daemon installs it. Defaults to the only active one; required when you have several.',
|
|
41
|
+
description: 'Which daemon installs it. Defaults to this machine on a Dedicated Runtime, otherwise to the only active one; required when you have several.',
|
|
41
42
|
}),
|
|
42
43
|
wait: core_1.Flags.boolean({
|
|
43
44
|
default: true,
|
|
@@ -48,19 +49,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
48
49
|
};
|
|
49
50
|
async run() {
|
|
50
51
|
const { flags } = await this.parse(CodeInstall);
|
|
51
|
-
|
|
52
|
-
// too, but with several active daemons this command would first ask which
|
|
53
|
-
// one — and "pick a machine" is the wrong question to put to someone whose
|
|
54
|
-
// answer is "none of them can do this here".
|
|
55
|
-
if ((0, sky_code_1.isDedicatedRuntimeWorkload)()) {
|
|
56
|
-
this.failWithCliError({
|
|
57
|
-
message: sky_code_1.DEDICATED_RUNTIME_ENGINE_MESSAGE,
|
|
58
|
-
code: 'DEDICATED_RUNTIME_ENGINE_SHIPS_WITH_IMAGE',
|
|
59
|
-
exit: 1,
|
|
60
|
-
retryable: false,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const daemonId = await this.resolveDaemon(flags.daemon);
|
|
52
|
+
const daemonId = this.installTarget(await this.resolveTarget(flags.daemon));
|
|
64
53
|
let started;
|
|
65
54
|
try {
|
|
66
55
|
started = await (0, api_fetch_1.apiFetch)(`/api/daemons/${encodeURIComponent(daemonId)}/cli-installers/${PROVIDER}/install`, { method: 'POST', body: {}, credential: this.resolvedCredential });
|
|
@@ -136,28 +125,48 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
136
125
|
this.log(`skrr Code installed. Run \`${this.config.bin} code\` to start it.`);
|
|
137
126
|
}
|
|
138
127
|
}
|
|
128
|
+
/** Which daemon the install targets, by the rule every daemon-picking command shares. */
|
|
129
|
+
async resolveTarget(explicit) {
|
|
130
|
+
try {
|
|
131
|
+
return await (0, daemon_target_1.resolveDaemonTarget)({
|
|
132
|
+
explicit,
|
|
133
|
+
local: (0, daemon_target_1.localMachine)({ baseURL: this.cliConfig?.baseURL }),
|
|
134
|
+
listActiveDaemons: () => (0, daemon_target_1.listActiveDaemons)(this.resolvedCredential),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
this.handleApiError(err);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
139
141
|
/**
|
|
140
|
-
*
|
|
142
|
+
* The daemon id to install through, or the refusal.
|
|
141
143
|
*
|
|
142
144
|
* Refuses rather than guessing when several are active. Picking one would mean
|
|
143
145
|
* installing a hundred megabytes onto a machine the user was not thinking
|
|
144
146
|
* about, and the list this reads is the same one `skrr list-daemons` prints —
|
|
145
147
|
* so the error can name the exact values that work.
|
|
148
|
+
*
|
|
149
|
+
* A Dedicated Runtime's own machine is refused here, before any request. The
|
|
150
|
+
* daemon and the API refuse it too; saying so locally is what keeps the answer
|
|
151
|
+
* the same whether or not this machine's daemon is reachable, and whether or
|
|
152
|
+
* not it has published its id. Another of the owner's machines, named with
|
|
153
|
+
* `--daemon`, is not this machine and goes to the API like anywhere else.
|
|
146
154
|
*/
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
installTarget(target) {
|
|
156
|
+
if (target.kind === 'daemon')
|
|
157
|
+
return target.daemonId;
|
|
158
|
+
if (target.kind === 'this-machine') {
|
|
159
|
+
this.failWithCliError({
|
|
160
|
+
message: target.explicit
|
|
161
|
+
? sky_code_1.DEDICATED_RUNTIME_ENGINE_MESSAGE
|
|
162
|
+
: `${sky_code_1.DEDICATED_RUNTIME_ENGINE_MESSAGE} This command installs on the machine it runs on; ` +
|
|
163
|
+
'to install on another of your machines, name it with --daemon.',
|
|
164
|
+
code: 'DEDICATED_RUNTIME_ENGINE_SHIPS_WITH_IMAGE',
|
|
165
|
+
exit: 1,
|
|
166
|
+
retryable: false,
|
|
167
|
+
});
|
|
156
168
|
}
|
|
157
|
-
|
|
158
|
-
if (daemons.length === 1)
|
|
159
|
-
return daemons[0].id;
|
|
160
|
-
if (daemons.length === 0) {
|
|
169
|
+
if (target.kind === 'none') {
|
|
161
170
|
this.failWithCliError({
|
|
162
171
|
message: 'No active daemon is registered for this account, so nothing can perform the install. ' +
|
|
163
172
|
`Start the local runtime with \`${this.config.bin} daemon start\`, then run this again.`,
|
|
@@ -166,6 +175,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
166
175
|
retryable: false,
|
|
167
176
|
});
|
|
168
177
|
}
|
|
178
|
+
const { daemons } = target;
|
|
169
179
|
const choices = daemons
|
|
170
180
|
.slice(0, 10)
|
|
171
181
|
.map((d) => ` ${(0, daemon_ref_1.formatDaemonRef)(d.id)} ${d.name || '—'}`)
|
|
@@ -68,12 +68,15 @@ class HarnessesLeasesShow extends base_command_1.BaseCommand {
|
|
|
68
68
|
if (os || arch)
|
|
69
69
|
line('Resources:', [os, arch].filter(Boolean).join('/'));
|
|
70
70
|
// `assignable` is the question a reader has about health, and the reasons
|
|
71
|
-
// are why — a bare status word would drop the actionable half.
|
|
71
|
+
// are why — a bare status word would drop the actionable half. The reasons
|
|
72
|
+
// are printed as sentences, judged against the lease state, in the words the
|
|
73
|
+
// machine page uses; `--json` keeps the codes (OSK-8768).
|
|
72
74
|
const assignable = nested(lease.health, 'assignable');
|
|
73
75
|
if (assignable !== undefined) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const why = assignable === true
|
|
77
|
+
? ''
|
|
78
|
+
: (0, harnesses_1.describeHealthReasonSentences)(lease.health?.reasons, lease.state);
|
|
79
|
+
line('Assignable:', `${assignable === true ? 'yes' : 'no'}${why ? ` — ${why}` : ''}`);
|
|
77
80
|
}
|
|
78
81
|
line('Created:', lease.createdAt);
|
|
79
82
|
line('Updated:', lease.updatedAt);
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* Stop `--follow` on Ctrl-C or SIGTERM, and end with the status a shell
|
|
4
|
+
* reports for that signal (130 / 143).
|
|
5
|
+
*
|
|
6
|
+
* The listener only started running with OSK-8708 — auth-core's lock handler
|
|
7
|
+
* used to exit the process first, with 130 for SIGINT. Now the follow loop
|
|
8
|
+
* stops and the command returns normally, which on its own would report
|
|
9
|
+
* success (0) for an interrupted follow and change what a script reads.
|
|
10
|
+
*/
|
|
11
|
+
export declare function listenForFollowStop(proc?: Pick<NodeJS.Process, 'once' | 'removeListener'> & {
|
|
12
|
+
exitCode?: number | string;
|
|
13
|
+
}): {
|
|
14
|
+
stopped: () => boolean;
|
|
15
|
+
dispose: () => void;
|
|
16
|
+
};
|
|
2
17
|
/**
|
|
3
18
|
* `skrr inbox` / `skrr inbox --follow` — what have my agents said to me?
|
|
4
19
|
*
|