@skrr-ai/cli 0.1.63 → 0.1.65

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.
Files changed (40) hide show
  1. package/dist/commands/goals/plan-now.d.ts +6 -0
  2. package/dist/commands/goals/plan-now.js +25 -0
  3. package/dist/commands/memory/approvals/issue.js +6 -3
  4. package/dist/commands/memory/stores/update.js +6 -3
  5. package/dist/commands/skills/visibility.d.ts +15 -0
  6. package/dist/commands/skills/visibility.js +49 -0
  7. package/dist/commands/store/admissions/approve.d.ts +1 -0
  8. package/dist/commands/store/admissions/approve.js +9 -3
  9. package/dist/commands/store/admissions/list.js +6 -0
  10. package/dist/commands/store/admissions/refuse.d.ts +1 -0
  11. package/dist/commands/store/admissions/refuse.js +9 -3
  12. package/dist/commands/store/artifacts/release.d.ts +3 -0
  13. package/dist/commands/store/artifacts/release.js +42 -0
  14. package/dist/commands/store/installs.d.ts +10 -0
  15. package/dist/commands/store/installs.js +54 -0
  16. package/dist/commands/store/updates.js +1 -1
  17. package/dist/commands/tasks/comments/list.d.ts +10 -0
  18. package/dist/commands/tasks/comments/list.js +53 -9
  19. package/dist/commands/tasks/comments/react.js +3 -1
  20. package/dist/commands/tasks/deliverable/add.d.ts +1 -0
  21. package/dist/commands/tasks/deliverable/add.js +13 -2
  22. package/dist/commands/tasks/deliverable/list.js +15 -1
  23. package/dist/commands/tasks/lifecycle/transition.js +6 -3
  24. package/dist/commands/tasks/plan/run-update.js +1 -1
  25. package/dist/commands/tasks/result/submit.js +1 -1
  26. package/dist/commands/tasks/resume/save.js +6 -3
  27. package/dist/commands/tasks/updates/add.js +11 -1
  28. package/dist/lib/file-mime.js +1 -0
  29. package/dist/lib/live-runtime-binary.d.ts +6 -0
  30. package/dist/lib/live-runtime-binary.js +32 -1
  31. package/dist/lib/node-adapter.js +10 -5
  32. package/dist/lib/prompt.d.ts +7 -0
  33. package/dist/lib/prompt.js +16 -0
  34. package/dist/lib/task-asset-upload.d.ts +5 -0
  35. package/dist/lib/task-asset-upload.js +4 -0
  36. package/dist/lib/task-closure.d.ts +3 -1
  37. package/dist/lib/task-closure.js +14 -4
  38. package/dist/node_modules/@skrr-ai/data-provider/index.js +10 -2
  39. package/oclif.manifest.json +24195 -24018
  40. package/package.json +1 -1
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../../base-command");
6
6
  const task_lifecycle_output_1 = require("../../../lib/task-lifecycle-output");
