@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
|
@@ -5,6 +5,7 @@ exports.describeBrokerRefusal = describeBrokerRefusal;
|
|
|
5
5
|
exports.describeGuestDaemonUnavailable = describeGuestDaemonUnavailable;
|
|
6
6
|
exports.describeLocalDaemonUnavailable = describeLocalDaemonUnavailable;
|
|
7
7
|
exports.describeBrokerFailure = describeBrokerFailure;
|
|
8
|
+
const daemon_command_hint_1 = require("./daemon-command-hint");
|
|
8
9
|
/**
|
|
9
10
|
* True when a daemon ANSWERED the hand-off and refused it — as opposed to no
|
|
10
11
|
* daemon being there to ask (`no_bootstrap`, a stale descriptor nobody listens
|
|
@@ -89,12 +90,12 @@ function describeBrokerRefusal(failure, bin = 'skrr') {
|
|
|
89
90
|
}
|
|
90
91
|
else if (code === 'DEVICE_PROOF_PIN_MISMATCH') {
|
|
91
92
|
remedy =
|
|
92
|
-
`Run \`${bin}
|
|
93
|
+
`Run \`${(0, daemon_command_hint_1.daemonCommandHint)('login', { bin })}\` to sign the background service in again — an interactive daemon login re-pins its device key — then retry. ` +
|
|
93
94
|
`\`${bin} login\` signs this CLI in on its own instead.`;
|
|
94
95
|
}
|
|
95
96
|
else if (DAEMON_RELOGIN_CODES.has(code) || failure.reason === 'daemon_not_authed') {
|
|
96
97
|
remedy =
|
|
97
|
-
`Run \`${bin}
|
|
98
|
+
`Run \`${(0, daemon_command_hint_1.daemonCommandHint)('login', { bin })}\` to sign the background service in again, then retry. ` +
|
|
98
99
|
`\`${bin} login\` signs this CLI in on its own instead.`;
|
|
99
100
|
}
|
|
100
101
|
else if (failure.reason === 'ci_token_refused') {
|
|
@@ -256,7 +256,8 @@ async function handOffToLocalDaemon(env = process.env, opts = {}) {
|
|
|
256
256
|
return {
|
|
257
257
|
status: 'skipped',
|
|
258
258
|
reason: 'unsupported-daemon',
|
|
259
|
-
|
|
259
|
+
// The remedy is named by the caller, spelled for the profile (OSK-16435).
|
|
260
|
+
detail: 'the installed daemon is too old to accept a hand-off',
|
|
260
261
|
};
|
|
261
262
|
}
|
|
262
263
|
// Ask the daemon which id it will bind, rather than deriving it here. The id
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the server this CLI talks to is running: the commit its API was built
|
|
3
|
+
* from and the commit its web shell was built from (OSK-15497).
|
|
4
|
+
*
|
|
5
|
+
* Both are public facts the server already publishes for anyone:
|
|
6
|
+
* - the API: `GET /api/health` → `build.commit`, baked into the image once at
|
|
7
|
+
* build time by the release pipeline;
|
|
8
|
+
* - the web: `GET /web-version.json` → `sha`, written into the shell when it
|
|
9
|
+
* is published.
|
|
10
|
+
*
|
|
11
|
+
* They are read separately and shown separately because they legitimately
|
|
12
|
+
* differ: a release that changes only API code keeps the published shell, and
|
|
13
|
+
* a frontend-only release keeps the API image. Collapsing them into one
|
|
14
|
+
* "deployed version" would be wrong whenever that happens, which is often.
|
|
15
|
+
*
|
|
16
|
+
* Neither read needs a credential, and neither failure is the user's problem,
|
|
17
|
+
* so a failure is reported as "could not read" with the reason, never as an
|
|
18
|
+
* error that fails `doctor`.
|
|
19
|
+
*/
|
|
20
|
+
export type BuildCommitRead = {
|
|
21
|
+
/** The 40-hex commit, or null when the server did not name one. */
|
|
22
|
+
commit: string | null;
|
|
23
|
+
/** Where it was read from, so a reader can check it themselves. */
|
|
24
|
+
source: string;
|
|
25
|
+
/** Why there is no commit: unreadable, or the server reports an unstamped build. */
|
|
26
|
+
error: string | null;
|
|
27
|
+
};
|
|
28
|
+
export type DeployedBuild = {
|
|
29
|
+
api: BuildCommitRead;
|
|
30
|
+
web: BuildCommitRead;
|
|
31
|
+
};
|
|
32
|
+
type FetchLike = (url: string, init?: {
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}) => Promise<{
|
|
36
|
+
ok: boolean;
|
|
37
|
+
status: number;
|
|
38
|
+
json: () => Promise<unknown>;
|
|
39
|
+
}>;
|
|
40
|
+
export declare function readDeployedBuild(baseURL: string, fetchImpl?: FetchLike): Promise<DeployedBuild>;
|
|
41
|
+
/** The lines `skrr doctor` prints under the server. */
|
|
42
|
+
export declare function deployedBuildLines(build: DeployedBuild): string[];
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What the server this CLI talks to is running: the commit its API was built
|
|
4
|
+
* from and the commit its web shell was built from (OSK-15497).
|
|
5
|
+
*
|
|
6
|
+
* Both are public facts the server already publishes for anyone:
|
|
7
|
+
* - the API: `GET /api/health` → `build.commit`, baked into the image once at
|
|
8
|
+
* build time by the release pipeline;
|
|
9
|
+
* - the web: `GET /web-version.json` → `sha`, written into the shell when it
|
|
10
|
+
* is published.
|
|
11
|
+
*
|
|
12
|
+
* They are read separately and shown separately because they legitimately
|
|
13
|
+
* differ: a release that changes only API code keeps the published shell, and
|
|
14
|
+
* a frontend-only release keeps the API image. Collapsing them into one
|
|
15
|
+
* "deployed version" would be wrong whenever that happens, which is often.
|
|
16
|
+
*
|
|
17
|
+
* Neither read needs a credential, and neither failure is the user's problem,
|
|
18
|
+
* so a failure is reported as "could not read" with the reason, never as an
|
|
19
|
+
* error that fails `doctor`.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.readDeployedBuild = readDeployedBuild;
|
|
23
|
+
exports.deployedBuildLines = deployedBuildLines;
|
|
24
|
+
const COMMIT = /^[a-f0-9]{40}$/;
|
|
25
|
+
const TIMEOUT_MS = 5_000;
|
|
26
|
+
async function readCommit(url, pick, unstamped, fetchImpl) {
|
|
27
|
+
try {
|
|
28
|
+
const res = await fetchImpl(url, {
|
|
29
|
+
headers: { accept: 'application/json' },
|
|
30
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
return { commit: null, source: url, error: `HTTP ${res.status}` };
|
|
34
|
+
const value = pick(await res.json());
|
|
35
|
+
if (typeof value === 'string' && COMMIT.test(value)) {
|
|
36
|
+
return { commit: value, source: url, error: null };
|
|
37
|
+
}
|
|
38
|
+
return { commit: null, source: url, error: unstamped };
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return {
|
|
42
|
+
commit: null,
|
|
43
|
+
source: url,
|
|
44
|
+
error: err instanceof Error ? err.message : String(err),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function readDeployedBuild(baseURL, fetchImpl = fetch) {
|
|
49
|
+
const origin = baseURL.replace(/\/+$/, '');
|
|
50
|
+
const [api, web] = await Promise.all([
|
|
51
|
+
readCommit(`${origin}/api/health`, (body) => body?.build?.commit, 'the server does not report the commit its API was built from', fetchImpl),
|
|
52
|
+
readCommit(`${origin}/web-version.json`, (body) => body?.sha, 'the published web shell carries no commit', fetchImpl),
|
|
53
|
+
]);
|
|
54
|
+
return { api, web };
|
|
55
|
+
}
|
|
56
|
+
const short = (sha) => sha.slice(0, 12);
|
|
57
|
+
/** The lines `skrr doctor` prints under the server. */
|
|
58
|
+
function deployedBuildLines(build) {
|
|
59
|
+
const describe = (read) => read.commit ? short(read.commit) : `unknown (${read.error ?? 'no commit'})`;
|
|
60
|
+
const lines = [`Deployed: API ${describe(build.api)} · web ${describe(build.web)}`];
|
|
61
|
+
if (build.api.commit && build.web.commit && build.api.commit !== build.web.commit) {
|
|
62
|
+
lines.push(' Built from different commits. That is normal: a release that changes only ' +
|
|
63
|
+
'one of them keeps the other as it was.');
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
package/dist/lib/grants.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GrantAccess, GrantAccessSource } from '@skrr-ai/data-provider';
|
|
2
|
+
import { type TableColumn } from './format';
|
|
2
3
|
export declare const GRANT_PRINCIPAL_KINDS: readonly ["agent", "commitment"];
|
|
3
4
|
export declare const GRANT_CREDENTIAL_KINDS: readonly ["env", "bearer", "basic", "json"];
|
|
4
5
|
export interface GrantPrincipal {
|
|
@@ -20,7 +21,8 @@ export interface Grant {
|
|
|
20
21
|
configured: boolean;
|
|
21
22
|
kind?: string;
|
|
22
23
|
envKeys: string[];
|
|
23
|
-
|
|
24
|
+
/** When the value was last set (create or rotate). Never a digest of it. */
|
|
25
|
+
setAt?: string;
|
|
24
26
|
} | null;
|
|
25
27
|
tools: string[];
|
|
26
28
|
runtime: {
|
|
@@ -58,4 +60,28 @@ export declare const grantApi: {
|
|
|
58
60
|
* gate that could drift.
|
|
59
61
|
*/
|
|
60
62
|
export declare function parseEnvPairs(pairs: string[]): Record<string, string>;
|
|
63
|
+
/** The cells every Grant table shows, in one place (OSK-15357). */
|
|
64
|
+
export type GrantTableCells = {
|
|
65
|
+
id: string;
|
|
66
|
+
label: string;
|
|
67
|
+
provider: string;
|
|
68
|
+
access: string;
|
|
69
|
+
principal: string;
|
|
70
|
+
credential: string;
|
|
71
|
+
runtime: string;
|
|
72
|
+
state: string;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* One Grant as table cells. `skrr grants list` and `skrr workspaces grants`
|
|
76
|
+
* both read this, so a column with the same header means the same thing in
|
|
77
|
+
* both and the two tables can be compared by eye. They used not to: the
|
|
78
|
+
* workspace table printed capabilities under ACCESS while this one printed the
|
|
79
|
+
* declared access class.
|
|
80
|
+
*/
|
|
81
|
+
export declare function grantTableCells(g: Pick<Grant, 'id' | 'label' | 'provider' | 'access' | 'accessSource' | 'credential' | 'revokedAt' | 'active'> & {
|
|
82
|
+
principal?: Partial<GrantPrincipal> | null;
|
|
83
|
+
runtime?: Partial<Grant['runtime']> | null;
|
|
84
|
+
}): GrantTableCells;
|
|
85
|
+
/** The shared columns, in order; a caller may add its own around them. */
|
|
86
|
+
export declare const GRANT_TABLE_COLUMNS: Array<TableColumn<GrantTableCells>>;
|
|
61
87
|
export declare function renderGrantList(grants: Grant[], log: (line: string) => void): void;
|
package/dist/lib/grants.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.grantApi = exports.GRANT_CREDENTIAL_KINDS = exports.GRANT_PRINCIPAL_KINDS = void 0;
|
|
3
|
+
exports.GRANT_TABLE_COLUMNS = exports.grantApi = exports.GRANT_CREDENTIAL_KINDS = exports.GRANT_PRINCIPAL_KINDS = void 0;
|
|
4
4
|
exports.parseEnvPairs = parseEnvPairs;
|
|
5
|
+
exports.grantTableCells = grantTableCells;
|
|
5
6
|
exports.renderGrantList = renderGrantList;
|
|
6
7
|
/**
|
|
7
8
|
* grants.ts — client for the Grant vault (`/api/grants/*`).
|
|
@@ -51,35 +52,52 @@ function parseEnvPairs(pairs) {
|
|
|
51
52
|
}
|
|
52
53
|
return out;
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
/**
|
|
56
|
+
* One Grant as table cells. `skrr grants list` and `skrr workspaces grants`
|
|
57
|
+
* both read this, so a column with the same header means the same thing in
|
|
58
|
+
* both and the two tables can be compared by eye. They used not to: the
|
|
59
|
+
* workspace table printed capabilities under ACCESS while this one printed the
|
|
60
|
+
* declared access class.
|
|
61
|
+
*/
|
|
62
|
+
function grantTableCells(g) {
|
|
63
|
+
const principal = g.principal ?? null;
|
|
64
|
+
let state = 'expired';
|
|
65
|
+
if (g.revokedAt)
|
|
66
|
+
state = 'revoked';
|
|
67
|
+
else if (g.active)
|
|
68
|
+
state = 'active';
|
|
69
|
+
return {
|
|
60
70
|
id: g.id,
|
|
61
71
|
label: g.label,
|
|
62
|
-
provider: g.provider,
|
|
72
|
+
provider: g.provider || '—',
|
|
63
73
|
access: g.access
|
|
64
74
|
? `${g.access} (${g.accessSource === 'attested' ? 'attested' : 'declared, not verified'})`
|
|
65
75
|
: 'undeclared',
|
|
66
|
-
principal:
|
|
76
|
+
principal: principal?.agentId || principal?.commitmentId || principal?.kind || '—',
|
|
67
77
|
// NAMES only. This is the audit surface; a value never reaches the CLI.
|
|
68
78
|
credential: g.credential?.configured
|
|
69
79
|
? g.credential.envKeys.join(',') || g.credential.kind || 'set'
|
|
70
80
|
: '-',
|
|
71
|
-
runtime: [g.runtime
|
|
81
|
+
runtime: [g.runtime?.sandbox ? 'sandbox' : null, g.runtime?.daemon ? 'daemon' : null]
|
|
72
82
|
.filter(Boolean)
|
|
73
83
|
.join('+') || 'none',
|
|
74
|
-
state
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
state,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** The shared columns, in order; a caller may add its own around them. */
|
|
88
|
+
exports.GRANT_TABLE_COLUMNS = [
|
|
89
|
+
{ key: 'label', header: 'LABEL', maxWidth: 26 },
|
|
90
|
+
{ key: 'provider', header: 'PROVIDER', maxWidth: 12 },
|
|
91
|
+
{ key: 'access', header: 'ACCESS', maxWidth: 32 },
|
|
92
|
+
{ key: 'principal', header: 'PRINCIPAL', maxWidth: 24 },
|
|
93
|
+
{ key: 'credential', header: 'VARIABLES', maxWidth: 34 },
|
|
94
|
+
{ key: 'runtime', header: 'RUNTIME', maxWidth: 16 },
|
|
95
|
+
{ key: 'state', header: 'STATE', maxWidth: 8 },
|
|
96
|
+
];
|
|
97
|
+
function renderGrantList(grants, log) {
|
|
98
|
+
if (grants.length === 0) {
|
|
99
|
+
log('No grants.');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
(0, format_1.renderTable)(grants.map((g) => grantTableCells(g)), [...exports.GRANT_TABLE_COLUMNS, { key: 'id', header: 'ID', verbatim: true }], log);
|
|
85
103
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a `skrr` invocation asked for JSON — ONE definition.
|
|
3
|
+
*
|
|
4
|
+
* `BaseCommand` decides this for a command that is running. A command that was
|
|
5
|
+
* never found has no `BaseCommand`: the `command_not_found` hook runs before one
|
|
6
|
+
* exists, so it asked nothing and printed prose to stderr, and a script that
|
|
7
|
+
* piped `--json` output to `jq` got "parse error: Invalid numeric literal"
|
|
8
|
+
* instead of the refusal (OSK-16434). Both now read the same rule, so a flag
|
|
9
|
+
* added to one cannot be missing from the other.
|
|
10
|
+
*/
|
|
11
|
+
export declare function wantsJsonFromArgv(argv: readonly string[], env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wantsJsonFromArgv = wantsJsonFromArgv;
|
|
4
|
+
/**
|
|
5
|
+
* Whether a `skrr` invocation asked for JSON — ONE definition.
|
|
6
|
+
*
|
|
7
|
+
* `BaseCommand` decides this for a command that is running. A command that was
|
|
8
|
+
* never found has no `BaseCommand`: the `command_not_found` hook runs before one
|
|
9
|
+
* exists, so it asked nothing and printed prose to stderr, and a script that
|
|
10
|
+
* piped `--json` output to `jq` got "parse error: Invalid numeric literal"
|
|
11
|
+
* instead of the refusal (OSK-16434). Both now read the same rule, so a flag
|
|
12
|
+
* added to one cannot be missing from the other.
|
|
13
|
+
*/
|
|
14
|
+
function wantsJsonFromArgv(argv, env = process.env) {
|
|
15
|
+
return (argv.includes('--json') ||
|
|
16
|
+
argv.includes('--ndjson') ||
|
|
17
|
+
env.SKY_JSON === '1' ||
|
|
18
|
+
env.OVERSKY_CLI_JSON === '1');
|
|
19
|
+
}
|
|
@@ -28,4 +28,29 @@ export declare function stripStorageFields(value: Json): Json;
|
|
|
28
28
|
* untouched, as is JSON with nothing to strip, so existing formatting survives.
|
|
29
29
|
*/
|
|
30
30
|
export declare function stripStorageFieldsFromText(text: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* The outcome marker for a `--json` RESULT document (OSK-16425).
|
|
33
|
+
*
|
|
34
|
+
* A failure prints `{ok:false, error:{...}}` on stdout, so that agents get the
|
|
35
|
+
* refusal structured. A success printed whatever the server sent, with no `ok`
|
|
36
|
+
* at all — so a script could not test one field for both, and a projection such
|
|
37
|
+
* as `jq '{id, title}'` turned the error document into `{"id":null,"title":null}`,
|
|
38
|
+
* which reads as an empty success. The reporter's own diagnosis of a failed
|
|
39
|
+
* command was wrong for exactly that reason.
|
|
40
|
+
*
|
|
41
|
+
* Adding `ok` here, to a document a command already holds, is truthful by
|
|
42
|
+
* construction: the caller knows whether the command succeeded when it prints.
|
|
43
|
+
* It is NOT done to text in `BaseCommand.log`, the way storage fields are
|
|
44
|
+
* stripped, because `log` cannot see the exit status to come: `doctor`,
|
|
45
|
+
* `tasks wait`, `outbox retry` and `code install` print a document and then
|
|
46
|
+
* exit non-zero, and stamping `ok:true` on those would assert a success the
|
|
47
|
+
* exit code denies.
|
|
48
|
+
*
|
|
49
|
+
* Only an object can carry it. An array is returned as it is, and so is an
|
|
50
|
+
* object that already says `ok` — that command's own meaning wins. `ok` is put
|
|
51
|
+
* first so it is the first thing read.
|
|
52
|
+
*/
|
|
53
|
+
export declare function withOk<T>(value: T, ok?: boolean): T | ({
|
|
54
|
+
ok: boolean;
|
|
55
|
+
} & Record<string, unknown>);
|
|
31
56
|
export {};
|
package/dist/lib/json-output.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.stripStorageFields = stripStorageFields;
|
|
22
22
|
exports.stripStorageFieldsFromText = stripStorageFieldsFromText;
|
|
23
|
+
exports.withOk = withOk;
|
|
23
24
|
function usableId(value) {
|
|
24
25
|
return (typeof value === 'string' && value.length > 0) || typeof value === 'number';
|
|
25
26
|
}
|
|
@@ -76,3 +77,32 @@ function stripStorageFieldsFromText(text) {
|
|
|
76
77
|
return text;
|
|
77
78
|
return JSON.stringify(stripped, null, text.includes('\n') ? 2 : undefined);
|
|
78
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* The outcome marker for a `--json` RESULT document (OSK-16425).
|
|
82
|
+
*
|
|
83
|
+
* A failure prints `{ok:false, error:{...}}` on stdout, so that agents get the
|
|
84
|
+
* refusal structured. A success printed whatever the server sent, with no `ok`
|
|
85
|
+
* at all — so a script could not test one field for both, and a projection such
|
|
86
|
+
* as `jq '{id, title}'` turned the error document into `{"id":null,"title":null}`,
|
|
87
|
+
* which reads as an empty success. The reporter's own diagnosis of a failed
|
|
88
|
+
* command was wrong for exactly that reason.
|
|
89
|
+
*
|
|
90
|
+
* Adding `ok` here, to a document a command already holds, is truthful by
|
|
91
|
+
* construction: the caller knows whether the command succeeded when it prints.
|
|
92
|
+
* It is NOT done to text in `BaseCommand.log`, the way storage fields are
|
|
93
|
+
* stripped, because `log` cannot see the exit status to come: `doctor`,
|
|
94
|
+
* `tasks wait`, `outbox retry` and `code install` print a document and then
|
|
95
|
+
* exit non-zero, and stamping `ok:true` on those would assert a success the
|
|
96
|
+
* exit code denies.
|
|
97
|
+
*
|
|
98
|
+
* Only an object can carry it. An array is returned as it is, and so is an
|
|
99
|
+
* object that already says `ok` — that command's own meaning wins. `ok` is put
|
|
100
|
+
* first so it is the first thing read.
|
|
101
|
+
*/
|
|
102
|
+
function withOk(value, ok = true) {
|
|
103
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
104
|
+
return value;
|
|
105
|
+
if (Object.prototype.hasOwnProperty.call(value, 'ok'))
|
|
106
|
+
return value;
|
|
107
|
+
return { ok, ...value };
|
|
108
|
+
}
|
|
@@ -64,6 +64,15 @@ export declare function limitNote(requested: number | undefined, rowCount: numbe
|
|
|
64
64
|
};
|
|
65
65
|
/** The text-mode twin of a warning: one `Note:` line a person can act on. */
|
|
66
66
|
export declare function warningLines(warnings: readonly ListWarning[] | undefined): string[];
|
|
67
|
+
/**
|
|
68
|
+
* The note a TABLE prints when it was given a JSON-only projection flag. `--compact` and
|
|
69
|
+
* `--fields` shape JSON output; on the table they did nothing and said nothing, so `--compact`
|
|
70
|
+
* looked broken (OSK-15374). Empty when neither flag was given.
|
|
71
|
+
*/
|
|
72
|
+
export declare function textModeProjectionNotes(flags: {
|
|
73
|
+
fields?: string;
|
|
74
|
+
compact?: boolean;
|
|
75
|
+
}): string[];
|
|
67
76
|
export type ListOutput = {
|
|
68
77
|
stdout: string[];
|
|
69
78
|
stderr: string[];
|
package/dist/lib/list-output.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.toListEnvelope = toListEnvelope;
|
|
|
7
7
|
exports.shapeListResponse = shapeListResponse;
|
|
8
8
|
exports.limitNote = limitNote;
|
|
9
9
|
exports.warningLines = warningLines;
|
|
10
|
+
exports.textModeProjectionNotes = textModeProjectionNotes;
|
|
10
11
|
exports.renderListOutput = renderListOutput;
|
|
11
12
|
/**
|
|
12
13
|
* Shared output shaping for `skrr` LIST commands (agent-surface audit
|
|
@@ -29,6 +30,7 @@ exports.renderListOutput = renderListOutput;
|
|
|
29
30
|
* Pure functions, no oclif: the commands own I/O, this owns the shape.
|
|
30
31
|
*/
|
|
31
32
|
const core_1 = require("@oclif/core");
|
|
33
|
+
const json_output_1 = require("./json-output");
|
|
32
34
|
/**
|
|
33
35
|
* The navigation projection per resource: enough to identify a row, decide
|
|
34
36
|
* whether it matters and fetch it with `show`. Allowlists, not drop-lists, so a
|
|
@@ -206,7 +208,11 @@ function shapeListResponse(response, options) {
|
|
|
206
208
|
if (limit.warning)
|
|
207
209
|
warnings.push(limit.warning);
|
|
208
210
|
return {
|
|
209
|
-
|
|
211
|
+
// A list that reached this shaper succeeded; a failure never does, and prints
|
|
212
|
+
// `{ok:false, error}`. Saying so lets a script test one field for both, and
|
|
213
|
+
// keeps a projection of a failure from reading as an empty list (OSK-16425).
|
|
214
|
+
// The server's own `ok`, if it sent one, is kept.
|
|
215
|
+
...(0, json_output_1.withOk)(envelope),
|
|
210
216
|
data,
|
|
211
217
|
...(limit.applied !== undefined ? { limit_applied: limit.applied } : {}),
|
|
212
218
|
...(options.filterApplied ? { filter_applied: options.filterApplied } : {}),
|
|
@@ -247,6 +253,19 @@ function limitNote(requested, rowCount, hasMore) {
|
|
|
247
253
|
function warningLines(warnings) {
|
|
248
254
|
return (warnings ?? []).map((w) => `Note: ${w.hint}`);
|
|
249
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* The note a TABLE prints when it was given a JSON-only projection flag. `--compact` and
|
|
258
|
+
* `--fields` shape JSON output; on the table they did nothing and said nothing, so `--compact`
|
|
259
|
+
* looked broken (OSK-15374). Empty when neither flag was given.
|
|
260
|
+
*/
|
|
261
|
+
function textModeProjectionNotes(flags) {
|
|
262
|
+
const given = [flags.fields ? '--fields' : null, flags.compact ? '--compact' : null].filter(Boolean);
|
|
263
|
+
if (given.length === 0)
|
|
264
|
+
return [];
|
|
265
|
+
return [
|
|
266
|
+
`Note: ${given.join(' and ')} ${given.length > 1 ? 'shape' : 'shapes'} JSON output only; the table is unchanged. Add --json (or --ndjson) to apply ${given.length > 1 ? 'them' : 'it'}.`,
|
|
267
|
+
];
|
|
268
|
+
}
|
|
250
269
|
/**
|
|
251
270
|
* Render a shaped list for `--json` (pretty envelope) or `--ndjson` (rows one per
|
|
252
271
|
* line, `_meta` on stderr). Pure; the caller writes the lines.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the local daemon is actually signed in and connected — as opposed to
|
|
3
|
+
* having been handed a credential (OSK-16437).
|
|
4
|
+
*
|
|
5
|
+
* `skrr login` used to print "The local daemon was signed in too" the moment
|
|
6
|
+
* `accept-handoff` exited 0. That means the credential was WRITTEN. It says
|
|
7
|
+
* nothing about a daemon using it, and a handoff that wrote a credential for a
|
|
8
|
+
* service that was not running, or that the server then refused, left a login
|
|
9
|
+
* that announced success on one line and failed to verify it on the next, with
|
|
10
|
+
* nothing saying which was true. `skrr daemon start` learned the same lesson
|
|
11
|
+
* ("a supervisor's successful `kickstart` means only that it accepted the
|
|
12
|
+
* request") and asks the daemon itself; this is that question for login.
|
|
13
|
+
*
|
|
14
|
+
* Two probes, in order of how directly they answer:
|
|
15
|
+
*
|
|
16
|
+
* 1. the loopback identity endpoint of THIS profile's daemon, which says it is
|
|
17
|
+
* connected as this account on this server — the only proof;
|
|
18
|
+
* 2. `skrrd status --json`, asked only once the first has not answered, to say
|
|
19
|
+
* WHY: the server refused the credential, the service is not running, or it
|
|
20
|
+
* is running and has not connected yet. Those have different fixes.
|
|
21
|
+
*/
|
|
22
|
+
export type LocalDaemonReadiness = {
|
|
23
|
+
state: 'connected';
|
|
24
|
+
daemonId: string;
|
|
25
|
+
} | {
|
|
26
|
+
state: 'reauth_required';
|
|
27
|
+
profile: string;
|
|
28
|
+
reason: string | null;
|
|
29
|
+
command: string;
|
|
30
|
+
} | {
|
|
31
|
+
state: 'not_running';
|
|
32
|
+
profile: string;
|
|
33
|
+
runtimeStatus: string | null;
|
|
34
|
+
} | {
|
|
35
|
+
state: 'not_connected';
|
|
36
|
+
profile: string;
|
|
37
|
+
} | {
|
|
38
|
+
state: 'unreadable';
|
|
39
|
+
profile: string;
|
|
40
|
+
};
|
|
41
|
+
export interface ReadinessDeps {
|
|
42
|
+
/** One look at the loopback identity endpoint; the daemon id when it is connected as us. */
|
|
43
|
+
inspect: () => Promise<string | null>;
|
|
44
|
+
/** `skrrd status --json` for this profile. */
|
|
45
|
+
status: () => Promise<{
|
|
46
|
+
stdout: string;
|
|
47
|
+
code: number;
|
|
48
|
+
}>;
|
|
49
|
+
now: () => number;
|
|
50
|
+
sleep: (ms: number) => Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
export interface ConfirmOptions {
|
|
53
|
+
/** How long a daemon that is still starting gets. */
|
|
54
|
+
waitMs?: number;
|
|
55
|
+
pollMs?: number;
|
|
56
|
+
/** How long a service that reads `stopped` is given before it is believed. */
|
|
57
|
+
stoppedGraceMs?: number;
|
|
58
|
+
/** Progress, printed once and only when the first look found nothing. */
|
|
59
|
+
log?: (line: string) => void;
|
|
60
|
+
deps?: Partial<ReadinessDeps>;
|
|
61
|
+
}
|
|
62
|
+
export declare function confirmLocalDaemonSignedIn(options?: ConfirmOptions): Promise<LocalDaemonReadiness>;
|
|
63
|
+
/**
|
|
64
|
+
* What `skrr login` prints about a daemon that was handed a credential and did
|
|
65
|
+
* not come up with it: what happened, why, and the one command. Pure, so every
|
|
66
|
+
* state can be read without a daemon.
|
|
67
|
+
*/
|
|
68
|
+
export declare function describeUnreadyLocalDaemon(readiness: Exclude<LocalDaemonReadiness, {
|
|
69
|
+
state: 'connected';
|
|
70
|
+
}>, commands: {
|
|
71
|
+
status: string;
|
|
72
|
+
logs: string;
|
|
73
|
+
start: string;
|
|
74
|
+
}): string[];
|
|
75
|
+
/**
|
|
76
|
+
* The end of `skrr login` for a daemon that was handed a credential: wait for it
|
|
77
|
+
* to connect, then say ONE true thing. "Signed in too" only for a daemon that
|
|
78
|
+
* verified connected as this account; otherwise what happened, why, and the one
|
|
79
|
+
* command — and never both, which is what the login used to do.
|
|
80
|
+
*/
|
|
81
|
+
export declare function reportLocalDaemonAfterHandoff(options: {
|
|
82
|
+
/** `skrr login` may have been handed the daemon's account/deployment line already. */
|
|
83
|
+
deliveredDetail: string;
|
|
84
|
+
bin: string;
|
|
85
|
+
log: (line: string) => void;
|
|
86
|
+
confirm?: typeof confirmLocalDaemonSignedIn;
|
|
87
|
+
}): Promise<{
|
|
88
|
+
daemonId?: string;
|
|
89
|
+
notReady: boolean;
|
|
90
|
+
}>;
|