@skrr-ai/cli 0.1.108 → 0.1.109
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 +10 -0
- package/dist/base-command.d.ts +59 -0
- package/dist/base-command.js +118 -7
- package/dist/commands/agents/update.d.ts +14 -0
- package/dist/commands/agents/update.js +40 -1
- package/dist/commands/commitments/accounting.js +7 -2
- package/dist/commands/commitments/cost.d.ts +11 -0
- package/dist/commands/commitments/cost.js +26 -3
- package/dist/commands/commitments/create.js +2 -1
- package/dist/commands/commitments/operating-review.d.ts +15 -0
- package/dist/commands/commitments/operating-review.js +103 -50
- package/dist/commands/commitments/update.js +12 -5
- package/dist/commands/doctor.js +9 -0
- package/dist/commands/goals/list.js +6 -1
- package/dist/commands/grants/rotate.js +4 -0
- package/dist/commands/login.js +42 -14
- package/dist/commands/machines/list.js +28 -6
- package/dist/commands/projects/show.js +1 -1
- package/dist/commands/revoke-daemon.js +2 -1
- package/dist/commands/spaces/workflows/archive.d.ts +1 -0
- package/dist/commands/spaces/workflows/archive.js +1 -0
- package/dist/commands/spaces/workflows/artifact.d.ts +1 -0
- package/dist/commands/spaces/workflows/artifact.js +1 -0
- package/dist/commands/spaces/workflows/cancel-node.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel-node.js +1 -0
- package/dist/commands/spaces/workflows/cancel.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel.js +6 -2
- package/dist/commands/spaces/workflows/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/create.js +7 -1
- package/dist/commands/spaces/workflows/events.d.ts +1 -0
- package/dist/commands/spaces/workflows/events.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.js +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +1 -0
- package/dist/commands/spaces/workflows/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/list.js +1 -0
- package/dist/commands/spaces/workflows/node-types.d.ts +2 -0
- package/dist/commands/spaces/workflows/node-types.js +24 -6
- package/dist/commands/spaces/workflows/pause.d.ts +1 -0
- package/dist/commands/spaces/workflows/pause.js +1 -0
- package/dist/commands/spaces/workflows/plan/show.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/show.js +1 -0
- package/dist/commands/spaces/workflows/plan/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/update.js +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.js +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.js +1 -0
- package/dist/commands/spaces/workflows/proposals/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/create.js +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.js +1 -0
- package/dist/commands/spaces/workflows/proposals/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/list.js +1 -0
- package/dist/commands/spaces/workflows/publish.d.ts +1 -0
- package/dist/commands/spaces/workflows/publish.js +9 -0
- package/dist/commands/spaces/workflows/reconcile.d.ts +1 -0
- package/dist/commands/spaces/workflows/reconcile.js +1 -0
- package/dist/commands/spaces/workflows/resume.d.ts +1 -0
- package/dist/commands/spaces/workflows/resume.js +1 -0
- package/dist/commands/spaces/workflows/retry.d.ts +1 -0
- package/dist/commands/spaces/workflows/retry.js +1 -0
- package/dist/commands/spaces/workflows/run.d.ts +1 -0
- package/dist/commands/spaces/workflows/run.js +14 -7
- package/dist/commands/spaces/workflows/runs.d.ts +1 -0
- package/dist/commands/spaces/workflows/runs.js +1 -0
- package/dist/commands/spaces/workflows/show.d.ts +25 -0
- package/dist/commands/spaces/workflows/show.js +42 -4
- package/dist/commands/spaces/workflows/signal.d.ts +12 -0
- package/dist/commands/spaces/workflows/signal.js +40 -2
- package/dist/commands/spaces/workflows/start.d.ts +1 -0
- package/dist/commands/spaces/workflows/start.js +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.js +1 -0
- package/dist/commands/spaces/workflows/test.d.ts +1 -0
- package/dist/commands/spaces/workflows/test.js +1 -0
- package/dist/commands/spaces/workflows/triggers/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/create.js +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.js +1 -0
- package/dist/commands/spaces/workflows/triggers/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/list.js +1 -0
- package/dist/commands/spaces/workflows/triggers/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/update.js +1 -0
- package/dist/commands/spaces/workflows/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/update.js +4 -0
- package/dist/commands/spaces/workflows/validate.d.ts +1 -0
- package/dist/commands/spaces/workflows/validate.js +15 -0
- package/dist/commands/tasks/activity.js +6 -2
- package/dist/commands/tasks/bulk-update.d.ts +1 -0
- package/dist/commands/tasks/bulk-update.js +7 -0
- package/dist/commands/tasks/complete.js +2 -1
- package/dist/commands/tasks/create-tree.d.ts +69 -0
- package/dist/commands/tasks/create-tree.js +413 -47
- package/dist/commands/tasks/list.d.ts +2 -0
- package/dist/commands/tasks/list.js +19 -1
- package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
- package/dist/commands/tasks/rearm-recurrence.js +68 -0
- package/dist/commands/tasks/show.d.ts +1 -0
- package/dist/commands/tasks/show.js +17 -3
- package/dist/commands/tasks/timeline.js +4 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +6 -1
- package/dist/commands/triggers/create.js +8 -2
- package/dist/commands/triggers/parse-intent.d.ts +7 -0
- package/dist/commands/triggers/parse-intent.js +31 -9
- package/dist/commands/workspaces/grants.js +23 -3
- package/dist/commands/workspaces/members.d.ts +20 -0
- package/dist/commands/workspaces/members.js +64 -0
- package/dist/commands/workspaces/stewardship.js +14 -2
- package/dist/help.js +12 -0
- package/dist/hooks/command-not-found.js +27 -1
- package/dist/lib/command-miss.d.ts +52 -2
- package/dist/lib/command-miss.js +187 -18
- package/dist/lib/commitment-loop-status.d.ts +8 -0
- package/dist/lib/commitment-loop-status.js +45 -0
- package/dist/lib/commitments.d.ts +16 -2
- package/dist/lib/commitments.js +48 -8
- package/dist/lib/daemon-command-hint.d.ts +15 -0
- package/dist/lib/daemon-command-hint.js +21 -0
- package/dist/lib/daemon-setup.js +10 -1
- package/dist/lib/daemonBrokerRefusal.js +3 -2
- package/dist/lib/daemonHandoff.js +2 -1
- package/dist/lib/deployed-build.d.ts +43 -0
- package/dist/lib/deployed-build.js +66 -0
- package/dist/lib/grants.d.ts +27 -1
- package/dist/lib/grants.js +39 -21
- package/dist/lib/json-mode.d.ts +11 -0
- package/dist/lib/json-mode.js +19 -0
- package/dist/lib/json-output.d.ts +25 -0
- package/dist/lib/json-output.js +30 -0
- package/dist/lib/list-output.d.ts +9 -0
- package/dist/lib/list-output.js +20 -1
- package/dist/lib/local-daemon-readiness.d.ts +90 -0
- package/dist/lib/local-daemon-readiness.js +143 -0
- package/dist/lib/machines.d.ts +117 -0
- package/dist/lib/machines.js +139 -0
- package/dist/lib/managed-instruction-local.d.ts +2 -0
- package/dist/lib/managed-instruction-local.js +2 -0
- package/dist/lib/profile-machine-identity-guard.d.ts +18 -0
- package/dist/lib/profile-machine-identity-guard.js +29 -0
- package/dist/lib/request-field-flags.d.ts +20 -0
- package/dist/lib/request-field-flags.js +34 -0
- package/dist/lib/stewardship-output.d.ts +1 -1
- package/dist/lib/stewardship-output.js +26 -30
- package/dist/lib/task-batch.d.ts +61 -4
- package/dist/lib/task-batch.js +316 -80
- package/dist/lib/task-economics.d.ts +5 -0
- package/dist/lib/task-economics.js +15 -0
- package/dist/lib/task-recurrence-outcome.d.ts +6 -6
- package/dist/lib/task-recurrence-outcome.js +27 -3
- package/dist/lib/tasks.d.ts +39 -0
- package/dist/lib/tasks.js +132 -0
- package/dist/lib/triggers.d.ts +31 -1
- package/dist/lib/triggers.js +55 -1
- package/dist/lib/workflow-issues.d.ts +14 -0
- package/dist/lib/workflow-issues.js +32 -0
- package/dist/lib/workflow-run-view.d.ts +55 -0
- package/dist/lib/workflow-run-view.js +105 -0
- package/dist/lib/workspaces.d.ts +31 -4
- package/dist/lib/workspaces.js +43 -0
- package/dist/lib/write-target.d.ts +29 -0
- package/dist/lib/write-target.js +72 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.js +50 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.js +46 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6718 -6370
- package/oclif.manifest.json +376 -52
- package/package.json +19 -1
package/README.md
CHANGED
|
@@ -1780,8 +1780,18 @@ sky tasks retry-schedule <task-id> --at 2026-06-02T15:00:00Z
|
|
|
1780
1780
|
# Pause / resume a recurring schedule without losing its definition
|
|
1781
1781
|
sky tasks pause-schedule <task-id>
|
|
1782
1782
|
sky tasks resume-schedule <task-id>
|
|
1783
|
+
|
|
1784
|
+
# A finished occurrence whose next occurrence was never armed (`tasks show` says
|
|
1785
|
+
# "The next occurrence was not scheduled: ..."): continue the series from it.
|
|
1786
|
+
# `tasks schedule` refuses a finished Task; this is the way back. --cron fixes an
|
|
1787
|
+
# invalid pattern first.
|
|
1788
|
+
skrr tasks rearm-recurrence <finished-task-id>
|
|
1789
|
+
skrr tasks rearm-recurrence <finished-task-id> --cron "0 9 * * 1-5"
|
|
1783
1790
|
```
|
|
1784
1791
|
|
|
1792
|
+
A pattern is validated when it is written: `--cron nonsense` is refused with the
|
|
1793
|
+
expected format and an example, never stored and armed at some other time.
|
|
1794
|
+
|
|
1785
1795
|
Pass `--from-json schedule.json` to `schedule` for fields not exposed as
|
|
1786
1796
|
typed flags (matches the `TaskScheduleParams` shape).
|
|
1787
1797
|
|
package/dist/base-command.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Command, Flags, type Interfaces } from '@oclif/core';
|
|
2
2
|
export { resolveProfileFromArgv } from './lib/keychain';
|
|
3
|
+
import { dataService } from '@skrr-ai/data-provider';
|
|
4
|
+
import { type WriteTarget } from './lib/write-target';
|
|
3
5
|
import { type AssigneeResolution, type MemberKind, type SpaceMember } from './lib/assignee-resolver';
|
|
4
6
|
import { type CliConfig } from './lib/config';
|
|
5
7
|
import { type OutboxMethod } from './lib/outbox';
|
|
@@ -59,6 +61,24 @@ export declare abstract class BaseCommand extends Command {
|
|
|
59
61
|
*/
|
|
60
62
|
log(message?: string, ...args: unknown[]): void;
|
|
61
63
|
protected wantsRawJson(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Print a command's `--json` RESULT: pretty JSON, with `ok` on an object so a
|
|
66
|
+
* script can test one field for success and failure alike (OSK-16425).
|
|
67
|
+
*
|
|
68
|
+
* A failure already prints `{ok:false, error}`; a success printed whatever the
|
|
69
|
+
* server sent, so `jq '{id, title}'` turned the failure into `{id:null,
|
|
70
|
+
* title:null}` — an empty success. `ok` is added HERE, by the command that
|
|
71
|
+
* knows its own outcome, and not in `log()`: `log` cannot see the exit status
|
|
72
|
+
* to come, and several commands print a document and then exit non-zero.
|
|
73
|
+
* Pass `{ ok: false }` for one of those; the command still owns its exit code.
|
|
74
|
+
*
|
|
75
|
+
* An array is printed as it is (it cannot carry a field), and an object that
|
|
76
|
+
* already has `ok` keeps its own. The exit status stays the test for a command
|
|
77
|
+
* that has not been moved here yet.
|
|
78
|
+
*/
|
|
79
|
+
protected printJsonResult(value: unknown, options?: {
|
|
80
|
+
ok?: boolean;
|
|
81
|
+
}): void;
|
|
62
82
|
protected cliConfig: CliConfig;
|
|
63
83
|
/**
|
|
64
84
|
* Resolved credential for this process. Populated in `init()` and
|
|
@@ -220,6 +240,17 @@ export declare abstract class BaseCommand extends Command {
|
|
|
220
240
|
* friendly error on any API failure, so callers can `await` it directly.
|
|
221
241
|
*/
|
|
222
242
|
protected resolveCurrentUserId(): Promise<string>;
|
|
243
|
+
/**
|
|
244
|
+
* The signed-in user, read ONCE per command run.
|
|
245
|
+
*
|
|
246
|
+
* `me`, the default-assignee policy and the write-target notice all need it,
|
|
247
|
+
* and a command that touches many nodes (`create-tree` with ninety-four)
|
|
248
|
+
* asked for it once per node. Who you are does not change during one command,
|
|
249
|
+
* so one read answers every caller — and a failed read is not remembered, so a
|
|
250
|
+
* transient error does not poison the rest of the run.
|
|
251
|
+
*/
|
|
252
|
+
private currentUserRead?;
|
|
253
|
+
protected fetchCurrentUser(): Promise<Awaited<ReturnType<typeof dataService.getUser>>>;
|
|
223
254
|
/**
|
|
224
255
|
* Decide who owns a task this invocation is creating, when the caller did
|
|
225
256
|
* not say.
|
|
@@ -249,6 +280,27 @@ export declare abstract class BaseCommand extends Command {
|
|
|
249
280
|
* worse, resolve a typo to a stranger the caller never intended to name.
|
|
250
281
|
*/
|
|
251
282
|
protected loadSpaceMembers(spaceId: string): Promise<SpaceMember[]>;
|
|
283
|
+
/** Lazily created: a command built without its constructor (specs do) must still work. */
|
|
284
|
+
private spaceMemberReads?;
|
|
285
|
+
/**
|
|
286
|
+
* Where a write from this invocation is about to land: the signed-in account,
|
|
287
|
+
* the server and the workspace. Best effort on the account (a failed read
|
|
288
|
+
* leaves it null and the line says so) — it is a statement, not a gate; the
|
|
289
|
+
* gate is `assertWriteTarget`.
|
|
290
|
+
*/
|
|
291
|
+
protected describeWriteTarget(): Promise<WriteTarget>;
|
|
292
|
+
/**
|
|
293
|
+
* Say where a bulk write is about to land, and refuse if the caller said it
|
|
294
|
+
* should land somewhere else.
|
|
295
|
+
*
|
|
296
|
+
* The line goes to stderr and only when stderr is a person's terminal: an
|
|
297
|
+
* agent capturing `2>&1` gets no extra bytes (the update nudge taught that
|
|
298
|
+
* lesson), and gets the same facts in-band instead — the commands that call
|
|
299
|
+
* this put `target` in their `--json`. `--expect-target` is the control for a
|
|
300
|
+
* caller that cannot watch: it is checked HERE, before the first write, and a
|
|
301
|
+
* mismatch is a non-retryable usage error.
|
|
302
|
+
*/
|
|
303
|
+
protected assertWriteTarget(expected?: readonly string[]): Promise<WriteTarget>;
|
|
252
304
|
/**
|
|
253
305
|
* Turn free-text assignee references ("frank", "frank@example.com", "me",
|
|
254
306
|
* or a raw id) into ids, failing the command if ANY of them can't be pinned
|
|
@@ -316,6 +368,13 @@ export declare abstract class BaseCommand extends Command {
|
|
|
316
368
|
* subject is folded into the message, so the human line and the `--json`
|
|
317
369
|
* envelope carry the same identifying text.
|
|
318
370
|
*/
|
|
371
|
+
/**
|
|
372
|
+
* Request body paths this invocation built from its flags, each mapped to the flag that wrote
|
|
373
|
+
* it. Set by a command before it sends a body it assembled from flags, and left unset when the
|
|
374
|
+
* person supplied the body themselves; `handleApiError` uses it to name the flag instead of a
|
|
375
|
+
* path the person never typed. See `lib/request-field-flags.ts`.
|
|
376
|
+
*/
|
|
377
|
+
protected requestFieldFlags?: Readonly<Record<string, string>>;
|
|
319
378
|
protected handleApiError(err: unknown, { subject }?: {
|
|
320
379
|
subject?: string;
|
|
321
380
|
}): never;
|
package/dist/base-command.js
CHANGED
|
@@ -22,7 +22,9 @@ const node_fs_1 = require("node:fs");
|
|
|
22
22
|
const core_1 = require("@oclif/core");
|
|
23
23
|
Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { return core_1.Flags; } });
|
|
24
24
|
const option_hint_1 = require("./lib/option-hint");
|
|
25
|
+
const request_field_flags_1 = require("./lib/request-field-flags");
|
|
25
26
|
const json_output_1 = require("./lib/json-output");
|
|
27
|
+
const json_mode_1 = require("./lib/json-mode");
|
|
26
28
|
const failed_lookup_1 = require("./lib/failed-lookup");
|
|
27
29
|
const internal_id_1 = require("./lib/internal-id");
|
|
28
30
|
const http_error_1 = require("./lib/http-error");
|
|
@@ -36,6 +38,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
36
38
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
37
39
|
const auth_failure_1 = require("./lib/auth-failure");
|
|
38
40
|
const list_output_1 = require("./lib/list-output");
|
|
41
|
+
const write_target_1 = require("./lib/write-target");
|
|
39
42
|
const assignee_resolver_1 = require("./lib/assignee-resolver");
|
|
40
43
|
const task_ref_resolver_1 = require("./lib/task-ref-resolver");
|
|
41
44
|
const space_resolver_1 = require("./lib/space-resolver");
|
|
@@ -176,6 +179,24 @@ class BaseCommand extends core_1.Command {
|
|
|
176
179
|
wantsRawJson() {
|
|
177
180
|
return this.argv.includes('--raw') || process.env.OVERSKY_CLI_RAW === '1';
|
|
178
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Print a command's `--json` RESULT: pretty JSON, with `ok` on an object so a
|
|
184
|
+
* script can test one field for success and failure alike (OSK-16425).
|
|
185
|
+
*
|
|
186
|
+
* A failure already prints `{ok:false, error}`; a success printed whatever the
|
|
187
|
+
* server sent, so `jq '{id, title}'` turned the failure into `{id:null,
|
|
188
|
+
* title:null}` — an empty success. `ok` is added HERE, by the command that
|
|
189
|
+
* knows its own outcome, and not in `log()`: `log` cannot see the exit status
|
|
190
|
+
* to come, and several commands print a document and then exit non-zero.
|
|
191
|
+
* Pass `{ ok: false }` for one of those; the command still owns its exit code.
|
|
192
|
+
*
|
|
193
|
+
* An array is printed as it is (it cannot carry a field), and an object that
|
|
194
|
+
* already has `ok` keeps its own. The exit status stays the test for a command
|
|
195
|
+
* that has not been moved here yet.
|
|
196
|
+
*/
|
|
197
|
+
printJsonResult(value, options = {}) {
|
|
198
|
+
this.log(JSON.stringify((0, json_output_1.withOk)(value, options.ok ?? true), null, 2));
|
|
199
|
+
}
|
|
179
200
|
cliConfig;
|
|
180
201
|
/**
|
|
181
202
|
* Resolved credential for this process. Populated in `init()` and
|
|
@@ -928,7 +949,7 @@ class BaseCommand extends core_1.Command {
|
|
|
928
949
|
async resolveCurrentUserId() {
|
|
929
950
|
let user;
|
|
930
951
|
try {
|
|
931
|
-
user = await
|
|
952
|
+
user = await this.fetchCurrentUser();
|
|
932
953
|
}
|
|
933
954
|
catch (err) {
|
|
934
955
|
this.handleApiError(err);
|
|
@@ -941,6 +962,25 @@ class BaseCommand extends core_1.Command {
|
|
|
941
962
|
}
|
|
942
963
|
return user.id;
|
|
943
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* The signed-in user, read ONCE per command run.
|
|
967
|
+
*
|
|
968
|
+
* `me`, the default-assignee policy and the write-target notice all need it,
|
|
969
|
+
* and a command that touches many nodes (`create-tree` with ninety-four)
|
|
970
|
+
* asked for it once per node. Who you are does not change during one command,
|
|
971
|
+
* so one read answers every caller — and a failed read is not remembered, so a
|
|
972
|
+
* transient error does not poison the rest of the run.
|
|
973
|
+
*/
|
|
974
|
+
currentUserRead;
|
|
975
|
+
fetchCurrentUser() {
|
|
976
|
+
if (!this.currentUserRead) {
|
|
977
|
+
this.currentUserRead = data_provider_1.dataService.getUser().catch((err) => {
|
|
978
|
+
this.currentUserRead = undefined;
|
|
979
|
+
throw err;
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
return this.currentUserRead;
|
|
983
|
+
}
|
|
944
984
|
/**
|
|
945
985
|
* Decide who owns a task this invocation is creating, when the caller did
|
|
946
986
|
* not say.
|
|
@@ -979,6 +1019,12 @@ class BaseCommand extends core_1.Command {
|
|
|
979
1019
|
* worse, resolve a typo to a stranger the caller never intended to name.
|
|
980
1020
|
*/
|
|
981
1021
|
async loadSpaceMembers(spaceId) {
|
|
1022
|
+
// A space's roster does not change during one command; a tree of ninety-four
|
|
1023
|
+
// nodes that name people read it ninety-four times.
|
|
1024
|
+
const reads = (this.spaceMemberReads ??= new Map());
|
|
1025
|
+
const cached = reads.get(spaceId);
|
|
1026
|
+
if (cached)
|
|
1027
|
+
return cached;
|
|
982
1028
|
let response;
|
|
983
1029
|
try {
|
|
984
1030
|
response = await data_provider_1.dataService.getResourcePermissions(data_provider_1.ResourceType.SPACE, spaceId);
|
|
@@ -987,7 +1033,67 @@ class BaseCommand extends core_1.Command {
|
|
|
987
1033
|
this.handleApiError(err);
|
|
988
1034
|
}
|
|
989
1035
|
const principals = (response.principals ?? []);
|
|
990
|
-
|
|
1036
|
+
const members = (0, assignee_resolver_1.membersFromPrincipals)(principals);
|
|
1037
|
+
reads.set(spaceId, Promise.resolve(members));
|
|
1038
|
+
return members;
|
|
1039
|
+
}
|
|
1040
|
+
/** Lazily created: a command built without its constructor (specs do) must still work. */
|
|
1041
|
+
spaceMemberReads;
|
|
1042
|
+
/**
|
|
1043
|
+
* Where a write from this invocation is about to land: the signed-in account,
|
|
1044
|
+
* the server and the workspace. Best effort on the account (a failed read
|
|
1045
|
+
* leaves it null and the line says so) — it is a statement, not a gate; the
|
|
1046
|
+
* gate is `assertWriteTarget`.
|
|
1047
|
+
*/
|
|
1048
|
+
async describeWriteTarget() {
|
|
1049
|
+
let user;
|
|
1050
|
+
try {
|
|
1051
|
+
user = await this.fetchCurrentUser();
|
|
1052
|
+
}
|
|
1053
|
+
catch {
|
|
1054
|
+
user = undefined;
|
|
1055
|
+
}
|
|
1056
|
+
const workspace = process.env.OVERSKY_SESSION_WORKSPACE_ID?.trim() ||
|
|
1057
|
+
process.env.OVERSKY_WORKSPACE_ID?.trim() ||
|
|
1058
|
+
null;
|
|
1059
|
+
return {
|
|
1060
|
+
server: this.cliConfig?.baseURL ?? '',
|
|
1061
|
+
account: user?.email ?? null,
|
|
1062
|
+
accountId: user?.id ?? null,
|
|
1063
|
+
workspace,
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Say where a bulk write is about to land, and refuse if the caller said it
|
|
1068
|
+
* should land somewhere else.
|
|
1069
|
+
*
|
|
1070
|
+
* The line goes to stderr and only when stderr is a person's terminal: an
|
|
1071
|
+
* agent capturing `2>&1` gets no extra bytes (the update nudge taught that
|
|
1072
|
+
* lesson), and gets the same facts in-band instead — the commands that call
|
|
1073
|
+
* this put `target` in their `--json`. `--expect-target` is the control for a
|
|
1074
|
+
* caller that cannot watch: it is checked HERE, before the first write, and a
|
|
1075
|
+
* mismatch is a non-retryable usage error.
|
|
1076
|
+
*/
|
|
1077
|
+
async assertWriteTarget(expected) {
|
|
1078
|
+
const target = await this.describeWriteTarget();
|
|
1079
|
+
const unmet = (0, write_target_1.firstUnmetExpectation)(target, expected);
|
|
1080
|
+
if (unmet !== undefined) {
|
|
1081
|
+
this.failWithCliError({
|
|
1082
|
+
message: `Refusing to write: this command would write to ${(0, write_target_1.formatWriteTarget)(target)}, ` +
|
|
1083
|
+
`which does not match --expect-target ${unmet}. Nothing was created.`,
|
|
1084
|
+
code: 'WRITE_TARGET_MISMATCH',
|
|
1085
|
+
exit: 2,
|
|
1086
|
+
retryable: false,
|
|
1087
|
+
details: {
|
|
1088
|
+
suggestion: `Sign in to the intended account or server (\`${this.config.bin} login\`, \`${this.config.bin} whoami\`), or correct --expect-target.`,
|
|
1089
|
+
context: { target, expected: [...(expected ?? [])] },
|
|
1090
|
+
},
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
if (process.stderr.isTTY) {
|
|
1094
|
+
process.stderr.write(`Writing to ${(0, write_target_1.formatWriteTarget)(target)}\n`);
|
|
1095
|
+
}
|
|
1096
|
+
return target;
|
|
991
1097
|
}
|
|
992
1098
|
/**
|
|
993
1099
|
* Turn free-text assignee references ("frank", "frank@example.com", "me",
|
|
@@ -1170,6 +1276,13 @@ class BaseCommand extends core_1.Command {
|
|
|
1170
1276
|
* subject is folded into the message, so the human line and the `--json`
|
|
1171
1277
|
* envelope carry the same identifying text.
|
|
1172
1278
|
*/
|
|
1279
|
+
/**
|
|
1280
|
+
* Request body paths this invocation built from its flags, each mapped to the flag that wrote
|
|
1281
|
+
* it. Set by a command before it sends a body it assembled from flags, and left unset when the
|
|
1282
|
+
* person supplied the body themselves; `handleApiError` uses it to name the flag instead of a
|
|
1283
|
+
* path the person never typed. See `lib/request-field-flags.ts`.
|
|
1284
|
+
*/
|
|
1285
|
+
requestFieldFlags;
|
|
1173
1286
|
handleApiError(err, { subject } = {}) {
|
|
1174
1287
|
// An error this command already raised for the user is not an API failure
|
|
1175
1288
|
// to translate. `this.error()` throws a CLIError carrying `oclif.exit`, and
|
|
@@ -1194,7 +1307,8 @@ class BaseCommand extends core_1.Command {
|
|
|
1194
1307
|
this.lastRetryAfterMs = e.retryAfterMs;
|
|
1195
1308
|
const body = parseErrorBody(e.body);
|
|
1196
1309
|
const bodyCode = stringProp(body, 'code') || stringProp(body, 'errorType');
|
|
1197
|
-
|
|
1310
|
+
// A body path the person never typed is read back as the flag that wrote it (OSK-15313).
|
|
1311
|
+
const bodyMessage = (0, request_field_flags_1.translateRequestFieldPaths)(stringProp(body, 'message') || stringProp(body, 'error'), this.requestFieldFlags);
|
|
1198
1312
|
// `requireAuth()` catches this before requests for normal commands, but
|
|
1199
1313
|
// keep the API-error check too: a handful of commands make a direct
|
|
1200
1314
|
// request and a supplied `--token` can bypass their normal preflight.
|
|
@@ -1817,10 +1931,7 @@ class BaseCommand extends core_1.Command {
|
|
|
1817
1931
|
process.stderr.write(`${line}\n`);
|
|
1818
1932
|
}
|
|
1819
1933
|
wantsJsonOutput() {
|
|
1820
|
-
return (this.argv
|
|
1821
|
-
this.argv.includes('--ndjson') ||
|
|
1822
|
-
process.env.SKY_JSON === '1' ||
|
|
1823
|
-
process.env.OVERSKY_CLI_JSON === '1');
|
|
1934
|
+
return (0, json_mode_1.wantsJsonFromArgv)(this.argv);
|
|
1824
1935
|
}
|
|
1825
1936
|
}
|
|
1826
1937
|
exports.BaseCommand = BaseCommand;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AgentWriteReceipt } from '@skrr-ai/data-provider';
|
|
1
2
|
import { BaseCommand } from '../../base-command';
|
|
2
3
|
export default class AgentsUpdate extends BaseCommand {
|
|
3
4
|
static description: string;
|
|
@@ -72,9 +73,22 @@ export default class AgentsUpdate extends BaseCommand {
|
|
|
72
73
|
* through `--from-json` was reported as "not an agent field" after the server
|
|
73
74
|
* had cleared it on purpose (OSK-12167).
|
|
74
75
|
*
|
|
76
|
+
* All of that is the fallback for a server that predates `writeReceipt`. When
|
|
77
|
+
* the response carries a receipt, the server's own account of each key is used
|
|
78
|
+
* instead and nothing is inferred from the response's shape (OSK-15070).
|
|
79
|
+
*
|
|
75
80
|
* Exported for tests.
|
|
76
81
|
*/
|
|
77
82
|
export declare function agentUpdateWarnings(payload: Record<string, unknown>, updated: unknown): string[];
|
|
83
|
+
/**
|
|
84
|
+
* Warnings from the server's `writeReceipt` (OSK-15070). The receipt names
|
|
85
|
+
* every key the write saw as applied (and through which control: the legacy
|
|
86
|
+
* `isAlwaysOn` goes to the Always-On control and is applied, not "not kept") or
|
|
87
|
+
* ignored with a reason. A sent key it does not mention never reached the write.
|
|
88
|
+
*
|
|
89
|
+
* Exported for tests.
|
|
90
|
+
*/
|
|
91
|
+
export declare function writeReceiptWarnings(payload: Record<string, unknown>, receipt: AgentWriteReceipt): string[];
|
|
78
92
|
/**
|
|
79
93
|
* The per-Agent mutation fence's "another request holds this right now"
|
|
80
94
|
* refusals. `updateAgent` takes a short lease on the Agent before it writes, so
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AGENT_FENCE_RETRY_CODES = void 0;
|
|
4
4
|
exports.agentUpdateWarnings = agentUpdateWarnings;
|
|
5
|
+
exports.writeReceiptWarnings = writeReceiptWarnings;
|
|
5
6
|
exports.retryOnceOnAgentFence = retryOnceOnAgentFence;
|
|
6
7
|
exports.defaultPermissionModePatch = defaultPermissionModePatch;
|
|
7
8
|
exports.defaultPermissionModeNote = defaultPermissionModeNote;
|
|
@@ -184,7 +185,9 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
184
185
|
this.error(err.message, { exit: 1 });
|
|
185
186
|
}
|
|
186
187
|
if (Object.keys(payload).length === 0) {
|
|
187
|
-
this.error('Nothing to update. Pass at least one flag (e.g. --name) or --from-json.', {
|
|
188
|
+
this.error('Nothing to update. Pass at least one flag (e.g. --name) or --from-json.', {
|
|
189
|
+
exit: 1,
|
|
190
|
+
});
|
|
188
191
|
}
|
|
189
192
|
let updated;
|
|
190
193
|
try {
|
|
@@ -240,9 +243,18 @@ exports.default = AgentsUpdate;
|
|
|
240
243
|
* through `--from-json` was reported as "not an agent field" after the server
|
|
241
244
|
* had cleared it on purpose (OSK-12167).
|
|
242
245
|
*
|
|
246
|
+
* All of that is the fallback for a server that predates `writeReceipt`. When
|
|
247
|
+
* the response carries a receipt, the server's own account of each key is used
|
|
248
|
+
* instead and nothing is inferred from the response's shape (OSK-15070).
|
|
249
|
+
*
|
|
243
250
|
* Exported for tests.
|
|
244
251
|
*/
|
|
245
252
|
function agentUpdateWarnings(payload, updated) {
|
|
253
|
+
// A server that reports what it did with each key is the authority; the
|
|
254
|
+
// shape comparison below is only for a server older than the receipt.
|
|
255
|
+
const receipt = (0, data_provider_1.readAgentWriteReceipt)(updated);
|
|
256
|
+
if (receipt)
|
|
257
|
+
return writeReceiptWarnings(payload, receipt);
|
|
246
258
|
const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
247
259
|
const controlReply = isRecord(updated) &&
|
|
248
260
|
typeof updated.agentId === 'string' &&
|
|
@@ -273,6 +285,33 @@ function agentUpdateWarnings(payload, updated) {
|
|
|
273
285
|
...(unapplied.length > 0 ? [(0, create_1.unappliedPayloadKeysWarning)(unapplied)] : []),
|
|
274
286
|
];
|
|
275
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Warnings from the server's `writeReceipt` (OSK-15070). The receipt names
|
|
290
|
+
* every key the write saw as applied (and through which control: the legacy
|
|
291
|
+
* `isAlwaysOn` goes to the Always-On control and is applied, not "not kept") or
|
|
292
|
+
* ignored with a reason. A sent key it does not mention never reached the write.
|
|
293
|
+
*
|
|
294
|
+
* Exported for tests.
|
|
295
|
+
*/
|
|
296
|
+
function writeReceiptWarnings(payload, receipt) {
|
|
297
|
+
const explained = receipt.ignored
|
|
298
|
+
.filter((entry) => entry.reason !== 'not_an_agent_field')
|
|
299
|
+
.map((entry) => `${entry.field}: ${entry.detail || 'ignored by the server'}`);
|
|
300
|
+
const notFields = receipt.ignored
|
|
301
|
+
.filter((entry) => entry.reason === 'not_an_agent_field')
|
|
302
|
+
.map((entry) => entry.field);
|
|
303
|
+
const unreported = (0, data_provider_1.unreportedAgentWriteFields)(payload, receipt);
|
|
304
|
+
const warnings = [...explained];
|
|
305
|
+
if (notFields.length > 0)
|
|
306
|
+
warnings.push((0, create_1.unappliedPayloadKeysWarning)(notFields));
|
|
307
|
+
if (unreported.length > 0) {
|
|
308
|
+
const one = unreported.length === 1;
|
|
309
|
+
warnings.push(`The server did not receive ${unreported.join(', ')}: a request filter removed ` +
|
|
310
|
+
`${one ? 'it' : 'them'} before the write, so nothing was changed for ${one ? 'it' : 'them'}. ` +
|
|
311
|
+
'`skrr agents show <id> --json` shows what the agent stores.');
|
|
312
|
+
}
|
|
313
|
+
return warnings;
|
|
314
|
+
}
|
|
276
315
|
/**
|
|
277
316
|
* The per-Agent mutation fence's "another request holds this right now"
|
|
278
317
|
* refusals. `updateAgent` takes a short lease on the Agent before it writes, so
|
|
@@ -4,6 +4,7 @@ exports.formatAccountingReadout = formatAccountingReadout;
|
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const commitments_1 = require("../../lib/commitments");
|
|
7
|
+
const cost_1 = require("./cost");
|
|
7
8
|
/**
|
|
8
9
|
* `skrr commitments accounting <id>` — the allowance-accounting SHADOW readout.
|
|
9
10
|
*
|
|
@@ -92,14 +93,18 @@ function formatAccountingReadout(readout) {
|
|
|
92
93
|
// a priced window and an unpriced one read alike (OSK-10940).
|
|
93
94
|
const costStatus = readout.costStatus ?? 'not_applicable';
|
|
94
95
|
const costCaveat = {
|
|
95
|
-
reported:
|
|
96
|
+
reported: "every run's cost is known",
|
|
96
97
|
partial: 'some runs reported no cost — the total is a FLOOR',
|
|
97
98
|
unavailable: 'no run reported a cost — the total is meaningless',
|
|
98
99
|
not_applicable: 'no closed runs in the window',
|
|
99
100
|
};
|
|
100
101
|
lines.push(`Window cost: $${(Number(readout.costUsdMicros || 0) / 1_000_000).toFixed(4)}` +
|
|
101
102
|
` — ${costStatus}: ${costCaveat[costStatus] ?? 'unrecognized cost status'}` +
|
|
102
|
-
` (${Number(readout.costReportedRunCount || 0)} of ${Number(readout.costRunCount || 0)} runs priced
|
|
103
|
+
` (${Number(readout.costReportedRunCount || 0)} of ${Number(readout.costRunCount || 0)} runs priced` +
|
|
104
|
+
`, ${Number(readout.costNoSpendRunCount || 0)} refused before starting)`);
|
|
105
|
+
const noSpendNote = (0, cost_1.noSpendInferenceNote)(Number(readout.costNoSpendRunCount || 0), Number(readout.costNoSpendInferredRunCount || 0));
|
|
106
|
+
if (noSpendNote)
|
|
107
|
+
lines.push(noSpendNote);
|
|
103
108
|
const multiplier = readout.recommendedAttemptMultiplier;
|
|
104
109
|
lines.push('');
|
|
105
110
|
lines.push(`Attempt multiplier: ${multiplier == null ? 'not derivable yet' : multiplier}` +
|
|
@@ -60,3 +60,14 @@ export default class CommitmentsCost extends BaseCommand {
|
|
|
60
60
|
* formed a belief by the time they reach the caveat.
|
|
61
61
|
*/
|
|
62
62
|
export declare function formatCostWindow(data: CommitmentCostWindow): string[];
|
|
63
|
+
/**
|
|
64
|
+
* The line that says when "cost nothing" rests on inference (OSK-15500).
|
|
65
|
+
*
|
|
66
|
+
* A Run refused before any model turn records that fact when it is refused.
|
|
67
|
+
* Runs written before the fact was recorded are judged by the platform's legacy
|
|
68
|
+
* inference (admission-denied status, a pre-start refusal code, cancelled before
|
|
69
|
+
* any claim). Both are counted as $0 by the live budget, the spend ledger and
|
|
70
|
+
* this read alike — but a reader must be able to tell a recorded fact from an
|
|
71
|
+
* inferred one, so an inferred share is always printed, never folded in.
|
|
72
|
+
*/
|
|
73
|
+
export declare function noSpendInferenceNote(noSpend: number, inferred: number): string | null;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveCostWindow = resolveCostWindow;
|
|
4
4
|
exports.formatCostWindow = formatCostWindow;
|
|
5
|
+
exports.noSpendInferenceNote = noSpendInferenceNote;
|
|
5
6
|
const core_1 = require("@oclif/core");
|
|
6
7
|
const base_command_1 = require("../../base-command");
|
|
7
8
|
const commitments_1 = require("../../lib/commitments");
|
|
@@ -113,18 +114,40 @@ function formatCostWindow(data) {
|
|
|
113
114
|
const micros = Number(data.costUsdMicros || 0);
|
|
114
115
|
const runs = Number(data.runCount || 0);
|
|
115
116
|
const unattributed = Number(data.unavailableRunCount || 0);
|
|
117
|
+
const noSpend = Number(data.noSpendRunCount || 0);
|
|
116
118
|
const status = data.costStatus ?? 'not_applicable';
|
|
117
119
|
const caveat = {
|
|
118
|
-
reported:
|
|
120
|
+
reported: "every run's cost in the window is known",
|
|
119
121
|
partial: 'some runs reported no cost — the total below is a FLOOR',
|
|
120
122
|
unavailable: 'no run in the window reported a cost — the total below is meaningless',
|
|
121
123
|
not_applicable: 'no closed runs in the window',
|
|
122
124
|
};
|
|
123
|
-
|
|
125
|
+
const lines = [
|
|
124
126
|
`Cost for ${data.commitmentId ?? '-'}`,
|
|
125
127
|
`Window: ${data.from ?? '-'} → ${data.to ?? '-'} (${Number(data.dayCount || 0)} closed day(s))`,
|
|
126
128
|
`Attribution: ${status} — ${caveat[status] ?? 'unrecognized cost status'}`,
|
|
127
129
|
`Cost: $${(micros / 1_000_000).toFixed(4)}`,
|
|
128
|
-
`Runs: ${runs} (${Number(data.reportedRunCount || 0)} with cost,
|
|
130
|
+
`Runs: ${runs} (${Number(data.reportedRunCount || 0)} with cost, ` +
|
|
131
|
+
`${noSpend} refused before starting, ${unattributed} without)`,
|
|
129
132
|
];
|
|
133
|
+
const note = noSpendInferenceNote(noSpend, Number(data.noSpendInferredRunCount || 0));
|
|
134
|
+
if (note)
|
|
135
|
+
lines.push(note);
|
|
136
|
+
return lines;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The line that says when "cost nothing" rests on inference (OSK-15500).
|
|
140
|
+
*
|
|
141
|
+
* A Run refused before any model turn records that fact when it is refused.
|
|
142
|
+
* Runs written before the fact was recorded are judged by the platform's legacy
|
|
143
|
+
* inference (admission-denied status, a pre-start refusal code, cancelled before
|
|
144
|
+
* any claim). Both are counted as $0 by the live budget, the spend ledger and
|
|
145
|
+
* this read alike — but a reader must be able to tell a recorded fact from an
|
|
146
|
+
* inferred one, so an inferred share is always printed, never folded in.
|
|
147
|
+
*/
|
|
148
|
+
function noSpendInferenceNote(noSpend, inferred) {
|
|
149
|
+
if (!(inferred > 0))
|
|
150
|
+
return null;
|
|
151
|
+
return (`Note: ${inferred} of the ${noSpend} run(s) refused before starting are counted ` +
|
|
152
|
+
'as $0 by inference (recorded before refusals were stored), not from a recorded refusal');
|
|
130
153
|
}
|
|
@@ -156,7 +156,8 @@ function createCadenceFromFlags({ eventDriven, cadence: cadenceInput, cadenceMod
|
|
|
156
156
|
* For a natural-language draft instead of flags, use `skrr commitments plan`.
|
|
157
157
|
*/
|
|
158
158
|
class CommitmentsCreate extends base_command_1.BaseCommand {
|
|
159
|
-
static description = 'Create a commitment (a durable agent contract)'
|
|
159
|
+
static description = 'Create a commitment (a durable agent contract). Its cadence is manual, an interval or daily clock times; ' +
|
|
160
|
+
'it has no weekday, holiday, business-day or trading-calendar fields';
|
|
160
161
|
static examples = [
|
|
161
162
|
'<%= config.bin %> commitments create --agent <agent-id> --title "Ship v2" --outcome "v2 is live for all users" --cadence 6h',
|
|
162
163
|
'<%= config.bin %> commitments create --agent <agent-id> --title "Hiring loop" --outcome "Every candidate hears back in 48h" --watch tasks --watch email --cadence 12h',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { CommitmentLoopStatus } from '@skrr-ai/data-provider';
|
|
1
2
|
import { BaseCommand } from '../../base-command';
|
|
2
3
|
type SystemCommitmentRow = {
|
|
4
|
+
id?: string;
|
|
3
5
|
systemRole?: string;
|
|
4
6
|
status?: string;
|
|
5
7
|
lastCheckedAt?: string;
|
|
@@ -13,6 +15,19 @@ type SystemCommitmentRow = {
|
|
|
13
15
|
* (OSK-15438): an Agent that cannot run cannot have asked.
|
|
14
16
|
*/
|
|
15
17
|
export declare function noQuestionsSentence(alwaysOn: boolean, roles: SystemCommitmentRow[]): string;
|
|
18
|
+
/**
|
|
19
|
+
* What the roster said about this Agent's loops, or why it could not be read. The review's own rows
|
|
20
|
+
* carry only the stored lifecycle status; whether a loop is armed, when it next wakes, why it last
|
|
21
|
+
* stopped and whether it can read its sources come from the roster (OSK-15438, OSK-15412).
|
|
22
|
+
*/
|
|
23
|
+
export type OperatingReviewLoops = {
|
|
24
|
+
rows: Map<string, CommitmentLoopStatus>;
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
} | {
|
|
27
|
+
error: string;
|
|
28
|
+
};
|
|
29
|
+
/** The review as text. Pure, so the whole output can be pinned. */
|
|
30
|
+
export declare function formatOperatingReview(review: Record<string, unknown>, loops: OperatingReviewLoops): string[];
|
|
16
31
|
export default class CommitmentsOperatingReview extends BaseCommand {
|
|
17
32
|
static description: string;
|
|
18
33
|
static args: {
|