7
+ const prompt_1 = require("../../../lib/prompt");
7
8
  class TasksLifecycleTransition extends base_command_1.BaseCommand {
8
9
  static description = 'Apply a declared lifecycle transition — records effects and advances the stage';
9
10
  static args = {
@@ -33,10 +34,12 @@ class TasksLifecycleTransition extends base_command_1.BaseCommand {
33
34
  let payload;
34
35
  if (flags.payload) {
35
36
  try {
36
- payload = JSON.parse(flags.payload);
37
+ payload = (0, prompt_1.parseJsonFlag)(flags.payload, '--payload');
37
38
  }
38
- catch {
39
- this.error('--payload must be valid JSON.', { exit: 2 });
39
+ catch (err) {
40
+ this.error(err instanceof Error ? err.message : '--payload must be valid JSON.', {
41
+ exit: 2,
42
+ });
40
43
  }
41
44
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
42
45
  this.error('--payload must be a JSON object.', { exit: 2 });
@@ -60,7 +60,7 @@ class TasksPlanRunUpdate extends base_command_1.BaseCommand {
60
60
  let body;
61
61
  try {
62
62
  if (flags.patch !== undefined) {
63
- const parsed = JSON.parse(flags.patch);
63
+ const parsed = (0, prompt_1.parseJsonFlag)(flags.patch, '--patch');
64
64
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
65
65
  throw new Error('--patch must be a JSON object');
66
66
  }
@@ -210,7 +210,7 @@ class TasksResultSubmit extends base_command_1.BaseCommand {
210
210
  // which the call did not carry. Submitting a Result is not closing a Task;
211
211
  // the response now reports where it stands and this prints exactly that.
212
212
  this.log(`Submitted Result v${result.result.version} for ${task.id}.`);
213
- for (const line of (0, task_closure_1.closureReport)(result.closure))
213
+ for (const line of (0, task_closure_1.closureReport)(result.closure, { closing: false }))
214
214
  this.log(line);
215
215
  if (url)
216
216
  this.log(`Task: ${url}`);
@@ -7,6 +7,7 @@ const outbox_1 = require("../../../lib/outbox");
7
7
  const task_resolver_1 = require("../../../lib/task-resolver");
8
8
  const api_fetch_1 = require("../../../lib/api-fetch");
9
9
  const session_task_endpoints_1 = require("../../../lib/session-task-endpoints");
10
+ const prompt_1 = require("../../../lib/prompt");
10
11
  class TasksResumeSave extends base_command_1.BaseCommand {
11
12
  static description = 'Save recovery state without publishing a visible Task Update';
12
13
  static args = {
@@ -36,10 +37,12 @@ class TasksResumeSave extends base_command_1.BaseCommand {
36
37
  const task = (0, task_resolver_1.resolveTaskIdentity)({ positionalId: args.task, flagTask: flags.task });
37
38
  let resume;
38
39
  try {
39
- resume = JSON.parse(flags.snapshot);
40
+ resume = (0, prompt_1.parseJsonFlag)(flags.snapshot, '--snapshot');
40
41
  }
41
- catch {
42
- this.error('--snapshot must be a JSON object', { exit: 1 });
42
+ catch (err) {
43
+ this.error(err instanceof Error ? err.message : '--snapshot must be a JSON object', {
44
+ exit: 1,
45
+ });
43
46
  }
44
47
  let result;
45
48
  try {
@@ -43,7 +43,17 @@ const session_task_endpoints_1 = require("../../../lib/session-task-endpoints");
43
43
  function parseJsonObject(value, label) {
44
44
  if (!value)
45
45
  return undefined;
46
- const parsed = JSON.parse(value);
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(value);
49
+ }
50
+ catch (err) {
51
+ // A bare SyntaxError ("Unexpected token …") never says WHICH flag was
52
+ // malformed — name it, the same way readJsonValue names its file
53
+ // (OSK-12753).
54
+ const msg = err instanceof Error ? err.message : String(err);
55
+ throw new Error(`Invalid JSON in ${label}: ${msg}`);
56
+ }
47
57
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
48
58
  throw new Error(`${label} must be a JSON object`);
49
59
  }
@@ -63,6 +63,7 @@ const MIME_TYPES = {
63
63
  '.jpg': 'image/jpeg',
64
64
  '.js': 'text/javascript',
65
65
  '.json': 'application/json',
66
+ '.log': 'text/plain',
66
67
  '.m4a': 'audio/m4a',
67
68
  '.m4v': 'video/m4v',
68
69
  '.md': 'text/markdown',
@@ -37,4 +37,10 @@ export declare function decideStatusBinary(pathBinary: string, profile: string,
37
37
  /** One stderr line naming the binary that rendered status, when it is not obvious. */
38
38
  export declare function describeStatusBinary(decision: StatusBinaryDecision): string | null;
39
39
  export declare function scriptFromArgs(exec: string, args: string): string | null;
40
+ /**
41
+ * The daemon keeps its singleton holder while auth is latched, but removes the
42
+ * broker bootstrap because it cannot serve hand-offs. Status and login still
43
+ * need to address that live process during recovery (OSK-12807).
44
+ */
45
+ export declare function livePidFromLockInfo(profile: string, homeDir?: string): number | null;
40
46
  export declare function defaultLiveRuntimeDeps(): LiveRuntimeDeps;
@@ -7,6 +7,7 @@ exports.profileFromArgs = profileFromArgs;
7
7
  exports.decideStatusBinary = decideStatusBinary;
8
8
  exports.describeStatusBinary = describeStatusBinary;
9
9
  exports.scriptFromArgs = scriptFromArgs;
10
+ exports.livePidFromLockInfo = livePidFromLockInfo;
10
11
  exports.defaultLiveRuntimeDeps = defaultLiveRuntimeDeps;
11
12
  /**
12
13
  * Which runtime binary is the LIVE daemon running, and is it the one
@@ -29,6 +30,7 @@ exports.defaultLiveRuntimeDeps = defaultLiveRuntimeDeps;
29
30
  */
30
31
  const node_child_process_1 = require("node:child_process");
31
32
  const node_fs_1 = require("node:fs");
33
+ const node_os_1 = __importDefault(require("node:os"));
32
34
  const node_path_1 = __importDefault(require("node:path"));
33
35
  const daemonBroker_1 = require("./daemonBroker");
34
36
  const exec_runtime_binary_1 = require("./exec-runtime-binary");
@@ -163,6 +165,35 @@ function scriptFromArgs(exec, args) {
163
165
  }
164
166
  return null;
165
167
  }
168
+ /**
169
+ * The daemon keeps its singleton holder while auth is latched, but removes the
170
+ * broker bootstrap because it cannot serve hand-offs. Status and login still
171
+ * need to address that live process during recovery (OSK-12807).
172
+ */
173
+ function livePidFromLockInfo(profile, homeDir = node_os_1.default.homedir()) {
174
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(profile) || profile === '..')
175
+ return null;
176
+ try {
177
+ const lockInfoPath = node_path_1.default.join(homeDir, '.skrr', 'profiles', profile, 'daemon.lock.info.json');
178
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(lockInfoPath, 'utf8'));
179
+ const pid = parsed?.pid;
180
+ if (typeof pid !== 'number' || !Number.isSafeInteger(pid) || pid <= 0)
181
+ return null;
182
+ try {
183
+ process.kill(pid, 0);
184
+ }
185
+ catch (error) {
186
+ // EPERM still means a process holds the PID; all other probe failures
187
+ // leave this untrusted, potentially stale sidecar out of the decision.
188
+ if (error.code !== 'EPERM')
189
+ return null;
190
+ }
191
+ return pid;
192
+ }
193
+ catch {
194
+ return null;
195
+ }
196
+ }
166
197
  function defaultLiveRuntimeDeps() {
167
198
  return {
168
199
  livePid(profile) {
@@ -171,7 +202,7 @@ function defaultLiveRuntimeDeps() {
171
202
  if (typeof bootstrap?.pid === 'number' && bootstrap.pid > 0)
172
203
  return bootstrap.pid;
173
204
  }
174
- return null;
205
+ return livePidFromLockInfo(profile);
175
206
  },
176
207
  programOf(pid) {
177
208
  if (process.platform === 'darwin')
@@ -239,11 +239,16 @@ function createNodeAdapter(opts = {}) {
239
239
  const cred = currentCredential();
240
240
  if (cred.kind === 'ci')
241
241
  return false;
242
- // Bare-mode signal is on the resolved credential when populated by
243
- // BaseCommand. We don't have direct access here; instead we look at
244
- // the override's source — `bareMode` produces tokens sourced from
245
- // `flag` or `env-token`, both of which we already filter above. The
246
- // remaining bareMode case (no token at all) wouldn't reach this
242
+ // An operator-supplied `--token` is that operator's explicit
243
+ // authority: when the server refuses it, the honest answer is the
244
+ // 401 — not a silent retry under the daemon's principal (OSK-12801).
245
+ // `isEnvAuthOverride` above covers OVERSKY_TOKEN; the flag slot was
246
+ // missed because `kind` stays 'standard' for arbitrary flag strings.
247
+ // Broker-minted overrides carry 'env-token' too but are the daemon's
248
+ // own principal — re-recovering them preserves today's renewal path.
249
+ if (_credOverride?.source === 'flag')
250
+ return false;
251
+ // The remaining bareMode case (no token at all) wouldn't reach this
247
252
  // function — the adapter only retries on 401, which presupposes a
248
253
  // request was made with SOME bearer.
249
254
  let outcome;
@@ -46,6 +46,13 @@ export declare function confirmDestructive(question: string, yesFlag: boolean, f
46
46
  * commands can surface the error via `this.handleApiError()` or similar.
47
47
  */
48
48
  export declare function readJsonValue(source: string | undefined): Promise<unknown>;
49
+ /**
50
+ * Parse a flag-supplied JSON value. A bare `JSON.parse` throws a SyntaxError
51
+ * that never names which flag was malformed — `--data '{bad'` reads
52
+ * "Unexpected token" with no owner. `readJsonValue` already names its file;
53
+ * this is the same contract for inline flag values (OSK-12753).
54
+ */
55
+ export declare function parseJsonFlag(value: string, flagName: string): unknown;
49
56
  export declare function readJsonBody(source: string | undefined): Promise<Record<string, unknown>>;
50
57
  export declare function readStdin(): Promise<string>;
51
58
  /**
@@ -37,6 +37,7 @@ exports.prompt = prompt;
37
37
  exports.confirm = confirm;
38
38
  exports.confirmDestructive = confirmDestructive;
39
39
  exports.readJsonValue = readJsonValue;
40
+ exports.parseJsonFlag = parseJsonFlag;
40
41
  exports.readJsonBody = readJsonBody;
41
42
  exports.readStdin = readStdin;
42
43
  exports.mergeDefined = mergeDefined;
@@ -152,6 +153,21 @@ async function readJsonValue(source) {
152
153
  }
153
154
  return parsed;
154
155
  }
156
+ /**
157
+ * Parse a flag-supplied JSON value. A bare `JSON.parse` throws a SyntaxError
158
+ * that never names which flag was malformed — `--data '{bad'` reads
159
+ * "Unexpected token" with no owner. `readJsonValue` already names its file;
160
+ * this is the same contract for inline flag values (OSK-12753).
161
+ */
162
+ function parseJsonFlag(value, flagName) {
163
+ try {
164
+ return JSON.parse(value);
165
+ }
166
+ catch (err) {
167
+ const msg = err instanceof Error ? err.message : String(err);
168
+ throw new Error(`Invalid JSON in ${flagName}: ${msg}`);
169
+ }
170
+ }
155
171
  async function readJsonBody(source) {
156
172
  const parsed = await readJsonValue(source);
157
173
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
@@ -1,3 +1,4 @@
1
+ import type { WorkResourceKind } from '@skrr-ai/data-provider';
1
2
  export type TaskAssetRole = 'input' | 'evidence' | 'deliverable';
2
3
  /**
3
4
  * What the bytes ARE in the work ledger, for an upload that is the archived
@@ -15,6 +16,10 @@ export type TaskAssetRole = 'input' | 'evidence' | 'deliverable';
15
16
  */
16
17
  export interface TaskAssetLedgerResource {
17
18
  identity: string;
19
+ /** The cited artifact's coordinate kind — the Resource identity depends on
20
+ * it (`url:` bytes must land on the `url` Resource, not a parallel `file`
21
+ * one). Omitted by callers that mean the literal file (OSK-12740). */
22
+ kind?: WorkResourceKind;
18
23
  title?: string;
19
24
  expectationIds?: string[];
20
25
  }
@@ -212,6 +212,8 @@ function fileFormData(filePath, filename, mimeType, role, ledger) {
212
212
  fd.append('role', role);
213
213
  if (ledger?.identity) {
214
214
  fd.append('identity', ledger.identity);
215
+ if (ledger.kind)
216
+ fd.append('kind', ledger.kind);
215
217
  if (ledger.title)
216
218
  fd.append('title', ledger.title);
217
219
  if (ledger.expectationIds?.length) {
@@ -243,6 +245,7 @@ async function genericDirectUpload(p) {
243
245
  ...(p.ledger?.identity
244
246
  ? {
245
247
  identity: p.ledger.identity,
248
+ kind: p.ledger.kind,
246
249
  title: p.ledger.title,
247
250
  expectationIds: p.ledger.expectationIds,
248
251
  }
@@ -275,6 +278,7 @@ async function sessionDirectUpload(p) {
275
278
  ...(p.ledger?.identity
276
279
  ? {
277
280
  identity: p.ledger.identity,
281
+ kind: p.ledger.kind,
278
282
  title: p.ledger.title,
279
283
  expectationIds: p.ledger.expectationIds,
280
284
  }
@@ -42,7 +42,9 @@ export type ClosureLike = TaskClosureEvaluation | {
42
42
  * gate's own details carried for a while, and they are unusable on their own —
43
43
  * the whole point is to be able to act without a second lookup.
44
44
  */
45
- export declare function closureReport(closure: ClosureLike): string[];
45
+ export declare function closureReport(closure: ClosureLike, { closing }?: {
46
+ closing?: boolean;
47
+ }): string[];
46
48
  /**
47
49
  * The ways forward when a closed Task refuses a Result (`TASK_REOPEN_REQUIRED`).
48
50
  *
@@ -38,7 +38,7 @@ const SHORT_BLOCKER = data_provider_1.CLOSURE_BLOCKER_PHRASE;
38
38
  * gate's own details carried for a while, and they are unusable on their own —
39
39
  * the whole point is to be able to act without a second lookup.
40
40
  */
41
- function closureReport(closure) {
41
+ function closureReport(closure, { closing = true } = {}) {
42
42
  if (!closure)
43
43
  return ['Closure state: not reported by this call.'];
44
44
  if (isUnavailable(closure)) {
@@ -98,9 +98,19 @@ function closureReport(closure) {
98
98
  */
99
99
  const reviewRequired = closure.blockers.some((b) => b.code === 'TASK_RESULT_REVIEW_REQUIRED');
100
100
  lines.push('');
101
- lines.push(reviewRequired
102
- ? 'Only an accepted independent Review — or the Task owner closing directly — settles this. The rest is recorded on resolution.closure.'
103
- : 'If this Task is closed anyway, all of the above is recorded on resolution.closure — closing over it settles nothing.');
101
+ // `result submit` does not close anything — "if closed anyway" there read
102
+ // as though the submit itself had tried to close and been stamped
103
+ // (OSK-12757). Distinguish the non-closing report from the close one.
104
+ if (closing) {
105
+ lines.push(reviewRequired
106
+ ? 'Only an accepted independent Review — or the Task owner closing directly — settles this. The rest is recorded on resolution.closure.'
107
+ : 'If this Task is closed anyway, all of the above is recorded on resolution.closure — closing over it settles nothing.');
108
+ }
109
+ else {
110
+ lines.push(reviewRequired
111
+ ? 'Only an accepted independent Review — or the Task owner closing directly — settles this. The rest is recorded on resolution.closure at close.'
112
+ : 'These stay outstanding — closing the Task later records them on resolution.closure.');
113
+ }
104
114
  return lines;
105
115
  }
106
116
  /**
@@ -42759,6 +42759,10 @@ var gR = Object.freeze({ __proto__: null, acceptAgentIntuition: function(e2, t2,
42759
42759
  return oR.post((function(e3) {
42760
42760
  return "".concat(Rc(e3), "/approve-pr");
42761
42761
  })(e2), {});
42762
+ }, approveStoreArtifactAdmission: function(e2, t2) {
42763
+ return oR.post((function(e3) {
42764
+ return "".concat(er(), "/admissions/artifacts/").concat(encodeURIComponent(e3), "/approve");
42765
+ })(e2), t2 ? { note: t2 } : {});
42762
42766
  }, approveStoreListingAdmission: dR, approveStoreReleaseAdmission: lR, approveWorkflowMerge: function(e2) {
42763
42767
  return oR.post((function(e3) {
42764
42768
  return "".concat(jr(), "/").concat(encodeURIComponent(e3), "/workflow/approve-merge");
@@ -43194,10 +43198,10 @@ var gR = Object.freeze({ __proto__: null, acceptAgentIntuition: function(e2, t2,
43194
43198
  var t2 = e2.taskId, n2 = e2.data;
43195
43199
  return oR.post(ka(t2), n2);
43196
43200
  }, createTaskAttachmentUploadIntent: function(e2) {
43197
- var t2 = e2.taskId, n2 = e2.filename, o2 = e2.mimeType, r2 = e2.sizeBytes, a2 = e2.sha256, i2 = e2.role, s2 = e2.commentId, c2 = e2.identity, l2 = e2.title, u2 = e2.expectationIds, d2 = e2.transport;
43201
+ var t2 = e2.taskId, n2 = e2.filename, o2 = e2.mimeType, r2 = e2.sizeBytes, a2 = e2.sha256, i2 = e2.role, s2 = e2.commentId, c2 = e2.identity, l2 = e2.kind, u2 = e2.title, d2 = e2.expectationIds, p2 = e2.transport;
43198
43202
  return oR.post((function(e3) {
43199
43203
  return "".concat(jr(), "/").concat(encodeURIComponent(e3), "/attachments/upload-intent");
43200
- })(t2), S(S(S(S(S(S({ filename: n2, mimeType: o2, sizeBytes: r2, sha256: a2 }, i2 ? { role: i2 } : {}), s2 ? { commentId: s2 } : {}), c2 ? { identity: c2 } : {}), c2 && l2 ? { title: l2 } : {}), c2 && (null == u2 ? void 0 : u2.length) ? { expectationIds: u2 } : {}), d2 ? { transport: d2 } : {}));
43204
+ })(t2), S(S(S(S(S(S(S({ filename: n2, mimeType: o2, sizeBytes: r2, sha256: a2 }, i2 ? { role: i2 } : {}), s2 ? { commentId: s2 } : {}), c2 ? { identity: c2 } : {}), c2 && l2 ? { kind: l2 } : {}), c2 && u2 ? { title: u2 } : {}), c2 && (null == d2 ? void 0 : d2.length) ? { expectationIds: d2 } : {}), p2 ? { transport: p2 } : {}));
43201
43205
  }, createTaskFollowUp: function(e2) {
43202
43206
  var t2 = e2.taskId, n2 = e2.data;
43203
43207
  return oR.post((function(e3) {
@@ -46040,6 +46044,10 @@ var gR = Object.freeze({ __proto__: null, acceptAgentIntuition: function(e2, t2,
46040
46044
  return oR.post((function(e3) {
46041
46045
  return "".concat(jr(), "/").concat(encodeURIComponent(e3), "/workflow-findings");
46042
46046
  })(e2), t2);
46047
+ }, refuseStoreArtifactAdmission: function(e2, t2) {
46048
+ return oR.post((function(e3) {
46049
+ return "".concat(er(), "/admissions/artifacts/").concat(encodeURIComponent(e3), "/refuse");
46050
+ })(e2), { note: t2 });
46043
46051
  }, refuseStoreListingAdmission: pR, refuseStoreReleaseAdmission: uR, regenerateBackupCodes: function() {
46044
46052
  return oR.post("".concat(en, "/api/auth/2fa/backup/regenerate"));
46045
46053
  }, register: function(e2) {