@skrr-ai/cli 0.1.19 → 0.1.21
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 +12 -9
- package/dist/base-command.js +27 -26
- package/dist/commands/commitments/action-proposals/decide.js +22 -5
- package/dist/commands/commitments/action-proposals/execute.js +7 -3
- package/dist/commands/commitments/action-proposals/propose.d.ts +32 -0
- package/dist/commands/commitments/action-proposals/propose.js +111 -0
- package/dist/commands/commitments/action-proposals.js +6 -4
- package/dist/commands/commitments/create.d.ts +2 -0
- package/dist/commands/commitments/create.js +42 -2
- package/dist/commands/commitments/doctor.js +11 -0
- package/dist/commands/commitments/update.d.ts +2 -0
- package/dist/commands/commitments/update.js +40 -5
- package/dist/commands/store/browse.d.ts +14 -0
- package/dist/commands/store/browse.js +71 -0
- package/dist/commands/store/install.d.ts +16 -0
- package/dist/commands/store/install.js +54 -0
- package/dist/commands/store/releases.d.ts +19 -0
- package/dist/commands/store/releases.js +60 -0
- package/dist/commands/store/update.d.ts +15 -0
- package/dist/commands/store/update.js +69 -0
- package/dist/commands/store/updates.d.ts +24 -0
- package/dist/commands/store/updates.js +113 -0
- package/dist/commands/subscriptions/cancel.d.ts +14 -0
- package/dist/commands/subscriptions/cancel.js +47 -0
- package/dist/commands/subscriptions/health.d.ts +24 -0
- package/dist/commands/subscriptions/health.js +62 -0
- package/dist/commands/subscriptions/list.d.ts +9 -0
- package/dist/commands/subscriptions/list.js +52 -0
- package/dist/commands/subscriptions/status.d.ts +12 -0
- package/dist/commands/subscriptions/status.js +49 -0
- package/dist/commands/subscriptions/subscribe.d.ts +15 -0
- package/dist/commands/subscriptions/subscribe.js +59 -0
- package/dist/commands/tasks/complete.d.ts +10 -4
- package/dist/commands/tasks/complete.js +7 -5
- package/dist/commands/tasks/self-schedule.d.ts +30 -0
- package/dist/commands/tasks/self-schedule.js +114 -0
- package/dist/commands/triggers/disable.js +3 -1
- package/dist/commands/triggers/enable.js +3 -1
- package/dist/commands/triggers/rotate-secret.js +5 -0
- package/dist/commands/triggers/show.js +3 -1
- package/dist/lib/commitment-product.js +9 -0
- package/dist/lib/commitments.d.ts +21 -1
- package/dist/lib/commitments.js +62 -1
- package/dist/lib/node-adapter.js +4 -0
- package/dist/lib/task-extras.d.ts +18 -0
- package/dist/lib/task-extras.js +19 -1
- package/dist/lib/triggers.d.ts +21 -2
- package/dist/lib/triggers.js +48 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/refreshClassification.js +9 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/refreshClassification.js +9 -2
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4073 -3904
- package/oclif.manifest.json +18424 -17362
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
export default class SubscriptionsStatus extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
class SubscriptionsStatus extends base_command_1.BaseCommand {
|
|
7
|
+
static description = 'Show whether a service subscription is live, and until when';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> subscriptions status agent_abc123',
|
|
10
|
+
'<%= config.bin %> subscriptions status agent_abc123 --json',
|
|
11
|
+
];
|
|
12
|
+
static args = {
|
|
13
|
+
agentId: core_1.Args.string({ description: 'The service to check', required: true }),
|
|
14
|
+
};
|
|
15
|
+
static flags = {
|
|
16
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
this.requireAuth();
|
|
20
|
+
const { args, flags } = await this.parse(SubscriptionsStatus);
|
|
21
|
+
let response;
|
|
22
|
+
try {
|
|
23
|
+
response = await data_provider_1.dataService.getStoreAgentSubscriptionStatus(args.agentId);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
this.handleApiError(err);
|
|
27
|
+
}
|
|
28
|
+
if (flags.json) {
|
|
29
|
+
this.log(JSON.stringify(response, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!response?.subscribed) {
|
|
33
|
+
// The two reasons want different next actions, so they are printed
|
|
34
|
+
// differently rather than collapsed into "not subscribed".
|
|
35
|
+
this.log(response?.reason === 'expired'
|
|
36
|
+
? 'Not live — the subscription expired. Renewing resumes it.'
|
|
37
|
+
: 'Not subscribed.');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.log(`Live. Entitlement ${response.entitlementId} (v${response.version})`);
|
|
41
|
+
this.log(`Your instance: ${response.instanceAgentId ?? '-'}`);
|
|
42
|
+
this.log(`Compute paid by: ${response.billingMode}`);
|
|
43
|
+
this.log(`Expires: ${response.expiresAt ? String(response.expiresAt) : 'no end date'}`);
|
|
44
|
+
if (response.commitmentId) {
|
|
45
|
+
this.log(`Obligation: ${response.commitmentId}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.default = SubscriptionsStatus;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
export default class SubscriptionsSubscribe extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
'billing-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
'expires-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
class SubscriptionsSubscribe extends base_command_1.BaseCommand {
|
|
7
|
+
static description = 'Subscribe to a published service — installs your own managed instance and opens the obligation it is delivered through';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> subscriptions subscribe agent_abc123',
|
|
10
|
+
'<%= config.bin %> subscriptions subscribe agent_abc123 --billing-mode per-user',
|
|
11
|
+
'<%= config.bin %> subscriptions subscribe agent_abc123 --space space_xyz --json',
|
|
12
|
+
];
|
|
13
|
+
static args = {
|
|
14
|
+
agentId: core_1.Args.string({ description: 'The published service to subscribe to', required: true }),
|
|
15
|
+
};
|
|
16
|
+
static flags = {
|
|
17
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
18
|
+
'billing-mode': core_1.Flags.string({
|
|
19
|
+
description: 'Who pays for the inference this service consumes',
|
|
20
|
+
options: ['owner', 'per-user', 'shared'],
|
|
21
|
+
}),
|
|
22
|
+
'expires-at': core_1.Flags.string({ description: 'Paid-through date (ISO 8601)' }),
|
|
23
|
+
space: core_1.Flags.string({ description: 'Space the service should work in' }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
this.requireAuth();
|
|
27
|
+
const { args, flags } = await this.parse(SubscriptionsSubscribe);
|
|
28
|
+
let response;
|
|
29
|
+
try {
|
|
30
|
+
response = await data_provider_1.dataService.subscribeToStoreAgent(args.agentId, {
|
|
31
|
+
...(flags['billing-mode']
|
|
32
|
+
? { billingMode: flags['billing-mode'] }
|
|
33
|
+
: {}),
|
|
34
|
+
...(flags['expires-at'] ? { expiresAt: flags['expires-at'] } : {}),
|
|
35
|
+
...(flags.space ? { spaceId: flags.space } : {}),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
this.handleApiError(err);
|
|
40
|
+
}
|
|
41
|
+
if (flags.json) {
|
|
42
|
+
this.log(JSON.stringify(response, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.log(`Subscribed. Entitlement ${response?.entitlementId}`);
|
|
46
|
+
this.log(`Your instance: ${response?.instanceAgentId ?? '-'}`);
|
|
47
|
+
this.log(`Compute paid by: ${response?.billingMode}`);
|
|
48
|
+
if (response?.commitmentId) {
|
|
49
|
+
// The commitment is the thing you can hold the service to — surface it,
|
|
50
|
+
// because "is it working" is answered there and nowhere else.
|
|
51
|
+
this.log(`Obligation: ${response.commitmentId}`);
|
|
52
|
+
}
|
|
53
|
+
if (response?.degraded === 'commitment_pending') {
|
|
54
|
+
this.log('');
|
|
55
|
+
this.log('Note: the subscription is live but its obligation has not been created yet. It is resumable and refundable; retry to finish opening it.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.default = SubscriptionsSubscribe;
|
|
@@ -6,12 +6,19 @@ type CompletionStatus = 'completed' | 'failed';
|
|
|
6
6
|
type TaskCommentResult = Awaited<ReturnType<typeof dataService.addTaskComment>>;
|
|
7
7
|
type MovedTaskResult = Awaited<ReturnType<typeof dataService.moveTask>>;
|
|
8
8
|
/** Wire shape of POST /api/tasks/:taskId/complete (OSK-1969). */
|
|
9
|
+
/** One thing a completion did not have. Never a refusal — `done` has none. */
|
|
10
|
+
export interface CompletionAdvisory {
|
|
11
|
+
code: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
9
14
|
export interface CompleteEndpointResponse {
|
|
10
15
|
task: MovedTaskResult | null;
|
|
11
16
|
comment: TaskCommentResult | null;
|
|
12
17
|
commentFailed?: boolean;
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
/** What the completion was missing. `done` has no preconditions, so every
|
|
19
|
+
* condition that used to refuse one now travels with it instead. The task
|
|
20
|
+
* reached the requested terminal state regardless. */
|
|
21
|
+
advisories?: CompletionAdvisory[];
|
|
15
22
|
}
|
|
16
23
|
export interface CompleteTransport {
|
|
17
24
|
/** POST /api/tasks/:taskId/complete — atomic move + TASK_OUTPUT comment. */
|
|
@@ -35,8 +42,7 @@ export interface DurableCompletionResult {
|
|
|
35
42
|
movedTask: MovedTaskResult | null;
|
|
36
43
|
moved: boolean;
|
|
37
44
|
commentFailed: boolean;
|
|
38
|
-
|
|
39
|
-
deliveryReason?: string;
|
|
45
|
+
advisories?: CompletionAdvisory[];
|
|
40
46
|
/** true when the atomic /complete endpoint handled the finalization. */
|
|
41
47
|
usedAtomicEndpoint: boolean;
|
|
42
48
|
}
|
|
@@ -194,8 +194,7 @@ async function completeTaskDurably(opts) {
|
|
|
194
194
|
movedTask: response.task ?? null,
|
|
195
195
|
moved: true,
|
|
196
196
|
commentFailed: response.commentFailed === true,
|
|
197
|
-
...(response.
|
|
198
|
-
...(response.deliveryReason ? { deliveryReason: response.deliveryReason } : {}),
|
|
197
|
+
...(response.advisories?.length ? { advisories: response.advisories } : {}),
|
|
199
198
|
usedAtomicEndpoint: true,
|
|
200
199
|
};
|
|
201
200
|
}
|
|
@@ -611,7 +610,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
611
610
|
...(report ? { report } : {}),
|
|
612
611
|
body,
|
|
613
612
|
},
|
|
614
|
-
...(result.
|
|
613
|
+
...(result.advisories?.length ? { advisories: result.advisories } : {}),
|
|
615
614
|
};
|
|
616
615
|
if (flags.json) {
|
|
617
616
|
const url = movedTask
|
|
@@ -634,8 +633,11 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
634
633
|
? `Task ${completedTaskId} was already ${newStatus}; the outcome was amended.`
|
|
635
634
|
: `Moved task ${completedTaskId} to ${newStatus}`);
|
|
636
635
|
}
|
|
637
|
-
|
|
638
|
-
|
|
636
|
+
for (const advisory of result.advisories ?? []) {
|
|
637
|
+
// One warn per unmet condition. Each already reads as an observation —
|
|
638
|
+
// the completion happened — so the CLI relays them rather than
|
|
639
|
+
// re-wording them into something that sounds like a failure.
|
|
640
|
+
this.warn(advisory.message);
|
|
639
641
|
}
|
|
640
642
|
if (report) {
|
|
641
643
|
const url = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, movedTask ?? { id: completedTaskId });
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks self-schedule` — park a task for a self-initiated return.
|
|
4
|
+
*
|
|
5
|
+
* Distinct from `skrr tasks schedule`, and the distinction is the point.
|
|
6
|
+
* `schedule` writes the AUTHOR's calendar (`scheduledFor` + `recurrence`), the
|
|
7
|
+
* thing the scheduler consumes and re-arms and every calendar surface renders.
|
|
8
|
+
* This writes a separate `self_scheduled` trigger row, so an agent working the
|
|
9
|
+
* task can say "come back in two hours" without editing the human's Monday-9am
|
|
10
|
+
* recurrence to mean something the human never asked for.
|
|
11
|
+
*
|
|
12
|
+
* Both can be set at once. Neither erases the other.
|
|
13
|
+
*/
|
|
14
|
+
export default class TasksSelfSchedule extends BaseCommand {
|
|
15
|
+
static description: string;
|
|
16
|
+
static examples: string[];
|
|
17
|
+
static args: {
|
|
18
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
19
|
+
};
|
|
20
|
+
static flags: {
|
|
21
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
at: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
in: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
note: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
'max-per-day': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
|
+
show: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
clear: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
28
|
+
};
|
|
29
|
+
run(): Promise<void>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_command_1 = require("../../base-command");
|
|
5
|
+
const task_extras_1 = require("../../lib/task-extras");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr tasks self-schedule` — park a task for a self-initiated return.
|
|
8
|
+
*
|
|
9
|
+
* Distinct from `skrr tasks schedule`, and the distinction is the point.
|
|
10
|
+
* `schedule` writes the AUTHOR's calendar (`scheduledFor` + `recurrence`), the
|
|
11
|
+
* thing the scheduler consumes and re-arms and every calendar surface renders.
|
|
12
|
+
* This writes a separate `self_scheduled` trigger row, so an agent working the
|
|
13
|
+
* task can say "come back in two hours" without editing the human's Monday-9am
|
|
14
|
+
* recurrence to mean something the human never asked for.
|
|
15
|
+
*
|
|
16
|
+
* Both can be set at once. Neither erases the other.
|
|
17
|
+
*/
|
|
18
|
+
class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
19
|
+
static description = "Park a task for a self-initiated return — separate from the author's schedule";
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> tasks self-schedule <task-id> --in 2h --note "check whether the deploy settled"',
|
|
22
|
+
'<%= config.bin %> tasks self-schedule <task-id> --at 2026-09-09T14:00:00Z',
|
|
23
|
+
'<%= config.bin %> tasks self-schedule <task-id> --show',
|
|
24
|
+
'<%= config.bin %> tasks self-schedule <task-id> --clear',
|
|
25
|
+
];
|
|
26
|
+
static args = {
|
|
27
|
+
id: core_1.Args.string({ description: 'Task ID', required: true, ignoreStdin: true }),
|
|
28
|
+
};
|
|
29
|
+
static flags = {
|
|
30
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
31
|
+
at: core_1.Flags.string({ description: 'Absolute return time (ISO-8601 UTC)' }),
|
|
32
|
+
in: core_1.Flags.string({ description: 'Relative return time such as 90m, 2h, 3d' }),
|
|
33
|
+
note: core_1.Flags.string({
|
|
34
|
+
// Was "handed to the agent as the return prompt", which is not true:
|
|
35
|
+
// `task_execute` builds the run's prompt from the Task, so this string is
|
|
36
|
+
// recorded and never read by the run. See `TaskScheduler/selfSchedule.js`.
|
|
37
|
+
description: 'Why you are coming back — recorded on the return for you to read back',
|
|
38
|
+
}),
|
|
39
|
+
'max-per-day': core_1.Flags.integer({
|
|
40
|
+
description: 'Cap on self-initiated returns per day (1-24, default 4)',
|
|
41
|
+
}),
|
|
42
|
+
show: core_1.Flags.boolean({ description: 'Print the pending return without changing it' }),
|
|
43
|
+
clear: core_1.Flags.boolean({ description: 'Cancel the pending return' }),
|
|
44
|
+
};
|
|
45
|
+
async run() {
|
|
46
|
+
this.requireAuth();
|
|
47
|
+
const { args, flags } = await this.parse(TasksSelfSchedule);
|
|
48
|
+
if (flags.show && flags.clear) {
|
|
49
|
+
this.error('Pass at most one of --show or --clear.', { exit: 1 });
|
|
50
|
+
}
|
|
51
|
+
let result;
|
|
52
|
+
try {
|
|
53
|
+
if (flags.show) {
|
|
54
|
+
result = await task_extras_1.taskSelfScheduleApi.get(args.id);
|
|
55
|
+
}
|
|
56
|
+
else if (flags.clear) {
|
|
57
|
+
result = await task_extras_1.taskSelfScheduleApi.clear(args.id);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const runAt = resolveRunAt(flags.at, flags.in);
|
|
61
|
+
if (!runAt) {
|
|
62
|
+
this.error('Pass --at <ISO-8601>, --in <duration>, or one of --show / --clear.', {
|
|
63
|
+
exit: 1,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
result = await task_extras_1.taskSelfScheduleApi.park(args.id, {
|
|
67
|
+
runAt,
|
|
68
|
+
...(flags.note ? { note: flags.note } : {}),
|
|
69
|
+
...(flags['max-per-day'] ? { maxFiresPerDay: flags['max-per-day'] } : {}),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
this.handleApiError(err);
|
|
75
|
+
}
|
|
76
|
+
if (flags.json) {
|
|
77
|
+
this.log(JSON.stringify(result ?? {}, null, 2));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const runAt = result?.runAt;
|
|
81
|
+
if (!runAt) {
|
|
82
|
+
this.log(`Task ${args.id} has no pending return.`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// `pending` is derived server-side: a return that has already fired keeps
|
|
86
|
+
// its instant on the row, and printing that verbatim would report a return
|
|
87
|
+
// as upcoming an hour after it happened.
|
|
88
|
+
const spent = result?.pending === false;
|
|
89
|
+
this.log(`Task ${args.id} ${spent ? 'last returned at' : 'returns at'} ${runAt}`);
|
|
90
|
+
if (result?.note)
|
|
91
|
+
this.log(`Note: ${result.note}`);
|
|
92
|
+
if (!spent && result?.maxFiresPerDay) {
|
|
93
|
+
this.log(`Cap: ${result.maxFiresPerDay} self-initiated returns/day`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.default = TasksSelfSchedule;
|
|
98
|
+
/** `90m` / `2h` / `3d`, or an absolute instant. Absolute wins when both are given. */
|
|
99
|
+
function resolveRunAt(at, relative) {
|
|
100
|
+
if (at)
|
|
101
|
+
return at;
|
|
102
|
+
if (!relative)
|
|
103
|
+
return undefined;
|
|
104
|
+
const match = /^(\d+)\s*(m|min|mins|h|hr|hrs|d)$/i.exec(relative.trim());
|
|
105
|
+
if (!match)
|
|
106
|
+
return undefined;
|
|
107
|
+
const value = Number(match[1]);
|
|
108
|
+
const unit = match[2].toLowerCase();
|
|
109
|
+
const MS_PER_UNIT = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
110
|
+
// `min`/`mins`/`hr`/`hrs` collapse onto their first character, which is
|
|
111
|
+
// unambiguous across the three units this accepts.
|
|
112
|
+
const ms = value * (MS_PER_UNIT[unit[0]] ?? MS_PER_UNIT.h);
|
|
113
|
+
return new Date(Date.now() + ms).toISOString();
|
|
114
|
+
}
|
|
@@ -26,7 +26,9 @@ class TriggersDisable extends base_command_1.BaseCommand {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
this.log('Disabled trigger:');
|
|
29
|
-
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line)
|
|
29
|
+
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line), {
|
|
30
|
+
baseURL: this.cliConfig.baseURL,
|
|
31
|
+
});
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
exports.default = TriggersDisable;
|
|
@@ -26,7 +26,9 @@ class TriggersEnable extends base_command_1.BaseCommand {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
this.log('Enabled trigger:');
|
|
29
|
-
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line)
|
|
29
|
+
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line), {
|
|
30
|
+
baseURL: this.cliConfig.baseURL,
|
|
31
|
+
});
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
exports.default = TriggersEnable;
|
|
@@ -30,6 +30,11 @@ class TriggersRotateSecret extends base_command_1.BaseCommand {
|
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
this.log(`Secret: ${String(result.secret ?? '-')}`);
|
|
33
|
+
// The secret alone does not let anyone call in. This command was the natural
|
|
34
|
+
// place to learn the endpoint and printed everything except it, so building
|
|
35
|
+
// the integration meant reading the API source or guessing the path.
|
|
36
|
+
this.log(`URL: POST ${(0, triggers_1.webhookIngestUrl)(this.cliConfig.baseURL, args.id)}`);
|
|
37
|
+
this.log('Sign with header x-trigger-signature; send x-trigger-timestamp alongside it.');
|
|
33
38
|
this.log(String(result.note ?? 'Save this secret now; it cannot be retrieved later.'));
|
|
34
39
|
if (result.lastRotatedAt)
|
|
35
40
|
this.log(`Rotated at: ${String(result.lastRotatedAt)}`);
|
|
@@ -29,7 +29,9 @@ class TriggersShow extends base_command_1.BaseCommand {
|
|
|
29
29
|
this.log(JSON.stringify(trigger, null, 2));
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line)
|
|
32
|
+
(0, triggers_1.renderTriggerSummary)(trigger, (line) => this.log(line), {
|
|
33
|
+
baseURL: this.cliConfig.baseURL,
|
|
34
|
+
});
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
exports.default = TriggersShow;
|
|
@@ -63,6 +63,10 @@ function deliveryForProductMode(mode, delivery) {
|
|
|
63
63
|
function renderExecutionReceipt(receipt, log, verbose = false) {
|
|
64
64
|
log(receipt.summary);
|
|
65
65
|
log(`Mode: ${productModeLabel(receipt.mode)}`);
|
|
66
|
+
if (receipt.runtime) {
|
|
67
|
+
const identity = receipt.runtime.name || receipt.runtime.id || 'the selected runtime';
|
|
68
|
+
log(`Runtime: ${identity}${receipt.runtime.kind ? ` · ${receipt.runtime.kind}` : ''}${receipt.runtime.provider ? ` · ${receipt.runtime.provider}` : ''}`);
|
|
69
|
+
}
|
|
66
70
|
for (const phase of receipt.phases) {
|
|
67
71
|
const mark = { done: '✓', working: '→', waiting: '…', failed: '×', skipped: '–' }[phase.status];
|
|
68
72
|
log(` ${mark} ${phase.phase[0].toUpperCase() + phase.phase.slice(1)}: ${phase.summary}${phase.at ? ` · ${phase.at}` : ''}`);
|
|
@@ -73,6 +77,11 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
|
|
|
73
77
|
}
|
|
74
78
|
if (receipt.nextActor)
|
|
75
79
|
log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${receipt.nextActor.reason ? ` — ${receipt.nextActor.reason}` : ''}`);
|
|
80
|
+
if (receipt.blocker) {
|
|
81
|
+
log(`Blocker: ${receipt.blocker.code}`);
|
|
82
|
+
for (const evidenceRef of receipt.blocker.evidenceRefs || [])
|
|
83
|
+
log(` Evidence: ${evidenceRef}`);
|
|
84
|
+
}
|
|
76
85
|
if (verbose)
|
|
77
86
|
log(`Diagnostics:\n${JSON.stringify({ id: receipt.id, revision: receipt.revision, evidence: receipt.evidence, diagnostics: receipt.diagnostics }, null, 2)}`);
|
|
78
87
|
}
|
|
@@ -260,7 +260,20 @@ export declare const commitmentApi: {
|
|
|
260
260
|
object: "commitment_action_proposal_list";
|
|
261
261
|
data: CommitmentActionProposal[];
|
|
262
262
|
}>;
|
|
263
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Propose a prepared action for a check.
|
|
265
|
+
*
|
|
266
|
+
* The half that was missing. `decide` and `execute` were both reachable, so a
|
|
267
|
+
* headless operator could only ever REACT to proposals the server made — an
|
|
268
|
+
* asymmetry that leaves the domain half-usable in exactly the way the parity
|
|
269
|
+
* rule exists to catch. A locally-running agent that has investigated a
|
|
270
|
+
* commitment and knows what should be done had no way to say so.
|
|
271
|
+
*/
|
|
272
|
+
proposeActionProposal: (id: string, body: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
273
|
+
decideActionProposal: (id: string, proposalId: string, decision: "approve" | "reject", snapshot?: {
|
|
274
|
+
snapshotDigest?: string;
|
|
275
|
+
bundleVersion?: number;
|
|
276
|
+
}) => Promise<Record<string, unknown>>;
|
|
264
277
|
executeActionProposal: (id: string, proposalId: string) => Promise<Record<string, unknown>>;
|
|
265
278
|
preflight: (id: string, nextRunCount?: number) => Promise<CommitmentPreflightReport>;
|
|
266
279
|
effectivePolicy: (id: string) => Promise<Record<string, unknown>>;
|
|
@@ -410,6 +423,13 @@ export declare function readCommitmentAgentDirective({ directive, file, }: {
|
|
|
410
423
|
* active or terminal state, but importing it must still create a draft.
|
|
411
424
|
*/
|
|
412
425
|
export declare function normalizeCreateBody(input: Record<string, unknown>): Record<string, unknown>;
|
|
426
|
+
export declare function parseCommitmentConcurrencyLimit(input: string | undefined): number | null | undefined;
|
|
427
|
+
export declare function commitmentConcurrencyEnvelopePatch({ rawValue, reason, autonomous, }: {
|
|
428
|
+
rawValue?: string;
|
|
429
|
+
reason?: string;
|
|
430
|
+
autonomous?: boolean;
|
|
431
|
+
}): Record<string, unknown> | undefined;
|
|
432
|
+
export declare function describeCommitmentConcurrency(commitment: CommitmentView): string;
|
|
413
433
|
export declare function parseIntervalToMs(input: string): number;
|
|
414
434
|
/**
|
|
415
435
|
* Assemble the `watchedSources` array from CLI flags — one place, so `create`
|
package/dist/lib/commitments.js
CHANGED
|
@@ -6,6 +6,9 @@ exports.authoringAutonomyModeForStored = authoringAutonomyModeForStored;
|
|
|
6
6
|
exports.commitmentAutonomyLabel = commitmentAutonomyLabel;
|
|
7
7
|
exports.readCommitmentAgentDirective = readCommitmentAgentDirective;
|
|
8
8
|
exports.normalizeCreateBody = normalizeCreateBody;
|
|
9
|
+
exports.parseCommitmentConcurrencyLimit = parseCommitmentConcurrencyLimit;
|
|
10
|
+
exports.commitmentConcurrencyEnvelopePatch = commitmentConcurrencyEnvelopePatch;
|
|
11
|
+
exports.describeCommitmentConcurrency = describeCommitmentConcurrency;
|
|
9
12
|
exports.parseIntervalToMs = parseIntervalToMs;
|
|
10
13
|
exports.buildWatchedSources = buildWatchedSources;
|
|
11
14
|
exports.describeEvidenceHealth = describeEvidenceHealth;
|
|
@@ -153,8 +156,20 @@ exports.commitmentApi = {
|
|
|
153
156
|
checks: (id, limit) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/checks`, { limit })),
|
|
154
157
|
actionHistory: (id, limit) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/action-history`, { limit })),
|
|
155
158
|
actionProposals: (id, query = {}) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/action-proposals`, query)),
|
|
156
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Propose a prepared action for a check.
|
|
161
|
+
*
|
|
162
|
+
* The half that was missing. `decide` and `execute` were both reachable, so a
|
|
163
|
+
* headless operator could only ever REACT to proposals the server made — an
|
|
164
|
+
* asymmetry that leaves the domain half-usable in exactly the way the parity
|
|
165
|
+
* rule exists to catch. A locally-running agent that has investigated a
|
|
166
|
+
* commitment and knows what should be done had no way to say so.
|
|
167
|
+
*/
|
|
168
|
+
proposeActionProposal: (id, body) => data_provider_1.request.post(`${base(id)}/action-proposals`, body),
|
|
169
|
+
decideActionProposal: (id, proposalId, decision, snapshot) => data_provider_1.request.post(`${base(id)}/action-proposals/${encodeURIComponent(proposalId)}/decision`, {
|
|
157
170
|
decision,
|
|
171
|
+
...(snapshot?.snapshotDigest ? { snapshotDigest: snapshot.snapshotDigest } : {}),
|
|
172
|
+
...(snapshot?.bundleVersion ? { bundleVersion: snapshot.bundleVersion } : {}),
|
|
158
173
|
}),
|
|
159
174
|
executeActionProposal: (id, proposalId) => data_provider_1.request.post(`${base(id)}/action-proposals/${encodeURIComponent(proposalId)}/execute`, {}),
|
|
160
175
|
preflight: (id, nextRunCount) => data_provider_1.request.post(`${base(id)}/preflight`, {
|
|
@@ -321,6 +336,51 @@ function normalizeCreateBody(input) {
|
|
|
321
336
|
}
|
|
322
337
|
return out;
|
|
323
338
|
}
|
|
339
|
+
function parseCommitmentConcurrencyLimit(input) {
|
|
340
|
+
if (input === undefined)
|
|
341
|
+
return undefined;
|
|
342
|
+
const raw = String(input).trim().toLowerCase();
|
|
343
|
+
if (['unlimited', 'unbounded', 'none', 'null'].includes(raw))
|
|
344
|
+
return null;
|
|
345
|
+
const value = Number(raw);
|
|
346
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 100) {
|
|
347
|
+
throw new Error('--max-concurrent-runs must be "unlimited" or an integer from 1 to 100.');
|
|
348
|
+
}
|
|
349
|
+
return value;
|
|
350
|
+
}
|
|
351
|
+
function commitmentConcurrencyEnvelopePatch({ rawValue, reason, autonomous = process.env.OVERSKY_AUTONOMOUS_SESSION === '1', }) {
|
|
352
|
+
const value = parseCommitmentConcurrencyLimit(rawValue);
|
|
353
|
+
const normalizedReason = reason?.trim();
|
|
354
|
+
if (value === undefined) {
|
|
355
|
+
if (normalizedReason) {
|
|
356
|
+
throw new Error('--concurrency-reason requires --max-concurrent-runs.');
|
|
357
|
+
}
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
if (autonomous && value !== null) {
|
|
361
|
+
throw new Error('An autonomous Agent may suggest a finite Commitment concurrency cap, but cannot activate owner policy. Omit the field or use unlimited; an owner can set the cap later.');
|
|
362
|
+
}
|
|
363
|
+
if (value !== null && !normalizedReason) {
|
|
364
|
+
throw new Error('A finite --max-concurrent-runs value requires --concurrency-reason.');
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
maxConcurrentRuns: value,
|
|
368
|
+
...(normalizedReason ? { maxConcurrentRunsReason: normalizedReason } : {}),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function describeCommitmentConcurrency(commitment) {
|
|
372
|
+
const envelope = commitment.policy?.autonomyEnvelope;
|
|
373
|
+
const limit = envelope?.maxConcurrentRuns;
|
|
374
|
+
const decision = envelope?.maxConcurrentRunsDecision;
|
|
375
|
+
if (limit == null) {
|
|
376
|
+
const source = decision?.mode === 'unlimited' ? decision.source : 'default';
|
|
377
|
+
return `unlimited (${source})`;
|
|
378
|
+
}
|
|
379
|
+
const authority = decision?.authority || 'unknown';
|
|
380
|
+
const source = decision?.source || 'legacy';
|
|
381
|
+
const review = authority === 'unknown' ? ' — review required' : '';
|
|
382
|
+
return `${limit} (${authority}/${source}${review})`;
|
|
383
|
+
}
|
|
324
384
|
/**
|
|
325
385
|
* Parse a human interval ("15m", "6h", "1d", or a bare number of minutes) into
|
|
326
386
|
* milliseconds. Throws on unparseable input, on trailing junk, and on values
|
|
@@ -672,6 +732,7 @@ latestCheck) {
|
|
|
672
732
|
log(`Next check:${` ${commitment.cadence.nextCheckAt}`}`);
|
|
673
733
|
log(`Mode: ${describeAutonomyLine(commitment, latestCheck)}`);
|
|
674
734
|
log(`Delivery: ${(0, commitment_product_1.deliveryLabel)(commitment.delivery?.mode)}${commitment.delivery?.repo ? ` · ${commitment.delivery.repo}` : ''}${commitment.delivery?.baseBranch ? ` → ${commitment.delivery.baseBranch}` : ''}`);
|
|
735
|
+
log(`Concurrency: ${describeCommitmentConcurrency(commitment)}`);
|
|
675
736
|
const criteria = commitment.target?.successCriteria || [];
|
|
676
737
|
const evidenceHealth = latestCheck?.inputs?.evidenceHealth;
|
|
677
738
|
const evidenceLine = describeEvidenceHealth(evidenceHealth, criteria.length);
|
package/dist/lib/node-adapter.js
CHANGED
|
@@ -79,6 +79,10 @@ function createNodeAdapter(opts = {}) {
|
|
|
79
79
|
const { token } = currentCredential();
|
|
80
80
|
const headers = {
|
|
81
81
|
Accept: 'application/json',
|
|
82
|
+
// Server-side authoring provenance. This is attribution, not authority;
|
|
83
|
+
// owner/admin decisions are still authenticated and validated at the
|
|
84
|
+
// route boundary.
|
|
85
|
+
'X-Skrr-Client': 'cli',
|
|
82
86
|
...(extra ?? {}),
|
|
83
87
|
};
|
|
84
88
|
if (token) {
|
|
@@ -32,6 +32,24 @@ export declare const taskLookupApi: {
|
|
|
32
32
|
treePosition: (id: string, body: Json) => Promise<Json>;
|
|
33
33
|
createAndStart: (body: Json) => Promise<Json>;
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Self-schedule — "come back to this task at T", owned by whoever is working it.
|
|
37
|
+
*
|
|
38
|
+
* A different resource from `/schedule`, not a variant of it. `/schedule` writes
|
|
39
|
+
* `Task.scheduledFor` + `recurrence` (the author's calendar, which the scheduler
|
|
40
|
+
* consumes and re-arms); this writes a `kind:'self_scheduled'` trigger row. Both
|
|
41
|
+
* can be set on one task and neither erases the other, so a headless agent can
|
|
42
|
+
* defer itself without editing the human's recurrence to mean something the
|
|
43
|
+
* human never asked for.
|
|
44
|
+
*
|
|
45
|
+
* PUT, not POST: a task has at most one pending return and parking again
|
|
46
|
+
* replaces it.
|
|
47
|
+
*/
|
|
48
|
+
export declare const taskSelfScheduleApi: {
|
|
49
|
+
get: (id: string) => Promise<Json>;
|
|
50
|
+
park: (id: string, body: Json) => Promise<Json>;
|
|
51
|
+
clear: (id: string) => Promise<Json>;
|
|
52
|
+
};
|
|
35
53
|
/**
|
|
36
54
|
* The completion URL as a STRING.
|
|
37
55
|
*
|
package/dist/lib/task-extras.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.taskChatApi = exports.taskLookupApi = exports.taskWorkflowApi = void 0;
|
|
3
|
+
exports.taskChatApi = exports.taskSelfScheduleApi = exports.taskLookupApi = exports.taskWorkflowApi = void 0;
|
|
4
4
|
exports.taskCompletePath = taskCompletePath;
|
|
5
5
|
/**
|
|
6
6
|
* task-extras.ts — the `/api/tasks` endpoints that have no `dataService.*`
|
|
@@ -51,6 +51,24 @@ exports.taskLookupApi = {
|
|
|
51
51
|
treePosition: (id, body) => data_provider_1.request.patch(`${base(id)}/tree-position`, body),
|
|
52
52
|
createAndStart: (body) => data_provider_1.request.post(`${MOUNT}/create-and-start`, body),
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Self-schedule — "come back to this task at T", owned by whoever is working it.
|
|
56
|
+
*
|
|
57
|
+
* A different resource from `/schedule`, not a variant of it. `/schedule` writes
|
|
58
|
+
* `Task.scheduledFor` + `recurrence` (the author's calendar, which the scheduler
|
|
59
|
+
* consumes and re-arms); this writes a `kind:'self_scheduled'` trigger row. Both
|
|
60
|
+
* can be set on one task and neither erases the other, so a headless agent can
|
|
61
|
+
* defer itself without editing the human's recurrence to mean something the
|
|
62
|
+
* human never asked for.
|
|
63
|
+
*
|
|
64
|
+
* PUT, not POST: a task has at most one pending return and parking again
|
|
65
|
+
* replaces it.
|
|
66
|
+
*/
|
|
67
|
+
exports.taskSelfScheduleApi = {
|
|
68
|
+
get: (id) => data_provider_1.request.get(`${base(id)}/self-schedule`),
|
|
69
|
+
park: (id, body) => data_provider_1.request.put(`${base(id)}/self-schedule`, body),
|
|
70
|
+
clear: (id) => data_provider_1.request.delete(`${base(id)}/self-schedule`),
|
|
71
|
+
};
|
|
54
72
|
/**
|
|
55
73
|
* The completion URL as a STRING.
|
|
56
74
|
*
|