@skrr-ai/cli 0.1.46 → 0.1.48
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 +1 -1
- package/dist/base-command.d.ts +18 -0
- package/dist/base-command.js +61 -1
- package/dist/commands/agents/chat.d.ts +21 -0
- package/dist/commands/agents/chat.js +26 -9
- package/dist/commands/balance/usage/breakdown.d.ts +14 -0
- package/dist/commands/balance/usage/breakdown.js +68 -0
- package/dist/commands/balance/usage.js +24 -0
- package/dist/commands/code/handover.d.ts +2 -2
- package/dist/commands/code/handover.js +12 -13
- package/dist/commands/code/install.js +31 -0
- package/dist/commands/code/update.d.ts +46 -0
- package/dist/commands/code/update.js +242 -0
- package/dist/commands/commerce/entitlements/explain.d.ts +22 -0
- package/dist/commands/commerce/entitlements/explain.js +40 -0
- package/dist/commands/commerce/incidents/audit.d.ts +20 -0
- package/dist/commands/commerce/incidents/audit.js +37 -0
- package/dist/commands/commerce/incidents/list.d.ts +22 -0
- package/dist/commands/commerce/incidents/list.js +49 -0
- package/dist/commands/commerce/incidents/resolve.d.ts +23 -0
- package/dist/commands/commerce/incidents/resolve.js +45 -0
- package/dist/commands/commerce/incidents/retry.d.ts +22 -0
- package/dist/commands/commerce/incidents/retry.js +45 -0
- package/dist/commands/commerce/incidents/show.d.ts +13 -0
- package/dist/commands/commerce/incidents/show.js +30 -0
- package/dist/commands/commerce/offers/approve.d.ts +17 -0
- package/dist/commands/commerce/offers/approve.js +33 -0
- package/dist/commands/commerce/offers/create.d.ts +18 -0
- package/dist/commands/commerce/offers/create.js +34 -0
- package/dist/commands/commerce/offers/publish.d.ts +13 -0
- package/dist/commands/commerce/offers/publish.js +28 -0
- package/dist/commands/commerce/offers/retire.d.ts +17 -0
- package/dist/commands/commerce/offers/retire.js +32 -0
- package/dist/commands/commerce/offers/validate.d.ts +18 -0
- package/dist/commands/commerce/offers/validate.js +33 -0
- package/dist/commands/commerce/products/create.d.ts +10 -0
- package/dist/commands/commerce/products/create.js +25 -0
- package/dist/commands/commerce/promotions/redeem.d.ts +13 -0
- package/dist/commands/commerce/promotions/redeem.js +32 -0
- package/dist/commands/commerce/terms/accept.d.ts +18 -0
- package/dist/commands/commerce/terms/accept.js +39 -0
- package/dist/commands/commerce/terms/show.d.ts +10 -0
- package/dist/commands/commerce/terms/show.js +34 -0
- package/dist/commands/commerce/trials/conversion-preview.d.ts +18 -0
- package/dist/commands/commerce/trials/conversion-preview.js +39 -0
- package/dist/commands/commerce/trials/start.d.ts +23 -0
- package/dist/commands/commerce/trials/start.js +58 -0
- package/dist/commands/commitments/accounting.d.ts +39 -0
- package/dist/commands/commitments/accounting.js +116 -0
- package/dist/commands/commitments/action-history.d.ts +1 -0
- package/dist/commands/commitments/action-history.js +8 -1
- package/dist/commands/commitments/case.d.ts +1 -0
- package/dist/commands/commitments/case.js +19 -3
- package/dist/commands/commitments/cases.d.ts +1 -0
- package/dist/commands/commitments/cases.js +11 -1
- package/dist/commands/commitments/checks.d.ts +1 -0
- package/dist/commands/commitments/checks.js +5 -1
- package/dist/commands/commitments/cost.d.ts +62 -0
- package/dist/commands/commitments/cost.js +130 -0
- package/dist/commands/commitments/create.js +1 -1
- package/dist/commands/commitments/delete.d.ts +1 -0
- package/dist/commands/commitments/delete.js +56 -6
- package/dist/commands/commitments/feedback.js +9 -1
- package/dist/commands/commitments/handover.js +16 -0
- package/dist/commands/commitments/hypotheses.d.ts +1 -0
- package/dist/commands/commitments/hypotheses.js +17 -2
- package/dist/commands/commitments/links/list.js +4 -1
- package/dist/commands/commitments/liveness.d.ts +18 -0
- package/dist/commands/commitments/liveness.js +80 -0
- package/dist/commands/commitments/metrics.d.ts +1 -0
- package/dist/commands/commitments/metrics.js +8 -1
- package/dist/commands/commitments/next-tick.d.ts +39 -0
- package/dist/commands/commitments/next-tick.js +122 -0
- package/dist/commands/commitments/observations.d.ts +1 -0
- package/dist/commands/commitments/observations.js +19 -2
- package/dist/commands/commitments/pause.js +12 -0
- package/dist/commands/commitments/receipt.d.ts +2 -0
- package/dist/commands/commitments/receipt.js +16 -2
- package/dist/commands/commitments/remediation-proposal.js +6 -2
- package/dist/commands/commitments/remediation-proposals.js +5 -1
- package/dist/commands/commitments/restore.d.ts +25 -0
- package/dist/commands/commitments/restore.js +46 -0
- package/dist/commands/commitments/show.js +32 -0
- package/dist/commands/commitments/trigger-status.js +33 -0
- package/dist/commands/commitments/update.d.ts +1 -0
- package/dist/commands/commitments/update.js +31 -5
- package/dist/commands/commitments/wake-capabilities.d.ts +0 -1
- package/dist/commands/commitments/wake-capabilities.js +8 -4
- package/dist/commands/convos/handover.d.ts +1 -0
- package/dist/commands/convos/handover.js +4 -0
- package/dist/commands/daemon/index.js +2 -0
- package/dist/commands/daemon/update.d.ts +19 -0
- package/dist/commands/daemon/update.js +48 -0
- package/dist/commands/enterprise/budgets/create.d.ts +11 -0
- package/dist/commands/enterprise/budgets/create.js +27 -0
- package/dist/commands/enterprise/budgets/evaluate.d.ts +17 -0
- package/dist/commands/enterprise/budgets/evaluate.js +32 -0
- package/dist/commands/enterprise/budgets/publish.d.ts +14 -0
- package/dist/commands/enterprise/budgets/publish.js +30 -0
- package/dist/commands/enterprise/contracts/activate.d.ts +14 -0
- package/dist/commands/enterprise/contracts/activate.js +30 -0
- package/dist/commands/enterprise/contracts/create.d.ts +14 -0
- package/dist/commands/enterprise/contracts/create.js +32 -0
- package/dist/commands/enterprise/contracts/issue.d.ts +18 -0
- package/dist/commands/enterprise/contracts/issue.js +34 -0
- package/dist/commands/enterprise/domains/begin.d.ts +20 -0
- package/dist/commands/enterprise/domains/begin.js +39 -0
- package/dist/commands/enterprise/domains/verify.d.ts +14 -0
- package/dist/commands/enterprise/domains/verify.js +32 -0
- package/dist/commands/enterprise/external-config/activate.d.ts +22 -0
- package/dist/commands/enterprise/external-config/activate.js +49 -0
- package/dist/commands/enterprise/external-config/set.d.ts +25 -0
- package/dist/commands/enterprise/external-config/set.js +123 -0
- package/dist/commands/enterprise/external-config/show.d.ts +12 -0
- package/dist/commands/enterprise/external-config/show.js +39 -0
- package/dist/commands/enterprise/external-config/suspend.d.ts +13 -0
- package/dist/commands/enterprise/external-config/suspend.js +41 -0
- package/dist/commands/enterprise/finance/close-snapshot.d.ts +11 -0
- package/dist/commands/enterprise/finance/close-snapshot.js +28 -0
- package/dist/commands/enterprise/finance/record-document.d.ts +18 -0
- package/dist/commands/enterprise/finance/record-document.js +35 -0
- package/dist/commands/enterprise/profile.d.ts +20 -0
- package/dist/commands/enterprise/profile.js +38 -0
- package/dist/commands/login.d.ts +13 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/machines/dedicated/index.js +3 -0
- package/dist/commands/machines/dedicated/service/apply.d.ts +23 -0
- package/dist/commands/machines/dedicated/service/apply.js +99 -0
- package/dist/commands/machines/dedicated/service/approve.d.ts +23 -0
- package/dist/commands/machines/dedicated/service/approve.js +52 -0
- package/dist/commands/machines/dedicated/service/create.d.ts +55 -0
- package/dist/commands/machines/dedicated/service/create.js +222 -0
- package/dist/commands/machines/dedicated/service/edit.d.ts +30 -0
- package/dist/commands/machines/dedicated/service/edit.js +146 -0
- package/dist/commands/machines/dedicated/service/exec.d.ts +26 -0
- package/dist/commands/machines/dedicated/service/exec.js +92 -0
- package/dist/commands/machines/dedicated/service/export.d.ts +29 -0
- package/dist/commands/machines/dedicated/service/export.js +106 -0
- package/dist/commands/machines/dedicated/service/index.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/index.js +49 -0
- package/dist/commands/machines/dedicated/service/logs.d.ts +33 -0
- package/dist/commands/machines/dedicated/service/logs.js +148 -0
- package/dist/commands/machines/dedicated/service/ls.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/ls.js +80 -0
- package/dist/commands/machines/dedicated/service/restart.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/restart.js +29 -0
- package/dist/commands/machines/dedicated/service/rm.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/rm.js +33 -0
- package/dist/commands/machines/dedicated/service/secret/index.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/secret/index.js +25 -0
- package/dist/commands/machines/dedicated/service/secret/rm.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/secret/rm.js +54 -0
- package/dist/commands/machines/dedicated/service/secret/set.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/secret/set.js +93 -0
- package/dist/commands/machines/dedicated/service/start.d.ts +14 -0
- package/dist/commands/machines/dedicated/service/start.js +29 -0
- package/dist/commands/machines/dedicated/service/status.d.ts +21 -0
- package/dist/commands/machines/dedicated/service/status.js +85 -0
- package/dist/commands/machines/dedicated/service/stop.d.ts +20 -0
- package/dist/commands/machines/dedicated/service/stop.js +35 -0
- package/dist/commands/machines/dedicated/service/top.d.ts +22 -0
- package/dist/commands/machines/dedicated/service/top.js +120 -0
- package/dist/commands/machines/dedicated/sign-in-status.d.ts +22 -0
- package/dist/commands/machines/dedicated/sign-in-status.js +79 -0
- package/dist/commands/machines/dedicated/ssh-config.d.ts +28 -0
- package/dist/commands/machines/dedicated/ssh-config.js +66 -0
- package/dist/commands/machines/dedicated/ssh-proxy.d.ts +22 -0
- package/dist/commands/machines/dedicated/ssh-proxy.js +62 -0
- package/dist/commands/machines/dedicated/ssh.d.ts +29 -0
- package/dist/commands/machines/dedicated/ssh.js +121 -0
- package/dist/commands/native/allow-all.d.ts +14 -0
- package/dist/commands/native/allow-all.js +25 -0
- package/dist/commands/native/allow.d.ts +24 -0
- package/dist/commands/native/allow.js +45 -0
- package/dist/commands/native/deny.d.ts +24 -0
- package/dist/commands/native/deny.js +43 -0
- package/dist/commands/native/disable.d.ts +17 -0
- package/dist/commands/native/disable.js +28 -0
- package/dist/commands/native/enable.d.ts +12 -0
- package/dist/commands/native/enable.js +29 -0
- package/dist/commands/native/list-apps.d.ts +19 -0
- package/dist/commands/native/list-apps.js +30 -0
- package/dist/commands/native/restrict-to.d.ts +22 -0
- package/dist/commands/native/restrict-to.js +41 -0
- package/dist/commands/native/status.d.ts +19 -0
- package/dist/commands/native/status.js +33 -0
- package/dist/commands/native.d.ts +39 -0
- package/dist/commands/native.js +76 -0
- package/dist/commands/service/index.d.ts +14 -0
- package/dist/commands/service/index.js +31 -0
- package/dist/commands/service/logs.d.ts +18 -0
- package/dist/commands/service/logs.js +53 -0
- package/dist/commands/service/ls.d.ts +13 -0
- package/dist/commands/service/ls.js +48 -0
- package/dist/commands/service/propose.d.ts +34 -0
- package/dist/commands/service/propose.js +113 -0
- package/dist/commands/service/status.d.ts +17 -0
- package/dist/commands/service/status.js +56 -0
- package/dist/commands/spaces/auto-executor/runs.d.ts +13 -0
- package/dist/commands/spaces/auto-executor/runs.js +56 -0
- package/dist/commands/spaces/briefs/get.js +8 -0
- package/dist/commands/spaces/briefs/list.js +2 -0
- package/dist/commands/spaces/briefs/set.d.ts +3 -0
- package/dist/commands/spaces/briefs/set.js +78 -13
- package/dist/commands/spaces/expertise-suggestions.d.ts +12 -0
- package/dist/commands/spaces/expertise-suggestions.js +71 -0
- package/dist/commands/spaces/members/add.d.ts +1 -0
- package/dist/commands/spaces/members/add.js +11 -3
- package/dist/commands/spaces/members/list.js +11 -0
- package/dist/commands/store/governance/artifacts.d.ts +25 -0
- package/dist/commands/store/governance/artifacts.js +55 -0
- package/dist/commands/store/governance/assign.d.ts +16 -0
- package/dist/commands/store/governance/assign.js +54 -0
- package/dist/commands/store/governance/create-license.d.ts +18 -0
- package/dist/commands/store/governance/create-license.js +35 -0
- package/dist/commands/store/governance/install-plan.d.ts +25 -0
- package/dist/commands/store/governance/install-plan.js +56 -0
- package/dist/commands/store/governance/license-policy.d.ts +30 -0
- package/dist/commands/store/governance/license-policy.js +69 -0
- package/dist/commands/store/governance/policy.d.ts +25 -0
- package/dist/commands/store/governance/policy.js +65 -0
- package/dist/commands/store/governance/publish-release.d.ts +17 -0
- package/dist/commands/store/governance/publish-release.js +34 -0
- package/dist/commands/tasks/assign.d.ts +9 -0
- package/dist/commands/tasks/assign.js +108 -1
- package/dist/commands/tasks/deliverable/add.d.ts +15 -0
- package/dist/commands/tasks/deliverable/add.js +174 -0
- package/dist/commands/tasks/deliverable/archive.d.ts +35 -0
- package/dist/commands/tasks/deliverable/archive.js +205 -0
- package/dist/commands/tasks/labels/detach.js +3 -0
- package/dist/commands/tasks/labels/suggest.d.ts +24 -0
- package/dist/commands/tasks/labels/suggest.js +69 -0
- package/dist/commands/tasks/list.d.ts +16 -2
- package/dist/commands/tasks/list.js +100 -24
- package/dist/commands/tasks/show.d.ts +23 -0
- package/dist/commands/tasks/show.js +51 -1
- package/dist/commands/wiki/rm.d.ts +22 -1
- package/dist/commands/wiki/rm.js +52 -3
- package/dist/lib/agentic-stream.js +10 -2
- package/dist/lib/assignee-resolver.d.ts +22 -0
- package/dist/lib/assignee-resolver.js +26 -0
- package/dist/lib/balance.d.ts +6 -1
- package/dist/lib/balance.js +15 -0
- package/dist/lib/code-handover.d.ts +0 -10
- package/dist/lib/code-handover.js +20 -5
- package/dist/lib/commerce-command.d.ts +48 -0
- package/dist/lib/commerce-command.js +70 -0
- package/dist/lib/commerce.d.ts +93 -0
- package/dist/lib/commerce.js +129 -0
- package/dist/lib/commitment-product.js +1 -1
- package/dist/lib/commitments.d.ts +202 -29
- package/dist/lib/commitments.js +300 -33
- package/dist/lib/dedicated-lease-command.js +10 -0
- package/dist/lib/dedicated-machines.d.ts +37 -0
- package/dist/lib/dedicated-machines.js +47 -0
- package/dist/lib/dedicated-service-command.d.ts +60 -0
- package/dist/lib/dedicated-service-command.js +182 -0
- package/dist/lib/dedicated-service.d.ts +342 -0
- package/dist/lib/dedicated-service.js +741 -0
- package/dist/lib/dedicated-ssh.d.ts +136 -0
- package/dist/lib/dedicated-ssh.js +387 -0
- package/dist/lib/delegated-cli.js +51 -13
- package/dist/lib/enterprise-commerce.d.ts +67 -0
- package/dist/lib/enterprise-commerce.js +112 -0
- package/dist/lib/enterprise-external-configuration.d.ts +60 -0
- package/dist/lib/enterprise-external-configuration.js +58 -0
- package/dist/lib/first-party-harness.js +17 -3
- package/dist/lib/governed-store.d.ts +110 -0
- package/dist/lib/governed-store.js +79 -0
- package/dist/lib/guest-service-command.d.ts +16 -0
- package/dist/lib/guest-service-command.js +32 -0
- package/dist/lib/guest-service.d.ts +67 -0
- package/dist/lib/guest-service.js +112 -0
- package/dist/lib/prompt.d.ts +1 -0
- package/dist/lib/prompt.js +1 -0
- package/dist/lib/task-asset-upload.d.ts +20 -0
- package/dist/lib/task-asset-upload.js +30 -8
- package/dist/lib/task-extras.d.ts +23 -0
- package/dist/lib/task-extras.js +6 -0
- package/dist/lib/update-check.d.ts +80 -7
- package/dist/lib/update-check.js +365 -33
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.d.ts +9 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.js +10 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +9 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +6 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +16 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +21 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +24 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.d.ts +9 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.js +10 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +9 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +8 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +16 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +20 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +24 -2
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5067 -4821
- package/oclif.manifest.json +34877 -28659
- package/package.json +43 -1
|
@@ -6,6 +6,7 @@ const base_command_1 = require("../../base-command");
|
|
|
6
6
|
const task_resolver_1 = require("../../lib/task-resolver");
|
|
7
7
|
const web_url_1 = require("../../lib/web-url");
|
|
8
8
|
const assignee_resolver_1 = require("../../lib/assignee-resolver");
|
|
9
|
+
const format_1 = require("../../lib/format");
|
|
9
10
|
const tasks_1 = require("../../lib/tasks");
|
|
10
11
|
/**
|
|
11
12
|
* A first-class `assign` verb.
|
|
@@ -37,6 +38,7 @@ class TasksAssign extends base_command_1.BaseCommand {
|
|
|
37
38
|
'<%= config.bin %> tasks assign <task-id> frank --replace',
|
|
38
39
|
'<%= config.bin %> tasks assign <task-id> frank --remove',
|
|
39
40
|
'<%= config.bin %> tasks assign <task-id> --list',
|
|
41
|
+
'<%= config.bin %> tasks assign <task-id> --suggest',
|
|
40
42
|
];
|
|
41
43
|
static args = {
|
|
42
44
|
id: core_1.Args.string({
|
|
@@ -76,6 +78,10 @@ class TasksAssign extends base_command_1.BaseCommand {
|
|
|
76
78
|
list: core_1.Flags.boolean({
|
|
77
79
|
description: "List the task's space members and exit (your own unshared agents can also be assigned, by id)",
|
|
78
80
|
}),
|
|
81
|
+
suggest: core_1.Flags.boolean({
|
|
82
|
+
description: "Advisory lookup: list space members whose declared expertise intersects this task's tags " +
|
|
83
|
+
'(or whose expertise appears in the task text). Suggests only — never assigns.',
|
|
84
|
+
}),
|
|
79
85
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
80
86
|
};
|
|
81
87
|
async run() {
|
|
@@ -105,7 +111,7 @@ class TasksAssign extends base_command_1.BaseCommand {
|
|
|
105
111
|
}
|
|
106
112
|
const personQueries = [...(args.person ? [args.person] : []), ...(flags.person ?? [])];
|
|
107
113
|
const agentQueries = flags.agent ?? [];
|
|
108
|
-
if (!flags.list && personQueries.length === 0 && agentQueries.length === 0) {
|
|
114
|
+
if (!flags.list && !flags.suggest && personQueries.length === 0 && agentQueries.length === 0) {
|
|
109
115
|
// Positional order is `<task-id> <person>`. Inside a task runtime the
|
|
110
116
|
// task id is implicit, so a single positional (`skrr tasks assign frank`)
|
|
111
117
|
// is silently eaten as the TASK id and the user sees a confusing
|
|
@@ -134,6 +140,10 @@ class TasksAssign extends base_command_1.BaseCommand {
|
|
|
134
140
|
this.renderMembers(spaceId ? await this.loadSpaceMembers(spaceId) : [], spaceId, flags.json);
|
|
135
141
|
return;
|
|
136
142
|
}
|
|
143
|
+
if (flags.suggest) {
|
|
144
|
+
await this.renderSuggestions(taskRecord, spaceId, flags.json);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
137
147
|
const people = await this.resolveAssignees(personQueries, 'user', spaceId);
|
|
138
148
|
const agents = await this.resolveAssignees(agentQueries, 'agent', spaceId);
|
|
139
149
|
const userIds = people.ids;
|
|
@@ -228,5 +238,102 @@ class TasksAssign extends base_command_1.BaseCommand {
|
|
|
228
238
|
this.log(`\nAgents (${agents.length}):`);
|
|
229
239
|
this.log(agents.length ? (0, assignee_resolver_1.describeCandidates)(agents, 100) : ' (none)');
|
|
230
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* `--suggest`: a deterministic LOOKUP, not a routing decision. Declared
|
|
243
|
+
* member expertise is intersected with the task's tags (strong match) and
|
|
244
|
+
* its title/description text (weak match); the caller still chooses the
|
|
245
|
+
* assignee. This mirrors how the contract stays advisory everywhere else —
|
|
246
|
+
* the data is shown, the judgment stays with whoever is assigning.
|
|
247
|
+
*/
|
|
248
|
+
async renderSuggestions(task, spaceId, json) {
|
|
249
|
+
if (!spaceId) {
|
|
250
|
+
this.failWithCliError({
|
|
251
|
+
message: 'Task is not in a space — no member expertise to suggest from. ' +
|
|
252
|
+
'Assign by raw id, or move the task into a space first.',
|
|
253
|
+
code: 'ASSIGN_NO_SPACE_CONTEXT',
|
|
254
|
+
exit: 2,
|
|
255
|
+
retryable: false,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
let principals = [];
|
|
259
|
+
let briefs = [];
|
|
260
|
+
try {
|
|
261
|
+
const [perms, briefList] = await Promise.all([
|
|
262
|
+
data_provider_1.dataService.getResourcePermissions(data_provider_1.ResourceType.SPACE, spaceId),
|
|
263
|
+
data_provider_1.dataService.listSpaceBriefs(spaceId).catch(() => []),
|
|
264
|
+
]);
|
|
265
|
+
principals = (perms.principals ?? []);
|
|
266
|
+
briefs = briefList;
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
this.handleApiError(err);
|
|
270
|
+
}
|
|
271
|
+
const members = (0, assignee_resolver_1.membersFromPrincipals)(principals);
|
|
272
|
+
// brief.principalId may be an agent UUID or a Mongo _id — resolve through
|
|
273
|
+
// the principal's raw id pair so either convention lands on the member.
|
|
274
|
+
const expertiseByMemberId = new Map();
|
|
275
|
+
for (const p of principals) {
|
|
276
|
+
const kind = String(p.type ?? '').toLowerCase();
|
|
277
|
+
const brief = briefs.find((b) => String(b.principalType ?? '').toLowerCase() === kind &&
|
|
278
|
+
[p.id, p.idOnTheSource]
|
|
279
|
+
.filter(Boolean)
|
|
280
|
+
.map(String)
|
|
281
|
+
.includes(String(b.principalId ?? '')));
|
|
282
|
+
if (!brief || !Array.isArray(brief.expertise) || brief.expertise.length === 0)
|
|
283
|
+
continue;
|
|
284
|
+
const resolvedId = kind === 'agent' ? String(p.idOnTheSource ?? p.id ?? '') : String(p.id ?? '');
|
|
285
|
+
if (resolvedId)
|
|
286
|
+
expertiseByMemberId.set(resolvedId, brief.expertise);
|
|
287
|
+
}
|
|
288
|
+
const norm = (v) => String(v ?? '')
|
|
289
|
+
.trim()
|
|
290
|
+
.toLowerCase();
|
|
291
|
+
const taskTags = (Array.isArray(task.tags) ? task.tags : []).map(norm);
|
|
292
|
+
const taskText = `${task.title ?? ''}\n${task.description ?? ''}`;
|
|
293
|
+
const suggestions = (0, assignee_resolver_1.suggestMembersByExpertise)({
|
|
294
|
+
members,
|
|
295
|
+
expertiseByMemberId,
|
|
296
|
+
taskTags,
|
|
297
|
+
taskText,
|
|
298
|
+
});
|
|
299
|
+
if (json) {
|
|
300
|
+
this.log(JSON.stringify({
|
|
301
|
+
spaceId,
|
|
302
|
+
taskTags,
|
|
303
|
+
suggestions: suggestions.map((s) => ({
|
|
304
|
+
kind: s.member.kind,
|
|
305
|
+
id: s.member.id,
|
|
306
|
+
name: s.member.name ?? s.member.email ?? null,
|
|
307
|
+
matchedTags: s.matchedTags,
|
|
308
|
+
matchedText: s.matchedText,
|
|
309
|
+
})),
|
|
310
|
+
}, null, 2));
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const ref = task.identifier ?? task.id;
|
|
314
|
+
if (suggestions.length === 0) {
|
|
315
|
+
this.log(`No member expertise matches ${ref}` +
|
|
316
|
+
(taskTags.length === 0
|
|
317
|
+
? ' (the task has no tags — tag tasks or declare expertise to make routing smarter)'
|
|
318
|
+
: ` (tags: ${taskTags.join(', ')})`) +
|
|
319
|
+
'.');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
this.log(`Suggested assignees for ${ref}${taskTags.length ? ` (tags: ${taskTags.join(', ')})` : ''}:`);
|
|
323
|
+
(0, format_1.renderTable)(suggestions.map((s) => ({
|
|
324
|
+
kind: s.member.kind,
|
|
325
|
+
member: s.member.name || s.member.email || s.member.id,
|
|
326
|
+
tag: s.matchedTags.join(', ') || '-',
|
|
327
|
+
text: s.matchedText.join(', ') || '-',
|
|
328
|
+
id: s.member.id,
|
|
329
|
+
})), [
|
|
330
|
+
{ key: 'kind', header: 'KIND' },
|
|
331
|
+
{ key: 'member', header: 'MEMBER', maxWidth: 28 },
|
|
332
|
+
{ key: 'tag', header: 'TAG MATCH', maxWidth: 32 },
|
|
333
|
+
{ key: 'text', header: 'TEXT MATCH', maxWidth: 32 },
|
|
334
|
+
{ key: 'id', header: 'ID', verbatim: true },
|
|
335
|
+
], (line) => this.log(line));
|
|
336
|
+
this.log(`\nAdvisory only — assign with: ${this.config.bin} tasks assign ${ref} --agent <name>`);
|
|
337
|
+
}
|
|
231
338
|
}
|
|
232
339
|
exports.default = TasksAssign;
|
|
@@ -25,7 +25,22 @@ export default class TasksDeliverableAdd extends BaseCommand {
|
|
|
25
25
|
expectation: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
26
|
'run-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
27
27
|
'idempotency-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
28
|
+
file: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
29
|
+
'no-upload': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
28
30
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
29
31
|
};
|
|
30
32
|
run(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Archive the bytes and let the upload's own ledger receipt BE the
|
|
35
|
+
* Deliverable record.
|
|
36
|
+
*
|
|
37
|
+
* One record, not two: the receipt is keyed on the same coordinate this
|
|
38
|
+
* command would have recorded, so the archived copy and the citation are one
|
|
39
|
+
* Resource rather than a path nobody can open beside an opaque blob nobody
|
|
40
|
+
* can match to it.
|
|
41
|
+
*
|
|
42
|
+
* Returns false when the file cannot be archived and the caller should fall
|
|
43
|
+
* back to recording the coordinate alone.
|
|
44
|
+
*/
|
|
45
|
+
private archive;
|
|
31
46
|
}
|
|
@@ -1,8 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("node:fs"));
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
3
38
|
const core_1 = require("@oclif/core");
|
|
4
39
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
40
|
const base_command_1 = require("../../../base-command");
|
|
41
|
+
const task_asset_upload_1 = require("../../../lib/task-asset-upload");
|
|
6
42
|
const outbox_1 = require("../../../lib/outbox");
|
|
7
43
|
const api_fetch_1 = require("../../../lib/api-fetch");
|
|
8
44
|
const session_task_endpoints_1 = require("../../../lib/session-task-endpoints");
|
|
@@ -68,6 +104,15 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
|
|
|
68
104
|
expectation: core_1.Flags.string({ multiple: true, description: 'Expectation id this satisfies' }),
|
|
69
105
|
'run-id': core_1.Flags.string({ description: 'Run that produced it' }),
|
|
70
106
|
'idempotency-key': core_1.Flags.string({ description: 'Stable retry key' }),
|
|
107
|
+
file: core_1.Flags.string({
|
|
108
|
+
description: "Archive THIS file as the Deliverable's exact content, when the bytes are somewhere " +
|
|
109
|
+
'other than the coordinate. Without it a coordinate that already names a readable ' +
|
|
110
|
+
'local file is archived automatically.',
|
|
111
|
+
}),
|
|
112
|
+
'no-upload': core_1.Flags.boolean({
|
|
113
|
+
description: 'Record the coordinate only. The Deliverable stays `locator_only`: nothing pins which ' +
|
|
114
|
+
'revision it is, and nobody can open it from the web app.',
|
|
115
|
+
}),
|
|
71
116
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
72
117
|
};
|
|
73
118
|
async run() {
|
|
@@ -84,6 +129,45 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
|
|
|
84
129
|
this.error('--status and deprecated --lifecycle cannot disagree', { exit: 1 });
|
|
85
130
|
}
|
|
86
131
|
const status = flags.status || flags.lifecycle;
|
|
132
|
+
// A bare path has no global meaning and nothing to open — the CLI's own
|
|
133
|
+
// help says so. Archiving the bytes is what turns it into something a
|
|
134
|
+
// person can click and a digest can pin, so a coordinate that already
|
|
135
|
+
// names a readable local file is archived by default.
|
|
136
|
+
//
|
|
137
|
+
// Not when the caller is pinning an EXTERNAL system's version
|
|
138
|
+
// (--provider/--version/--content-digest): that names a revision in
|
|
139
|
+
// somebody else's namespace, and a local file of the same name is not
|
|
140
|
+
// evidence about it.
|
|
141
|
+
const pinsExternalVersion = Boolean(flags.provider || flags.version || flags['content-digest']);
|
|
142
|
+
const archivePath = flags['no-upload']
|
|
143
|
+
? null
|
|
144
|
+
: flags.file
|
|
145
|
+
? flags.file
|
|
146
|
+
: pinsExternalVersion
|
|
147
|
+
? null
|
|
148
|
+
: localFileFor(parsed.value);
|
|
149
|
+
if (flags.file && flags['no-upload']) {
|
|
150
|
+
this.error('--file and --no-upload cannot both be given', { exit: 1 });
|
|
151
|
+
}
|
|
152
|
+
let archiveRefused = false;
|
|
153
|
+
if (archivePath) {
|
|
154
|
+
const uploaded = await this.archive({
|
|
155
|
+
taskId: args.task,
|
|
156
|
+
filePath: archivePath,
|
|
157
|
+
identity: parsed.value.identity,
|
|
158
|
+
title: flags.title,
|
|
159
|
+
expectationIds: flags.expectation,
|
|
160
|
+
requestedStatus: status,
|
|
161
|
+
json: Boolean(flags.json),
|
|
162
|
+
// `--file` is an instruction; a coordinate that happens to name a file
|
|
163
|
+
// is an inference. Only the inference is size-bounded.
|
|
164
|
+
auto: !flags.file,
|
|
165
|
+
});
|
|
166
|
+
if (uploaded)
|
|
167
|
+
return;
|
|
168
|
+
// It tried and said why; the closing hint below would only repeat it.
|
|
169
|
+
archiveRefused = true;
|
|
170
|
+
}
|
|
87
171
|
let result;
|
|
88
172
|
try {
|
|
89
173
|
const payload = {
|
|
@@ -116,6 +200,96 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
|
|
|
116
200
|
}
|
|
117
201
|
const where = parsed.value.location ? ` at ${parsed.value.location}` : '';
|
|
118
202
|
this.log(`Recorded Deliverable ${result.link.id}: ${parsed.value.kind}:${parsed.value.identity}${where} (${status || 'local'}; self reported).`);
|
|
203
|
+
if (!pinsExternalVersion && !flags['no-upload'] && !archiveRefused) {
|
|
204
|
+
this.log('No copy was archived, so nothing pins this version and it cannot be opened from the ' +
|
|
205
|
+
'web app. Pass --file <path> to archive the bytes.');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Archive the bytes and let the upload's own ledger receipt BE the
|
|
210
|
+
* Deliverable record.
|
|
211
|
+
*
|
|
212
|
+
* One record, not two: the receipt is keyed on the same coordinate this
|
|
213
|
+
* command would have recorded, so the archived copy and the citation are one
|
|
214
|
+
* Resource rather than a path nobody can open beside an opaque blob nobody
|
|
215
|
+
* can match to it.
|
|
216
|
+
*
|
|
217
|
+
* Returns false when the file cannot be archived and the caller should fall
|
|
218
|
+
* back to recording the coordinate alone.
|
|
219
|
+
*/
|
|
220
|
+
async archive(opts) {
|
|
221
|
+
let stat;
|
|
222
|
+
try {
|
|
223
|
+
stat = fs.statSync(opts.filePath);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
this.error(`File not found: ${opts.filePath}`, { exit: 1 });
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
if (!stat.isFile()) {
|
|
230
|
+
this.error(`Not a regular file: ${opts.filePath}`, { exit: 1 });
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
// The platform archives a file of any supported size under its own name.
|
|
234
|
+
// What is bounded is the INFERENCE: `deliverable add code:…@build/out.bin`
|
|
235
|
+
// should not quietly send 400 MB because the coordinate resolved to a
|
|
236
|
+
// path. Naming the file with --file says do it anyway, and the only limit
|
|
237
|
+
// left is the platform's own.
|
|
238
|
+
if (opts.auto && stat.size > data_provider_1.TASK_ATTACHMENT_MAX_BYTES) {
|
|
239
|
+
const mb = Math.floor(data_provider_1.TASK_ATTACHMENT_MAX_BYTES / (1024 * 1024));
|
|
240
|
+
this.warn(`${path.basename(opts.filePath)} is larger than ${mb} MB, so it was not archived ` +
|
|
241
|
+
'automatically. Pass --file to archive it anyway.');
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
let asset;
|
|
245
|
+
try {
|
|
246
|
+
asset = await (0, task_asset_upload_1.uploadTaskAssetFile)({
|
|
247
|
+
taskId: opts.taskId,
|
|
248
|
+
filePath: opts.filePath,
|
|
249
|
+
role: 'deliverable',
|
|
250
|
+
ledger: {
|
|
251
|
+
identity: opts.identity,
|
|
252
|
+
title: opts.title,
|
|
253
|
+
expectationIds: opts.expectationIds,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
this.handleApiError(err);
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (opts.json) {
|
|
262
|
+
this.log(JSON.stringify({ archived: asset, identity: opts.identity }, null, 2));
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
this.log(`Recorded Deliverable ${opts.identity} — archived copy ${asset.id} ` +
|
|
266
|
+
`(sha256 pinned; observed available; self reported).`);
|
|
267
|
+
if (opts.requestedStatus && opts.requestedStatus !== 'available') {
|
|
268
|
+
// Not silent: the platform WATCHED these bytes land, which is a stronger
|
|
269
|
+
// and verified statement than the one that was asked for. Who vouched
|
|
270
|
+
// is unchanged — still `self_reported`, still nobody else's word.
|
|
271
|
+
this.log(`--status ${opts.requestedStatus} did not apply: an archived copy is observed available.`);
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
119
274
|
}
|
|
120
275
|
}
|
|
121
276
|
exports.default = TasksDeliverableAdd;
|
|
277
|
+
/**
|
|
278
|
+
* The local file a coordinate already names, if it names one. `location` is
|
|
279
|
+
* checked first because it is where the thing IS; `identity` is the fallback
|
|
280
|
+
* for the common `file:docs/x.md` with no location.
|
|
281
|
+
*/
|
|
282
|
+
function localFileFor(value) {
|
|
283
|
+
for (const candidate of [value.location, value.identity]) {
|
|
284
|
+
if (!candidate)
|
|
285
|
+
continue;
|
|
286
|
+
try {
|
|
287
|
+
if (fs.statSync(candidate).isFile())
|
|
288
|
+
return candidate;
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
/* not a local file */
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks deliverable archive <task>`
|
|
4
|
+
*
|
|
5
|
+
* Backfill for Deliverables recorded before archiving existed, or recorded
|
|
6
|
+
* with `--no-upload`. They are bare coordinates: nothing pins which revision
|
|
7
|
+
* they are, and nobody can open them from the web app.
|
|
8
|
+
*
|
|
9
|
+
* This is a CLI command and not a server migration, and that is not a
|
|
10
|
+
* shortcut. A Deliverable recorded as `docs/policies/payout.md` names a file
|
|
11
|
+
* the server has never seen and cannot reach — the bytes exist on the machine
|
|
12
|
+
* that produced them. Only something running THERE can archive them, so the
|
|
13
|
+
* backfill has to be a command someone runs in the working tree, not a job
|
|
14
|
+
* that sweeps a collection.
|
|
15
|
+
*
|
|
16
|
+
* What it will not do: guess. A row is archived only when its own coordinate
|
|
17
|
+
* resolves to a readable regular file relative to the current directory. Every
|
|
18
|
+
* row it skips is reported with the reason, because a backfill that silently
|
|
19
|
+
* covers 3 of 5 is worse than one that covers none.
|
|
20
|
+
*/
|
|
21
|
+
export default class TasksDeliverableArchive extends BaseCommand {
|
|
22
|
+
static description: string;
|
|
23
|
+
static examples: string[];
|
|
24
|
+
static args: {
|
|
25
|
+
task: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
26
|
+
};
|
|
27
|
+
static flags: {
|
|
28
|
+
task: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
29
|
+
'dry-run': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
30
|
+
'max-bytes': import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
31
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
32
|
+
};
|
|
33
|
+
run(): Promise<void>;
|
|
34
|
+
private report;
|
|
35
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("node:fs"));
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const core_1 = require("@oclif/core");
|
|
39
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
40
|
+
const base_command_1 = require("../../../base-command");
|
|
41
|
+
const task_resolver_1 = require("../../../lib/task-resolver");
|
|
42
|
+
const task_asset_upload_1 = require("../../../lib/task-asset-upload");
|
|
43
|
+
/**
|
|
44
|
+
* `skrr tasks deliverable archive <task>`
|
|
45
|
+
*
|
|
46
|
+
* Backfill for Deliverables recorded before archiving existed, or recorded
|
|
47
|
+
* with `--no-upload`. They are bare coordinates: nothing pins which revision
|
|
48
|
+
* they are, and nobody can open them from the web app.
|
|
49
|
+
*
|
|
50
|
+
* This is a CLI command and not a server migration, and that is not a
|
|
51
|
+
* shortcut. A Deliverable recorded as `docs/policies/payout.md` names a file
|
|
52
|
+
* the server has never seen and cannot reach — the bytes exist on the machine
|
|
53
|
+
* that produced them. Only something running THERE can archive them, so the
|
|
54
|
+
* backfill has to be a command someone runs in the working tree, not a job
|
|
55
|
+
* that sweeps a collection.
|
|
56
|
+
*
|
|
57
|
+
* What it will not do: guess. A row is archived only when its own coordinate
|
|
58
|
+
* resolves to a readable regular file relative to the current directory. Every
|
|
59
|
+
* row it skips is reported with the reason, because a backfill that silently
|
|
60
|
+
* covers 3 of 5 is worse than one that covers none.
|
|
61
|
+
*/
|
|
62
|
+
class TasksDeliverableArchive extends base_command_1.BaseCommand {
|
|
63
|
+
static description = 'Archive the bytes of Deliverables that are still bare coordinates.\n' +
|
|
64
|
+
'\n' +
|
|
65
|
+
'Run it from the directory their paths are relative to — usually the repository root. ' +
|
|
66
|
+
'Each row whose coordinate resolves to a readable local file is uploaded and recorded ' +
|
|
67
|
+
'against those exact bytes, converging onto the SAME Deliverable rather than adding a ' +
|
|
68
|
+
'second one. Rows that already carry an archived copy are left alone.';
|
|
69
|
+
static examples = [
|
|
70
|
+
'<%= config.bin %> tasks deliverable archive <task-id> --dry-run',
|
|
71
|
+
'<%= config.bin %> tasks deliverable archive <task-id>',
|
|
72
|
+
];
|
|
73
|
+
static args = {
|
|
74
|
+
task: core_1.Args.string({ required: false, ignoreStdin: true, description: 'Task id or ref' }),
|
|
75
|
+
};
|
|
76
|
+
static flags = {
|
|
77
|
+
task: core_1.Flags.string({ description: 'Task id; overrides runtime context' }),
|
|
78
|
+
'dry-run': core_1.Flags.boolean({
|
|
79
|
+
description: 'Report what would be archived and upload nothing',
|
|
80
|
+
}),
|
|
81
|
+
'max-bytes': core_1.Flags.integer({
|
|
82
|
+
description: `Skip files larger than this (default ${data_provider_1.TASK_ATTACHMENT_MAX_BYTES})`,
|
|
83
|
+
default: data_provider_1.TASK_ATTACHMENT_MAX_BYTES,
|
|
84
|
+
}),
|
|
85
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
86
|
+
};
|
|
87
|
+
async run() {
|
|
88
|
+
this.requireAuth();
|
|
89
|
+
const { args, flags } = await this.parse(TasksDeliverableArchive);
|
|
90
|
+
const task = (0, task_resolver_1.resolveTaskIdentity)({ positionalId: args.task, flagTask: flags.task });
|
|
91
|
+
let listed;
|
|
92
|
+
try {
|
|
93
|
+
listed = await data_provider_1.dataService.listTaskDeliverables(task.id);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
this.handleApiError(error);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const plan = (listed.data || []).map((row) => classify(row, flags['max-bytes']));
|
|
100
|
+
const actionable = plan.filter((entry) => entry.action === 'archive');
|
|
101
|
+
if (flags['dry-run']) {
|
|
102
|
+
const report = { taskId: task.id, dryRun: true, plan };
|
|
103
|
+
if (flags.json)
|
|
104
|
+
return this.log(JSON.stringify(report, null, 2));
|
|
105
|
+
return this.report(plan, []);
|
|
106
|
+
}
|
|
107
|
+
const results = [];
|
|
108
|
+
for (const entry of actionable) {
|
|
109
|
+
try {
|
|
110
|
+
const asset = await (0, task_asset_upload_1.uploadTaskAssetFile)({
|
|
111
|
+
taskId: task.id,
|
|
112
|
+
filePath: entry.filePath,
|
|
113
|
+
role: 'deliverable',
|
|
114
|
+
ledger: {
|
|
115
|
+
identity: entry.identity,
|
|
116
|
+
title: entry.title,
|
|
117
|
+
expectationIds: entry.expectationIds,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
results.push({ identity: entry.identity, ok: true, attachmentId: asset.id });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
results.push({
|
|
124
|
+
identity: entry.identity,
|
|
125
|
+
ok: false,
|
|
126
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (flags.json) {
|
|
131
|
+
return this.log(JSON.stringify({ taskId: task.id, plan, results }, null, 2));
|
|
132
|
+
}
|
|
133
|
+
this.report(plan, results);
|
|
134
|
+
if (results.some((entry) => !entry.ok)) {
|
|
135
|
+
this.error(`${results.filter((entry) => !entry.ok).length} Deliverable(s) failed to archive`, {
|
|
136
|
+
exit: 1,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
report(plan, results) {
|
|
141
|
+
const byIdentity = new Map(results.map((entry) => [entry.identity, entry]));
|
|
142
|
+
for (const entry of plan) {
|
|
143
|
+
const outcome = byIdentity.get(entry.identity);
|
|
144
|
+
if (entry.action === 'archive' && !outcome) {
|
|
145
|
+
this.log(`would archive ${entry.identity} (${entry.filePath})`);
|
|
146
|
+
}
|
|
147
|
+
else if (outcome?.ok) {
|
|
148
|
+
this.log(`archived ${entry.identity} → ${outcome.attachmentId}`);
|
|
149
|
+
}
|
|
150
|
+
else if (outcome) {
|
|
151
|
+
this.log(`FAILED ${entry.identity} — ${outcome.reason}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.log(`skipped ${entry.identity} — ${entry.reason}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!plan.length)
|
|
158
|
+
this.log('No Deliverables recorded.');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.default = TasksDeliverableArchive;
|
|
162
|
+
/**
|
|
163
|
+
* One row's verdict. Every `skip` carries the reason it was skipped — the
|
|
164
|
+
* whole point of the command is that a partial backfill says which part.
|
|
165
|
+
*/
|
|
166
|
+
function classify(row, maxBytes) {
|
|
167
|
+
const identity = row.identity || row.location || row.title || '';
|
|
168
|
+
const base = {
|
|
169
|
+
linkId: row.linkId,
|
|
170
|
+
identity,
|
|
171
|
+
title: row.title,
|
|
172
|
+
expectationIds: row.expectationIds,
|
|
173
|
+
action: 'skip',
|
|
174
|
+
};
|
|
175
|
+
if (row.managedContentRef) {
|
|
176
|
+
return { ...base, reason: 'already archived' };
|
|
177
|
+
}
|
|
178
|
+
if (!identity) {
|
|
179
|
+
return { ...base, reason: 'no coordinate to resolve' };
|
|
180
|
+
}
|
|
181
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(row.location || identity)) {
|
|
182
|
+
return { ...base, reason: 'remote locator — nothing local to archive' };
|
|
183
|
+
}
|
|
184
|
+
for (const candidate of [row.location, identity]) {
|
|
185
|
+
if (!candidate)
|
|
186
|
+
continue;
|
|
187
|
+
let stat;
|
|
188
|
+
try {
|
|
189
|
+
stat = fs.statSync(candidate);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (!stat.isFile())
|
|
195
|
+
continue;
|
|
196
|
+
if (stat.size > maxBytes) {
|
|
197
|
+
return {
|
|
198
|
+
...base,
|
|
199
|
+
reason: `${path.basename(candidate)} is ${stat.size} bytes, over the ${maxBytes} limit`,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return { ...base, action: 'archive', filePath: candidate };
|
|
203
|
+
}
|
|
204
|
+
return { ...base, reason: 'no readable local file at this coordinate' };
|
|
205
|
+
}
|
|
@@ -21,6 +21,9 @@ class TasksLabelsDetach extends base_command_1.BaseCommand {
|
|
|
21
21
|
static flags = {
|
|
22
22
|
json: core_1.Flags.boolean({ description: 'Output the updated task as JSON' }),
|
|
23
23
|
'label-id': core_1.Flags.string({
|
|
24
|
+
// Same alias `attach` carries: `--label` is what `spaces labels
|
|
25
|
+
// remove` takes, and the two directions should spell one flag alike.
|
|
26
|
+
aliases: ['label'],
|
|
24
27
|
description: 'Label to detach — id, or a name from `skrr labels list --applicable-to task` (repeatable)',
|
|
25
28
|
multiple: true,
|
|
26
29
|
required: true,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* Ask the lightweight model which of the DECLARED labels fit this task —
|
|
4
|
+
* `POST /api/tasks/:taskId/labels/suggest`, the on-demand half of the
|
|
5
|
+
* create-time `_labelGuidance` hint. The server keeps the suggestion off the
|
|
6
|
+
* task-create path (the hottest write in the product); this command is how a
|
|
7
|
+
* caller pays for one suggestion when it actually wants it.
|
|
8
|
+
*
|
|
9
|
+
* Same contract as create: the model may only CHOOSE from the declared
|
|
10
|
+
* vocabulary, a label the task already wears is never suggested, and a
|
|
11
|
+
* suggestion is a proposal, not a write — attach is still the caller's
|
|
12
|
+
* decision (`tasks labels attach --label <id>`).
|
|
13
|
+
*/
|
|
14
|
+
export default class TasksLabelsSuggest 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
|
+
};
|
|
23
|
+
run(): Promise<void>;
|
|
24
|
+
}
|