@skrr-ai/cli 0.1.36 → 0.1.37
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/README.md +17 -4
- package/dist/commands/agents/actions/create.d.ts +5 -0
- package/dist/commands/agents/actions/create.js +26 -0
- package/dist/commands/agents/actions/update.d.ts +5 -0
- package/dist/commands/agents/actions/update.js +24 -0
- package/dist/commands/agents/api-actions/add.d.ts +41 -0
- package/dist/commands/agents/api-actions/add.js +202 -0
- package/dist/commands/agents/api-actions/index.d.ts +28 -0
- package/dist/commands/agents/api-actions/index.js +77 -0
- package/dist/commands/agents/api-actions/remove.d.ts +14 -0
- package/dist/commands/agents/api-actions/remove.js +36 -0
- package/dist/commands/agents/avatar/default/apply.d.ts +25 -0
- package/dist/commands/agents/avatar/default/apply.js +56 -0
- package/dist/commands/agents/avatar/default/index.d.ts +18 -0
- package/dist/commands/agents/avatar/default/index.js +44 -0
- package/dist/commands/agents/avatar/default/set.d.ts +23 -0
- package/dist/commands/agents/avatar/default/set.js +95 -0
- package/dist/commands/agents/avatar/library/delete.d.ts +20 -0
- package/dist/commands/agents/avatar/library/delete.js +47 -0
- package/dist/commands/agents/avatar/library/index.d.ts +16 -0
- package/dist/commands/agents/avatar/library/index.js +56 -0
- package/dist/commands/agents/avatar/presets.d.ts +19 -0
- package/dist/commands/agents/avatar/presets.js +56 -0
- package/dist/commands/agents/categories/create.d.ts +14 -0
- package/dist/commands/agents/categories/create.js +46 -0
- package/dist/commands/agents/categories/delete.d.ts +13 -0
- package/dist/commands/agents/categories/delete.js +33 -0
- package/dist/commands/agents/categories/index.d.ts +20 -0
- package/dist/commands/agents/categories/index.js +82 -0
- package/dist/commands/agents/categories/update.d.ts +22 -0
- package/dist/commands/agents/categories/update.js +59 -0
- package/dist/commands/agents/create.d.ts +10 -0
- package/dist/commands/agents/create.js +57 -3
- package/dist/commands/agents/ensure-default.d.ts +27 -0
- package/dist/commands/agents/ensure-default.js +65 -0
- package/dist/commands/agents/mute.d.ts +16 -0
- package/dist/commands/agents/mute.js +38 -0
- package/dist/commands/agents/pin.d.ts +16 -0
- package/dist/commands/agents/pin.js +38 -0
- package/dist/commands/agents/tools/auth.d.ts +19 -0
- package/dist/commands/agents/tools/auth.js +41 -0
- package/dist/commands/agents/tools/calls.d.ts +18 -0
- package/dist/commands/agents/tools/calls.js +54 -0
- package/dist/commands/agents/tools/index.d.ts +20 -0
- package/dist/commands/agents/tools/index.js +77 -0
- package/dist/commands/agents/unmute.d.ts +16 -0
- package/dist/commands/agents/unmute.js +38 -0
- package/dist/commands/agents/unpin.d.ts +16 -0
- package/dist/commands/agents/unpin.js +38 -0
- package/dist/commands/agents/update.d.ts +12 -0
- package/dist/commands/agents/update.js +64 -3
- package/dist/commands/followups/act.d.ts +22 -0
- package/dist/commands/followups/act.js +102 -0
- package/dist/commands/followups/cancel.d.ts +17 -0
- package/dist/commands/followups/cancel.js +40 -0
- package/dist/commands/followups/checkin.d.ts +27 -0
- package/dist/commands/followups/checkin.js +107 -0
- package/dist/commands/followups/list.d.ts +24 -0
- package/dist/commands/followups/list.js +90 -0
- package/dist/commands/followups/remind.d.ts +26 -0
- package/dist/commands/followups/remind.js +106 -0
- package/dist/commands/followups/reschedule.d.ts +15 -0
- package/dist/commands/followups/reschedule.js +48 -0
- package/dist/commands/followups/resolve.d.ts +23 -0
- package/dist/commands/followups/resolve.js +54 -0
- package/dist/commands/followups/show.d.ts +12 -0
- package/dist/commands/followups/show.js +45 -0
- package/dist/commands/followups/watch.d.ts +51 -0
- package/dist/commands/followups/watch.js +207 -0
- package/dist/commands/goals/labels/add.js +19 -2
- package/dist/commands/goals/labels/remove.js +22 -2
- package/dist/commands/labels/list.d.ts +24 -1
- package/dist/commands/labels/list.js +62 -19
- package/dist/commands/memory/governance/index.d.ts +21 -0
- package/dist/commands/memory/governance/index.js +64 -0
- package/dist/commands/memory/governance/set.d.ts +24 -0
- package/dist/commands/memory/governance/set.js +95 -0
- package/dist/commands/memory/provenance.d.ts +18 -0
- package/dist/commands/memory/provenance.js +66 -0
- package/dist/commands/spaces/create.d.ts +3 -0
- package/dist/commands/spaces/create.js +32 -2
- package/dist/commands/spaces/labels/add.js +19 -2
- package/dist/commands/spaces/labels/remove.js +22 -2
- package/dist/commands/spaces/show.js +2 -0
- package/dist/commands/spaces/update.d.ts +5 -0
- package/dist/commands/spaces/update.js +41 -4
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +11 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +45 -0
- package/dist/commands/tasks/complete.d.ts +1 -0
- package/dist/commands/tasks/complete.js +8 -1
- package/dist/commands/tasks/create.d.ts +3 -0
- package/dist/commands/tasks/create.js +34 -2
- package/dist/commands/tasks/expectations/assess.d.ts +11 -0
- package/dist/commands/tasks/expectations/assess.js +85 -14
- package/dist/commands/tasks/expectations.js +19 -2
- package/dist/commands/tasks/labels/attach.js +20 -3
- package/dist/commands/tasks/labels/detach.js +21 -3
- package/dist/commands/tasks/lifecycle/attach.d.ts +14 -0
- package/dist/commands/tasks/lifecycle/attach.js +47 -0
- package/dist/commands/tasks/lifecycle/definitions.d.ts +11 -0
- package/dist/commands/tasks/lifecycle/definitions.js +40 -0
- package/dist/commands/tasks/lifecycle/detach.d.ts +12 -0
- package/dist/commands/tasks/lifecycle/detach.js +32 -0
- package/dist/commands/tasks/lifecycle/history.d.ts +13 -0
- package/dist/commands/tasks/lifecycle/history.js +38 -0
- package/dist/commands/tasks/lifecycle/migrate.d.ts +12 -0
- package/dist/commands/tasks/lifecycle/migrate.js +29 -0
- package/dist/commands/tasks/lifecycle/reconcile.d.ts +11 -0
- package/dist/commands/tasks/lifecycle/reconcile.js +27 -0
- package/dist/commands/tasks/lifecycle/reopen.d.ts +13 -0
- package/dist/commands/tasks/lifecycle/reopen.js +34 -0
- package/dist/commands/tasks/lifecycle/show.d.ts +11 -0
- package/dist/commands/tasks/lifecycle/show.js +27 -0
- package/dist/commands/tasks/lifecycle/start.d.ts +12 -0
- package/dist/commands/tasks/lifecycle/start.js +30 -0
- package/dist/commands/tasks/lifecycle/transition.d.ts +15 -0
- package/dist/commands/tasks/lifecycle/transition.js +78 -0
- package/dist/commands/tasks/move.d.ts +1 -0
- package/dist/commands/tasks/move.js +31 -0
- package/dist/commands/tasks/result/submit.js +11 -1
- package/dist/commands/tasks/review-queue.d.ts +29 -0
- package/dist/commands/tasks/review-queue.js +165 -0
- package/dist/commands/tasks/show.js +42 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +47 -0
- package/dist/lib/agent-actions.d.ts +36 -10
- package/dist/lib/agent-actions.js +63 -9
- package/dist/lib/agent-config.d.ts +11 -0
- package/dist/lib/agent-config.js +85 -3
- package/dist/lib/agent-pin-mute.d.ts +24 -0
- package/dist/lib/agent-pin-mute.js +17 -0
- package/dist/lib/agent-visual-refs.d.ts +6 -0
- package/dist/lib/agent-visual-refs.js +142 -0
- package/dist/lib/first-party-harness-agent.js +7 -0
- package/dist/lib/followups.d.ts +107 -0
- package/dist/lib/followups.js +102 -0
- package/dist/lib/label-ref.d.ts +95 -0
- package/dist/lib/label-ref.js +163 -0
- package/dist/lib/label-scope.d.ts +2 -2
- package/dist/lib/label-scope.js +3 -3
- package/dist/lib/task-closure.d.ts +32 -0
- package/dist/lib/task-closure.js +106 -0
- package/dist/lib/task-lifecycle-output.d.ts +14 -0
- package/dist/lib/task-lifecycle-output.js +101 -0
- package/dist/lib/tasks.d.ts +3 -0
- package/dist/lib/tasks.js +31 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5079 -4546
- package/oclif.manifest.json +20216 -16002
- package/package.json +16 -4
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr followups remind` — a `reminder` follow-up.
|
|
4
|
+
*
|
|
5
|
+
* A passive reminder (the default) delivers the user's own words back at T
|
|
6
|
+
* and needs no agent. `--active` wakes the agent to word it, which is why it
|
|
7
|
+
* needs an agent context the passive form does not.
|
|
8
|
+
*/
|
|
9
|
+
export default class FollowupsRemind extends BaseCommand {
|
|
10
|
+
static description: string;
|
|
11
|
+
static examples: string[];
|
|
12
|
+
static flags: {
|
|
13
|
+
subject: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
content: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
at: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
in: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
+
why: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
|
+
active: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
19
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
endpoint: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
'endpoint-target': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
pin: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
24
|
+
};
|
|
25
|
+
run(): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
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 followups_1 = require("../../lib/followups");
|
|
6
|
+
const agent_resolver_1 = require("../../lib/agent-resolver");
|
|
7
|
+
/**
|
|
8
|
+
* `skrr followups remind` — a `reminder` follow-up.
|
|
9
|
+
*
|
|
10
|
+
* A passive reminder (the default) delivers the user's own words back at T
|
|
11
|
+
* and needs no agent. `--active` wakes the agent to word it, which is why it
|
|
12
|
+
* needs an agent context the passive form does not.
|
|
13
|
+
*/
|
|
14
|
+
class FollowupsRemind extends base_command_1.BaseCommand {
|
|
15
|
+
static description = 'Remind the owner at a time — the reminder follow-up';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> followups remind --subject task:OSK-42 --content "pick the deploy up" --in 2h',
|
|
18
|
+
'<%= config.bin %> followups remind --subject conversation:conv_9 --content "ask how the move went" --at 2026-09-20T09:00:00Z',
|
|
19
|
+
'<%= config.bin %> followups remind --subject task:OSK-42 --content "ping me" --at 2026-09-20T09:00:00Z --active --agent agent_1',
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
subject: core_1.Flags.string({ description: 'Subject ref: type:externalKey', required: true }),
|
|
23
|
+
content: core_1.Flags.string({ description: "The reminder's own words", required: true }),
|
|
24
|
+
at: core_1.Flags.string({ description: 'ISO-8601 instant to remind at', exclusive: ['in'] }),
|
|
25
|
+
in: core_1.Flags.string({
|
|
26
|
+
description: 'Duration from now (90m, 2h, 3d)',
|
|
27
|
+
exclusive: ['at'],
|
|
28
|
+
}),
|
|
29
|
+
why: core_1.Flags.string({ description: 'Why this wake exists (defaults to the content)' }),
|
|
30
|
+
active: core_1.Flags.boolean({
|
|
31
|
+
description: 'Wake the agent to word the reminder (needs an agent context)',
|
|
32
|
+
default: false,
|
|
33
|
+
}),
|
|
34
|
+
agent: core_1.Flags.string({ description: 'Agent ID (default: bound session agent)' }),
|
|
35
|
+
endpoint: core_1.Flags.string({ description: 'Delivery endpoint id (EndpointRegistry)' }),
|
|
36
|
+
'endpoint-target': core_1.Flags.string({ description: 'Channel target for channel endpoints' }),
|
|
37
|
+
pin: core_1.Flags.string({ description: 'Conversation id the reminder was authored in' }),
|
|
38
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
39
|
+
};
|
|
40
|
+
async run() {
|
|
41
|
+
this.requireAuth();
|
|
42
|
+
const { flags } = await this.parse(FollowupsRemind);
|
|
43
|
+
const at = flags.at ?? flags.in;
|
|
44
|
+
if (!at) {
|
|
45
|
+
this.failWithCliError({
|
|
46
|
+
message: 'Either --at <iso> or --in <duration> is required.',
|
|
47
|
+
code: 'FOLLOWUP_KIND_FIELD_MISSING',
|
|
48
|
+
exit: 2,
|
|
49
|
+
retryable: false,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// A passive reminder runs no agent, so it may legitimately have none — the
|
|
53
|
+
// ONLY kind/form where the resolver is allowed to fall through empty.
|
|
54
|
+
let agent;
|
|
55
|
+
try {
|
|
56
|
+
agent = (0, followups_1.resolveFollowUpAgent)(flags.agent);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (flags.active) {
|
|
60
|
+
const failure = (0, agent_resolver_1.agentResolutionFailure)(err);
|
|
61
|
+
this.failWithCliError({
|
|
62
|
+
message: failure.message,
|
|
63
|
+
code: failure.code,
|
|
64
|
+
exit: 2,
|
|
65
|
+
retryable: false,
|
|
66
|
+
details: { suggestion: failure.suggestion, context: failure.details },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
let response;
|
|
71
|
+
try {
|
|
72
|
+
response = await followups_1.followupsApi.create({
|
|
73
|
+
kind: 'reminder',
|
|
74
|
+
form: flags.active ? 'active' : 'passive',
|
|
75
|
+
producer: agent?.boundToSession ? 'agent_in_turn' : 'user_explicit',
|
|
76
|
+
...(agent ? { agentId: agent.id } : {}),
|
|
77
|
+
subject: (0, followups_1.parseSubjectRef)(flags.subject),
|
|
78
|
+
why: flags.why ?? flags.content,
|
|
79
|
+
content: flags.content,
|
|
80
|
+
at,
|
|
81
|
+
...(flags.endpoint ? { endpointId: flags.endpoint } : {}),
|
|
82
|
+
...(flags['endpoint-target'] ? { endpointTarget: flags['endpoint-target'] } : {}),
|
|
83
|
+
...(flags.pin ? { pinnedConversationId: flags.pin } : {}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.handleApiError(err);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (flags.json) {
|
|
91
|
+
this.log(JSON.stringify(response, null, 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const row = response.followup;
|
|
95
|
+
if (response.reused) {
|
|
96
|
+
this.log(`Already armed: ${row?.id} — the same reminder exists and was not duplicated.`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (response.dropped) {
|
|
100
|
+
this.log('Dropped by the daily budget — no reminder was armed.');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.log(`Reminder armed: ${row?.id} at ${row?.nextRunAt ?? at}.`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.default = FollowupsRemind;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/** `skrr followups reschedule <id>` — move a pending wake to a new time. */
|
|
3
|
+
export default class FollowupsReschedule extends BaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
at: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
in: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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 followups_1 = require("../../lib/followups");
|
|
6
|
+
/** `skrr followups reschedule <id>` — move a pending wake to a new time. */
|
|
7
|
+
class FollowupsReschedule extends base_command_1.BaseCommand {
|
|
8
|
+
static description = 'Reschedule a pending follow-up';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> followups reschedule fu_abc --in 2h',
|
|
11
|
+
'<%= config.bin %> followups reschedule fu_abc --at 2026-09-20T09:00:00Z',
|
|
12
|
+
];
|
|
13
|
+
static args = {
|
|
14
|
+
id: core_1.Args.string({ description: 'Follow-up (trigger) id', required: true, ignoreStdin: true }),
|
|
15
|
+
};
|
|
16
|
+
static flags = {
|
|
17
|
+
at: core_1.Flags.string({ description: 'New instant (ISO-8601)', exclusive: ['in'] }),
|
|
18
|
+
in: core_1.Flags.string({ description: 'New offset from now (90m, 2h)', exclusive: ['at'] }),
|
|
19
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
this.requireAuth();
|
|
23
|
+
const { args, flags } = await this.parse(FollowupsReschedule);
|
|
24
|
+
const at = flags.at ?? flags.in;
|
|
25
|
+
if (!at) {
|
|
26
|
+
this.failWithCliError({
|
|
27
|
+
message: 'Either --at <iso> or --in <duration> is required.',
|
|
28
|
+
code: 'FOLLOWUP_KIND_FIELD_MISSING',
|
|
29
|
+
exit: 2,
|
|
30
|
+
retryable: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
let result;
|
|
34
|
+
try {
|
|
35
|
+
result = (await followups_1.followupsApi.reschedule(args.id, { at }));
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
this.handleApiError(err);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (flags.json) {
|
|
42
|
+
this.log(JSON.stringify(result, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.log(`Follow-up ${args.id} rescheduled to ${at}.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.default = FollowupsReschedule;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr followups resolve <id>` — settle with the honest outcome.
|
|
4
|
+
*
|
|
5
|
+
* Distinct from `cancel` on purpose: "the thing I was waiting for happened"
|
|
6
|
+
* (`resolved`) and "never mind" (`abandoned`) are different facts, and a
|
|
7
|
+
* system that cannot tell them apart cannot tell a working watch from a
|
|
8
|
+
* pointless one. The runtime's own terminals (expired/exhausted/escalated)
|
|
9
|
+
* are not accepted here.
|
|
10
|
+
*/
|
|
11
|
+
export default class FollowupsResolve extends BaseCommand {
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
14
|
+
static args: {
|
|
15
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
16
|
+
};
|
|
17
|
+
static flags: {
|
|
18
|
+
outcome: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
note: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
+
};
|
|
22
|
+
run(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 followups_1 = require("../../lib/followups");
|
|
6
|
+
/**
|
|
7
|
+
* `skrr followups resolve <id>` — settle with the honest outcome.
|
|
8
|
+
*
|
|
9
|
+
* Distinct from `cancel` on purpose: "the thing I was waiting for happened"
|
|
10
|
+
* (`resolved`) and "never mind" (`abandoned`) are different facts, and a
|
|
11
|
+
* system that cannot tell them apart cannot tell a working watch from a
|
|
12
|
+
* pointless one. The runtime's own terminals (expired/exhausted/escalated)
|
|
13
|
+
* are not accepted here.
|
|
14
|
+
*/
|
|
15
|
+
class FollowupsResolve extends base_command_1.BaseCommand {
|
|
16
|
+
static description = 'Settle a follow-up with its honest outcome';
|
|
17
|
+
static examples = [
|
|
18
|
+
'<%= config.bin %> followups resolve fu_abc --outcome resolved --note "the deploy landed"',
|
|
19
|
+
'<%= config.bin %> followups resolve fu_abc --outcome abandoned',
|
|
20
|
+
];
|
|
21
|
+
static args = {
|
|
22
|
+
id: core_1.Args.string({ description: 'Follow-up (trigger) id', required: true, ignoreStdin: true }),
|
|
23
|
+
};
|
|
24
|
+
static flags = {
|
|
25
|
+
outcome: core_1.Flags.string({
|
|
26
|
+
description: 'How it ended',
|
|
27
|
+
options: [...followups_1.FOLLOWUP_RESOLVE_OUTCOMES],
|
|
28
|
+
required: true,
|
|
29
|
+
}),
|
|
30
|
+
note: core_1.Flags.string({ description: 'What happened, for the audit' }),
|
|
31
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
32
|
+
};
|
|
33
|
+
async run() {
|
|
34
|
+
this.requireAuth();
|
|
35
|
+
const { args, flags } = await this.parse(FollowupsResolve);
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = (await followups_1.followupsApi.resolve(args.id, {
|
|
39
|
+
outcome: flags.outcome,
|
|
40
|
+
...(flags.note ? { note: flags.note } : {}),
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
this.handleApiError(err);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (flags.json) {
|
|
48
|
+
this.log(JSON.stringify(result, null, 2));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.log(`Follow-up ${args.id} settled: ${flags.outcome}.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = FollowupsResolve;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/** `skrr followups show <id>` — one follow-up, the same shape as the list rows. */
|
|
3
|
+
export default class FollowupsShow extends BaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
id: 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,45 @@
|
|
|
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 followups_1 = require("../../lib/followups");
|
|
6
|
+
/** `skrr followups show <id>` — one follow-up, the same shape as the list rows. */
|
|
7
|
+
class FollowupsShow extends base_command_1.BaseCommand {
|
|
8
|
+
static description = 'Show one follow-up';
|
|
9
|
+
static args = {
|
|
10
|
+
id: core_1.Args.string({ description: 'Follow-up (trigger) id', required: true, ignoreStdin: true }),
|
|
11
|
+
};
|
|
12
|
+
static flags = {
|
|
13
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
this.requireAuth();
|
|
17
|
+
const { args, flags } = await this.parse(FollowupsShow);
|
|
18
|
+
let response;
|
|
19
|
+
try {
|
|
20
|
+
response = await followups_1.followupsApi.show(args.id);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
this.handleApiError(err);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (flags.json) {
|
|
27
|
+
this.log(JSON.stringify(response, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const row = response.followup;
|
|
31
|
+
this.log((0, followups_1.followupSummaryLine)(row));
|
|
32
|
+
for (const [label, value] of [
|
|
33
|
+
['why', row.why],
|
|
34
|
+
['intent', row.intentKey],
|
|
35
|
+
['producer', row.producer],
|
|
36
|
+
['terminal', row.terminal ? `${row.terminal} (${row.terminalReason ?? '—'})` : undefined],
|
|
37
|
+
['deadline', row.deadlineAt],
|
|
38
|
+
['escalated to', row.escalatedInitiativeId],
|
|
39
|
+
]) {
|
|
40
|
+
if (value)
|
|
41
|
+
this.log(` ${label}: ${value}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.default = FollowupsShow;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr followups watch` — a `watch` follow-up: keep an eye on an
|
|
4
|
+
* asynchronous tail until a condition settles or a stop fires.
|
|
5
|
+
*
|
|
6
|
+
* Two forms, matching the service vocabulary exactly:
|
|
7
|
+
*
|
|
8
|
+
* predicate `--url` + `--until <json>` — a machine-settled observation of
|
|
9
|
+
* an HTTPS endpoint. Free (no agent turn per check), termination
|
|
10
|
+
* guaranteed by `--deadline`. Prefer this when the thing watched
|
|
11
|
+
* is a URL whose response can answer the question.
|
|
12
|
+
* recipe `--check` + `--success` + `--abort` + `--wait <kind>` — a
|
|
13
|
+
* deterministic check a future agent turn runs. One turn per
|
|
14
|
+
* check; the abort criterion is mandatory because a watch that
|
|
15
|
+
* cannot stop is a leak.
|
|
16
|
+
*/
|
|
17
|
+
export default class FollowupsWatch extends BaseCommand {
|
|
18
|
+
static description: string;
|
|
19
|
+
static examples: string[];
|
|
20
|
+
static flags: {
|
|
21
|
+
subject: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
why: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
url: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
until: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
|
+
grant: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
27
|
+
deadline: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
28
|
+
'check-every': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
29
|
+
'max-checks': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
30
|
+
'root-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
31
|
+
'resume-prompt': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
32
|
+
check: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
33
|
+
success: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
34
|
+
abort: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
35
|
+
wait: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
36
|
+
'wait-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
37
|
+
'wait-entity': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
38
|
+
'wait-event': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
39
|
+
'wait-filter-field': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
40
|
+
'wait-filter-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
41
|
+
'wait-filter-value': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
42
|
+
'wait-interval-seconds': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
43
|
+
'wait-max-attempts': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
44
|
+
'wait-fallback': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
45
|
+
endpoint: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
46
|
+
'endpoint-target': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
47
|
+
pin: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
48
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
49
|
+
};
|
|
50
|
+
run(): Promise<void>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
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 followups_1 = require("../../lib/followups");
|
|
6
|
+
const agent_resolver_1 = require("../../lib/agent-resolver");
|
|
7
|
+
const triggers_1 = require("../../lib/triggers");
|
|
8
|
+
/**
|
|
9
|
+
* `skrr followups watch` — a `watch` follow-up: keep an eye on an
|
|
10
|
+
* asynchronous tail until a condition settles or a stop fires.
|
|
11
|
+
*
|
|
12
|
+
* Two forms, matching the service vocabulary exactly:
|
|
13
|
+
*
|
|
14
|
+
* predicate `--url` + `--until <json>` — a machine-settled observation of
|
|
15
|
+
* an HTTPS endpoint. Free (no agent turn per check), termination
|
|
16
|
+
* guaranteed by `--deadline`. Prefer this when the thing watched
|
|
17
|
+
* is a URL whose response can answer the question.
|
|
18
|
+
* recipe `--check` + `--success` + `--abort` + `--wait <kind>` — a
|
|
19
|
+
* deterministic check a future agent turn runs. One turn per
|
|
20
|
+
* check; the abort criterion is mandatory because a watch that
|
|
21
|
+
* cannot stop is a leak.
|
|
22
|
+
*/
|
|
23
|
+
class FollowupsWatch extends base_command_1.BaseCommand {
|
|
24
|
+
static description = 'Watch an asynchronous tail until it settles — the watch follow-up';
|
|
25
|
+
static examples = [
|
|
26
|
+
'<%= config.bin %> followups watch --subject task:OSK-42 --why "deploy still running" --url https://ci.example.com/api/build/9 --until \'{"field":"status","eq":"done"}\' --deadline 6h',
|
|
27
|
+
'<%= config.bin %> followups watch --subject task:OSK-42 --why "pr review" --check "gh pr view 42 --json state" --success "state == MERGED" --abort "state == CLOSED" --wait event --wait-entity github --wait-event pull_request --wait-fallback 2h',
|
|
28
|
+
'<%= config.bin %> followups watch --subject conv:abc --why "vendor reply" --check "check inbox" --success "reply arrived" --abort "3 days" --wait clock --wait-at 2026-09-20T09:00:00Z',
|
|
29
|
+
];
|
|
30
|
+
static flags = {
|
|
31
|
+
subject: core_1.Flags.string({ description: 'Subject ref: type:externalKey', required: true }),
|
|
32
|
+
why: core_1.Flags.string({ description: 'Why this watch exists, in one line', required: true }),
|
|
33
|
+
agent: core_1.Flags.string({ description: 'Agent ID (default: bound session agent)' }),
|
|
34
|
+
// predicate form
|
|
35
|
+
url: core_1.Flags.string({ description: 'HTTPS endpoint to observe (predicate form)' }),
|
|
36
|
+
until: core_1.Flags.string({ description: 'Predicate tree as JSON (predicate form)' }),
|
|
37
|
+
grant: core_1.Flags.string({ description: 'Grant id supplying the watch credentials' }),
|
|
38
|
+
deadline: core_1.Flags.string({ description: 'When the watch must stop (predicate form, required)' }),
|
|
39
|
+
'check-every': core_1.Flags.string({ description: 'Observation interval (predicate form)' }),
|
|
40
|
+
'max-checks': core_1.Flags.integer({ description: 'Observation cap (predicate form)' }),
|
|
41
|
+
'root-path': core_1.Flags.string({ description: 'JSON root path inside the response' }),
|
|
42
|
+
'resume-prompt': core_1.Flags.string({ description: 'What to tell the settling turn' }),
|
|
43
|
+
// recipe form
|
|
44
|
+
check: core_1.Flags.string({ description: 'The deterministic check to run (recipe form)' }),
|
|
45
|
+
success: core_1.Flags.string({ description: 'What state means the wait resolved (recipe form)' }),
|
|
46
|
+
abort: core_1.Flags.string({ description: 'When to stop and escalate (recipe form)' }),
|
|
47
|
+
wait: core_1.Flags.string({
|
|
48
|
+
description: 'Wait kind (recipe form)',
|
|
49
|
+
options: [...followups_1.WATCH_WAIT_KINDS],
|
|
50
|
+
}),
|
|
51
|
+
'wait-at': core_1.Flags.string({ description: 'Instant/duration for --wait clock' }),
|
|
52
|
+
'wait-entity': core_1.Flags.string({ description: 'Entity type for --wait event' }),
|
|
53
|
+
'wait-event': core_1.Flags.string({ description: 'Event type for --wait event' }),
|
|
54
|
+
'wait-filter-field': core_1.Flags.string({ description: 'Payload field for an event filter' }),
|
|
55
|
+
'wait-filter-mode': core_1.Flags.string({
|
|
56
|
+
description: 'Event filter mode',
|
|
57
|
+
options: ['equals', 'contains', 'in_set'],
|
|
58
|
+
}),
|
|
59
|
+
'wait-filter-value': core_1.Flags.string({ description: 'Event filter value' }),
|
|
60
|
+
'wait-interval-seconds': core_1.Flags.integer({ description: 'Poll interval for --wait poll_until' }),
|
|
61
|
+
'wait-max-attempts': core_1.Flags.integer({ description: 'Attempt cap for --wait poll_until' }),
|
|
62
|
+
'wait-fallback': core_1.Flags.string({ description: 'SLA backstop for --wait event/poll_until' }),
|
|
63
|
+
// delivery
|
|
64
|
+
endpoint: core_1.Flags.string({ description: 'Delivery endpoint id (EndpointRegistry)' }),
|
|
65
|
+
'endpoint-target': core_1.Flags.string({ description: 'Channel target for channel endpoints' }),
|
|
66
|
+
pin: core_1.Flags.string({ description: 'Conversation id the watch was authored in' }),
|
|
67
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
68
|
+
};
|
|
69
|
+
async run() {
|
|
70
|
+
this.requireAuth();
|
|
71
|
+
const { flags } = await this.parse(FollowupsWatch);
|
|
72
|
+
let agent;
|
|
73
|
+
try {
|
|
74
|
+
agent = (0, followups_1.resolveFollowUpAgent)(flags.agent);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const failure = (0, agent_resolver_1.agentResolutionFailure)(err);
|
|
78
|
+
this.failWithCliError({
|
|
79
|
+
message: failure.message,
|
|
80
|
+
code: failure.code,
|
|
81
|
+
exit: 2,
|
|
82
|
+
retryable: false,
|
|
83
|
+
details: { suggestion: failure.suggestion, context: failure.details },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const predicate = flags.url !== undefined || flags.until !== undefined;
|
|
87
|
+
const recipe = flags.check !== undefined || flags.success !== undefined || flags.abort !== undefined;
|
|
88
|
+
if (predicate && recipe) {
|
|
89
|
+
this.failWithCliError({
|
|
90
|
+
message: 'Predicate and recipe flags cannot mix. A watch is either an observed URL (--url/--until) or a checked recipe (--check/--success/--abort/--wait).',
|
|
91
|
+
code: 'FOLLOWUP_INVALID_FORM',
|
|
92
|
+
exit: 2,
|
|
93
|
+
retryable: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!predicate && !recipe) {
|
|
97
|
+
this.failWithCliError({
|
|
98
|
+
message: 'A watch needs a form: --url + --until for a predicate watch, or --check + --success + --abort + --wait for a recipe watch.',
|
|
99
|
+
code: 'FOLLOWUP_INVALID_FORM',
|
|
100
|
+
exit: 2,
|
|
101
|
+
retryable: false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const body = {
|
|
105
|
+
kind: 'watch',
|
|
106
|
+
producer: agent.boundToSession ? 'agent_in_turn' : 'user_explicit',
|
|
107
|
+
agentId: agent.id,
|
|
108
|
+
subject: (0, followups_1.parseSubjectRef)(flags.subject),
|
|
109
|
+
why: flags.why,
|
|
110
|
+
...(flags.endpoint ? { endpointId: flags.endpoint } : {}),
|
|
111
|
+
...(flags['endpoint-target'] ? { endpointTarget: flags['endpoint-target'] } : {}),
|
|
112
|
+
...(flags.pin ? { pinnedConversationId: flags.pin } : {}),
|
|
113
|
+
};
|
|
114
|
+
if (predicate) {
|
|
115
|
+
if (!flags.url || !flags.until) {
|
|
116
|
+
this.failWithCliError({
|
|
117
|
+
message: 'A predicate watch needs both --url <https> and --until <predicate-json>.',
|
|
118
|
+
code: 'FOLLOWUP_KIND_FIELD_MISSING',
|
|
119
|
+
exit: 2,
|
|
120
|
+
retryable: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (!flags.deadline) {
|
|
124
|
+
this.failWithCliError({
|
|
125
|
+
message: '--deadline is required — a watch that cannot stop is a leak.',
|
|
126
|
+
code: 'FOLLOWUP_KIND_FIELD_MISSING',
|
|
127
|
+
exit: 2,
|
|
128
|
+
retryable: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
Object.assign(body, {
|
|
132
|
+
form: 'predicate',
|
|
133
|
+
url: flags.url,
|
|
134
|
+
until: (0, triggers_1.parseJsonObject)(flags.until, '--until'),
|
|
135
|
+
grantId: flags.grant,
|
|
136
|
+
deadline: flags.deadline,
|
|
137
|
+
checkEvery: flags['check-every'],
|
|
138
|
+
maxChecks: flags['max-checks'],
|
|
139
|
+
rootPath: flags['root-path'],
|
|
140
|
+
resumePrompt: flags['resume-prompt'],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
for (const field of ['check', 'success', 'abort', 'wait']) {
|
|
145
|
+
if (!flags[field]) {
|
|
146
|
+
this.failWithCliError({
|
|
147
|
+
message: `A recipe watch needs --${field}.`,
|
|
148
|
+
code: 'FOLLOWUP_KIND_FIELD_MISSING',
|
|
149
|
+
exit: 2,
|
|
150
|
+
retryable: false,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const wait = { kind: flags.wait };
|
|
155
|
+
if (flags.wait === 'clock') {
|
|
156
|
+
wait.at = flags['wait-at'];
|
|
157
|
+
}
|
|
158
|
+
else if (flags.wait === 'event') {
|
|
159
|
+
Object.assign(wait, {
|
|
160
|
+
entityType: flags['wait-entity'],
|
|
161
|
+
eventType: flags['wait-event'],
|
|
162
|
+
filterField: flags['wait-filter-field'],
|
|
163
|
+
filterMode: flags['wait-filter-mode'],
|
|
164
|
+
filterValue: flags['wait-filter-value'],
|
|
165
|
+
fallback: flags['wait-fallback'],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
Object.assign(wait, {
|
|
170
|
+
intervalSeconds: flags['wait-interval-seconds'],
|
|
171
|
+
maxAttempts: flags['wait-max-attempts'],
|
|
172
|
+
fallback: flags['wait-fallback'],
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
Object.assign(body, {
|
|
176
|
+
form: 'recipe',
|
|
177
|
+
check: flags.check,
|
|
178
|
+
success: flags.success,
|
|
179
|
+
abort: flags.abort,
|
|
180
|
+
wait,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await followups_1.followupsApi.create(body);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
this.handleApiError(err);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (flags.json) {
|
|
192
|
+
this.log(JSON.stringify(response, null, 2));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const row = response.followup;
|
|
196
|
+
if (response.reused) {
|
|
197
|
+
this.log(`Already armed: ${row?.id} — an identical watch exists and was not duplicated.`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (response.dropped) {
|
|
201
|
+
this.log('Dropped by the daily watch budget — no watch was armed.');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.log(`Watch armed: ${row?.id}${row?.deadlineAt ? `, deadline ${row.deadlineAt}` : ''}.`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.default = FollowupsWatch;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
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
|
+
const label_ref_1 = require("../../../lib/label-ref");
|
|
6
7
|
class GoalsLabelsAdd extends base_command_1.BaseCommand {
|
|
7
8
|
static description = 'Attach a label to a goal';
|
|
8
9
|
static examples = [
|
|
@@ -15,7 +16,7 @@ class GoalsLabelsAdd extends base_command_1.BaseCommand {
|
|
|
15
16
|
static flags = {
|
|
16
17
|
json: core_1.Flags.boolean({ description: 'Output the updated goal as JSON' }),
|
|
17
18
|
label: core_1.Flags.string({
|
|
18
|
-
description: 'Label
|
|
19
|
+
description: 'Label — id, or a name from `skrr labels list --applicable-to goal`. An unknown name is refused, never created.',
|
|
19
20
|
required: true,
|
|
20
21
|
}),
|
|
21
22
|
};
|
|
@@ -25,9 +26,25 @@ class GoalsLabelsAdd extends base_command_1.BaseCommand {
|
|
|
25
26
|
// `goals update` deliberately refuses labelIds — the server routes label
|
|
26
27
|
// changes through this atomic endpoint so exclusive-group eviction and
|
|
27
28
|
// usage counters stay correct. Re-attaching an attached label is a no-op.
|
|
29
|
+
// A name is resolved against the DECLARED vocabulary; an unknown one is
|
|
30
|
+
// refused with the near misses named, never created. See lib/label-ref.
|
|
31
|
+
let labelId = flags.label;
|
|
32
|
+
try {
|
|
33
|
+
[labelId] = await (0, label_ref_1.resolveLabelRefs)([flags.label], {
|
|
34
|
+
applicableTo: 'goal',
|
|
35
|
+
...(process.env.OVERSKY_WORKSPACE_ID
|
|
36
|
+
? { workspaceId: process.env.OVERSKY_WORKSPACE_ID }
|
|
37
|
+
: {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err instanceof label_ref_1.LabelRefError)
|
|
42
|
+
this.error(err.message, { exit: 1 });
|
|
43
|
+
this.handleApiError(err);
|
|
44
|
+
}
|
|
28
45
|
let result;
|
|
29
46
|
try {
|
|
30
|
-
result = await data_provider_1.dataService.addLabelToGoal({ goalId: args.id, labelId
|
|
47
|
+
result = await data_provider_1.dataService.addLabelToGoal({ goalId: args.id, labelId });
|
|
31
48
|
}
|
|
32
49
|
catch (err) {
|
|
33
50
|
this.handleApiError(err);
|