@skrr-ai/cli 0.1.43 → 0.1.44
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 +2 -0
- package/dist/base-command.js +1 -0
- package/dist/commands/balance/index.js +1 -1
- package/dist/commands/balance/show.d.ts +32 -0
- package/dist/commands/balance/show.js +74 -3
- package/dist/commands/code/handover.d.ts +1 -0
- package/dist/commands/code/handover.js +4 -0
- package/dist/commands/code/jobs/run.d.ts +1 -0
- package/dist/commands/code/jobs/run.js +4 -0
- package/dist/commands/daemon/install.js +7 -0
- package/dist/commands/harnesses/leases/show.js +14 -0
- package/dist/commands/instructions/install.d.ts +22 -0
- package/dist/commands/instructions/install.js +83 -7
- package/dist/commands/instructions/list.js +5 -0
- package/dist/commands/instructions/show.d.ts +6 -0
- package/dist/commands/instructions/show.js +34 -1
- package/dist/commands/instructions/status.js +17 -1
- package/dist/commands/payments/wallet.js +2 -2
- package/dist/commands/tasks/create.d.ts +6 -0
- package/dist/commands/tasks/create.js +26 -3
- package/dist/commands/tasks/labels/attach.js +4 -0
- package/dist/commands/tasks/list.d.ts +1 -0
- package/dist/commands/tasks/list.js +9 -0
- package/dist/commands/tasks/show.d.ts +23 -0
- package/dist/commands/tasks/show.js +61 -1
- package/dist/commands/tasks/update.js +12 -0
- package/dist/commands/views/create.js +12 -2
- package/dist/commands/views/list.js +3 -2
- package/dist/commands/views/show.js +2 -0
- package/dist/lib/agentic-stream.d.ts +10 -4
- package/dist/lib/agentic-stream.js +25 -11
- package/dist/lib/cli-installers.js +9 -1
- package/dist/lib/daemon-setup.d.ts +18 -1
- package/dist/lib/daemon-setup.js +33 -1
- package/dist/lib/first-party-harness-agent.d.ts +16 -1
- package/dist/lib/first-party-harness-agent.js +41 -12
- package/dist/lib/first-party-harness-doctor.js +34 -16
- package/dist/lib/first-party-harness.d.ts +18 -11
- package/dist/lib/first-party-harness.js +26 -21
- package/dist/lib/harnesses.d.ts +6 -0
- package/dist/lib/instruction-input.d.ts +21 -0
- package/dist/lib/instruction-input.js +30 -0
- package/dist/lib/instruction-provenance.d.ts +54 -0
- package/dist/lib/instruction-provenance.js +84 -0
- package/dist/lib/task-view-render.d.ts +14 -0
- package/dist/lib/task-view-render.js +55 -0
- package/dist/lib/tasks.d.ts +18 -0
- package/dist/lib/tasks.js +22 -1
- package/dist/lib/views/vocabulary.d.ts +1 -1
- package/dist/lib/views/vocabulary.js +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +75 -24
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +143 -34
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +21 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +75 -19
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +75 -24
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +138 -34
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +21 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +74 -19
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3516 -3513
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +21649 -21626
- package/package.json +3 -3
|
@@ -71,6 +71,12 @@ export default class TasksCreate extends BaseCommand {
|
|
|
71
71
|
'lifecycle-pack': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
72
72
|
};
|
|
73
73
|
run(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* A create refusal, translated. `TASK_GOAL_NOT_FOUND` names the goal id this
|
|
76
|
+
* command sent and the flag that gets past it, where the server's sentence
|
|
77
|
+
* names API fields (OSK-10219); everything else is the uniform translation.
|
|
78
|
+
*/
|
|
79
|
+
private failTaskCreate;
|
|
74
80
|
private createTaskOrRecover;
|
|
75
81
|
/**
|
|
76
82
|
* Predict the create route's `ASSIGNEE_NOT_IN_SPACE` degradation for a
|
|
@@ -333,7 +333,7 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
333
333
|
instantiated = (await data_provider_1.dataService.createTask(payload));
|
|
334
334
|
}
|
|
335
335
|
catch (err) {
|
|
336
|
-
this.
|
|
336
|
+
this.failTaskCreate(err, payload, flags.goal !== undefined);
|
|
337
337
|
}
|
|
338
338
|
const root = instantiated.rootTask ||
|
|
339
339
|
(instantiated.rootTasks || [])[0];
|
|
@@ -475,7 +475,7 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
if (!(defaultAgentResolution.applied && failureCode === 'ASSIGNEE_NOT_IN_SPACE')) {
|
|
478
|
-
this.
|
|
478
|
+
this.failTaskCreate(err, payload, flags.goal !== undefined);
|
|
479
479
|
}
|
|
480
480
|
delete payload.assigneeAgentIds;
|
|
481
481
|
delete payload.assigneeUserIds;
|
|
@@ -502,7 +502,7 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
502
502
|
createRecovery = result.recovery;
|
|
503
503
|
}
|
|
504
504
|
catch (retryErr) {
|
|
505
|
-
this.
|
|
505
|
+
this.failTaskCreate(retryErr, payload, flags.goal !== undefined);
|
|
506
506
|
}
|
|
507
507
|
}
|
|
508
508
|
const url = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, created);
|
|
@@ -537,6 +537,29 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
537
537
|
this.log(`View it at ${url}`);
|
|
538
538
|
await (0, task_instruction_offer_1.maybeOfferTaskManagementInstructions)((message) => this.log(message));
|
|
539
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* A create refusal, translated. `TASK_GOAL_NOT_FOUND` names the goal id this
|
|
542
|
+
* command sent and the flag that gets past it, where the server's sentence
|
|
543
|
+
* names API fields (OSK-10219); everything else is the uniform translation.
|
|
544
|
+
*/
|
|
545
|
+
failTaskCreate(err, payload, goalFromFlag) {
|
|
546
|
+
const goalId = payload.goalId;
|
|
547
|
+
if ((0, base_command_1.apiRefusal)(err).code === tasks_1.TASK_GOAL_NOT_FOUND && typeof goalId === 'string' && goalId) {
|
|
548
|
+
this.failWithCliError({
|
|
549
|
+
message: (0, tasks_1.describeTaskGoalNotFound)({
|
|
550
|
+
goalId,
|
|
551
|
+
command: 'create',
|
|
552
|
+
bin: this.config.bin,
|
|
553
|
+
goalSource: goalFromFlag ? 'flag' : 'json',
|
|
554
|
+
}),
|
|
555
|
+
code: tasks_1.TASK_GOAL_NOT_FOUND,
|
|
556
|
+
status: 400,
|
|
557
|
+
exit: 1,
|
|
558
|
+
retryable: false,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
this.handleApiError(err);
|
|
562
|
+
}
|
|
540
563
|
async createTaskOrRecover(payload) {
|
|
541
564
|
try {
|
|
542
565
|
return {
|
|
@@ -28,6 +28,10 @@ class TasksLabelsAttach extends base_command_1.BaseCommand {
|
|
|
28
28
|
static flags = {
|
|
29
29
|
json: core_1.Flags.boolean({ description: 'Output the updated task as JSON' }),
|
|
30
30
|
'label-id': core_1.Flags.string({
|
|
31
|
+
// `--label` is what `spaces labels add` takes and what the managed
|
|
32
|
+
// task-management instructions tell every agent to type; without the
|
|
33
|
+
// alias that documented command died at parse.
|
|
34
|
+
aliases: ['label'],
|
|
31
35
|
description: 'Label to attach — id, or a name from `skrr labels list --applicable-to task`. An unknown name is refused, never created (repeatable).',
|
|
32
36
|
multiple: true,
|
|
33
37
|
required: true,
|
|
@@ -58,6 +58,7 @@ export default class TasksList extends BaseCommand {
|
|
|
58
58
|
tag: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
59
59
|
'label-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
60
60
|
q: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
61
|
+
view: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
61
62
|
limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
62
63
|
cursor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
63
64
|
calendar: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -48,6 +48,7 @@ const FILTER_FLAGS = new Set([
|
|
|
48
48
|
'tag',
|
|
49
49
|
'label-id',
|
|
50
50
|
'q',
|
|
51
|
+
'view',
|
|
51
52
|
]);
|
|
52
53
|
/**
|
|
53
54
|
* What the zero-row case is allowed to say. The bare `No tasks.` was step 3
|
|
@@ -182,6 +183,7 @@ function buildListParams(flags, assigneeUserId) {
|
|
|
182
183
|
? { labelIds: flag('label-id') }
|
|
183
184
|
: {}),
|
|
184
185
|
q: flag('q'),
|
|
186
|
+
...(flag('view') ? { taskViewId: flag('view') } : {}),
|
|
185
187
|
limit: flag('limit'),
|
|
186
188
|
cursor: flag('cursor'),
|
|
187
189
|
};
|
|
@@ -205,6 +207,7 @@ class TasksList extends base_command_1.BaseCommand {
|
|
|
205
207
|
'<%= config.bin %> tasks list --actionable-by agent --actionable-for-agent <agent-id>',
|
|
206
208
|
'<%= config.bin %> tasks list --needs-me',
|
|
207
209
|
'<%= config.bin %> tasks list --dri me',
|
|
210
|
+
'<%= config.bin %> tasks list --view <task-view-id>',
|
|
208
211
|
'<%= config.bin %> tasks list --calendar 2026-08-01..2026-08-31 --json',
|
|
209
212
|
];
|
|
210
213
|
static flags = {
|
|
@@ -314,6 +317,12 @@ class TasksList extends base_command_1.BaseCommand {
|
|
|
314
317
|
multiple: true,
|
|
315
318
|
}),
|
|
316
319
|
q: core_1.Flags.string({ description: 'Free-text search query' }),
|
|
320
|
+
// A saved Task View is a query; this is the one place outside the browser
|
|
321
|
+
// that runs it (OSK-4651). Resolved server-side by the same evaluator the
|
|
322
|
+
// view subscriptions use, over the tasks this caller may read.
|
|
323
|
+
view: core_1.Flags.string({
|
|
324
|
+
description: 'Only tasks a saved Task View selects (task_view_…; see `views list`). Combines with other filters',
|
|
325
|
+
}),
|
|
317
326
|
limit: core_1.Flags.integer({ description: 'Max results', default: 50 }),
|
|
318
327
|
cursor: core_1.Flags.string({ description: 'Pagination cursor from a previous page' }),
|
|
319
328
|
calendar: core_1.Flags.string({
|
|
@@ -10,6 +10,29 @@ import { BaseCommand } from '../../base-command';
|
|
|
10
10
|
* exists for them to show.
|
|
11
11
|
*/
|
|
12
12
|
export declare function taskPointerLines(t: Record<string, unknown>, ref: string, bin?: string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* The `Labels:` value.
|
|
15
|
+
*
|
|
16
|
+
* `labels attach` writes ids, and without this line the only way to see
|
|
17
|
+
* whether one landed was to attach it again and read "No change — labels
|
|
18
|
+
* already attached" (OSK-10313). Names, because an id is not something a
|
|
19
|
+
* person recognises — but an id beats nothing, so a vocabulary that could not
|
|
20
|
+
* be read (or a label missing from it) degrades per entry rather than failing
|
|
21
|
+
* the command or hiding the label.
|
|
22
|
+
*/
|
|
23
|
+
export declare function labelsLine(labelIds?: string[], nameById?: Map<string, string>): string;
|
|
24
|
+
/**
|
|
25
|
+
* The `Executor:` value — who does this task, and whether anyone said so.
|
|
26
|
+
*
|
|
27
|
+
* It printed `legacy / inferred` whenever `executionMode` was absent, which is
|
|
28
|
+
* the normal result of `tasks create` without `--execution-mode`. A task made
|
|
29
|
+
* ten seconds earlier by the most ordinary invocation there is read as old data
|
|
30
|
+
* made the deprecated way (OSK-4629). The executor is now the one the server
|
|
31
|
+
* acts on (`resolveTaskExecutor`, shared with `TaskActionability`), and the
|
|
32
|
+
* value says when it is implied by the assignees rather than declared, because
|
|
33
|
+
* reassigning the task changes only the implied kind.
|
|
34
|
+
*/
|
|
35
|
+
export declare function taskExecutorLabel(t: Record<string, unknown>, bin?: string): string;
|
|
13
36
|
/**
|
|
14
37
|
* The failure rows of `tasks show`, when there are any. (S3 dogfood finding)
|
|
15
38
|
*
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.taskPointerLines = taskPointerLines;
|
|
4
|
+
exports.labelsLine = labelsLine;
|
|
5
|
+
exports.taskExecutorLabel = taskExecutorLabel;
|
|
4
6
|
exports.taskFailureLines = taskFailureLines;
|
|
5
7
|
const core_1 = require("@oclif/core");
|
|
6
8
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
@@ -11,6 +13,7 @@ const task_closure_1 = require("../../lib/task-closure");
|
|
|
11
13
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
12
14
|
const web_url_1 = require("../../lib/web-url");
|
|
13
15
|
const html_text_1 = require("../../lib/html-text");
|
|
16
|
+
const label_ref_1 = require("../../lib/label-ref");
|
|
14
17
|
const format_1 = require("../../lib/format");
|
|
15
18
|
/**
|
|
16
19
|
* Where to look next — as COMMANDS, not as facts about the task.
|
|
@@ -39,6 +42,46 @@ function taskPointerLines(t, ref, bin = 'skrr') {
|
|
|
39
42
|
...pointers.map(([command, what]) => ` ${command.padEnd(width)} ${what}`),
|
|
40
43
|
];
|
|
41
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The `Labels:` value.
|
|
47
|
+
*
|
|
48
|
+
* `labels attach` writes ids, and without this line the only way to see
|
|
49
|
+
* whether one landed was to attach it again and read "No change — labels
|
|
50
|
+
* already attached" (OSK-10313). Names, because an id is not something a
|
|
51
|
+
* person recognises — but an id beats nothing, so a vocabulary that could not
|
|
52
|
+
* be read (or a label missing from it) degrades per entry rather than failing
|
|
53
|
+
* the command or hiding the label.
|
|
54
|
+
*/
|
|
55
|
+
function labelsLine(labelIds, nameById) {
|
|
56
|
+
const ids = (labelIds ?? []).filter((id) => typeof id === 'string' && id.length > 0);
|
|
57
|
+
if (ids.length === 0)
|
|
58
|
+
return '-';
|
|
59
|
+
return ids.map((id) => nameById?.get(id) ?? id).join(', ');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The `Executor:` value — who does this task, and whether anyone said so.
|
|
63
|
+
*
|
|
64
|
+
* It printed `legacy / inferred` whenever `executionMode` was absent, which is
|
|
65
|
+
* the normal result of `tasks create` without `--execution-mode`. A task made
|
|
66
|
+
* ten seconds earlier by the most ordinary invocation there is read as old data
|
|
67
|
+
* made the deprecated way (OSK-4629). The executor is now the one the server
|
|
68
|
+
* acts on (`resolveTaskExecutor`, shared with `TaskActionability`), and the
|
|
69
|
+
* value says when it is implied by the assignees rather than declared, because
|
|
70
|
+
* reassigning the task changes only the implied kind.
|
|
71
|
+
*/
|
|
72
|
+
function taskExecutorLabel(t, bin = 'skrr') {
|
|
73
|
+
const executor = (0, data_provider_1.resolveTaskExecutor)(t);
|
|
74
|
+
switch (executor.source) {
|
|
75
|
+
case 'declared':
|
|
76
|
+
return executor.mode;
|
|
77
|
+
case 'assignees':
|
|
78
|
+
return `${executor.mode} (not declared; implied by the ${executor.mode} assignees)`;
|
|
79
|
+
case 'mixed-assignees':
|
|
80
|
+
return `not chosen — Human and Agent assignees; pick one with \`${bin} tasks update --execution-mode\``;
|
|
81
|
+
case 'unassigned':
|
|
82
|
+
return '-';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
42
85
|
/**
|
|
43
86
|
* The failure rows of `tasks show`, when there are any. (S3 dogfood finding)
|
|
44
87
|
*
|
|
@@ -284,7 +327,7 @@ class TasksShow extends base_command_1.BaseCommand {
|
|
|
284
327
|
}
|
|
285
328
|
this.log(`Result: ${priorLine}`);
|
|
286
329
|
}
|
|
287
|
-
this.log(`Executor: ${t.
|
|
330
|
+
this.log(`Executor: ${taskExecutorLabel(t, this.config.bin)}`);
|
|
288
331
|
this.log(`Priority: ${t.priority ?? '-'}`);
|
|
289
332
|
this.log(`Parent: ${t.parentId ?? '-'}`);
|
|
290
333
|
this.log(`Space: ${t.spaceId ?? '-'}`);
|
|
@@ -293,6 +336,23 @@ class TasksShow extends base_command_1.BaseCommand {
|
|
|
293
336
|
// was no way to tell an ignored flag from an honoured one.
|
|
294
337
|
this.log(`Project: ${t.projectId ?? '-'}`);
|
|
295
338
|
this.log(`Goal: ${t.goalId ?? '-'}`);
|
|
339
|
+
// Printed for the same reason Project is: `labels attach` writes ids, and
|
|
340
|
+
// without this line the only way to see whether one landed was to attach
|
|
341
|
+
// it again and read "No change — labels already attached" (OSK-10313).
|
|
342
|
+
// Names, because an id is not something a person recognises; a vocabulary
|
|
343
|
+
// read that fails prints the ids rather than failing the whole command.
|
|
344
|
+
const labelIds = Array.isArray(t.labelIds) ? t.labelIds : [];
|
|
345
|
+
let labelNames;
|
|
346
|
+
if (labelIds.length > 0) {
|
|
347
|
+
try {
|
|
348
|
+
const vocabulary = await (0, label_ref_1.collectLabelVocabulary)({ applicableTo: 'task' });
|
|
349
|
+
labelNames = new Map(vocabulary.map((row) => [row.id, row.name]));
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
// A vocabulary that cannot be read prints ids; see labelsLine.
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
this.log(`Labels: ${labelsLine(labelIds, labelNames)}`);
|
|
296
356
|
this.log(`Author: ${t.author ?? '-'}`);
|
|
297
357
|
const dri = t.dri;
|
|
298
358
|
if (dri?.resolved) {
|
|
@@ -403,6 +403,18 @@ class TasksUpdate extends base_command_1.BaseCommand {
|
|
|
403
403
|
});
|
|
404
404
|
}
|
|
405
405
|
catch (err) {
|
|
406
|
+
// The server's refusal says "detach the goal with goalId: null", which is
|
|
407
|
+
// not something a CLI user can type. Name the id sent and `--goal none`.
|
|
408
|
+
const goalId = payload.goalId;
|
|
409
|
+
if ((0, base_command_1.apiRefusal)(err).code === tasks_2.TASK_GOAL_NOT_FOUND && typeof goalId === 'string' && goalId) {
|
|
410
|
+
this.failWithCliError({
|
|
411
|
+
message: (0, tasks_2.describeTaskGoalNotFound)({ goalId, command: 'update', bin: this.config.bin }),
|
|
412
|
+
code: tasks_2.TASK_GOAL_NOT_FOUND,
|
|
413
|
+
status: 400,
|
|
414
|
+
exit: 1,
|
|
415
|
+
retryable: false,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
406
418
|
this.handleApiError(err);
|
|
407
419
|
}
|
|
408
420
|
// Postcondition, not a prediction — see `droppedEstimateWarning`. On stderr
|
|
@@ -19,7 +19,10 @@ class ViewsCreate extends base_command_1.BaseCommand {
|
|
|
19
19
|
'from-json': core_1.Flags.string({ description: 'JSON Task View body' }),
|
|
20
20
|
name: core_1.Flags.string({ description: 'View name' }),
|
|
21
21
|
description: core_1.Flags.string({ description: 'Short explanation shown in the modal' }),
|
|
22
|
-
type: core_1.Flags.string({
|
|
22
|
+
type: core_1.Flags.string({
|
|
23
|
+
options: [...vocabulary_1.VIEW_TYPES],
|
|
24
|
+
description: `Layout (default: ${vocabulary_1.TASK_VIEW_DEFAULT_TYPE})`,
|
|
25
|
+
}),
|
|
23
26
|
'task-id': core_1.Flags.string({ multiple: true, description: 'Include this exact task ID' }),
|
|
24
27
|
space: core_1.Flags.string({ multiple: true, description: 'Limit to this Space ID' }),
|
|
25
28
|
// Status labels are per-Space. The API bounds and normalizes values but
|
|
@@ -41,7 +44,7 @@ class ViewsCreate extends base_command_1.BaseCommand {
|
|
|
41
44
|
}),
|
|
42
45
|
'group-by': core_1.Flags.string({
|
|
43
46
|
options: [...vocabulary_1.VIEW_GROUP_BY_VALUES],
|
|
44
|
-
description:
|
|
47
|
+
description: `Grouping dimension (default: ${vocabulary_1.TASK_VIEW_DEFAULT_GROUP_BY})`,
|
|
45
48
|
}),
|
|
46
49
|
sort: core_1.Flags.string({
|
|
47
50
|
options: [...vocabulary_1.VIEW_SORT_KEYS],
|
|
@@ -107,6 +110,13 @@ class ViewsCreate extends base_command_1.BaseCommand {
|
|
|
107
110
|
return void this.log(JSON.stringify(result, null, 2));
|
|
108
111
|
}
|
|
109
112
|
this.log(`Created Task View ${view.id}`);
|
|
113
|
+
// What the SERVER saved, read back from the response — not an echo of the
|
|
114
|
+
// flags. Creation is the one moment the caller still knows what they meant
|
|
115
|
+
// to type, so it is where a filter that silently did not take has to show
|
|
116
|
+
// (dogfood OSK-4646). Same renderer as `views show`.
|
|
117
|
+
for (const line of (0, task_view_render_1.describeTaskView)(view)) {
|
|
118
|
+
this.log(line);
|
|
119
|
+
}
|
|
110
120
|
this.log(result.url);
|
|
111
121
|
if (warning)
|
|
112
122
|
this.warn(warning);
|
|
@@ -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 format_1 = require("../../lib/format");
|
|
7
|
+
const task_view_render_1 = require("../../lib/task-view-render");
|
|
7
8
|
const web_url_1 = require("../../lib/web-url");
|
|
8
9
|
class ViewsList extends base_command_1.BaseCommand {
|
|
9
10
|
static description = 'List personalized Task Views';
|
|
@@ -30,13 +31,13 @@ class ViewsList extends base_command_1.BaseCommand {
|
|
|
30
31
|
name: view.name,
|
|
31
32
|
type: view.viewType,
|
|
32
33
|
group: view.groupBy,
|
|
33
|
-
|
|
34
|
+
selection: (0, task_view_render_1.summarizeTaskViewSelection)(view),
|
|
34
35
|
id: view.id,
|
|
35
36
|
})), [
|
|
36
37
|
{ key: 'name', header: 'NAME', maxWidth: 36 },
|
|
37
38
|
{ key: 'type', header: 'TYPE' },
|
|
38
39
|
{ key: 'group', header: 'GROUP BY' },
|
|
39
|
-
{ key: '
|
|
40
|
+
{ key: 'selection', header: 'SELECTION', maxWidth: 48 },
|
|
40
41
|
{ key: 'id', header: 'ID' },
|
|
41
42
|
], (line) => this.log(line));
|
|
42
43
|
}
|
|
@@ -33,6 +33,8 @@ class ViewsShow extends base_command_1.BaseCommand {
|
|
|
33
33
|
this.log(line);
|
|
34
34
|
}
|
|
35
35
|
this.log(result.url);
|
|
36
|
+
// The criteria are half the answer; the tasks they select are the other.
|
|
37
|
+
this.log(`Tasks: ${this.config.bin} tasks list --view ${view.id}`);
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
exports.default = ViewsShow;
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Why a question was dismissed when no terminal exists to ask it on. It is the
|
|
3
|
+
* REASON for a dismissal, never an answer: it travels as the value of the
|
|
4
|
+
* reserved `_dismissed` key, which every consumer already reads as "nobody
|
|
5
|
+
* answered" — the web card sends the same key when a person closes it.
|
|
6
|
+
*
|
|
7
|
+
* It used to be written into every question's answer slot (OSK-9767). That was
|
|
8
|
+
* harmless text for Claude's AskUserQuestion, but a Devin form elicitation read
|
|
9
|
+
* it as the field's value and was answered `accept` with this sentence filled in
|
|
10
|
+
* — consent nobody gave, on a form nobody saw (OSK-10274).
|
|
5
11
|
*/
|
|
6
|
-
export declare const
|
|
12
|
+
export declare const NON_INTERACTIVE_QUESTION_DISMISSAL: string;
|
|
7
13
|
export type AgenticStreamEvent = {
|
|
8
14
|
type: string;
|
|
9
15
|
sessionId?: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.
|
|
3
|
+
exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.NON_INTERACTIVE_QUESTION_DISMISSAL = void 0;
|
|
4
4
|
exports.writeJsonLine = writeJsonLine;
|
|
5
5
|
exports.envelopesText = envelopesText;
|
|
6
6
|
exports.envelopeKey = envelopeKey;
|
|
@@ -20,11 +20,17 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
20
20
|
const config_1 = require("./config");
|
|
21
21
|
const publicEndpoints_generated_1 = require("./publicEndpoints.generated");
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* Why a question was dismissed when no terminal exists to ask it on. It is the
|
|
24
|
+
* REASON for a dismissal, never an answer: it travels as the value of the
|
|
25
|
+
* reserved `_dismissed` key, which every consumer already reads as "nobody
|
|
26
|
+
* answered" — the web card sends the same key when a person closes it.
|
|
27
|
+
*
|
|
28
|
+
* It used to be written into every question's answer slot (OSK-9767). That was
|
|
29
|
+
* harmless text for Claude's AskUserQuestion, but a Devin form elicitation read
|
|
30
|
+
* it as the field's value and was answered `accept` with this sentence filled in
|
|
31
|
+
* — consent nobody gave, on a form nobody saw (OSK-10274).
|
|
26
32
|
*/
|
|
27
|
-
exports.
|
|
33
|
+
exports.NON_INTERACTIVE_QUESTION_DISMISSAL = 'No answer: this turn was started non-interactively (no terminal), so nobody can reply to ' +
|
|
28
34
|
'questions. Proceed with your best judgement, or finish by stating exactly what you need.';
|
|
29
35
|
/**
|
|
30
36
|
* Write exactly one complete JSON value on one line.
|
|
@@ -710,13 +716,21 @@ class AgenticStreamClient {
|
|
|
710
716
|
// "Not answered … timed out", at the cost of the whole wait. Answer at once
|
|
711
717
|
// instead, saying why, so the agent proceeds or states what it needs. An
|
|
712
718
|
// explicit `input` (tests, an embedding caller) is treated as interactive.
|
|
719
|
+
//
|
|
720
|
+
// "At once" is a DISMISSAL, never an answer. Nothing goes in any question's
|
|
721
|
+
// answer slot: a harness that turns answers into structured consent (an ACP
|
|
722
|
+
// form elicitation) would otherwise accept whatever text sits there
|
|
723
|
+
// (OSK-10274). The reason rides on `_dismissed`, where the server maps it to
|
|
724
|
+
// a dismissal for daemon sessions and a cloud session still reads why.
|
|
713
725
|
const interactive = this.options.input !== undefined || Boolean(process.stdin.isTTY);
|
|
714
726
|
const answers = {};
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
727
|
+
if (interactive) {
|
|
728
|
+
for (const question of event.questions ?? []) {
|
|
729
|
+
answers[question.header ?? question.question] = await this.ask(`${question.header ?? 'Question'}: ${question.question}\n> `);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
answers._dismissed = exports.NON_INTERACTIVE_QUESTION_DISMISSAL;
|
|
720
734
|
}
|
|
721
735
|
if (event.questionId && this.socket) {
|
|
722
736
|
this.socket.emit('question:answer', {
|
|
@@ -824,7 +838,7 @@ class AgenticStreamClient {
|
|
|
824
838
|
this.renderedText = this.accumulated;
|
|
825
839
|
}
|
|
826
840
|
else if (event.type === 'question.answered' && event.nonInteractive) {
|
|
827
|
-
output.write('\n[question] no terminal to answer on, so the
|
|
841
|
+
output.write('\n[question] no terminal to answer on, so the question was dismissed unanswered\n');
|
|
828
842
|
this.renderedText = this.accumulated;
|
|
829
843
|
}
|
|
830
844
|
}
|
|
@@ -129,9 +129,17 @@ function readinessOf(provider) {
|
|
|
129
129
|
ok: false,
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
+
// Every other not-ready state tells the reader what to do next, so this one
|
|
133
|
+
// does too. Name the install command only when the daemon says it can
|
|
134
|
+
// install the provider. Otherwise the daemon's own reason, or the bare
|
|
135
|
+
// fact, is the whole answer.
|
|
136
|
+
const key = canonical(provider.provider);
|
|
132
137
|
return {
|
|
133
138
|
label: 'not-installed',
|
|
134
|
-
detail: provider.installDisabledReason ||
|
|
139
|
+
detail: provider.installDisabledReason ||
|
|
140
|
+
(provider.installable === true && key
|
|
141
|
+
? `No binary for this provider on this machine. Install it with \`skrr harnesses install ${key}\`.`
|
|
142
|
+
: 'No binary for this provider on this machine.'),
|
|
135
143
|
ok: false,
|
|
136
144
|
};
|
|
137
145
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* whole area has been removing. So: asked when there is someone to ask, skipped
|
|
29
29
|
* with an instruction when there is not, and never on the opt-out.
|
|
30
30
|
*/
|
|
31
|
+
import { type LegacyStateReport } from '@skrr-ai/auth-core';
|
|
31
32
|
import { confirm } from './prompt';
|
|
32
33
|
export interface DaemonSetupOutcome {
|
|
33
34
|
status: 'installed' | 'declined' | 'skipped' | 'failed';
|
|
@@ -41,9 +42,25 @@ export interface DaemonSetupOptions {
|
|
|
41
42
|
confirmImpl?: typeof confirm;
|
|
42
43
|
interactive?: boolean;
|
|
43
44
|
log?: (line: string) => void;
|
|
45
|
+
/** Injected by tests. */
|
|
46
|
+
findLegacyStateImpl?: () => LegacyStateReport;
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Refuse, before anything is fetched or asked, on a machine still carrying state
|
|
50
|
+
* from the previous product identity.
|
|
51
|
+
*
|
|
52
|
+
* The runtime refuses the same machine in `skrrd install`, and it is right to —
|
|
53
|
+
* but that check can only run once there is a runtime to run it, so onboarding
|
|
54
|
+
* downloaded a release, asked "Set it up now?", and only then stopped (OSK-6895).
|
|
55
|
+
* The detection is a read of two directory names, so the CLI does it first and
|
|
56
|
+
* the refusal names `skrr daemon install`, the command the person actually uses.
|
|
57
|
+
* The runtime's own check stays: `skrrd install` can be run directly.
|
|
58
|
+
*
|
|
59
|
+
* Returns true when it refused, having printed why and what to run.
|
|
60
|
+
*/
|
|
61
|
+
export declare function refuseOnLegacyLocalState(log: (line: string) => void, findImpl?: () => LegacyStateReport): boolean;
|
|
45
62
|
/**
|
|
46
63
|
* Ask, then install. Returns without touching anything when there is nobody to
|
|
47
|
-
* ask
|
|
64
|
+
* ask, the operator has opted out, or the machine still needs its cutover.
|
|
48
65
|
*/
|
|
49
66
|
export declare function offerDaemonSetup(opts?: DaemonSetupOptions): Promise<DaemonSetupOutcome>;
|
package/dist/lib/daemon-setup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.refuseOnLegacyLocalState = refuseOnLegacyLocalState;
|
|
3
4
|
exports.offerDaemonSetup = offerDaemonSetup;
|
|
4
5
|
/**
|
|
5
6
|
* daemon-setup.ts — offer to set this machine up, at the moment we can.
|
|
@@ -31,15 +32,40 @@ exports.offerDaemonSetup = offerDaemonSetup;
|
|
|
31
32
|
* whole area has been removing. So: asked when there is someone to ask, skipped
|
|
32
33
|
* with an instruction when there is not, and never on the opt-out.
|
|
33
34
|
*/
|
|
35
|
+
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
34
36
|
const prompt_1 = require("./prompt");
|
|
35
37
|
const daemon_binding_1 = require("./daemon-binding");
|
|
36
38
|
const daemon_handoff_opt_out_1 = require("./daemon-handoff-opt-out");
|
|
37
39
|
const exec_runtime_binary_1 = require("./exec-runtime-binary");
|
|
38
40
|
const daemon_installer_1 = require("./daemon-installer");
|
|
39
41
|
const ssh_detect_1 = require("./ssh-detect");
|
|
42
|
+
/**
|
|
43
|
+
* Refuse, before anything is fetched or asked, on a machine still carrying state
|
|
44
|
+
* from the previous product identity.
|
|
45
|
+
*
|
|
46
|
+
* The runtime refuses the same machine in `skrrd install`, and it is right to —
|
|
47
|
+
* but that check can only run once there is a runtime to run it, so onboarding
|
|
48
|
+
* downloaded a release, asked "Set it up now?", and only then stopped (OSK-6895).
|
|
49
|
+
* The detection is a read of two directory names, so the CLI does it first and
|
|
50
|
+
* the refusal names `skrr daemon install`, the command the person actually uses.
|
|
51
|
+
* The runtime's own check stays: `skrrd install` can be run directly.
|
|
52
|
+
*
|
|
53
|
+
* Returns true when it refused, having printed why and what to run.
|
|
54
|
+
*/
|
|
55
|
+
function refuseOnLegacyLocalState(log, findImpl = auth_core_1.findLegacyLocalState) {
|
|
56
|
+
const report = findImpl();
|
|
57
|
+
if (!report.hasLegacyState)
|
|
58
|
+
return false;
|
|
59
|
+
log('');
|
|
60
|
+
for (const line of (0, auth_core_1.describeLegacyState)(report, 'skrr daemon').split('\n')) {
|
|
61
|
+
log(line ? ` ${line}` : '');
|
|
62
|
+
}
|
|
63
|
+
log('');
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
40
66
|
/**
|
|
41
67
|
* Ask, then install. Returns without touching anything when there is nobody to
|
|
42
|
-
* ask
|
|
68
|
+
* ask, the operator has opted out, or the machine still needs its cutover.
|
|
43
69
|
*/
|
|
44
70
|
async function offerDaemonSetup(opts = {}) {
|
|
45
71
|
const env = opts.env ?? process.env;
|
|
@@ -51,6 +77,12 @@ async function offerDaemonSetup(opts = {}) {
|
|
|
51
77
|
if (optOut) {
|
|
52
78
|
return { status: 'skipped', detail: `skipped by ${optOut}` };
|
|
53
79
|
}
|
|
80
|
+
if (refuseOnLegacyLocalState(log, opts.findLegacyStateImpl)) {
|
|
81
|
+
return {
|
|
82
|
+
status: 'skipped',
|
|
83
|
+
detail: 'state from the previous product identity is still here — finish the cutover above, then run `skrr daemon install`',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
54
86
|
const interactive = opts.interactive ?? !(0, ssh_detect_1.isNonInteractive)();
|
|
55
87
|
if (!interactive) {
|
|
56
88
|
// A prompt with no terminal hangs forever, which is worse than the extra
|
|
@@ -83,7 +83,7 @@ interface AgentRow {
|
|
|
83
83
|
defaultForHarness?: string;
|
|
84
84
|
}
|
|
85
85
|
export interface FirstPartyHarnessAgentDeps {
|
|
86
|
-
/** Agents this user may run.
|
|
86
|
+
/** Agents this user may run. Paged and bounded; see `ROSTER_MAX`. */
|
|
87
87
|
listAgents?: (signal?: AbortSignal) => Promise<AgentRow[]>;
|
|
88
88
|
/** Direct read, for an id outside the bounded roster. `null` when unreadable. */
|
|
89
89
|
getAgent?: (id: string, signal?: AbortSignal) => Promise<AgentRow | null>;
|
|
@@ -158,4 +158,19 @@ export declare function readConfiguredAgentId(): string | undefined;
|
|
|
158
158
|
export declare function setConfiguredAgentId(ref: string, deps?: FirstPartyHarnessAgentDeps, options?: FirstPartyHarnessAgentOptions): Promise<FirstPartyHarnessAgent>;
|
|
159
159
|
/** Remove the setting. Returns what was there, so the caller can say so. */
|
|
160
160
|
export declare function clearConfiguredAgentId(): string | undefined;
|
|
161
|
+
/** One `/api/agents` response: a list page, or a bare array from an older server. */
|
|
162
|
+
export type AgentRosterPage = {
|
|
163
|
+
data?: AgentRow[];
|
|
164
|
+
agents?: AgentRow[];
|
|
165
|
+
has_more?: boolean;
|
|
166
|
+
after?: string | null;
|
|
167
|
+
} | AgentRow[];
|
|
168
|
+
/**
|
|
169
|
+
* Read roster pages until the server reports no more, up to `max` rows.
|
|
170
|
+
*
|
|
171
|
+
* A page that does not say `has_more` with a usable `after` cursor ends the walk,
|
|
172
|
+
* so an older server that ignores `cursor` (or returns a bare array) costs exactly
|
|
173
|
+
* the one request it always did.
|
|
174
|
+
*/
|
|
175
|
+
export declare function collectRosterPages(fetchPage: (cursor?: string) => Promise<AgentRosterPage>, max?: number): Promise<AgentRow[]>;
|
|
161
176
|
export {};
|