@skrr-ai/cli 0.1.51 → 0.1.53
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/chat.js +2 -1
- package/dist/commands/agents/create.js +21 -8
- package/dist/commands/agents/update.d.ts +55 -0
- package/dist/commands/agents/update.js +119 -23
- 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/approvals/list.d.ts +22 -0
- package/dist/commands/tasks/approvals/list.js +80 -3
- package/dist/commands/tasks/create.d.ts +5 -0
- package/dist/commands/tasks/create.js +28 -7
- package/dist/commands/tasks/start.d.ts +14 -0
- package/dist/commands/tasks/start.js +31 -1
- package/dist/commands/tasks/update.js +30 -3
- package/dist/commands/whoami.js +5 -3
- package/dist/lib/agent-config.d.ts +15 -0
- package/dist/lib/agent-config.js +46 -10
- package/dist/lib/agentic-stream.d.ts +21 -1
- package/dist/lib/agentic-stream.js +40 -6
- package/dist/lib/auth-failure.js +4 -1
- package/dist/lib/auth-storage.d.ts +24 -0
- package/dist/lib/auth-storage.js +348 -13
- 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/cred-envelope.d.ts +12 -1
- package/dist/lib/cred-envelope.js +20 -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 +53 -4
- package/dist/lib/dedicated-machines.js +95 -5
- 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 +254 -40
- package/dist/lib/label-ref.d.ts +34 -0
- package/dist/lib/label-ref.js +72 -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/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +13 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +9 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +13 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +8 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3138 -3100
- package/oclif.manifest.json +26470 -26449
- package/package.json +1 -1
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
|
+
}
|
|
@@ -14,6 +14,28 @@ export declare function approvalRow(a: Record<string, unknown>): {
|
|
|
14
14
|
approver: string;
|
|
15
15
|
created: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* OSK-12165 — the task's latest run, when it ended blocked on a permission
|
|
19
|
+
* nobody could grant.
|
|
20
|
+
*
|
|
21
|
+
* An unattended run (`tasks start`) cannot ask anyone: the permission bridge
|
|
22
|
+
* declines every ask at once, so no approval row is ever created, and this
|
|
23
|
+
* command used to answer "No approvals." in exactly the case `tasks start`
|
|
24
|
+
* sends people here for. The evidence lives on the run, not in the approvals
|
|
25
|
+
* collection: the leaf finalizer settles it with `error.code`
|
|
26
|
+
* `permission_blocked`, which `GET /tasks/:id/runs` projects as `failureCode`
|
|
27
|
+
* alongside the sentence (what was refused, and the fix) as `failureReason`.
|
|
28
|
+
*
|
|
29
|
+
* Only the LATEST run counts (the route sorts newest first): a permission block
|
|
30
|
+
* on an older run that a later run got past is history, not the current state.
|
|
31
|
+
*/
|
|
32
|
+
export declare function permissionBlockedRun(runs: Array<Record<string, unknown>>): Record<string, unknown> | null;
|
|
33
|
+
/**
|
|
34
|
+
* What to print for such a run: why it is blocked, what needed approval (the
|
|
35
|
+
* run's own recorded sentence, which names the refused tool), that an
|
|
36
|
+
* unattended run cannot prompt, and the two ways forward with exact commands.
|
|
37
|
+
*/
|
|
38
|
+
export declare function permissionBlockLines(run: Record<string, unknown>, bin?: string, taskRef?: string): string[];
|
|
17
39
|
export default class TasksApprovalsList extends BaseCommand {
|
|
18
40
|
static description: string;
|
|
19
41
|
static examples: string[];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.approvalRow = approvalRow;
|
|
4
|
+
exports.permissionBlockedRun = permissionBlockedRun;
|
|
5
|
+
exports.permissionBlockLines = permissionBlockLines;
|
|
4
6
|
const core_1 = require("@oclif/core");
|
|
5
7
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
8
|
const base_command_1 = require("../../../base-command");
|
|
@@ -22,6 +24,52 @@ function approvalRow(a) {
|
|
|
22
24
|
created: a.createdAt ? (0, format_1.localTime)(a.createdAt) : '-',
|
|
23
25
|
};
|
|
24
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* OSK-12165 — the task's latest run, when it ended blocked on a permission
|
|
29
|
+
* nobody could grant.
|
|
30
|
+
*
|
|
31
|
+
* An unattended run (`tasks start`) cannot ask anyone: the permission bridge
|
|
32
|
+
* declines every ask at once, so no approval row is ever created, and this
|
|
33
|
+
* command used to answer "No approvals." in exactly the case `tasks start`
|
|
34
|
+
* sends people here for. The evidence lives on the run, not in the approvals
|
|
35
|
+
* collection: the leaf finalizer settles it with `error.code`
|
|
36
|
+
* `permission_blocked`, which `GET /tasks/:id/runs` projects as `failureCode`
|
|
37
|
+
* alongside the sentence (what was refused, and the fix) as `failureReason`.
|
|
38
|
+
*
|
|
39
|
+
* Only the LATEST run counts (the route sorts newest first): a permission block
|
|
40
|
+
* on an older run that a later run got past is history, not the current state.
|
|
41
|
+
*/
|
|
42
|
+
function permissionBlockedRun(runs) {
|
|
43
|
+
const latest = runs[0];
|
|
44
|
+
if (!latest)
|
|
45
|
+
return null;
|
|
46
|
+
const code = latest.failureCode ?? latest.failureClass;
|
|
47
|
+
return code === data_provider_1.PERMISSION_BLOCKED_FAILURE_CODE ? latest : null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* What to print for such a run: why it is blocked, what needed approval (the
|
|
51
|
+
* run's own recorded sentence, which names the refused tool), that an
|
|
52
|
+
* unattended run cannot prompt, and the two ways forward with exact commands.
|
|
53
|
+
*/
|
|
54
|
+
function permissionBlockLines(run, bin = 'skrr', taskRef = '<task-id>') {
|
|
55
|
+
const runId = String(run.runId ?? run.id ?? '-');
|
|
56
|
+
const reason = typeof run.failureReason === 'string' ? run.failureReason.trim() : '';
|
|
57
|
+
const executor = run.executor;
|
|
58
|
+
const agent = executor && typeof executor.agentId === 'string' && executor.agentId
|
|
59
|
+
? executor.agentId
|
|
60
|
+
: '<agent>';
|
|
61
|
+
return [
|
|
62
|
+
`The latest run (${runId}) is blocked on a permission, not on an approval request.`,
|
|
63
|
+
...(reason ? [` ${reason}`] : []),
|
|
64
|
+
'It ran unattended (`tasks start`), and an unattended run cannot prompt anyone to approve a',
|
|
65
|
+
'tool call, so the ask was refused on the spot and no approval request was recorded here.',
|
|
66
|
+
'',
|
|
67
|
+
'To continue, either:',
|
|
68
|
+
` ${bin} agents chat ${agent} --approve-tools --prompt "..." do the work interactively, approving each call`,
|
|
69
|
+
` ${bin} agents update ${agent} --default-permission-mode bypassPermissions let unattended runs use the tool`,
|
|
70
|
+
` then: ${bin} tasks start ${taskRef} (or pick an agent/harness whose mode allows it)`,
|
|
71
|
+
];
|
|
72
|
+
}
|
|
25
73
|
class TasksApprovalsList extends base_command_1.BaseCommand {
|
|
26
74
|
static description = 'List approvals for a task';
|
|
27
75
|
static examples = [
|
|
@@ -44,14 +92,38 @@ class TasksApprovalsList extends base_command_1.BaseCommand {
|
|
|
44
92
|
catch (err) {
|
|
45
93
|
this.handleApiError(err);
|
|
46
94
|
}
|
|
95
|
+
const r = response;
|
|
96
|
+
const items = r?.data ?? [];
|
|
97
|
+
// OSK-12165 — an unattended run blocked on a permission leaves no approval
|
|
98
|
+
// row, so the empty list alone would say "nothing to act on" about a task
|
|
99
|
+
// that is waiting on exactly that. Read the latest run, best-effort: the
|
|
100
|
+
// approvals list is still the answer if the runs read fails.
|
|
101
|
+
let blockedRun = null;
|
|
102
|
+
try {
|
|
103
|
+
const runs = (await data_provider_1.dataService.getTaskRuns(args.id));
|
|
104
|
+
blockedRun = permissionBlockedRun(runs?.data ?? []);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
blockedRun = null;
|
|
108
|
+
}
|
|
109
|
+
const blockLines = blockedRun ? permissionBlockLines(blockedRun, this.config.bin, args.id) : [];
|
|
47
110
|
if (flags.json) {
|
|
111
|
+
// The server payload stays byte-identical; the pointer goes to stderr so
|
|
112
|
+
// `--json` remains parseable and a script still learns the task is stuck.
|
|
113
|
+
if (blockedRun) {
|
|
114
|
+
this.warn(`The latest run is blocked on a permission an unattended run could not ask for; ` +
|
|
115
|
+
`\`${this.config.bin} tasks approvals list ${args.id}\` (without --json) says what to do.`);
|
|
116
|
+
}
|
|
48
117
|
this.log(JSON.stringify(response, null, 2));
|
|
49
118
|
return;
|
|
50
119
|
}
|
|
51
|
-
const r = response;
|
|
52
|
-
const items = r?.data ?? [];
|
|
53
120
|
if (items.length === 0) {
|
|
54
|
-
this.log('No
|
|
121
|
+
this.log('No approval requests.');
|
|
122
|
+
if (blockLines.length > 0) {
|
|
123
|
+
this.log('');
|
|
124
|
+
for (const line of blockLines)
|
|
125
|
+
this.log(line);
|
|
126
|
+
}
|
|
55
127
|
return;
|
|
56
128
|
}
|
|
57
129
|
const rows = items.map(approvalRow);
|
|
@@ -62,6 +134,11 @@ class TasksApprovalsList extends base_command_1.BaseCommand {
|
|
|
62
134
|
{ key: 'approver', header: 'APPROVER', maxWidth: 30 },
|
|
63
135
|
{ key: 'created', header: 'CREATED' },
|
|
64
136
|
], (line) => this.log(line));
|
|
137
|
+
if (blockLines.length > 0) {
|
|
138
|
+
this.log('');
|
|
139
|
+
for (const line of blockLines)
|
|
140
|
+
this.log(line);
|
|
141
|
+
}
|
|
65
142
|
}
|
|
66
143
|
}
|
|
67
144
|
exports.default = TasksApprovalsList;
|
|
@@ -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;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskTagLabelWarning = taskTagLabelWarning;
|
|
3
4
|
exports.droppedEstimateWarning = droppedEstimateWarning;
|
|
4
5
|
exports.taskCreateMayHaveCommitted = taskCreateMayHaveCommitted;
|
|
5
6
|
exports.taskMatchesCreateIdentity = taskMatchesCreateIdentity;
|
|
@@ -36,6 +37,16 @@ const web_url_1 = require("../../lib/web-url");
|
|
|
36
37
|
* Same shape as `warnDroppedDefaultAssignees` below, which does this for a
|
|
37
38
|
* silently-dropped assignee (OSK-4857).
|
|
38
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* `tagLabelWarning` for a task: the task-label vocabulary of the ambient
|
|
42
|
+
* workspace. Shared by `tasks create` and `tasks update`.
|
|
43
|
+
*/
|
|
44
|
+
function taskTagLabelWarning(tags, attachedLabelIds) {
|
|
45
|
+
return (0, label_ref_1.tagLabelWarning)(tags, attachedLabelIds, {
|
|
46
|
+
applicableTo: 'task',
|
|
47
|
+
...(process.env.OVERSKY_WORKSPACE_ID ? { workspaceId: process.env.OVERSKY_WORKSPACE_ID } : {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
39
50
|
function droppedEstimateWarning(requested, written, spaceId) {
|
|
40
51
|
if (requested === undefined || requested === null)
|
|
41
52
|
return null;
|
|
@@ -137,7 +148,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
137
148
|
description: 'Stable caller-supplied idempotency key for create/upsert workflows',
|
|
138
149
|
}),
|
|
139
150
|
'default-agent': core_1.Flags.string({
|
|
140
|
-
description: 'Override the automatic assignee with an Agent
|
|
151
|
+
description: 'Override the automatic assignee with an Agent (it becomes the assignee that runs the task; ' +
|
|
152
|
+
'--assignee-agent names one directly). Use "auto" to inherit the space lead ' +
|
|
141
153
|
'agent. Without this, a task falls to the agent this session is bound to ' +
|
|
142
154
|
'(OVERSKY_SESSION_AGENT_ID), to you when --execution-mode is human, or to nobody. ' +
|
|
143
155
|
'Cannot be combined with --execution-mode human.',
|
|
@@ -161,8 +173,9 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
161
173
|
description: 'Commitment ID that owns this task. Use for work created directly from a commitment without a linked goal.',
|
|
162
174
|
}),
|
|
163
175
|
project: core_1.Flags.string({
|
|
164
|
-
description: '
|
|
165
|
-
|
|
176
|
+
description: 'Project to attach the task to: its `id` (uuid; a pasted `_id` is accepted and rewritten ' +
|
|
177
|
+
'to the `id`), or its ref (OSK-P-5) with --space. Without --space the task inherits ' +
|
|
178
|
+
"the project's space.",
|
|
166
179
|
}),
|
|
167
180
|
cycle: core_1.Flags.string({
|
|
168
181
|
description: 'Cycle ID, or "current" / "next" within the task Space',
|
|
@@ -174,7 +187,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
174
187
|
description: 'Source message ID (requires --source-conversation or daemon session context)',
|
|
175
188
|
}),
|
|
176
189
|
'assignee-agent': core_1.Flags.string({
|
|
177
|
-
description: 'Assignee agent
|
|
190
|
+
description: 'Assignee agent: the agent that DOES the work when the task starts (the first listed runs). ' +
|
|
191
|
+
'Id or name, resolved within --space (repeatable)',
|
|
178
192
|
multiple: true,
|
|
179
193
|
}),
|
|
180
194
|
'assignee-user': core_1.Flags.string({
|
|
@@ -182,7 +196,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
182
196
|
multiple: true,
|
|
183
197
|
}),
|
|
184
198
|
'dri-agent': core_1.Flags.string({
|
|
185
|
-
description: '
|
|
199
|
+
description: 'Accountable agent (DRI), separate from assignees. `tasks start` runs an assignee, ' +
|
|
200
|
+
'not the DRI — use --assignee-agent for the agent that does the work',
|
|
186
201
|
}),
|
|
187
202
|
'dri-user': core_1.Flags.string({
|
|
188
203
|
description: 'Explicit accountable human DRI (separate from assignees)',
|
|
@@ -219,11 +234,12 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
219
234
|
'ancestor it inherits from); with none set, the server drops the value. Set one with ' +
|
|
220
235
|
'`spaces update <space> --estimate-scale <scale> --estimate-migration <clear|migrate>`.',
|
|
221
236
|
}),
|
|
222
|
-
tag: core_1.Flags.string({ description:
|
|
237
|
+
tag: core_1.Flags.string({ description: label_ref_1.TAG_FLAG_HELP, multiple: true }),
|
|
223
238
|
'label-id': core_1.Flags.string({
|
|
224
239
|
aliases: ['label'],
|
|
225
240
|
description: 'Attach a task label (repeatable) — id, or a name from `skrr labels list --applicable-to task`. ' +
|
|
226
|
-
'An unknown name is refused, never created.
|
|
241
|
+
'An unknown name is refused, never created. Also accepted as --label. ' +
|
|
242
|
+
'Manage labels with `skrr labels`.',
|
|
227
243
|
multiple: true,
|
|
228
244
|
}),
|
|
229
245
|
'working-directory': core_1.Flags.string({
|
|
@@ -517,6 +533,11 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
517
533
|
const estimateWarning = droppedEstimateWarning(flags.estimate, created, payload.spaceId);
|
|
518
534
|
if (estimateWarning)
|
|
519
535
|
this.warn(estimateWarning);
|
|
536
|
+
// A `--tag` spelled like a declared label is almost always a label someone
|
|
537
|
+
// meant to attach (OSK-12053). Say so; never convert it implicitly.
|
|
538
|
+
const tagWarning = await taskTagLabelWarning(flags.tag, resolvedLabelIds);
|
|
539
|
+
if (tagWarning)
|
|
540
|
+
this.warn(tagWarning);
|
|
520
541
|
if (flags.json) {
|
|
521
542
|
// The raw task row alone cannot tell a script the assignee it implicitly
|
|
522
543
|
// asked for was dropped (`assigneeAgentIds: []`) from "nobody was ever
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
2
|
export default class TasksStart extends BaseCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static aliases: string[];
|
|
4
5
|
static examples: string[];
|
|
5
6
|
static args: {
|
|
6
7
|
id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
@@ -13,3 +14,16 @@ export default class TasksStart extends BaseCommand {
|
|
|
13
14
|
};
|
|
14
15
|
run(): Promise<void>;
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* What to run next, printed after a start (OSK-12128). A start used to print
|
|
19
|
+
* two ids and stop, and the way to watch it was found by accident. Every
|
|
20
|
+
* command named here exists; `tasks-start.follow-up.spec.ts` pins that.
|
|
21
|
+
*
|
|
22
|
+
* The last line announces the files a local run adds to its working directory:
|
|
23
|
+
* the daemon writes a per-run instruction file (AGENTS.md for Codex, CLAUDE.md
|
|
24
|
+
* for a worktree session, plus any server-sent context file) when nothing is
|
|
25
|
+
* at that name, and removes what it wrote afterwards unless it was changed
|
|
26
|
+
* (`daemon/src/session-context-file.ts`). Unannounced, those were the first two
|
|
27
|
+
* files in a directory the task said to leave alone.
|
|
28
|
+
*/
|
|
29
|
+
export declare function startFollowUpLines(bin: string, taskRef: string): string[];
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startFollowUpLines = startFollowUpLines;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
7
8
|
const git_task_1 = require("../../lib/git-task");
|
|
8
9
|
class TasksStart extends base_command_1.BaseCommand {
|
|
9
|
-
static description = 'Start execution of a task
|
|
10
|
+
static description = 'Start execution of a task: its assignee agent runs it in a new conversation + session. ' +
|
|
11
|
+
'Alias: tasks execute';
|
|
12
|
+
// `tasks execute` is the verb a first-time user reaches for, and it was
|
|
13
|
+
// "not a command" (OSK-12128). One command, two names.
|
|
14
|
+
static aliases = ['tasks:execute'];
|
|
10
15
|
static examples = [
|
|
11
16
|
'<%= config.bin %> tasks start <task-id>',
|
|
12
17
|
'<%= config.bin %> tasks start',
|
|
@@ -109,6 +114,31 @@ class TasksStart extends base_command_1.BaseCommand {
|
|
|
109
114
|
this.log(`${branch.created ? 'Created' : 'Checked out'} branch: ${branch.branch}`);
|
|
110
115
|
this.log(`Conversation: ${result.conversationId}`);
|
|
111
116
|
this.log(`Session: ${result.sessionId}`);
|
|
117
|
+
for (const line of startFollowUpLines(this.config.bin, taskIdentity.id))
|
|
118
|
+
this.log(line);
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
121
|
exports.default = TasksStart;
|
|
122
|
+
/**
|
|
123
|
+
* What to run next, printed after a start (OSK-12128). A start used to print
|
|
124
|
+
* two ids and stop, and the way to watch it was found by accident. Every
|
|
125
|
+
* command named here exists; `tasks-start.follow-up.spec.ts` pins that.
|
|
126
|
+
*
|
|
127
|
+
* The last line announces the files a local run adds to its working directory:
|
|
128
|
+
* the daemon writes a per-run instruction file (AGENTS.md for Codex, CLAUDE.md
|
|
129
|
+
* for a worktree session, plus any server-sent context file) when nothing is
|
|
130
|
+
* at that name, and removes what it wrote afterwards unless it was changed
|
|
131
|
+
* (`daemon/src/session-context-file.ts`). Unannounced, those were the first two
|
|
132
|
+
* files in a directory the task said to leave alone.
|
|
133
|
+
*/
|
|
134
|
+
function startFollowUpLines(bin, taskRef) {
|
|
135
|
+
return [
|
|
136
|
+
'',
|
|
137
|
+
'Follow it:',
|
|
138
|
+
` ${bin} tasks watch ${taskRef} status until it finishes or fails`,
|
|
139
|
+
` ${bin} tasks transcript ${taskRef} what the agent has said so far`,
|
|
140
|
+
` ${bin} tasks approvals list ${taskRef} approvals waiting on you, or why a run is blocked on one`,
|
|
141
|
+
'On a local machine, the run may add its instruction file (AGENTS.md / CLAUDE.md) to the',
|
|
142
|
+
'working directory while it runs; files the run wrote are removed afterwards unless changed.',
|
|
143
|
+
];
|
|
144
|
+
}
|
|
@@ -9,6 +9,7 @@ const tasks_1 = require("../../lib/tasks");
|
|
|
9
9
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
10
10
|
const tasks_2 = require("../../lib/tasks");
|
|
11
11
|
const create_1 = require("./create");
|
|
12
|
+
const label_ref_1 = require("../../lib/label-ref");
|
|
12
13
|
class TasksUpdate extends base_command_1.BaseCommand {
|
|
13
14
|
static description = 'Update an existing task (partial patch). Status and PROJECT changes go through ' +
|
|
14
15
|
'`tasks move` — see `--goal` / `--space` below.';
|
|
@@ -156,10 +157,14 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
156
157
|
description: 'Remove a task from the list this one blocks (repeatable)',
|
|
157
158
|
multiple: true,
|
|
158
159
|
}),
|
|
159
|
-
tag: core_1.Flags.string({
|
|
160
|
+
tag: core_1.Flags.string({
|
|
161
|
+
description: `Replace tags (repeatable, whole-list). ${label_ref_1.TAG_FLAG_HELP}`,
|
|
162
|
+
multiple: true,
|
|
163
|
+
}),
|
|
160
164
|
'label-id': core_1.Flags.string({
|
|
161
165
|
aliases: ['label'],
|
|
162
|
-
description: 'Replace the task labels
|
|
166
|
+
description: 'Replace the task labels (repeatable, whole-list) — id, or a name from ' +
|
|
167
|
+
'`skrr labels list --applicable-to task`. Also accepted as --label. ' +
|
|
163
168
|
'Use `skrr tasks labels attach/detach` for incremental changes.',
|
|
164
169
|
multiple: true,
|
|
165
170
|
}),
|
|
@@ -318,6 +323,25 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
325
|
}
|
|
326
|
+
// Same resolution as `tasks create`: the `--tag` warning below suggests
|
|
327
|
+
// `--label-id <name>`, so this flag has to accept the name it suggests
|
|
328
|
+
// (OSK-12166). Ids pass through untouched.
|
|
329
|
+
let resolvedLabelIds;
|
|
330
|
+
if (flags['label-id'] !== undefined) {
|
|
331
|
+
try {
|
|
332
|
+
resolvedLabelIds = await (0, label_ref_1.resolveLabelRefs)(flags['label-id'], {
|
|
333
|
+
applicableTo: 'task',
|
|
334
|
+
...(process.env.OVERSKY_WORKSPACE_ID
|
|
335
|
+
? { workspaceId: process.env.OVERSKY_WORKSPACE_ID }
|
|
336
|
+
: {}),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
if (err instanceof label_ref_1.LabelRefError)
|
|
341
|
+
this.error(err.message, { exit: 1 });
|
|
342
|
+
this.handleApiError(err);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
321
345
|
const payload = (0, prompt_1.mergeDefined)((0, task_resolver_1.stripTaskIdentityFields)(body), {
|
|
322
346
|
title: flags.title,
|
|
323
347
|
expectations: expectationsPatch === null ? [] : (expectationsPatch ?? undefined),
|
|
@@ -349,7 +373,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
349
373
|
addBlocksIds: flags['add-blocks'],
|
|
350
374
|
removeBlocksIds: flags['remove-blocks'],
|
|
351
375
|
tags: flags.tag,
|
|
352
|
-
labelIds:
|
|
376
|
+
labelIds: resolvedLabelIds,
|
|
353
377
|
// Worktree isolation override. --worktree ⇒ 'ephemeral', --no-worktree ⇒
|
|
354
378
|
// 'shared', omitted ⇒ unchanged. (Clear-to-inherit via --from-json.)
|
|
355
379
|
executionIsolation: (0, tasks_2.executionIsolationFromWorktreeFlag)(flags.worktree),
|
|
@@ -444,6 +468,9 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
444
468
|
const estimateWarning = (0, create_1.droppedEstimateWarning)(flags.estimate, updated, updated?.spaceId);
|
|
445
469
|
if (estimateWarning)
|
|
446
470
|
this.warn(estimateWarning);
|
|
471
|
+
const tagWarning = await (0, create_1.taskTagLabelWarning)(flags.tag, resolvedLabelIds);
|
|
472
|
+
if (tagWarning)
|
|
473
|
+
this.warn(tagWarning);
|
|
447
474
|
if (flags.json) {
|
|
448
475
|
this.log(JSON.stringify((0, web_url_1.withTaskUrl)(this.cliConfig.baseURL, updated), null, 2));
|
|
449
476
|
return;
|
package/dist/commands/whoami.js
CHANGED
|
@@ -10,6 +10,7 @@ const auth_storage_1 = require("../lib/auth-storage");
|
|
|
10
10
|
const keychain_1 = require("../lib/keychain");
|
|
11
11
|
const cred_envelope_1 = require("../lib/cred-envelope");
|
|
12
12
|
const config_1 = require("../lib/config");
|
|
13
|
+
const cli_id_1 = require("../lib/cli-id");
|
|
13
14
|
/**
|
|
14
15
|
* `skrr whoami`
|
|
15
16
|
*
|
|
@@ -101,7 +102,7 @@ class Whoami extends base_command_1.BaseCommand {
|
|
|
101
102
|
daemonId,
|
|
102
103
|
server: this.cliConfig.baseURL,
|
|
103
104
|
profile: (0, keychain_1.getActiveProfile)(),
|
|
104
|
-
cliId: this.cliConfig
|
|
105
|
+
cliId: (0, cli_id_1.resolveCliId)(this.cliConfig) ?? null,
|
|
105
106
|
token: {
|
|
106
107
|
source: this.credentialSourceLabel(),
|
|
107
108
|
kind: this.resolvedCredential.kind,
|
|
@@ -135,8 +136,9 @@ class Whoami extends base_command_1.BaseCommand {
|
|
|
135
136
|
// where the login message sends you to find out where you are (OSK-301).
|
|
136
137
|
this.log(`Server: ${(0, config_1.describeDeployment)(this.cliConfig.baseURL)}`);
|
|
137
138
|
this.log(`Profile: ${(0, keychain_1.getActiveProfile)()}`);
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
const cliId = (0, cli_id_1.resolveCliId)(this.cliConfig);
|
|
140
|
+
if (cliId) {
|
|
141
|
+
this.log(`CLI ID: ${cliId}`);
|
|
140
142
|
}
|
|
141
143
|
this.log(`Token source: ${this.credentialSourceLabel() ?? '(none)'}`);
|
|
142
144
|
this.log(`Token kind: ${this.resolvedCredential.kind}`);
|
|
@@ -2,6 +2,21 @@ export declare const COMMITMENT_RESPONSIBILITY_LEVELS: readonly ["reasoning_only
|
|
|
2
2
|
export declare const REASONING_TOOL_ESCAPES: readonly ["bash", "browser"];
|
|
3
3
|
/** Warning printed by the three retired Commitment permission flags (OSK-11975). */
|
|
4
4
|
export declare const COMMITMENT_PERMISSION_FLAG_RETIRED = "This setting no longer has any effect: Commitment runs use the agent's own permissions.";
|
|
5
|
+
/** The three retired flags themselves: accepted at parse, warned on, never sent. */
|
|
6
|
+
export declare const RETIRED_COMMITMENT_PERMISSION_FLAGS: readonly ["reasoning-tool-escape", "commitment-responsibility", "commitment-responsibility-ceiling"];
|
|
7
|
+
/**
|
|
8
|
+
* The deprecation warning ONE retired flag prints. oclif prints a
|
|
9
|
+
* `deprecated.message` verbatim, so a shared message printed the same line
|
|
10
|
+
* once per flag with nothing saying which flag each one was about (OSK-12068).
|
|
11
|
+
*/
|
|
12
|
+
export declare function retiredCommitmentPermissionFlagMessage(flag: (typeof RETIRED_COMMITMENT_PERMISSION_FLAGS)[number]): string;
|
|
13
|
+
/**
|
|
14
|
+
* The fields the server ignored and why, from a write's `deprecatedFields`
|
|
15
|
+
* notice, one line each. Empty when the response carries none.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deprecatedFieldsWarnings(response: unknown): string[];
|
|
18
|
+
/** The payload keys the server reported as ignored, so they are not also called "not kept". */
|
|
19
|
+
export declare function deprecatedFieldNames(response: unknown): string[];
|
|
5
20
|
export declare const RELEASE_INSTALL_CHANNELS: readonly ["latest", "manual"];
|
|
6
21
|
/**
|
|
7
22
|
* `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
|