@skrr-ai/cli 0.1.68 → 0.1.70
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/agents/chat.js +7 -2
- package/dist/commands/agents/releases/rollback.js +1 -2
- package/dist/commands/agents/visibility.js +7 -27
- package/dist/commands/harnesses/leases/billing.d.ts +2 -1
- package/dist/commands/harnesses/leases/billing.js +8 -3
- package/dist/commands/machines/dedicated/create.d.ts +1 -0
- package/dist/commands/machines/dedicated/create.js +9 -8
- package/dist/commands/machines/dedicated/restore.d.ts +1 -0
- package/dist/commands/machines/dedicated/restore.js +7 -1
- package/dist/commands/machines/dedicated/spending-limit.d.ts +3 -2
- package/dist/commands/machines/dedicated/spending-limit.js +7 -3
- package/dist/commands/step-up.d.ts +3 -4
- package/dist/commands/step-up.js +10 -11
- package/dist/lib/dedicated-machines.d.ts +5 -5
- package/dist/lib/dedicated-machines.js +7 -5
- package/dist/lib/dedicated-service.js +2 -0
- package/dist/lib/file-mime.js +4 -0
- package/dist/lib/harnesses.d.ts +5 -0
- package/dist/lib/step-up.d.ts +4 -6
- package/dist/lib/step-up.js +4 -4
- package/dist/node_modules/@skrr-ai/data-provider/index.js +62 -15
- package/oclif.manifest.json +22997 -22952
- package/package.json +1 -1
|
@@ -374,6 +374,8 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
374
374
|
async run() {
|
|
375
375
|
this.requireAuth();
|
|
376
376
|
const { args, flags } = await this.parse(AgentsChat);
|
|
377
|
+
if (flags.quick && flags.task)
|
|
378
|
+
this.error('--task requires a runtime session; remove --quick to bind this turn.', { exit: 2 });
|
|
377
379
|
if (flags.json && !flags['no-stream'] && !flags.quick) {
|
|
378
380
|
this.error('--json is only supported with --no-stream or --quick; use --jsonl for streaming.', {
|
|
379
381
|
exit: 2,
|
|
@@ -413,6 +415,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
413
415
|
}
|
|
414
416
|
}
|
|
415
417
|
// OSK-2946 — resolve the explicit binding, or warn that there isn't one.
|
|
418
|
+
let boundTaskId;
|
|
416
419
|
let taskContextBlock = null;
|
|
417
420
|
if (flags.task) {
|
|
418
421
|
let task;
|
|
@@ -423,6 +426,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
423
426
|
this.handleApiError(err);
|
|
424
427
|
}
|
|
425
428
|
const taskId = String(task.id ?? flags.task);
|
|
429
|
+
boundTaskId = taskId;
|
|
426
430
|
taskContextBlock = [
|
|
427
431
|
'You are working on a specific skrr task. This is your canonical scope:',
|
|
428
432
|
` task id: ${taskId}`,
|
|
@@ -441,8 +445,8 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
441
445
|
` skrr tasks complete ${taskId} --summary "<what you did>" --deliverables "<refs>"`,
|
|
442
446
|
'',
|
|
443
447
|
'This is a chat turn, not a managed task run: the task is NOT checked out and its ' +
|
|
444
|
-
'status is not managed for you.
|
|
445
|
-
'
|
|
448
|
+
'status is not managed for you. The runtime session is bound to this task for ' +
|
|
449
|
+
'task context and artifact uploads; use the canonical id above.',
|
|
446
450
|
].join('\n');
|
|
447
451
|
if (!flags.jsonl) {
|
|
448
452
|
this.log(`Bound to task ${task.identifier ?? taskId} (chat turn — task not checked out)`);
|
|
@@ -515,6 +519,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
515
519
|
try {
|
|
516
520
|
started = await data_provider_1.dataService.startAgenticChat({
|
|
517
521
|
agentId: args.agentId,
|
|
522
|
+
taskId: boundTaskId,
|
|
518
523
|
text: contextParts ? `${contextParts}\n\n${prompt}` : prompt,
|
|
519
524
|
conversationId: threadConversationId,
|
|
520
525
|
workspaceId: process.env.OVERSKY_WORKSPACE_ID,
|
|
@@ -13,8 +13,7 @@ class AgentsReleasesRollback extends base_command_1.BaseCommand {
|
|
|
13
13
|
async run() {
|
|
14
14
|
this.requireAuth();
|
|
15
15
|
const { args, flags } = await this.parse(AgentsReleasesRollback);
|
|
16
|
-
// Through handleApiError so a refusal says why
|
|
17
|
-
// STEP_UP_REQUIRED (OSK-12275), whose remedy is `step-up`, not a retry.
|
|
16
|
+
// Through handleApiError so a release or permission refusal says why.
|
|
18
17
|
let result;
|
|
19
18
|
try {
|
|
20
19
|
result = await data_provider_1.dataService.rollbackAgentRelease({
|
|
@@ -36,7 +36,9 @@ function parseReleaseDependencies(entries) {
|
|
|
36
36
|
for (const entry of entries) {
|
|
37
37
|
const match = /^(skill|action):([^@]+)@(store_release_[A-Za-z0-9_-]+)$/.exec(entry);
|
|
38
38
|
if (!match) {
|
|
39
|
-
return {
|
|
39
|
+
return {
|
|
40
|
+
error: `--dependency expects skill:<id>@<store-release-id> or action:<id>@<store-release-id>, got "${entry}".`,
|
|
41
|
+
};
|
|
40
42
|
}
|
|
41
43
|
dependencies.push({
|
|
42
44
|
entityType: match[1],
|
|
@@ -47,10 +49,9 @@ function parseReleaseDependencies(entries) {
|
|
|
47
49
|
return { dependencies };
|
|
48
50
|
}
|
|
49
51
|
class AgentsVisibility extends base_command_1.BaseCommand {
|
|
50
|
-
static description = 'Set the visibility of an agent
|
|
52
|
+
static description = 'Set the visibility of an agent';
|
|
51
53
|
static examples = [
|
|
52
54
|
'<%= config.bin %> agents visibility <agent-id> --public',
|
|
53
|
-
'<%= config.bin %> step-up # required before public publishing when 2FA is enabled',
|
|
54
55
|
'<%= config.bin %> agents visibility <agent-id> --private',
|
|
55
56
|
'<%= config.bin %> agents visibility <agent-id> --public --json',
|
|
56
57
|
'<%= config.bin %> agents visibility <agent-id> --public --capability-rationale "network:*=Looks up flight prices on the web"',
|
|
@@ -111,7 +112,9 @@ class AgentsVisibility extends base_command_1.BaseCommand {
|
|
|
111
112
|
if (dependencyError)
|
|
112
113
|
this.error(dependencyError, { exit: 1 });
|
|
113
114
|
if (dependencies && !isPublic) {
|
|
114
|
-
this.error('--dependency declares pins on a public release; pass it with --public.', {
|
|
115
|
+
this.error('--dependency declares pins on a public release; pass it with --public.', {
|
|
116
|
+
exit: 1,
|
|
117
|
+
});
|
|
115
118
|
}
|
|
116
119
|
if (capabilityRationales && !isPublic) {
|
|
117
120
|
this.error('--capability-rationale applies only when publishing with --public.', {
|
|
@@ -125,29 +128,6 @@ class AgentsVisibility extends base_command_1.BaseCommand {
|
|
|
125
128
|
exit: 1,
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
|
-
// A public release reaches buyers. Accounts with 2FA need a recent
|
|
129
|
-
// second-factor proof; accounts without 2FA can publish from a verified
|
|
130
|
-
// sign-in session. Read that state before the publish write so the
|
|
131
|
-
// remedy is visible in the normal CLI flow instead of only after a 403.
|
|
132
|
-
// The server remains authoritative: this is user guidance, not a bypass.
|
|
133
|
-
if (isPublic) {
|
|
134
|
-
let stepUp;
|
|
135
|
-
try {
|
|
136
|
-
stepUp = await data_provider_1.dataService.getStepUpStatus(data_provider_1.STORE_PUBLISH_STEP_UP_PURPOSE);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
this.handleApiError(err);
|
|
140
|
-
}
|
|
141
|
-
if (!stepUp.active) {
|
|
142
|
-
const bin = this.config.bin;
|
|
143
|
-
if (!stepUp.sessionBound) {
|
|
144
|
-
this.error(`This credential is not tied to a sign-in session. Run \`${bin} login\` before publishing, then \`${bin} step-up\` if your account has 2FA enabled.`, { code: 'STEP_UP_SESSION_UNBOUND', exit: 2 });
|
|
145
|
-
}
|
|
146
|
-
if (stepUp.methods.length > 0) {
|
|
147
|
-
this.error(`Public publishing needs a recent two-factor verification. Run \`${bin} step-up\`, then retry this command.`, { code: 'STEP_UP_PROOF_REQUIRED', exit: 2 });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
131
|
let result;
|
|
152
132
|
try {
|
|
153
133
|
result = await data_provider_1.dataService.setAgentVisibility({
|
|
@@ -17,8 +17,9 @@ export default class HarnessesLeasesBilling extends BaseCommand {
|
|
|
17
17
|
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
18
18
|
};
|
|
19
19
|
static flags: {
|
|
20
|
+
'no-spending-limit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
-
'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
23
|
};
|
|
23
24
|
run(): Promise<void>;
|
|
24
25
|
}
|
|
@@ -33,25 +33,30 @@ class HarnessesLeasesBilling extends base_command_1.BaseCommand {
|
|
|
33
33
|
lease: core_1.Args.string({ ignoreStdin: true, description: 'Lease ID', required: true }),
|
|
34
34
|
};
|
|
35
35
|
static flags = {
|
|
36
|
+
'no-spending-limit': core_1.Flags.boolean({ description: 'Remove a Dedicated Runtime cap. Other hosted products refuse this.', exclusive: ['spending-limit-cents'] }),
|
|
36
37
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
37
38
|
'spending-limit-cents': core_1.Flags.integer({
|
|
38
39
|
description: 'New monthly cap in cents. Refused below what the month has already spent or is projected to. Who pays for a lease is set at create and cannot be changed.',
|
|
39
|
-
|
|
40
|
+
exclusive: ['no-spending-limit'],
|
|
40
41
|
min: 1,
|
|
41
42
|
}),
|
|
42
43
|
};
|
|
43
44
|
async run() {
|
|
44
45
|
this.requireAuth();
|
|
45
46
|
const { args, flags } = await this.parse(HarnessesLeasesBilling);
|
|
47
|
+
if (flags['spending-limit-cents'] === undefined && !flags['no-spending-limit']) {
|
|
48
|
+
this.error('Choose --spending-limit-cents or --no-spending-limit.', { exit: 2 });
|
|
49
|
+
}
|
|
46
50
|
try {
|
|
47
51
|
const response = await (0, harnesses_1.setHarnessLeaseBilling)(args.lease, {
|
|
48
|
-
spendingLimitCents: flags['spending-limit-cents'],
|
|
52
|
+
spendingLimitCents: flags['no-spending-limit'] ? null : flags['spending-limit-cents'],
|
|
49
53
|
});
|
|
50
54
|
if (flags.json) {
|
|
51
55
|
this.log(JSON.stringify(response, null, 2));
|
|
52
56
|
return;
|
|
53
57
|
}
|
|
54
|
-
this.log(`${args.lease} monthly spending cap
|
|
58
|
+
this.log(flags['no-spending-limit'] ? `${args.lease} has no monthly spending cap.` :
|
|
59
|
+
`${args.lease} monthly spending cap set to $${(flags['spending-limit-cents'] / 100).toFixed(2)}.`);
|
|
55
60
|
}
|
|
56
61
|
catch (err) {
|
|
57
62
|
this.handleApiError(err);
|
|
@@ -13,6 +13,7 @@ export default class DedicatedMachinesCreate extends DedicatedLeaseCommand {
|
|
|
13
13
|
'retention-class': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
14
|
'backup-profile': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
15
|
'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
'no-spending-limit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
17
|
repository: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
18
|
workspace: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
19
|
'request-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -41,13 +41,14 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
|
|
|
41
41
|
'backup-profile': core_1.Flags.string({
|
|
42
42
|
description: 'crash-consistent (AWS Backup) or application-consistent (guest fsfreeze while the daemon is reachable; scheduled DLM when that plan is enabled). Defaults to the product catalog value.',
|
|
43
43
|
}),
|
|
44
|
-
// Not required, and no client-side value either. Every lease has a cap:
|
|
45
|
-
// when create names none, the server applies the product's default cap,
|
|
46
|
-
// the "default monthly cap" `machines dedicated catalog` shows. Marking the
|
|
47
|
-
// flag required contradicted that catalog line (OSK-8691), and restating
|
|
48
|
-
// the default here would be the `--storage-gb` bug above.
|
|
49
44
|
'spending-limit-cents': core_1.Flags.integer({
|
|
50
|
-
description:
|
|
45
|
+
description: 'Optional monthly spending cap in cents. New machines default to no cap; restore inherits the source cap.',
|
|
46
|
+
min: 1,
|
|
47
|
+
exclusive: ['no-spending-limit'],
|
|
48
|
+
}),
|
|
49
|
+
'no-spending-limit': core_1.Flags.boolean({
|
|
50
|
+
description: 'Run without a monthly spending cap. Compute and retained storage still bill.',
|
|
51
|
+
exclusive: ['spending-limit-cents'],
|
|
51
52
|
}),
|
|
52
53
|
repository: core_1.Flags.string({
|
|
53
54
|
description: 'Default GitHub repository (owner/repo) for git credentials on the machine. Requires the GitHub App installed for it; a clone of another repository the App covers still works.',
|
|
@@ -81,7 +82,7 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
|
|
|
81
82
|
requestId,
|
|
82
83
|
displayName: flags.name,
|
|
83
84
|
sizePreset,
|
|
84
|
-
monthlySpendingLimitCents: flags['spending-limit-cents'],
|
|
85
|
+
monthlySpendingLimitCents: flags['no-spending-limit'] ? null : flags['spending-limit-cents'],
|
|
85
86
|
regionClass: flags['region-class'],
|
|
86
87
|
storageGb: flags['storage-gb'],
|
|
87
88
|
retentionClass: flags['retention-class'],
|
|
@@ -100,7 +101,7 @@ class DedicatedMachinesCreate extends dedicated_lease_command_1.DedicatedLeaseCo
|
|
|
100
101
|
sizePreset,
|
|
101
102
|
storageGb: flags['storage-gb'],
|
|
102
103
|
retentionClass: flags['retention-class'],
|
|
103
|
-
spendingLimitCents: flags['spending-limit-cents'],
|
|
104
|
+
spendingLimitCents: flags['no-spending-limit'] ? null : flags['spending-limit-cents'],
|
|
104
105
|
});
|
|
105
106
|
if (flags['dry-run']) {
|
|
106
107
|
if (flags.json) {
|
|
@@ -25,6 +25,7 @@ export default class DedicatedMachinesRestore extends DedicatedLeaseCommand {
|
|
|
25
25
|
interval: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
26
|
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
27
27
|
'spending-limit-cents': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
28
|
+
'no-spending-limit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
28
29
|
'request-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
29
30
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
30
31
|
};
|
|
@@ -36,8 +36,13 @@ class DedicatedMachinesRestore extends dedicated_lease_command_1.DedicatedLeaseC
|
|
|
36
36
|
description: 'Display name for the new machine (defaults to the deleted one, "(restored)")',
|
|
37
37
|
}),
|
|
38
38
|
'spending-limit-cents': core_1.Flags.integer({
|
|
39
|
-
description:
|
|
39
|
+
description: 'Optional monthly spending cap in cents. New machines default to no cap; restore inherits the source cap.',
|
|
40
40
|
min: 1,
|
|
41
|
+
exclusive: ['no-spending-limit'],
|
|
42
|
+
}),
|
|
43
|
+
'no-spending-limit': core_1.Flags.boolean({
|
|
44
|
+
description: 'Run without a monthly spending cap. Compute and retained storage still bill.',
|
|
45
|
+
exclusive: ['spending-limit-cents'],
|
|
41
46
|
}),
|
|
42
47
|
'request-id': core_1.Flags.string({
|
|
43
48
|
description: 'Idempotency key. Reuse it to safely retry the same restore.',
|
|
@@ -53,6 +58,7 @@ class DedicatedMachinesRestore extends dedicated_lease_command_1.DedicatedLeaseC
|
|
|
53
58
|
...this.scopeFrom(flags),
|
|
54
59
|
requestId,
|
|
55
60
|
...(flags.name ? { displayName: flags.name } : {}),
|
|
61
|
+
...(flags['no-spending-limit'] ? { monthlySpendingLimitCents: null } : {}),
|
|
56
62
|
...(flags['spending-limit-cents'] !== undefined
|
|
57
63
|
? { monthlySpendingLimitCents: flags['spending-limit-cents'] }
|
|
58
64
|
: {}),
|
|
@@ -2,7 +2,7 @@ import { DedicatedLeaseCommand } from '../../../lib/dedicated-lease-command';
|
|
|
2
2
|
/**
|
|
3
3
|
* `skrr machines dedicated spending-limit` — change the monthly cap after create.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* An optional cap is a spending guardrail on a machine with no wall-clock TTL: when a
|
|
6
6
|
* month's spend reaches it, the platform stops compute and keeps the workspace.
|
|
7
7
|
* It could be set at create and changed from the web lease card, but not from
|
|
8
8
|
* here — so an operator who hit it at 2am had to find a browser to raise it,
|
|
@@ -15,7 +15,8 @@ export default class DedicatedMachinesSpendingLimit extends DedicatedLeaseComman
|
|
|
15
15
|
lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
16
16
|
};
|
|
17
17
|
static flags: {
|
|
18
|
-
cents: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
+
cents: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
'no-spending-limit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
20
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
};
|
|
21
22
|
run(): Promise<void>;
|
|
@@ -6,7 +6,7 @@ const dedicated_machines_1 = require("../../../lib/dedicated-machines");
|
|
|
6
6
|
/**
|
|
7
7
|
* `skrr machines dedicated spending-limit` — change the monthly cap after create.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* An optional cap is a spending guardrail on a machine with no wall-clock TTL: when a
|
|
10
10
|
* month's spend reaches it, the platform stops compute and keeps the workspace.
|
|
11
11
|
* It could be set at create and changed from the web lease card, but not from
|
|
12
12
|
* here — so an operator who hit it at 2am had to find a browser to raise it,
|
|
@@ -28,16 +28,20 @@ class DedicatedMachinesSpendingLimit extends dedicated_lease_command_1.Dedicated
|
|
|
28
28
|
static flags = {
|
|
29
29
|
cents: core_1.Flags.integer({
|
|
30
30
|
description: 'New monthly cap in cents (30000 = $300). Refused below what this month has already spent or is projected to.',
|
|
31
|
-
|
|
31
|
+
exclusive: ['no-spending-limit'],
|
|
32
32
|
min: 1,
|
|
33
33
|
}),
|
|
34
|
+
'no-spending-limit': core_1.Flags.boolean({ description: 'Remove the cap. Compute and storage still bill.', exclusive: ['cents'] }),
|
|
34
35
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
35
36
|
};
|
|
36
37
|
async run() {
|
|
37
38
|
this.requireAuth();
|
|
38
39
|
const { args, flags } = await this.parse(DedicatedMachinesSpendingLimit);
|
|
40
|
+
if (flags.cents === undefined && !flags['no-spending-limit']) {
|
|
41
|
+
this.error('Choose --cents or --no-spending-limit.', { exit: 2 });
|
|
42
|
+
}
|
|
39
43
|
const leaseId = await this.resolveLeaseArg(args.lease);
|
|
40
|
-
const response = await this.callDedicated(() => (0, dedicated_machines_1.setDedicatedRuntimeSpendingLimit)(leaseId, flags.cents, this.scopeFrom(flags)));
|
|
44
|
+
const response = await this.callDedicated(() => (0, dedicated_machines_1.setDedicatedRuntimeSpendingLimit)(leaseId, flags['no-spending-limit'] ? null : flags.cents, this.scopeFrom(flags)));
|
|
41
45
|
if (flags.json) {
|
|
42
46
|
this.log(JSON.stringify(response, null, 2));
|
|
43
47
|
return;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { BaseCommand } from '../base-command';
|
|
2
2
|
/**
|
|
3
|
-
* `skrr step-up` — confirm it is you
|
|
3
|
+
* `skrr step-up` — confirm it is you for a gated Store administration action
|
|
4
4
|
* (OSK-12275).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* that has not recently proved a second factor. This command proves one — the
|
|
6
|
+
* Governed release administration may ask for a recent second factor.
|
|
7
|
+
* Self-service Agent publishing and rollback do not. This command proves one — the
|
|
9
8
|
* code from the account's authenticator app, or an unused backup code — and
|
|
10
9
|
* the verification is bound to THIS login only, for a few minutes. A token
|
|
11
10
|
* stolen from another device, or a browser session, is not covered by it.
|
package/dist/commands/step-up.js
CHANGED
|
@@ -6,18 +6,17 @@ const base_command_1 = require("../base-command");
|
|
|
6
6
|
const prompt_1 = require("../lib/prompt");
|
|
7
7
|
const step_up_1 = require("../lib/step-up");
|
|
8
8
|
/**
|
|
9
|
-
* `skrr step-up` — confirm it is you
|
|
9
|
+
* `skrr step-up` — confirm it is you for a gated Store administration action
|
|
10
10
|
* (OSK-12275).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* that has not recently proved a second factor. This command proves one — the
|
|
12
|
+
* Governed release administration may ask for a recent second factor.
|
|
13
|
+
* Self-service Agent publishing and rollback do not. This command proves one — the
|
|
15
14
|
* code from the account's authenticator app, or an unused backup code — and
|
|
16
15
|
* the verification is bound to THIS login only, for a few minutes. A token
|
|
17
16
|
* stolen from another device, or a browser session, is not covered by it.
|
|
18
17
|
*/
|
|
19
18
|
class StepUp extends base_command_1.BaseCommand {
|
|
20
|
-
static description = 'Confirm it is you with an authenticator code
|
|
19
|
+
static description = 'Confirm it is you with an authenticator code for gated Store administration';
|
|
21
20
|
static examples = [
|
|
22
21
|
'<%= config.bin %> step-up',
|
|
23
22
|
'<%= config.bin %> step-up --otp 123456',
|
|
@@ -34,7 +33,7 @@ class StepUp extends base_command_1.BaseCommand {
|
|
|
34
33
|
exclusive: ['otp', 'status'],
|
|
35
34
|
}),
|
|
36
35
|
status: core_1.Flags.boolean({
|
|
37
|
-
description: 'Show whether this login
|
|
36
|
+
description: 'Show whether this login has a recent second-factor proof',
|
|
38
37
|
}),
|
|
39
38
|
json: core_1.Flags.boolean({ description: 'Output the result as JSON' }),
|
|
40
39
|
};
|
|
@@ -56,15 +55,15 @@ class StepUp extends base_command_1.BaseCommand {
|
|
|
56
55
|
return;
|
|
57
56
|
}
|
|
58
57
|
this.log(status.active
|
|
59
|
-
? `Verified for
|
|
60
|
-
: `
|
|
58
|
+
? `Verified for gated Store actions from this login until ${(0, step_up_1.formatStepUpExpiry)(status.expiresAt)}.`
|
|
59
|
+
: `No recent second-factor proof for this login. Run \`${bin} step-up\` to verify.`);
|
|
61
60
|
return;
|
|
62
61
|
}
|
|
63
62
|
if (!status.sessionBound) {
|
|
64
|
-
this.error(`This credential is not tied to a sign-in session, so it cannot
|
|
63
|
+
this.error(`This credential is not tied to a sign-in session, so it cannot record a second-factor proof. Run \`${bin} login\`, then \`${bin} step-up\`.`, { code: 'STEP_UP_SESSION_UNBOUND', exit: 2 });
|
|
65
64
|
}
|
|
66
65
|
if (status.methods.length === 0) {
|
|
67
|
-
this.error('
|
|
66
|
+
this.error('A second-factor proof needs two-factor authentication, and this account has none. ' +
|
|
68
67
|
`Turn it on in the web app (Settings → Account → Two-factor authentication), then run \`${bin} step-up\`.`, { code: 'STEP_UP_TWO_FACTOR_REQUIRED', exit: 2 });
|
|
69
68
|
}
|
|
70
69
|
let token = flags.otp?.trim();
|
|
@@ -90,7 +89,7 @@ class StepUp extends base_command_1.BaseCommand {
|
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
91
|
const via = result.method === 'backup_code' ? 'a backup code' : 'your authenticator app';
|
|
93
|
-
this.log(`Verified with ${via}. Store
|
|
92
|
+
this.log(`Verified with ${via}. Gated Store actions from this login are allowed until ${(0, step_up_1.formatStepUpExpiry)(result.expiresAt)}.`);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
exports.default = StepUp;
|
|
@@ -244,7 +244,7 @@ export interface CreateDedicatedRuntimeInput extends DedicatedRuntimeScope {
|
|
|
244
244
|
storageGb?: number;
|
|
245
245
|
retentionClass?: string;
|
|
246
246
|
backupProfile?: 'crash-consistent' | 'application-consistent';
|
|
247
|
-
monthlySpendingLimitCents?: number;
|
|
247
|
+
monthlySpendingLimitCents?: number | null;
|
|
248
248
|
repositoryRef?: string;
|
|
249
249
|
}
|
|
250
250
|
export interface DedicatedRuntimeResponse {
|
|
@@ -267,7 +267,7 @@ export declare function dedicatedRuntimeCreateInput(input: {
|
|
|
267
267
|
requestId: string;
|
|
268
268
|
displayName: string;
|
|
269
269
|
sizePreset: string;
|
|
270
|
-
monthlySpendingLimitCents?: number;
|
|
270
|
+
monthlySpendingLimitCents?: number | null;
|
|
271
271
|
regionClass?: string;
|
|
272
272
|
storageGb?: number;
|
|
273
273
|
retentionClass?: string;
|
|
@@ -345,7 +345,7 @@ export declare function growDedicatedRuntime(leaseId: string, storageGb: number,
|
|
|
345
345
|
*/
|
|
346
346
|
export declare function restoreDeletedDedicatedRuntime(leaseId: string, options?: {
|
|
347
347
|
displayName?: string;
|
|
348
|
-
monthlySpendingLimitCents?: number;
|
|
348
|
+
monthlySpendingLimitCents?: number | null;
|
|
349
349
|
requestId?: string;
|
|
350
350
|
} & DedicatedRuntimeScope): Promise<DedicatedRuntimeResponse>;
|
|
351
351
|
/**
|
|
@@ -356,7 +356,7 @@ export declare function restoreDeletedDedicatedRuntime(leaseId: string, options?
|
|
|
356
356
|
* already spent (or is projected to) is refused rather than stopping the machine
|
|
357
357
|
* on the next reconciler pass.
|
|
358
358
|
*/
|
|
359
|
-
export declare function setDedicatedRuntimeSpendingLimit(leaseId: string, spendingLimitCents: number, scope?: DedicatedRuntimeScope): Promise<DedicatedRuntimeResponse>;
|
|
359
|
+
export declare function setDedicatedRuntimeSpendingLimit(leaseId: string, spendingLimitCents: number | null, scope?: DedicatedRuntimeScope): Promise<DedicatedRuntimeResponse>;
|
|
360
360
|
export declare function closeDedicatedRuntimeIncidentHold(leaseId: string, options?: {
|
|
361
361
|
requestId?: string;
|
|
362
362
|
} & DedicatedRuntimeScope): Promise<DedicatedRuntimeResponse>;
|
|
@@ -687,7 +687,7 @@ export interface DedicatedCreateChoices {
|
|
|
687
687
|
sizePreset: string;
|
|
688
688
|
storageGb?: number;
|
|
689
689
|
retentionClass?: string;
|
|
690
|
-
spendingLimitCents?: number;
|
|
690
|
+
spendingLimitCents?: number | null;
|
|
691
691
|
}
|
|
692
692
|
/**
|
|
693
693
|
* What a create is about to buy, in the words and numbers the catalog uses.
|
|
@@ -569,15 +569,16 @@ function dedicatedSpendSummary(lease) {
|
|
|
569
569
|
const billing = lease.billing;
|
|
570
570
|
if (!billing)
|
|
571
571
|
return '-';
|
|
572
|
-
const cap = billing.monthlySpendLimitCents
|
|
572
|
+
const cap = billing.monthlySpendLimitCents !== undefined
|
|
573
|
+
? billing.monthlySpendLimitCents : billing.spendingLimitCents;
|
|
573
574
|
if (billing.spendStatusUnavailable) {
|
|
574
|
-
return `spend unavailable right now · cap ${formatUsdCents(cap)}`;
|
|
575
|
+
return `spend unavailable right now · ${cap === null ? 'no monthly cap' : `cap ${formatUsdCents(cap)}`}`;
|
|
575
576
|
}
|
|
576
577
|
const spent = billing.currentMonthSpendCents;
|
|
577
578
|
const projected = billing.projectedCurrentMonthSpendCents;
|
|
578
579
|
const parts = [
|
|
579
580
|
`${formatUsdCents(spent)} this month for machines of this type`,
|
|
580
|
-
`${formatUsdCents(cap)} cap for this machine`,
|
|
581
|
+
cap === null ? 'no monthly cap' : `${formatUsdCents(cap)} cap for this machine`,
|
|
581
582
|
];
|
|
582
583
|
if (typeof projected === 'number' && projected !== spent) {
|
|
583
584
|
parts.push(`projected ${formatUsdCents(projected)}`);
|
|
@@ -1117,10 +1118,11 @@ function dedicatedCreateSummary(catalog, choices) {
|
|
|
1117
1118
|
? `${retentionClass}${choices.retentionClass === undefined ? ' (catalog default)' : ''}`
|
|
1118
1119
|
: 'server default';
|
|
1119
1120
|
const retains = retentionClass !== undefined && retentionClass !== 'none';
|
|
1120
|
-
const capCents = choices.spendingLimitCents
|
|
1121
|
+
const capCents = choices.spendingLimitCents !== undefined
|
|
1122
|
+
? choices.spendingLimitCents : catalog?.defaultMonthlySpendingLimitCents;
|
|
1121
1123
|
const cap = capCents !== undefined && capCents !== null
|
|
1122
1124
|
? `${formatUsdCents(capCents)} per month${choices.spendingLimitCents === undefined ? ' (catalog default)' : ''}`
|
|
1123
|
-
: 'server default';
|
|
1125
|
+
: capCents === null ? 'no cap — compute and retained storage still bill' : 'server default';
|
|
1124
1126
|
return [
|
|
1125
1127
|
`Create Dedicated Runtime "${choices.name}" — it bills while it runs:`,
|
|
1126
1128
|
` size ${choices.sizePreset} — ${spec}`,
|
|
@@ -416,6 +416,8 @@ function socketOptions(token) {
|
|
|
416
416
|
path: '/ws/agentic',
|
|
417
417
|
auth: {
|
|
418
418
|
token,
|
|
419
|
+
// This proves origin only; the normal CLI token retains full execution authority.
|
|
420
|
+
...(process.env.OVERSKY_SESSION_TOKEN ? { executionOriginToken: process.env.OVERSKY_SESSION_TOKEN } : {}),
|
|
419
421
|
...(0, data_provider_1.getClientIdentityAuth)(),
|
|
420
422
|
clientPlatform: 'cli',
|
|
421
423
|
clientOs: process.platform === 'darwin' ? 'macos' : process.platform,
|
package/dist/lib/file-mime.js
CHANGED
|
@@ -63,6 +63,10 @@ const MIME_TYPES = {
|
|
|
63
63
|
'.jpg': 'image/jpeg',
|
|
64
64
|
'.js': 'text/javascript',
|
|
65
65
|
'.json': 'application/json',
|
|
66
|
+
// A stream of JSON values is text evidence, not one JSON document.
|
|
67
|
+
// Preserve the original bytes and filename rather than forcing an array.
|
|
68
|
+
'.jsonl': 'text/plain',
|
|
69
|
+
'.ndjson': 'text/plain',
|
|
66
70
|
'.log': 'text/plain',
|
|
67
71
|
'.m4a': 'audio/m4a',
|
|
68
72
|
'.m4v': 'video/m4v',
|
package/dist/lib/harnesses.d.ts
CHANGED
|
@@ -134,6 +134,11 @@ export interface HarnessLease {
|
|
|
134
134
|
billing?: {
|
|
135
135
|
billableState?: string;
|
|
136
136
|
monthlySpendLimitCents?: number | null;
|
|
137
|
+
/** Monthly total shared by all machines of this product in the billing account. */
|
|
138
|
+
spendScope?: {
|
|
139
|
+
kind: 'owner_product' | 'workspace_product';
|
|
140
|
+
machineProduct: string;
|
|
141
|
+
};
|
|
137
142
|
currentMonthSpendCents?: number;
|
|
138
143
|
openUsageEstimateCents?: number;
|
|
139
144
|
projectedCurrentMonthSpendCents?: number;
|
package/dist/lib/step-up.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What the CLI says about step-up authentication
|
|
2
|
+
* What the CLI says about step-up authentication for gated Store actions
|
|
3
3
|
* (OSK-12275).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* CLI token alone: it answers `STEP_UP_REQUIRED` until this same login has
|
|
8
|
-
* proved a second factor through `skrr step-up`. The vocabulary (the code, the
|
|
5
|
+
* An explicitly gated Store action answers `STEP_UP_REQUIRED` until this login
|
|
6
|
+
* has proved a second factor through `skrr step-up`. The vocabulary (the code, the
|
|
9
7
|
* reasons, the window) is `@skrr-ai/data-provider`'s; only the sentences live
|
|
10
8
|
* here, because each reason has a different next step and a refusal that says
|
|
11
|
-
* "forbidden" would leave
|
|
9
|
+
* "forbidden" would leave the caller guessing which one applies.
|
|
12
10
|
*/
|
|
13
11
|
import type { TStepUpChallenge } from '@skrr-ai/data-provider';
|
|
14
12
|
/**
|
package/dist/lib/step-up.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.describeStepUpRequired = describeStepUpRequired;
|
|
4
4
|
exports.formatStepUpExpiry = formatStepUpExpiry;
|
|
5
|
-
const NOTHING_CHANGED = 'Nothing
|
|
5
|
+
const NOTHING_CHANGED = 'Nothing changed.';
|
|
6
6
|
function windowPhrase(windowSeconds) {
|
|
7
7
|
const minutes = Math.max(1, Math.round((windowSeconds ?? 600) / 60));
|
|
8
8
|
return `${minutes} minute${minutes === 1 ? '' : 's'}`;
|
|
@@ -17,18 +17,18 @@ function describeStepUpRequired(challenge, bin) {
|
|
|
17
17
|
switch (challenge?.reason) {
|
|
18
18
|
case 'two_factor_not_enabled':
|
|
19
19
|
return [
|
|
20
|
-
`
|
|
20
|
+
`This action needs two-factor authentication, and this account has none. ${NOTHING_CHANGED}`,
|
|
21
21
|
'Turn it on in the web app (Settings → Account → Two-factor authentication),',
|
|
22
22
|
`then run \`${bin} step-up\` and run this command again within ${window}.`,
|
|
23
23
|
].join('\n');
|
|
24
24
|
case 'credential_not_session_bound':
|
|
25
25
|
return [
|
|
26
|
-
`This credential is not tied to a sign-in session, so it cannot
|
|
26
|
+
`This credential is not tied to a sign-in session, so it cannot record a second-factor proof. ${NOTHING_CHANGED}`,
|
|
27
27
|
`Run \`${bin} login\`, then \`${bin} step-up\`, and run this command again within ${window}.`,
|
|
28
28
|
].join('\n');
|
|
29
29
|
default:
|
|
30
30
|
return [
|
|
31
|
-
`
|
|
31
|
+
`This action needs a fresh identity check; this login alone is not enough. ${NOTHING_CHANGED}`,
|
|
32
32
|
`Run \`${bin} step-up\` and enter the code from your authenticator app, then run this command again within ${window}.`,
|
|
33
33
|
].join('\n');
|
|
34
34
|
}
|