@skrr-ai/cli 0.1.51 → 0.1.52
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 +7 -0
- package/dist/base-command.js +38 -8
- package/dist/commands/agents/create.js +8 -4
- package/dist/commands/agents/update.d.ts +23 -0
- package/dist/commands/agents/update.js +54 -9
- package/dist/commands/commitments/next-tick.js +9 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/daemon/config.js +16 -1
- package/dist/commands/doctor.js +8 -1
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.js +26 -7
- package/dist/commands/logout.js +3 -0
- package/dist/commands/machines/dedicated/show.js +25 -2
- package/dist/commands/tasks/create.d.ts +5 -0
- package/dist/commands/tasks/create.js +26 -6
- package/dist/commands/tasks/start.d.ts +14 -0
- package/dist/commands/tasks/start.js +31 -1
- package/dist/commands/tasks/update.js +8 -1
- package/dist/commands/whoami.js +5 -3
- package/dist/lib/agent-config.d.ts +2 -0
- package/dist/lib/agent-config.js +14 -10
- package/dist/lib/agentic-stream.d.ts +20 -0
- package/dist/lib/agentic-stream.js +39 -5
- package/dist/lib/auth-failure.js +4 -1
- package/dist/lib/auth-storage.d.ts +24 -0
- package/dist/lib/auth-storage.js +342 -11
- package/dist/lib/cli-id.d.ts +22 -0
- package/dist/lib/cli-id.js +56 -0
- package/dist/lib/cli-identity.d.ts +49 -0
- package/dist/lib/cli-identity.js +159 -0
- package/dist/lib/commitments.d.ts +4 -0
- package/dist/lib/commitments.js +12 -1
- package/dist/lib/config.d.ts +17 -0
- package/dist/lib/config.js +15 -3
- package/dist/lib/daemonBroker.d.ts +26 -4
- package/dist/lib/daemonBroker.js +82 -10
- package/dist/lib/daemonBrokerRefusal.d.ts +51 -0
- package/dist/lib/daemonBrokerRefusal.js +127 -0
- package/dist/lib/dedicated-machines.d.ts +33 -2
- package/dist/lib/dedicated-machines.js +50 -1
- package/dist/lib/delegated-cli.js +16 -0
- package/dist/lib/first-party-harness-broker.js +6 -4
- package/dist/lib/keychain.d.ts +39 -0
- package/dist/lib/keychain.js +228 -39
- package/dist/lib/label-ref.d.ts +34 -0
- package/dist/lib/label-ref.js +68 -1
- package/dist/lib/login.js +24 -12
- package/dist/lib/oauthLogin.js +2 -8
- package/dist/lib/refresh.js +6 -2
- package/dist/lib/tasks.js +33 -8
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3138 -3100
- package/oclif.manifest.json +36364 -36355
- package/package.json +1 -1
package/dist/base-command.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type AssigneeResolution, type MemberKind, type SpaceMember } from './li
|
|
|
4
4
|
import { type CliConfig } from './lib/config';
|
|
5
5
|
import { type OutboxMethod } from './lib/outbox';
|
|
6
6
|
import { type ResolvedCredential } from './lib/credential-resolver';
|
|
7
|
+
import { type BrokerRefusalDescription } from './lib/daemonBrokerRefusal';
|
|
7
8
|
import { type DefaultAgentResolution } from './lib/tasks';
|
|
8
9
|
export declare function assigneePayloadNeedsResolution(payload: Record<string, unknown>): boolean;
|
|
9
10
|
/**
|
|
@@ -68,6 +69,12 @@ export declare abstract class BaseCommand extends Command {
|
|
|
68
69
|
*/
|
|
69
70
|
protected resolvedCredential: ResolvedCredential;
|
|
70
71
|
protected bareMode: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Set when the auto-broker reached a live daemon and it REFUSED the hand-off.
|
|
74
|
+
* `requireAuth()` reports it instead of a bare "Not signed in", because the
|
|
75
|
+
* daemon's reason decides the repair (OSK-12149, OSK-12151).
|
|
76
|
+
*/
|
|
77
|
+
protected brokerRefusal: BrokerRefusalDescription | null;
|
|
71
78
|
init(): Promise<void>;
|
|
72
79
|
/**
|
|
73
80
|
* Read only the two global base flags. A narrow parser — oclif's
|
package/dist/base-command.js
CHANGED
|
@@ -35,6 +35,7 @@ const task_ref_resolver_1 = require("./lib/task-ref-resolver");
|
|
|
35
35
|
const space_resolver_1 = require("./lib/space-resolver");
|
|
36
36
|
const projects_1 = require("./lib/projects");
|
|
37
37
|
const config_1 = require("./lib/config");
|
|
38
|
+
const cli_id_1 = require("./lib/cli-id");
|
|
38
39
|
const keychain_2 = require("./lib/keychain");
|
|
39
40
|
const outbox_1 = require("./lib/outbox");
|
|
40
41
|
const node_adapter_1 = require("./lib/node-adapter");
|
|
@@ -42,6 +43,7 @@ const migrate_1 = require("./lib/migrate");
|
|
|
42
43
|
const auth_core_init_1 = require("./lib/auth-core-init");
|
|
43
44
|
const credential_resolver_1 = require("./lib/credential-resolver");
|
|
44
45
|
const daemonBroker_1 = require("./lib/daemonBroker");
|
|
46
|
+
const daemonBrokerRefusal_1 = require("./lib/daemonBrokerRefusal");
|
|
45
47
|
const refresh_1 = require("./lib/refresh");
|
|
46
48
|
const auth_storage_1 = require("./lib/auth-storage");
|
|
47
49
|
const update_check_1 = require("./lib/update-check");
|
|
@@ -131,6 +133,12 @@ class BaseCommand extends core_1.Command {
|
|
|
131
133
|
*/
|
|
132
134
|
resolvedCredential;
|
|
133
135
|
bareMode = false;
|
|
136
|
+
/**
|
|
137
|
+
* Set when the auto-broker reached a live daemon and it REFUSED the hand-off.
|
|
138
|
+
* `requireAuth()` reports it instead of a bare "Not signed in", because the
|
|
139
|
+
* daemon's reason decides the repair (OSK-12149, OSK-12151).
|
|
140
|
+
*/
|
|
141
|
+
brokerRefusal = null;
|
|
134
142
|
async init() {
|
|
135
143
|
await super.init();
|
|
136
144
|
// The profile is resolved from raw argv, BEFORE oclif parses anything.
|
|
@@ -269,11 +277,13 @@ class BaseCommand extends core_1.Command {
|
|
|
269
277
|
// skrr command reads it through the normal resolver chain — no
|
|
270
278
|
// per-command broker round-trip.
|
|
271
279
|
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
280
|
+
// A broker failure leaves resolvedCredential null and the command's own
|
|
281
|
+
// `requireAuth()` surfaces the error a moment later. It is quiet only when
|
|
282
|
+
// no daemon was there to ask. A daemon that ANSWERED and refused is kept
|
|
283
|
+
// in `brokerRefusal`, and `requireAuth()` reports its reason and the
|
|
284
|
+
// repair that reason implies — "Not signed in. Run `skrr login`" was true
|
|
285
|
+
// and pointed at the wrong fix (OSK-12149). Rate limiting and a mismatched
|
|
286
|
+
// baseURL keep their own one-line hints below.
|
|
277
287
|
if (!this.resolvedCredential.token) {
|
|
278
288
|
const autoResult = await (0, daemonBroker_1.maybeAutoBroker)({
|
|
279
289
|
cliConfig: this.cliConfig,
|
|
@@ -318,6 +328,9 @@ class BaseCommand extends core_1.Command {
|
|
|
318
328
|
kind: (0, auth_core_1.classifyTokenKind)(accessToken),
|
|
319
329
|
};
|
|
320
330
|
}
|
|
331
|
+
else if ((this.brokerRefusal = (0, daemonBrokerRefusal_1.describeBrokerRefusal)(autoResult.outcome, this.config.bin))) {
|
|
332
|
+
// Reported by requireAuth(); nothing to print here.
|
|
333
|
+
}
|
|
321
334
|
else if (autoResult.outcome.reason === 'rate_limited') {
|
|
322
335
|
// Otherwise the only thing the user sees is "Not signed in", which is
|
|
323
336
|
// true and points at the wrong repair: they are signed in, the local
|
|
@@ -340,7 +353,12 @@ class BaseCommand extends core_1.Command {
|
|
|
340
353
|
}
|
|
341
354
|
}
|
|
342
355
|
if (!this.resolvedCredential.token && ignoredStoredCredential) {
|
|
343
|
-
|
|
356
|
+
const foreign = (0, auth_storage_1.describeForeignCredential)(ignoredStoredCredential);
|
|
357
|
+
if (foreign) {
|
|
358
|
+
process.stderr.write(`[skrr] ${foreign} Run \`skrr login\` to sign this configuration in; the other ` +
|
|
359
|
+
`configuration keeps its own credential.\n`);
|
|
360
|
+
}
|
|
361
|
+
else if (ignoredStoredCredential.originStatus === 'mismatch') {
|
|
344
362
|
process.stderr.write(`[skrr] Ignoring stored credentials issued by ${ignoredStoredCredential.storedServerOrigin}; ` +
|
|
345
363
|
`skrr is configured for ${this.cliConfig.baseURL}. Run \`skrr login --base-url ${this.cliConfig.baseURL}\` ` +
|
|
346
364
|
`or switch back to the issuing server.\n`);
|
|
@@ -616,6 +634,18 @@ class BaseCommand extends core_1.Command {
|
|
|
616
634
|
/** Abort the command with a helpful message if the user isn't signed in. */
|
|
617
635
|
requireAuth() {
|
|
618
636
|
if (!this.resolvedCredential?.token) {
|
|
637
|
+
const refusal = this.brokerRefusal;
|
|
638
|
+
if (refusal) {
|
|
639
|
+
// Same code as the plain case — scripts branch on NOT_SIGNED_IN — with
|
|
640
|
+
// the daemon's answer in the message and in `details.broker`.
|
|
641
|
+
this.failWithCliError({
|
|
642
|
+
message: `Not signed in. ${refusal.headline}\n→ ${refusal.remedy}`,
|
|
643
|
+
code: 'NOT_SIGNED_IN',
|
|
644
|
+
exit: 2,
|
|
645
|
+
retryable: refusal.transient,
|
|
646
|
+
details: { broker: refusal.broker, suggestion: refusal.remedy },
|
|
647
|
+
});
|
|
648
|
+
}
|
|
619
649
|
this.failWithCliError({
|
|
620
650
|
message: `Not signed in. Run \`${this.config.bin} login\` first.`,
|
|
621
651
|
code: 'NOT_SIGNED_IN',
|
|
@@ -1011,7 +1041,7 @@ class BaseCommand extends core_1.Command {
|
|
|
1011
1041
|
? null
|
|
1012
1042
|
: (0, auth_failure_1.describeRefreshRefusal)(readRefreshRefusal(), {
|
|
1013
1043
|
bin: this.config.bin,
|
|
1014
|
-
cliId: this.cliConfig
|
|
1044
|
+
cliId: this.cliConfig ? (0, cli_id_1.resolveCliId)(this.cliConfig) : undefined,
|
|
1015
1045
|
});
|
|
1016
1046
|
this.failWithCliError({
|
|
1017
1047
|
message: say(presentedCiToken
|
|
@@ -1309,7 +1339,7 @@ class BaseCommand extends core_1.Command {
|
|
|
1309
1339
|
if (e.status === 401) {
|
|
1310
1340
|
const refusal = (0, auth_failure_1.describeRefreshRefusal)(readRefreshRefusal(), {
|
|
1311
1341
|
bin: this.config.bin,
|
|
1312
|
-
cliId: this.cliConfig
|
|
1342
|
+
cliId: this.cliConfig ? (0, cli_id_1.resolveCliId)(this.cliConfig) : undefined,
|
|
1313
1343
|
});
|
|
1314
1344
|
const why = refusal
|
|
1315
1345
|
? refusal.message.replace(/\s*Run `[^`]+` to sign in again\.$/, '')
|
|
@@ -246,22 +246,26 @@ class AgentsCreate extends base_command_1.BaseCommand {
|
|
|
246
246
|
'09:00-18:00@America/Los_Angeles',
|
|
247
247
|
}),
|
|
248
248
|
// Retired (OSK-11975): a Commitment turn works under the Agent's own
|
|
249
|
-
// permissions,
|
|
249
|
+
// permissions. Hidden, warned on use, and never sent (OSK-12068) — see
|
|
250
|
+
// `mergeAgentConfigFlags`. Kept parseable so an old script still runs.
|
|
250
251
|
'reasoning-tool-escape': core_1.Flags.string({
|
|
251
|
-
description: "
|
|
252
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
252
253
|
options: [...agent_config_1.REASONING_TOOL_ESCAPES],
|
|
253
254
|
multiple: true,
|
|
254
255
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
256
|
+
hidden: true,
|
|
255
257
|
}),
|
|
256
258
|
'commitment-responsibility': core_1.Flags.string({
|
|
257
|
-
description: "
|
|
259
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
258
260
|
options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
|
|
259
261
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
262
|
+
hidden: true,
|
|
260
263
|
}),
|
|
261
264
|
'commitment-responsibility-ceiling': core_1.Flags.string({
|
|
262
|
-
description: "
|
|
265
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
263
266
|
options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
|
|
264
267
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
268
|
+
hidden: true,
|
|
265
269
|
}),
|
|
266
270
|
'max-turns': core_1.Flags.integer({ description: 'Maximum turns in one agent run (1-500)' }),
|
|
267
271
|
'tool-resources': core_1.Flags.string({
|
|
@@ -55,6 +55,29 @@ export default class AgentsUpdate extends BaseCommand {
|
|
|
55
55
|
};
|
|
56
56
|
run(): Promise<void>;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The per-Agent mutation fence's "another request holds this right now"
|
|
60
|
+
* refusals. `updateAgent` takes a short lease on the Agent before it writes, so
|
|
61
|
+
* a concurrent writer — or, until OSK-12138, a lagging replica read of a lease
|
|
62
|
+
* row the same request had just created — refuses the update with one of these.
|
|
63
|
+
* Nothing was written, so repeating the identical PATCH is safe.
|
|
64
|
+
*/
|
|
65
|
+
export declare const AGENT_FENCE_RETRY_CODES: ReadonlySet<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Run an Agent write, and repeat it ONCE if the fence refused it as transient.
|
|
68
|
+
*
|
|
69
|
+
* Retried only when the server said both things: a fence code AND
|
|
70
|
+
* `retryable: true`. `MARKETPLACE_SOURCE_LIFECYCLE_BUSY` also covers a source
|
|
71
|
+
* that is being deleted or published, where the server answers
|
|
72
|
+
* `retryable: false`, and those must surface rather than be repeated. One retry,
|
|
73
|
+
* after a short pause, because the dogfooded case (OSK-12138) cleared on the
|
|
74
|
+
* first identical retry; anything that survives it is worth showing.
|
|
75
|
+
*
|
|
76
|
+
* Exported for tests.
|
|
77
|
+
*/
|
|
78
|
+
export declare function retryOnceOnAgentFence<T>(operation: () => Promise<T>, { delayMs }?: {
|
|
79
|
+
delayMs?: number;
|
|
80
|
+
}): Promise<T>;
|
|
58
81
|
/**
|
|
59
82
|
* What changing the agent's default permission mode actually changed
|
|
60
83
|
* (OSK-12124). It is the AGENT's default, so it governs every conversation,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENT_FENCE_RETRY_CODES = void 0;
|
|
4
|
+
exports.retryOnceOnAgentFence = retryOnceOnAgentFence;
|
|
3
5
|
exports.defaultPermissionModeNote = defaultPermissionModeNote;
|
|
4
6
|
const core_1 = require("@oclif/core");
|
|
5
7
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
@@ -99,22 +101,26 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
99
101
|
'09:00-18:00@America/Los_Angeles',
|
|
100
102
|
}),
|
|
101
103
|
// Retired (OSK-11975): a Commitment turn works under the Agent's own
|
|
102
|
-
// permissions,
|
|
104
|
+
// permissions. Hidden, warned on use, and never sent (OSK-12068) — see
|
|
105
|
+
// `mergeAgentConfigFlags`. Kept parseable so an old script still runs.
|
|
103
106
|
'reasoning-tool-escape': core_1.Flags.string({
|
|
104
|
-
description: "
|
|
107
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
105
108
|
options: [...agent_config_1.REASONING_TOOL_ESCAPES],
|
|
106
109
|
multiple: true,
|
|
107
110
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
111
|
+
hidden: true,
|
|
108
112
|
}),
|
|
109
113
|
'commitment-responsibility': core_1.Flags.string({
|
|
110
|
-
description: "
|
|
114
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
111
115
|
options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
|
|
112
116
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
117
|
+
hidden: true,
|
|
113
118
|
}),
|
|
114
119
|
'commitment-responsibility-ceiling': core_1.Flags.string({
|
|
115
|
-
description: "
|
|
120
|
+
description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
|
|
116
121
|
options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
|
|
117
122
|
deprecated: { message: agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED },
|
|
123
|
+
hidden: true,
|
|
118
124
|
}),
|
|
119
125
|
'max-turns': core_1.Flags.integer({ description: 'Maximum turns in one agent run (1-500)' }),
|
|
120
126
|
'tool-resources': core_1.Flags.string({
|
|
@@ -184,16 +190,18 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
184
190
|
this.error(err.message, { exit: 1 });
|
|
185
191
|
}
|
|
186
192
|
if (Object.keys(payload).length === 0) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
const retired = agent_config_1.RETIRED_COMMITMENT_PERMISSION_FLAGS.filter((flag) => flags[flag] !== undefined);
|
|
194
|
+
this.error(retired.length > 0
|
|
195
|
+
? `Nothing to update: ${retired.map((flag) => `--${flag}`).join(', ')} ` +
|
|
196
|
+
`${retired.length === 1 ? 'is' : 'are'} retired and not sent. ${agent_config_1.COMMITMENT_PERMISSION_FLAG_RETIRED}`
|
|
197
|
+
: 'Nothing to update. Pass at least one flag (e.g. --name) or --from-json.', { exit: 1 });
|
|
190
198
|
}
|
|
191
199
|
let updated;
|
|
192
200
|
try {
|
|
193
|
-
updated = await data_provider_1.dataService.updateAgent({
|
|
201
|
+
updated = await retryOnceOnAgentFence(() => data_provider_1.dataService.updateAgent({
|
|
194
202
|
agent_id: args.id,
|
|
195
203
|
data: payload,
|
|
196
|
-
});
|
|
204
|
+
}));
|
|
197
205
|
}
|
|
198
206
|
catch (err) {
|
|
199
207
|
this.handleApiError(err);
|
|
@@ -222,6 +230,43 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
222
230
|
}
|
|
223
231
|
}
|
|
224
232
|
exports.default = AgentsUpdate;
|
|
233
|
+
/**
|
|
234
|
+
* The per-Agent mutation fence's "another request holds this right now"
|
|
235
|
+
* refusals. `updateAgent` takes a short lease on the Agent before it writes, so
|
|
236
|
+
* a concurrent writer — or, until OSK-12138, a lagging replica read of a lease
|
|
237
|
+
* row the same request had just created — refuses the update with one of these.
|
|
238
|
+
* Nothing was written, so repeating the identical PATCH is safe.
|
|
239
|
+
*/
|
|
240
|
+
exports.AGENT_FENCE_RETRY_CODES = new Set([
|
|
241
|
+
'MARKETPLACE_SOURCE_LIFECYCLE_RACE',
|
|
242
|
+
'MARKETPLACE_SOURCE_LIFECYCLE_BUSY',
|
|
243
|
+
'MARKETPLACE_SOURCE_LIFECYCLE_CAS_LOST',
|
|
244
|
+
]);
|
|
245
|
+
/**
|
|
246
|
+
* Run an Agent write, and repeat it ONCE if the fence refused it as transient.
|
|
247
|
+
*
|
|
248
|
+
* Retried only when the server said both things: a fence code AND
|
|
249
|
+
* `retryable: true`. `MARKETPLACE_SOURCE_LIFECYCLE_BUSY` also covers a source
|
|
250
|
+
* that is being deleted or published, where the server answers
|
|
251
|
+
* `retryable: false`, and those must surface rather than be repeated. One retry,
|
|
252
|
+
* after a short pause, because the dogfooded case (OSK-12138) cleared on the
|
|
253
|
+
* first identical retry; anything that survives it is worth showing.
|
|
254
|
+
*
|
|
255
|
+
* Exported for tests.
|
|
256
|
+
*/
|
|
257
|
+
async function retryOnceOnAgentFence(operation, { delayMs = 750 } = {}) {
|
|
258
|
+
try {
|
|
259
|
+
return await operation();
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
const { code, body } = (0, base_command_1.apiRefusal)(err);
|
|
263
|
+
if (!code || !exports.AGENT_FENCE_RETRY_CODES.has(code) || body?.retryable !== true)
|
|
264
|
+
throw err;
|
|
265
|
+
if (delayMs > 0)
|
|
266
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
267
|
+
return operation();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
225
270
|
/**
|
|
226
271
|
* What changing the agent's default permission mode actually changed
|
|
227
272
|
* (OSK-12124). It is the AGENT's default, so it governs every conversation,
|
|
@@ -86,10 +86,18 @@ function formatNextTick(projection) {
|
|
|
86
86
|
// dispatch-refusal loop can't hide behind an on_track projection.
|
|
87
87
|
if (projection.health || projection.liveness) {
|
|
88
88
|
const census = projection.liveness?.terminalDispositionCensus;
|
|
89
|
+
const plural = (count) => (count === 1 ? '' : 's');
|
|
89
90
|
const detail = [
|
|
90
91
|
projection.liveness?.diagnosisKind,
|
|
91
92
|
census?.workFailures
|
|
92
|
-
? `${census.workFailures} recent run failure${census.workFailures
|
|
93
|
+
? `${census.workFailures} recent run failure${plural(census.workFailures)}`
|
|
94
|
+
: null,
|
|
95
|
+
// A run that never started is not a failure of the work (OSK-12137).
|
|
96
|
+
census?.machineFailures
|
|
97
|
+
? `${census.machineFailures} run${plural(census.machineFailures)} never started`
|
|
98
|
+
: null,
|
|
99
|
+
census?.contractFailures
|
|
100
|
+
? `${census.contractFailures} result${plural(census.contractFailures)} rejected`
|
|
93
101
|
: null,
|
|
94
102
|
]
|
|
95
103
|
.filter(Boolean)
|
package/dist/commands/context.js
CHANGED
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../base-command");
|
|
6
6
|
const config_1 = require("../lib/config");
|
|
7
|
+
const cli_id_1 = require("../lib/cli-id");
|
|
7
8
|
const keychain_1 = require("../lib/keychain");
|
|
8
9
|
const cli_runtime_1 = require("../lib/cli-runtime");
|
|
9
10
|
const runtime_context_1 = require("../lib/runtime-context");
|
|
@@ -48,7 +49,7 @@ class Context extends base_command_1.BaseCommand {
|
|
|
48
49
|
profile: (0, keychain_1.getActiveProfile)(),
|
|
49
50
|
config: {
|
|
50
51
|
path: (0, config_1.getConfigPath)(),
|
|
51
|
-
cliId: this.cliConfig
|
|
52
|
+
cliId: (0, cli_id_1.resolveCliId)(this.cliConfig) ?? null,
|
|
52
53
|
},
|
|
53
54
|
auth: {
|
|
54
55
|
signedIn: Boolean(this.resolvedCredential?.token),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
|
|
5
|
+
const live_runtime_binary_1 = require("../../lib/live-runtime-binary");
|
|
5
6
|
/**
|
|
6
7
|
* `skrr daemon config` — set this machine's daemon configuration, including
|
|
7
8
|
* each tool category's permission mode (OSK-12099). Before this the only way
|
|
@@ -23,7 +24,21 @@ class DaemonConfig extends core_1.Command {
|
|
|
23
24
|
// flag this wrapper has not redeclared (OSK-296).
|
|
24
25
|
const argv = this.argv;
|
|
25
26
|
try {
|
|
26
|
-
|
|
27
|
+
// Config writes the file the LIVE daemon reloads, so it must be parsed by
|
|
28
|
+
// the live daemon's own binary — the same routing status uses (OSK-12102).
|
|
29
|
+
// Forwarding to the first `skrrd` on PATH let an older build accept
|
|
30
|
+
// `--auto-approve-bash banana` as false and reject `--mode`, then write
|
|
31
|
+
// the live daemon's config anyway (OSK-12099 verifier).
|
|
32
|
+
const pathBinary = (0, exec_runtime_binary_1.findRuntimeBinary)();
|
|
33
|
+
const decision = pathBinary
|
|
34
|
+
? (0, live_runtime_binary_1.decideStatusBinary)(pathBinary, (0, live_runtime_binary_1.profileFromArgs)(argv), (0, live_runtime_binary_1.defaultLiveRuntimeDeps)())
|
|
35
|
+
: null;
|
|
36
|
+
const note = decision ? (0, live_runtime_binary_1.describeStatusBinary)(decision) : null;
|
|
37
|
+
if (note)
|
|
38
|
+
process.stderr.write(`${note}\n`);
|
|
39
|
+
const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(['config', ...argv], {
|
|
40
|
+
binary: decision?.binary,
|
|
41
|
+
});
|
|
27
42
|
this.exit(code);
|
|
28
43
|
}
|
|
29
44
|
catch (err) {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -43,6 +43,7 @@ const auth_core_1 = require("@skrr-ai/auth-core");
|
|
|
43
43
|
const base_command_1 = require("../base-command");
|
|
44
44
|
const auth_storage_1 = require("../lib/auth-storage");
|
|
45
45
|
const config_1 = require("../lib/config");
|
|
46
|
+
const cli_id_1 = require("../lib/cli-id");
|
|
46
47
|
const keychain_1 = require("../lib/keychain");
|
|
47
48
|
const cli_runtime_1 = require("../lib/cli-runtime");
|
|
48
49
|
const command_manifest_1 = require("../lib/command-manifest");
|
|
@@ -272,6 +273,9 @@ class Doctor extends base_command_1.BaseCommand {
|
|
|
272
273
|
? ['auth_token_present_but_user_lookup_failed']
|
|
273
274
|
: []),
|
|
274
275
|
...(!checks.authToken ? ['not_signed_in_or_no_token_available'] : []),
|
|
276
|
+
...(credential.ownerStatus === 'foreign'
|
|
277
|
+
? ['stored_credential_belongs_to_another_skrr_config_run_skrr_login']
|
|
278
|
+
: []),
|
|
275
279
|
];
|
|
276
280
|
const payload = {
|
|
277
281
|
ok: issues.length === 0,
|
|
@@ -286,13 +290,16 @@ class Doctor extends base_command_1.BaseCommand {
|
|
|
286
290
|
profile: (0, keychain_1.getActiveProfile)(),
|
|
287
291
|
config: {
|
|
288
292
|
path: (0, config_1.getConfigPath)(),
|
|
289
|
-
cliId: this.cliConfig
|
|
293
|
+
cliId: (0, cli_id_1.resolveCliId)(this.cliConfig) ?? null,
|
|
290
294
|
},
|
|
291
295
|
auth: {
|
|
292
296
|
signedIn: Boolean(account),
|
|
293
297
|
tokenSource: this.credentialSourceLabel(),
|
|
294
298
|
tokenKind: this.resolvedCredential?.kind ?? null,
|
|
295
299
|
refreshAvailable: Boolean(credential.bundle?.refreshToken),
|
|
300
|
+
// OSK-12135 — why a stored credential was not used, when it belongs to
|
|
301
|
+
// another skrr config on this machine. Null when there is nothing to say.
|
|
302
|
+
storedCredentialOwner: (0, auth_storage_1.describeForeignCredential)(credential),
|
|
296
303
|
accessExpiresAt: accessExpiresAt ? new Date(accessExpiresAt).toISOString() : null,
|
|
297
304
|
accessExpired: accessExpiresAt ? accessExpiresAt <= now : null,
|
|
298
305
|
refreshExpiresAt: refreshExpiresAt ? new Date(refreshExpiresAt).toISOString() : null,
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -44,7 +44,8 @@ export default class Login extends BaseCommand {
|
|
|
44
44
|
*
|
|
45
45
|
* `cliId` resolution priority:
|
|
46
46
|
* 1. `OVERSKY_CLI_ID` env var — explicit, wins over everything.
|
|
47
|
-
* 2. `cliConfig
|
|
47
|
+
* 2. `resolveCliId(cliConfig)` — the id a prior `skrr login` of this
|
|
48
|
+
* profile presents (its own, or the machine's).
|
|
48
49
|
* Honored so a same-machine restore "just works" without forcing
|
|
49
50
|
* the operator to re-export the cliId.
|
|
50
51
|
* 3. error — without a cliId the refresh row binding can't be matched
|
package/dist/commands/login.js
CHANGED
|
@@ -7,6 +7,7 @@ const base_command_1 = require("../base-command");
|
|
|
7
7
|
const daemonHandoff_1 = require("../lib/daemonHandoff");
|
|
8
8
|
const daemon_setup_1 = require("../lib/daemon-setup");
|
|
9
9
|
const config_1 = require("../lib/config");
|
|
10
|
+
const cli_id_1 = require("../lib/cli-id");
|
|
10
11
|
const keychain_1 = require("../lib/keychain");
|
|
11
12
|
const login_1 = require("../lib/login");
|
|
12
13
|
const device_code_1 = require("../lib/device-code");
|
|
@@ -40,16 +41,33 @@ const prompt_1 = require("../lib/prompt");
|
|
|
40
41
|
* `cliLoginWithRefreshToken` for the full rationale.
|
|
41
42
|
*/
|
|
42
43
|
class Login extends base_command_1.BaseCommand {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// OSK-12152 — the help used to describe only the browser flow. `skrr login`
|
|
45
|
+
// asks the local daemon FIRST, and nothing said so; nor that what it stores
|
|
46
|
+
// differs from what plain commands borrow from the daemon on their own.
|
|
47
|
+
static description = 'Sign in to a skrr instance and store a durable session for this profile.\n\n' +
|
|
48
|
+
'It tries, in order:\n' +
|
|
49
|
+
' 1. the skrr background service on this computer, when one is running and signed in to the ' +
|
|
50
|
+
'same server — it mints this profile a CLI session with no browser;\n' +
|
|
51
|
+
' 2. a browser sign-in through a localhost callback (PKCE);\n' +
|
|
52
|
+
' 3. a device code — under SSH, with --device-code, or when the callback cannot run.\n' +
|
|
53
|
+
'If the background service answers but refuses, skrr says why before moving on.\n\n' +
|
|
54
|
+
'What it stores: a refresh session in this profile’s credential store (the macOS Keychain, ' +
|
|
55
|
+
'or ~/.skrr/cli-auth.json elsewhere) that keeps renewing itself. Plain skrr commands on a ' +
|
|
56
|
+
'computer whose background service is signed in need no login at all: they borrow a ' +
|
|
57
|
+
'short-lived token from it for each run and store nothing (an older background service ' +
|
|
58
|
+
'mints them a stored session instead). On a Dedicated Runtime, where the service only ' +
|
|
59
|
+
'lends short-lived tokens, nothing is stored.\n\n' +
|
|
60
|
+
'Set OVERSKY_SKIP_DAEMON_BROKER=1 to skip step 1 (--device-code skips it too). After signing ' +
|
|
61
|
+
'in, skrr also signs the background service in, and on an interactive first login on a ' +
|
|
62
|
+
'computer without it, offers to install it. Set SKRR_SKIP_DAEMON_HANDOFF=1 to skip that ' +
|
|
63
|
+
'optional setup; it does not skip step 1.';
|
|
47
64
|
static examples = [
|
|
48
65
|
'<%= config.bin %> login',
|
|
49
66
|
'<%= config.bin %> login --env prod',
|
|
50
67
|
'<%= config.bin %> login --base-url https://skrr.ai',
|
|
51
68
|
'<%= config.bin %> login --server http://localhost:3090',
|
|
52
69
|
'<%= config.bin %> login --device-code',
|
|
70
|
+
'OVERSKY_SKIP_DAEMON_BROKER=1 <%= config.bin %> login',
|
|
53
71
|
'<%= config.bin %> login --port 1455',
|
|
54
72
|
'<%= config.bin %> login --recovery-code "W7K2J-X9P3M-R6Y4Q-..."',
|
|
55
73
|
'OVERSKY_REFRESH_TOKEN=... OVERSKY_CLI_ID=... <%= config.bin %> login',
|
|
@@ -244,7 +262,8 @@ class Login extends base_command_1.BaseCommand {
|
|
|
244
262
|
*
|
|
245
263
|
* `cliId` resolution priority:
|
|
246
264
|
* 1. `OVERSKY_CLI_ID` env var — explicit, wins over everything.
|
|
247
|
-
* 2. `cliConfig
|
|
265
|
+
* 2. `resolveCliId(cliConfig)` — the id a prior `skrr login` of this
|
|
266
|
+
* profile presents (its own, or the machine's).
|
|
248
267
|
* Honored so a same-machine restore "just works" without forcing
|
|
249
268
|
* the operator to re-export the cliId.
|
|
250
269
|
* 3. error — without a cliId the refresh row binding can't be matched
|
|
@@ -252,8 +271,8 @@ class Login extends base_command_1.BaseCommand {
|
|
|
252
271
|
* pointing at `skrr whoami` on the original box.
|
|
253
272
|
*/
|
|
254
273
|
async runRefreshTokenLogin(refreshToken) {
|
|
255
|
-
|
|
256
|
-
const cliId =
|
|
274
|
+
// `resolveCliId` already puts OVERSKY_CLI_ID / SKRR_CLI_ID first.
|
|
275
|
+
const cliId = (0, cli_id_1.resolveCliId)(this.cliConfig);
|
|
257
276
|
if (!cliId) {
|
|
258
277
|
this.error('OVERSKY_REFRESH_TOKEN is set but no cliId is available. The refresh\n' +
|
|
259
278
|
'row is bound to the cliId it was issued for; without it the server\n' +
|
package/dist/commands/logout.js
CHANGED
|
@@ -244,6 +244,9 @@ class Logout extends base_command_1.BaseCommand {
|
|
|
244
244
|
(0, config_1.saveConfig)({
|
|
245
245
|
baseURL: cfg.baseURL,
|
|
246
246
|
...(cfg.cliId ? { cliId: cfg.cliId } : {}),
|
|
247
|
+
// A named profile's own identity outlives a session exactly as the
|
|
248
|
+
// machine's does, so its next login re-presents it (OSK-12156).
|
|
249
|
+
...(cfg.profileCliId ? { profileCliId: cfg.profileCliId } : {}),
|
|
247
250
|
...(agentSetting.value ? { [first_party_harness_managed_1.AGENT_CONFIG_KEY]: agentSetting.value } : {}),
|
|
248
251
|
...(cfg.credentials ? { credentials: cfg.credentials } : {}),
|
|
249
252
|
migrated: { ...(cfg.migrated ?? {}), authOutOfConfig: true },
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const dedicated_lease_command_1 = require("../../../lib/dedicated-lease-command");
|
|
5
|
+
const daemon_installer_1 = require("../../../lib/daemon-installer");
|
|
5
6
|
const dedicated_machines_1 = require("../../../lib/dedicated-machines");
|
|
6
7
|
const harnesses_1 = require("../../../lib/harnesses");
|
|
7
8
|
class DedicatedMachinesShow extends dedicated_lease_command_1.DedicatedLeaseCommand {
|
|
@@ -40,6 +41,12 @@ class DedicatedMachinesShow extends dedicated_lease_command_1.DedicatedLeaseComm
|
|
|
40
41
|
catch {
|
|
41
42
|
harnesses = [];
|
|
42
43
|
}
|
|
44
|
+
// The latest daemon RELEASE, from the same signed feed a daemon updates
|
|
45
|
+
// from. The lease says which daemon the approved image carries; only this
|
|
46
|
+
// says whether a newer release exists that no approved image carries yet —
|
|
47
|
+
// which is what `skrrd update --check` on the machine reports (OSK-12153).
|
|
48
|
+
// Best-effort: an unreachable feed is said, never read as "none newer".
|
|
49
|
+
const latestDaemonRelease = lease.health?.image ? await readLatestDaemonRelease() : null;
|
|
43
50
|
if (flags.json) {
|
|
44
51
|
const engines = (0, dedicated_machines_1.dedicatedLeaseHarnesses)(lease, harnesses).map((harness) => ({
|
|
45
52
|
harnessId: harness._id,
|
|
@@ -47,11 +54,27 @@ class DedicatedMachinesShow extends dedicated_lease_command_1.DedicatedLeaseComm
|
|
|
47
54
|
status: harness.status,
|
|
48
55
|
lastSeenAt: harness.lastSeenAt,
|
|
49
56
|
}));
|
|
50
|
-
this.log(JSON.stringify({
|
|
57
|
+
this.log(JSON.stringify({
|
|
58
|
+
...response,
|
|
59
|
+
harnesses: engines,
|
|
60
|
+
...(latestDaemonRelease ? { latestDaemonRelease } : {}),
|
|
61
|
+
}, null, 2));
|
|
51
62
|
return;
|
|
52
63
|
}
|
|
53
|
-
for (const line of (0, dedicated_machines_1.describeDedicatedLease)(lease, harnesses))
|
|
64
|
+
for (const line of (0, dedicated_machines_1.describeDedicatedLease)(lease, harnesses, { latestDaemonRelease })) {
|
|
54
65
|
this.log(line);
|
|
66
|
+
}
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
69
|
exports.default = DedicatedMachinesShow;
|
|
70
|
+
async function readLatestDaemonRelease() {
|
|
71
|
+
try {
|
|
72
|
+
const result = await (0, daemon_installer_1.fetchVerifiedDaemonManifest)();
|
|
73
|
+
if (result.ok)
|
|
74
|
+
return { version: result.manifest.version };
|
|
75
|
+
return { error: result.code };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return { error: err?.message || 'unreadable' };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -25,6 +25,11 @@ import { type TaskRecord } from '../../lib/tasks';
|
|
|
25
25
|
* Same shape as `warnDroppedDefaultAssignees` below, which does this for a
|
|
26
26
|
* silently-dropped assignee (OSK-4857).
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* `tagLabelWarning` for a task: the task-label vocabulary of the ambient
|
|
30
|
+
* workspace. Shared by `tasks create` and `tasks update`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function taskTagLabelWarning(tags: string[] | undefined, attachedLabelIds: string[] | undefined): Promise<string>;
|
|
28
33
|
export declare function droppedEstimateWarning(requested: number | undefined, written: Record<string, unknown> | null | undefined, spaceId?: string | null): string | null;
|
|
29
34
|
/** A create may have committed before any of these response failures surfaced. */
|
|
30
35
|
export declare function taskCreateMayHaveCommitted(error: unknown): boolean;
|