@skrr-ai/cli 0.1.94 → 0.1.95
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 +153 -165
- package/bin/run.js +32 -0
- package/bin/update-npm-runner.cjs +33 -0
- package/bin/update-session.cjs +194 -0
- package/bin/update-worker.cjs +208 -0
- package/dist/base-command.js +0 -15
- package/dist/commands/code/doctor.d.ts +1 -0
- package/dist/commands/code/doctor.js +6 -0
- package/dist/commands/code/index.js +4 -0
- package/dist/commands/code/install.d.ts +5 -62
- package/dist/commands/code/install.js +86 -279
- package/dist/commands/code/rollback.d.ts +8 -0
- package/dist/commands/code/rollback.js +20 -0
- package/dist/commands/code/update.d.ts +8 -37
- package/dist/commands/code/update.js +134 -213
- package/dist/commands/daemon/install.js +4 -0
- package/dist/commands/generate-recovery-code.d.ts +13 -1
- package/dist/commands/generate-recovery-code.js +146 -13
- package/dist/commands/instructions/guide.d.ts +20 -0
- package/dist/commands/instructions/guide.js +84 -0
- package/dist/commands/instructions/install.js +57 -33
- package/dist/commands/instructions/setup.d.ts +14 -0
- package/dist/commands/instructions/setup.js +72 -0
- package/dist/commands/integrations/execute.js +1 -1
- package/dist/commands/login.d.ts +5 -6
- package/dist/commands/login.js +27 -11
- package/dist/commands/logout.js +7 -7
- package/dist/commands/security/overview.d.ts +11 -0
- package/dist/commands/security/overview.js +158 -0
- package/dist/commands/spaces/claim.d.ts +12 -0
- package/dist/commands/spaces/claim.js +42 -0
- package/dist/commands/spaces/ownership.d.ts +12 -0
- package/dist/commands/spaces/ownership.js +46 -0
- package/dist/commands/tasks/cancel-cascade.js +1 -1
- package/dist/commands/tasks/create-tree.d.ts +5 -1
- package/dist/commands/tasks/create-tree.js +35 -7
- package/dist/commands/tasks/interrupt.js +2 -2
- package/dist/commands/tasks/pause-schedule.js +1 -1
- package/dist/commands/token/list.d.ts +3 -5
- package/dist/commands/token/list.js +99 -57
- package/dist/commands/update.d.ts +14 -0
- package/dist/commands/update.js +61 -0
- package/dist/generated/managed-instruction-guides.json +9 -0
- package/dist/hooks/init.d.ts +5 -0
- package/dist/hooks/init.js +27 -0
- package/dist/lib/commitments.d.ts +0 -8
- package/dist/lib/commitments.js +0 -52
- package/dist/lib/daemon-installer.d.ts +2 -0
- package/dist/lib/daemon-installer.js +100 -24
- package/dist/lib/first-party-harness-doctor.js +31 -1
- package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
- package/dist/lib/first-party-harness-remote-install.js +310 -0
- package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
- package/dist/lib/first-party-harness-remote-update.js +242 -0
- package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
- package/dist/lib/first-party-harness-startup-update.js +166 -0
- package/dist/lib/first-party-harness-update.d.ts +46 -0
- package/dist/lib/first-party-harness-update.js +289 -0
- package/dist/lib/first-party-harness.js +7 -4
- package/dist/lib/managed-instruction-local.d.ts +12 -0
- package/dist/lib/managed-instruction-local.js +58 -0
- package/dist/lib/managed-instruction-setup.d.ts +35 -0
- package/dist/lib/managed-instruction-setup.js +306 -0
- package/dist/lib/memory-sync.d.ts +1 -0
- package/dist/lib/memory-sync.js +279 -68
- package/dist/lib/platform-update-local.d.ts +2 -0
- package/dist/lib/platform-update-local.js +113 -0
- package/dist/lib/platform-update.d.ts +39 -0
- package/dist/lib/platform-update.js +84 -0
- package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
- package/dist/lib/profile-machine-identity-guard.js +13 -0
- package/dist/lib/prompt.d.ts +1 -0
- package/dist/lib/prompt.js +4 -3
- package/dist/lib/task-batch.d.ts +9 -0
- package/dist/lib/task-batch.js +94 -17
- package/dist/lib/task-instruction-offer.d.ts +1 -4
- package/dist/lib/task-instruction-offer.js +3 -129
- package/dist/lib/update-check.d.ts +61 -53
- package/dist/lib/update-check.js +453 -314
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +31 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6584 -6567
- package/docs/v1/README.md +28 -0
- package/docs/v1/api.md +55 -0
- package/docs/v1/changelog.md +16 -0
- package/docs/v1/check-examples.mjs +178 -0
- package/docs/v1/examples/api-readonly.mjs +144 -0
- package/docs/v1/operations.md +87 -0
- package/docs/v1/quickstart.md +71 -0
- package/oclif.manifest.json +46007 -45462
- package/package.json +13 -11
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const base_command_1 = require("../../base-command");
|
|
9
|
+
const managed_instruction_guides_json_1 = __importDefault(require("../../generated/managed-instruction-guides.json"));
|
|
10
|
+
const api_fetch_1 = require("../../lib/api-fetch");
|
|
11
|
+
const auth_core_init_1 = require("../../lib/auth-core-init");
|
|
12
|
+
const credential_resolver_1 = require("../../lib/credential-resolver");
|
|
13
|
+
const harness_context_1 = require("../../lib/harness-context");
|
|
14
|
+
const keychain_1 = require("../../lib/keychain");
|
|
15
|
+
// Documentation stays available when a daemon-issued Agent credential is
|
|
16
|
+
// unavailable. Do not run BaseCommand's mandatory credential initialization.
|
|
17
|
+
class InstructionsGuide extends core_1.Command {
|
|
18
|
+
static baseFlags = base_command_1.BaseCommand.baseFlags;
|
|
19
|
+
static description = 'Read the detailed task-management guide on demand';
|
|
20
|
+
static args = {
|
|
21
|
+
name: core_1.Args.string({
|
|
22
|
+
description: 'Guide to read',
|
|
23
|
+
options: ['task-management'],
|
|
24
|
+
default: 'task-management',
|
|
25
|
+
ignoreStdin: true,
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
static flags = {
|
|
29
|
+
json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
|
|
30
|
+
offline: core_1.Flags.boolean({
|
|
31
|
+
description: 'Read the bundled release guide without checking credentials or the server',
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
static examples = [
|
|
35
|
+
'<%= config.bin %> instructions guide task-management',
|
|
36
|
+
'<%= config.bin %> instructions guide --json',
|
|
37
|
+
];
|
|
38
|
+
async run() {
|
|
39
|
+
const { flags } = await this.parse(InstructionsGuide);
|
|
40
|
+
let guide = managed_instruction_guides_json_1.default['oversky-task-management'];
|
|
41
|
+
let source = 'bundled';
|
|
42
|
+
let reason = flags.offline ? 'offline-requested' : 'no-session';
|
|
43
|
+
if (!flags.offline) {
|
|
44
|
+
try {
|
|
45
|
+
(0, keychain_1.setActiveProfile)(flags.profile ?? process.env.OVERSKY_PROFILE ?? '');
|
|
46
|
+
const autonomous = (0, credential_resolver_1.resolveTrustedAutonomousCredential)();
|
|
47
|
+
if (!autonomous && !flags.bare && !(0, harness_context_1.isHarnessCliContext)())
|
|
48
|
+
await (0, auth_core_init_1.prefetchCliAuth)();
|
|
49
|
+
const credential = autonomous ??
|
|
50
|
+
(await (0, credential_resolver_1.resolveCredential)({
|
|
51
|
+
flagToken: flags.token,
|
|
52
|
+
bareMode: flags.bare,
|
|
53
|
+
allowUnreadableStoredCredential: true,
|
|
54
|
+
}));
|
|
55
|
+
if (credential.token) {
|
|
56
|
+
reason = 'server-unavailable';
|
|
57
|
+
const response = await (0, api_fetch_1.apiFetch)('/api/instructions/presets/oversky-task-management/guide', { credential, signal: AbortSignal.timeout(3_000) });
|
|
58
|
+
if ((0, node_crypto_1.createHash)('sha256').update(response.body).digest('hex') !== response.hash) {
|
|
59
|
+
throw new Error('The server guide hash does not match its body.');
|
|
60
|
+
}
|
|
61
|
+
guide = response;
|
|
62
|
+
source = 'server';
|
|
63
|
+
reason = undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// A missing/unreadable auth envelope or delegated daemon cannot make
|
|
68
|
+
// a read-only local guide unavailable, and must never emit auth JSON.
|
|
69
|
+
reason ??= 'server-unavailable';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (flags.json)
|
|
73
|
+
this.log(JSON.stringify({
|
|
74
|
+
...guide,
|
|
75
|
+
provenance: { source, fallback: source === 'bundled', ...(reason ? { reason } : {}) },
|
|
76
|
+
}, null, 2));
|
|
77
|
+
else {
|
|
78
|
+
if (source === 'bundled')
|
|
79
|
+
this.logToStderr(`Using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`);
|
|
80
|
+
this.log(guide.body);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.default = InstructionsGuide;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.conflictingTargetOf = conflictingTargetOf;
|
|
4
4
|
exports.describeTargetConflict = describeTargetConflict;
|
|
5
5
|
const core_1 = require("@oclif/core");
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
7
|
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
7
8
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
8
9
|
const base_command_1 = require("../../base-command");
|
|
@@ -107,41 +108,67 @@ class InstructionsInstall extends base_command_1.BaseCommand {
|
|
|
107
108
|
exit: 2,
|
|
108
109
|
});
|
|
109
110
|
}
|
|
110
|
-
|
|
111
|
+
const daemonId = await this.resolveDaemon(flags.daemon);
|
|
112
|
+
const inputs = providers.map((provider) => ({
|
|
113
|
+
daemonId,
|
|
114
|
+
bundleId: flags.bundle,
|
|
115
|
+
provider,
|
|
116
|
+
scope,
|
|
117
|
+
mode: flags.mode,
|
|
118
|
+
projectPath: flags.project,
|
|
119
|
+
trackingMode: flags.pin ? 'pinned' : 'latest',
|
|
120
|
+
pinnedVersionId: flags.pin,
|
|
121
|
+
label: flags.label,
|
|
122
|
+
}));
|
|
123
|
+
// Inspect the selected machine before any consent or target write. This is
|
|
124
|
+
// also required for --yes: unattended consent does not waive readability.
|
|
125
|
+
let preparedTargets;
|
|
111
126
|
try {
|
|
112
|
-
|
|
113
|
-
if (flags.pin) {
|
|
114
|
-
const versions = await data_provider_1.dataService.listManagedInstructionVersions(flags.bundle, true);
|
|
115
|
-
const pinned = versions.versions.find((version) => version.id === flags.pin);
|
|
116
|
-
if (!pinned || pinned.archivedAt) {
|
|
117
|
-
this.error('The requested pinned version is missing or archived', { exit: 2 });
|
|
118
|
-
}
|
|
119
|
-
previewBody = pinned.body;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
if (!bundle.currentVersion)
|
|
123
|
-
this.error('The prompt has no current version', { exit: 2 });
|
|
124
|
-
previewBody = bundle.currentVersion.body;
|
|
125
|
-
}
|
|
127
|
+
preparedTargets = await Promise.all(inputs.map((input) => data_provider_1.dataService.prepareManagedInstructionTarget(input)));
|
|
126
128
|
}
|
|
127
129
|
catch (error) {
|
|
128
130
|
this.handleApiError(error);
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
for (let index = 0; index < preparedTargets.length; index += 1) {
|
|
133
|
+
const entry = preparedTargets[index];
|
|
134
|
+
const provider = providers[index];
|
|
135
|
+
const placement = entry.providers.find((candidate) => candidate.provider === provider);
|
|
136
|
+
const preview = entry.previews.find((candidate) => candidate.provider === provider);
|
|
137
|
+
if (entry.daemonId !== daemonId || entry.readiness !== 'ready') {
|
|
138
|
+
this.error(entry.message ||
|
|
139
|
+
'The selected daemon cannot inspect its instruction files. Update or reconnect it and retry; no targets were created.', { exit: 1 });
|
|
140
|
+
}
|
|
141
|
+
if (!placement?.canInstall ||
|
|
142
|
+
!placement.instructionPath ||
|
|
143
|
+
placement.loadingStatus === 'shadowed') {
|
|
144
|
+
this.error(`${(0, harness_provider_input_1.canonicalHarnessInput)(provider)} cannot use this placement: ${placement?.issues.map((issue) => issue.message).join(' ') || placement?.loadingStatus || 'preflight unavailable'}. No targets were created.`, { exit: 1 });
|
|
145
|
+
}
|
|
146
|
+
if (!preview || (0, node_crypto_1.createHash)('sha256').update(preview.body).digest('hex') !== preview.hash) {
|
|
147
|
+
this.error('The instruction preview could not be verified; no targets were created.', {
|
|
148
|
+
exit: 1,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (!flags.json) {
|
|
152
|
+
this.log(`${(0, harness_provider_input_1.canonicalHarnessInput)(provider)} on ${daemonId}: ${placement.instructionPath} (${placement.loadingStatus})`);
|
|
153
|
+
for (const issue of placement.issues)
|
|
154
|
+
this.log(` ${issue.severity}: ${issue.message}`);
|
|
155
|
+
}
|
|
132
156
|
}
|
|
133
157
|
// The body is shown so the confirmation below is informed. With `--yes`
|
|
134
158
|
// there is no confirmation to inform, and printing a ~9 KB body anyway put
|
|
135
159
|
// the command's actual outcome — often a refusal — a screen of scrollback
|
|
136
160
|
// away (OSK-4631). Say what was authorized, and where to read it.
|
|
137
161
|
if (!flags.json && !flags.yes) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
for (let index = 0; index < providers.length; index += 1) {
|
|
163
|
+
const preview = preparedTargets[index].previews.find((entry) => entry.provider === providers[index]);
|
|
164
|
+
this.log(`Exact ${(0, harness_provider_input_1.canonicalHarnessInput)(providers[index])} managed instruction body (SHA-256 ${preview.hash}):`);
|
|
165
|
+
this.log('---');
|
|
166
|
+
this.log(preview.body);
|
|
167
|
+
this.log('---');
|
|
168
|
+
}
|
|
142
169
|
}
|
|
143
170
|
else if (!flags.json) {
|
|
144
|
-
this.log(`Authorizing ${Buffer.byteLength(
|
|
171
|
+
this.log(`Authorizing ${Buffer.byteLength(preparedTargets[0].version.body, 'utf8').toLocaleString()} bytes of instruction ` +
|
|
145
172
|
`${flags.bundle}; \`${this.config.bin} instructions show ${flags.bundle}\` prints them.`);
|
|
146
173
|
}
|
|
147
174
|
const teamWarning = scope === 'project' && flags.mode === 'team'
|
|
@@ -150,20 +177,17 @@ class InstructionsInstall extends base_command_1.BaseCommand {
|
|
|
150
177
|
const confirmed = await (0, prompt_1.confirmDestructive)(`Authorize continuous ${providers.join(', ')} reconciliation on this target?${teamWarning}`, flags.yes);
|
|
151
178
|
if (!confirmed)
|
|
152
179
|
return this.log('Aborted.');
|
|
153
|
-
const daemonId = await this.resolveDaemon(flags.daemon);
|
|
154
180
|
const results = [];
|
|
155
|
-
for (
|
|
181
|
+
for (let index = 0; index < providers.length; index += 1) {
|
|
182
|
+
const provider = providers[index];
|
|
183
|
+
const entry = preparedTargets[index];
|
|
156
184
|
try {
|
|
157
185
|
results.push(await data_provider_1.dataService.createManagedInstructionTarget({
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
projectPath: flags.project,
|
|
164
|
-
trackingMode: flags.pin ? 'pinned' : 'latest',
|
|
165
|
-
pinnedVersionId: flags.pin,
|
|
166
|
-
label: flags.label,
|
|
186
|
+
...inputs[index],
|
|
187
|
+
expectedHash: entry.version.hash,
|
|
188
|
+
expectedVersionId: entry.version.id,
|
|
189
|
+
expectedRenderedHash: entry.previews.find((preview) => preview.provider === provider)
|
|
190
|
+
.hash,
|
|
167
191
|
}));
|
|
168
192
|
}
|
|
169
193
|
catch (error) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
export default class InstructionsSetup extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
daemon: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
provider: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
'update-policy': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
review: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const auth_core_1 = require("@skrr-ai/auth-core");
|
|
5
|
+
const base_command_1 = require("../../base-command");
|
|
6
|
+
const harness_provider_input_1 = require("../../lib/harness-provider-input");
|
|
7
|
+
const managed_instruction_setup_1 = require("../../lib/managed-instruction-setup");
|
|
8
|
+
class InstructionsSetup extends base_command_1.BaseCommand {
|
|
9
|
+
static description = 'Review and install removable global instructions for coding agents on this machine.\n\n' +
|
|
10
|
+
'Verifies the local daemon’s account, server and profile, then asks that daemon to check ' +
|
|
11
|
+
'its real instruction files. Choose providers separately and choose automatic updates or ' +
|
|
12
|
+
'a fixed current version. Existing installation decisions are preserved. Pass --daemon ' +
|
|
13
|
+
'to explicitly choose another connected machine. No files are written by the CLI.';
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> instructions setup',
|
|
16
|
+
'<%= config.bin %> instructions setup --provider codex --review',
|
|
17
|
+
'<%= config.bin %> instructions setup --provider claude --update-policy pinned --yes --json',
|
|
18
|
+
'<%= config.bin %> instructions setup --daemon <id> --provider codex --update-policy latest --yes',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
daemon: core_1.Flags.string({
|
|
22
|
+
description: 'Explicit connected daemon ID; defaults only to a verified daemon on this computer',
|
|
23
|
+
}),
|
|
24
|
+
provider: core_1.Flags.string({
|
|
25
|
+
description: 'Provider to set up or revisit (repeatable); otherwise offers newly detected providers',
|
|
26
|
+
options: (0, harness_provider_input_1.harnessFlagOptions)(['claude', 'codex', auth_core_1.FIRST_PARTY_HARNESS.provider]),
|
|
27
|
+
multiple: true,
|
|
28
|
+
}),
|
|
29
|
+
'update-policy': core_1.Flags.string({
|
|
30
|
+
description: 'Track automatic updates, or pin today’s reviewed version',
|
|
31
|
+
options: ['latest', 'pinned'],
|
|
32
|
+
}),
|
|
33
|
+
yes: core_1.Flags.boolean({
|
|
34
|
+
char: 'y',
|
|
35
|
+
description: 'Authorize selected providers and update policy without prompting; requires --provider and --update-policy',
|
|
36
|
+
}),
|
|
37
|
+
review: core_1.Flags.boolean({
|
|
38
|
+
description: 'Show exact previews and daemon preflight without installing or changing decisions',
|
|
39
|
+
}),
|
|
40
|
+
json: core_1.Flags.boolean({ description: 'Output one JSON result; requires --review or --yes' }),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
this.requireAuth();
|
|
44
|
+
const { flags } = await this.parse(InstructionsSetup);
|
|
45
|
+
if (flags.json && !flags.review && !flags.yes)
|
|
46
|
+
this.error('--json requires --review or --yes', { exit: 2 });
|
|
47
|
+
if (flags.review && flags.yes)
|
|
48
|
+
this.error('--review cannot be combined with --yes', { exit: 2 });
|
|
49
|
+
try {
|
|
50
|
+
const result = await (0, managed_instruction_setup_1.runManagedInstructionSetup)({
|
|
51
|
+
trigger: 'manual',
|
|
52
|
+
log: flags.json ? () => { } : (message) => this.log(message),
|
|
53
|
+
daemonId: flags.daemon,
|
|
54
|
+
providers: flags.provider
|
|
55
|
+
? (0, harness_provider_input_1.canonicalHarnessInputs)(flags.provider)
|
|
56
|
+
: undefined,
|
|
57
|
+
trackingMode: flags['update-policy'],
|
|
58
|
+
yes: flags.yes,
|
|
59
|
+
review: flags.review,
|
|
60
|
+
json: flags.json,
|
|
61
|
+
});
|
|
62
|
+
if (flags.json)
|
|
63
|
+
this.log(JSON.stringify(result, null, 2));
|
|
64
|
+
if (result.status === 'unavailable')
|
|
65
|
+
this.exit(1);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
this.handleApiError(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.default = InstructionsSetup;
|
|
@@ -33,7 +33,7 @@ class IntegrationsExecute extends base_command_1.BaseCommand {
|
|
|
33
33
|
description: 'Agent ID (falls back to OVERSKY_AGENT_ID, then primary agent)',
|
|
34
34
|
}),
|
|
35
35
|
'request-id': core_1.Flags.string({
|
|
36
|
-
description: '
|
|
36
|
+
description: 'Reuse only for the same tool, arguments and connection. A pending or unknown outcome requires reconciliation before another write (auto-generated if omitted)',
|
|
37
37
|
}),
|
|
38
38
|
json: core_1.Flags.boolean({
|
|
39
39
|
description: 'Output full response as JSON (recommended for daemon use)',
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -12,14 +12,13 @@ import { BaseCommand } from '../base-command';
|
|
|
12
12
|
* Refresh, rotation, family revoke, and the cross-process auth.lock are
|
|
13
13
|
* all the same substrate the daemon uses (@skrr-ai/auth-core).
|
|
14
14
|
*
|
|
15
|
-
* CI.
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
* command rejects it with 401 (see create-token.ts and base-command.ts). What
|
|
19
|
-
* works today is a session minted for the machine with
|
|
15
|
+
* CI. `skrr tokens create` mints an expiring, revocable personal access token
|
|
16
|
+
* (`osk_pat_*`) for SKRR_TOKEN. It does not rotate and is distinct from a
|
|
17
|
+
* per-machine SESSION minted with
|
|
20
18
|
* `skrr token mint --for <id>`, restored by the `OVERSKY_REFRESH_TOKEN` path
|
|
21
19
|
* below — whose refresh token rotates on use — or `OVERSKY_AUTH_HELPER`.
|
|
22
|
-
* docs/cli/SKRR_AUTH.md
|
|
20
|
+
* docs/cli/SKRR_AUTH.md states credential precedence and revocation scopes.
|
|
21
|
+
* Legacy `skrr create-token` credentials only bootstrap a daemon.
|
|
23
22
|
*
|
|
24
23
|
* L13 — non-interactive restore from a refresh token. When
|
|
25
24
|
* `OVERSKY_REFRESH_TOKEN` is set in the environment, the browser /
|
package/dist/commands/login.js
CHANGED
|
@@ -6,9 +6,11 @@ const auth_core_1 = require("@skrr-ai/auth-core");
|
|
|
6
6
|
const base_command_1 = require("../base-command");
|
|
7
7
|
const daemonHandoff_1 = require("../lib/daemonHandoff");
|
|
8
8
|
const daemon_setup_1 = require("../lib/daemon-setup");
|
|
9
|
+
const managed_instruction_setup_1 = require("../lib/managed-instruction-setup");
|
|
9
10
|
const config_1 = require("../lib/config");
|
|
10
11
|
const cli_id_1 = require("../lib/cli-id");
|
|
11
12
|
const keychain_1 = require("../lib/keychain");
|
|
13
|
+
const profile_machine_identity_guard_1 = require("../lib/profile-machine-identity-guard");
|
|
12
14
|
const login_1 = require("../lib/login");
|
|
13
15
|
const device_code_1 = require("../lib/device-code");
|
|
14
16
|
const api_fetch_1 = require("../lib/api-fetch");
|
|
@@ -29,14 +31,13 @@ const prompt_1 = require("../lib/prompt");
|
|
|
29
31
|
* Refresh, rotation, family revoke, and the cross-process auth.lock are
|
|
30
32
|
* all the same substrate the daemon uses (@skrr-ai/auth-core).
|
|
31
33
|
*
|
|
32
|
-
* CI.
|
|
33
|
-
* `
|
|
34
|
-
*
|
|
35
|
-
* command rejects it with 401 (see create-token.ts and base-command.ts). What
|
|
36
|
-
* works today is a session minted for the machine with
|
|
34
|
+
* CI. `skrr tokens create` mints an expiring, revocable personal access token
|
|
35
|
+
* (`osk_pat_*`) for SKRR_TOKEN. It does not rotate and is distinct from a
|
|
36
|
+
* per-machine SESSION minted with
|
|
37
37
|
* `skrr token mint --for <id>`, restored by the `OVERSKY_REFRESH_TOKEN` path
|
|
38
38
|
* below — whose refresh token rotates on use — or `OVERSKY_AUTH_HELPER`.
|
|
39
|
-
* docs/cli/SKRR_AUTH.md
|
|
39
|
+
* docs/cli/SKRR_AUTH.md states credential precedence and revocation scopes.
|
|
40
|
+
* Legacy `skrr create-token` credentials only bootstrap a daemon.
|
|
40
41
|
*
|
|
41
42
|
* L13 — non-interactive restore from a refresh token. When
|
|
42
43
|
* `OVERSKY_REFRESH_TOKEN` is set in the environment, the browser /
|
|
@@ -60,15 +61,18 @@ class Login extends base_command_1.BaseCommand {
|
|
|
60
61
|
'If the background service answers but refuses, skrr says why before moving on.\n\n' +
|
|
61
62
|
'What it stores: a refresh session in this profile’s credential store (the macOS Keychain, ' +
|
|
62
63
|
'or ~/.skrr/cli-auth.json elsewhere) that keeps renewing itself. Plain skrr commands on a ' +
|
|
63
|
-
'computer whose background service is signed in
|
|
64
|
-
'
|
|
65
|
-
'mints them a stored session instead). On a Dedicated Runtime, where the service only ' +
|
|
64
|
+
'computer whose background service is signed in can adopt a durable CLI session automatically ' +
|
|
65
|
+
'unless this profile was explicitly signed out. On a Dedicated Runtime, where the service only ' +
|
|
66
66
|
'lends short-lived tokens, nothing is stored.\n\n' +
|
|
67
67
|
'Set OVERSKY_SKIP_DAEMON_BROKER=1 to skip step 1 (--device-code skips it too). After signing ' +
|
|
68
68
|
'in, skrr also signs the background service in, and on an interactive first login on a ' +
|
|
69
69
|
'computer without it, offers to install it. Set SKRR_SKIP_DAEMON_HANDOFF=1 to skip that ' +
|
|
70
|
-
'optional setup; it does not skip step 1
|
|
71
|
-
'
|
|
70
|
+
'optional setup; it does not skip step 1. On interactive sign-in, skrr checks managed ' +
|
|
71
|
+
'instruction setup for coding agents on the verified local daemon. You choose each ' +
|
|
72
|
+
'provider and its update policy; run `skrr instructions setup` to revisit this later. ' +
|
|
73
|
+
'Set OVERSKY_DISABLE_INSTRUCTION_OFFER=1 to skip these prompts.\n\n' +
|
|
74
|
+
'For CI, create an expiring personal access token with `skrr tokens create` and supply it as ' +
|
|
75
|
+
'SKRR_TOKEN in the secret store; no login is needed. For a persistent machine, mint a session for that ' +
|
|
72
76
|
'machine with `skrr token mint --for <id>` and run this command there with ' +
|
|
73
77
|
'OVERSKY_REFRESH_TOKEN and OVERSKY_CLI_ID set. The refresh token rotates when used, so one ' +
|
|
74
78
|
'minted session serves one machine. Tokens from `skrr create-token` only bring up a ' +
|
|
@@ -116,6 +120,11 @@ class Login extends base_command_1.BaseCommand {
|
|
|
116
120
|
};
|
|
117
121
|
async run() {
|
|
118
122
|
const { flags } = await this.parse(Login);
|
|
123
|
+
if ((0, profile_machine_identity_guard_1.legacyProfileSharesDefaultMachine)()) {
|
|
124
|
+
this.error(`Profile '${(0, keychain_1.getActiveProfile)()}' was created before profiles had separate machine identities. ` +
|
|
125
|
+
"Signing in could replace the default machine's device-key pin. " +
|
|
126
|
+
'Create a new profile name and sign in there; the existing profile was not changed.', { exit: 78 });
|
|
127
|
+
}
|
|
119
128
|
if (flags['base-url'] && flags.server && flags['base-url'] !== flags.server) {
|
|
120
129
|
this.error('--base-url and --server were both provided with different values.', { exit: 2 });
|
|
121
130
|
}
|
|
@@ -289,6 +298,13 @@ class Login extends base_command_1.BaseCommand {
|
|
|
289
298
|
this.log(`The local daemon was not signed in (${handoff.detail}). ` +
|
|
290
299
|
`Run \`${this.config.bin} daemon login\` to finish.`);
|
|
291
300
|
}
|
|
301
|
+
// Authentication above is complete. The common onboarding helper verifies
|
|
302
|
+
// the daemon's live account/server/profile and handles failures nonfatally.
|
|
303
|
+
await (0, managed_instruction_setup_1.maybeOfferManagedInstructionSetup)({
|
|
304
|
+
trigger: 'login',
|
|
305
|
+
log: (line) => this.log(line),
|
|
306
|
+
waitForLocalReady: handoff.status === 'delivered',
|
|
307
|
+
});
|
|
292
308
|
}
|
|
293
309
|
catch (err) {
|
|
294
310
|
// Ctrl-C while waiting for device-code approval: a person's decision,
|
package/dist/commands/logout.js
CHANGED
|
@@ -68,13 +68,13 @@ function shouldSkipLocalLogout(input) {
|
|
|
68
68
|
* laptop scenario where the user wants every persistent daemon credential
|
|
69
69
|
* gone immediately, regardless of which machine they're calling from.
|
|
70
70
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
71
|
+
* This implementation iterates `DELETE /api/daemon-resources/:id` rather
|
|
72
|
+
* than the broader `POST /api/auth/sessions/revoke-all` endpoint. Its actual
|
|
73
|
+
* scope is registered daemons plus this CLI family; independent browser/CLI
|
|
74
|
+
* refresh families and PATs are not swept. The auth endpoint's current
|
|
75
|
+
* HUMAN_SCOPES includes CLI, so the historical explanation that CLI scope
|
|
76
|
+
* cannot call it is obsolete. Preserve the disclosed narrower behavior until
|
|
77
|
+
* a separately reviewed authority/UX migration changes it.
|
|
78
78
|
* N round trips for N daemons; for typical fleet sizes (< 20) this is
|
|
79
79
|
* sub-second. Each per-daemon DELETE produces its own audit row keyed by
|
|
80
80
|
* `'user_initiated_per_device'`, which preserves forensic granularity.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
/** A single read of the three owner-only Security Center surfaces. */
|
|
3
|
+
export default class SecurityOverview extends BaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_command_1 = require("../../base-command");
|
|
5
|
+
const api_fetch_1 = require("../../lib/api-fetch");
|
|
6
|
+
function rows(value, name) {
|
|
7
|
+
if (!Array.isArray(value))
|
|
8
|
+
throw new Error(`Security overview: invalid ${name} response`);
|
|
9
|
+
if (value.some((row) => row === null || typeof row !== 'object' || Array.isArray(row))) {
|
|
10
|
+
throw new Error(`Security overview: invalid ${name} row`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function hasMore(value, name) {
|
|
15
|
+
if (typeof value !== 'boolean') {
|
|
16
|
+
throw new Error(`Security overview: invalid ${name} pagination`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function activeTokens(value) {
|
|
21
|
+
const tokens = rows(value, 'access tokens');
|
|
22
|
+
// A missing or unfamiliar state must not turn an active credential into a
|
|
23
|
+
// reassuring "None". Validate the whole list before filtering it.
|
|
24
|
+
if (tokens.some((row) => typeof row.state !== 'string' || !['active', 'expired', 'revoked'].includes(row.state))) {
|
|
25
|
+
throw new Error('Security overview: invalid access token state');
|
|
26
|
+
}
|
|
27
|
+
return tokens.filter((row) => row.state === 'active');
|
|
28
|
+
}
|
|
29
|
+
function string(value) {
|
|
30
|
+
return typeof value === 'string' ? value : null;
|
|
31
|
+
}
|
|
32
|
+
/** Explicit allow-lists: the sessions API also returns a full IP address. */
|
|
33
|
+
function safeSession(row) {
|
|
34
|
+
return {
|
|
35
|
+
id: string(row.id) ?? '',
|
|
36
|
+
kind: string(row.kind) ?? 'unknown',
|
|
37
|
+
device: string(row.device) ?? 'Unknown device',
|
|
38
|
+
label: string(row.label),
|
|
39
|
+
daemonId: string(row.daemonId),
|
|
40
|
+
network: string(row.network),
|
|
41
|
+
country: string(row.country),
|
|
42
|
+
createdAt: string(row.createdAt),
|
|
43
|
+
lastActiveAt: string(row.lastActiveAt),
|
|
44
|
+
current: row.current === true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function safeActivity(row) {
|
|
48
|
+
const device = row.device && typeof row.device === 'object' && !Array.isArray(row.device)
|
|
49
|
+
? row.device
|
|
50
|
+
: {};
|
|
51
|
+
const session = row.session && typeof row.session === 'object' && !Array.isArray(row.session)
|
|
52
|
+
? row.session
|
|
53
|
+
: {};
|
|
54
|
+
return {
|
|
55
|
+
id: string(row.id) ?? '',
|
|
56
|
+
action: string(row.action) ?? 'unknown',
|
|
57
|
+
outcome: string(row.outcome) ?? 'unknown',
|
|
58
|
+
method: string(row.method),
|
|
59
|
+
device: string(device.label),
|
|
60
|
+
network: string(row.network),
|
|
61
|
+
country: string(row.country),
|
|
62
|
+
newDevice: row.newDevice === true,
|
|
63
|
+
reportedNotMe: row.reportedNotMe === true,
|
|
64
|
+
createdAt: string(row.createdAt) ?? '',
|
|
65
|
+
sessionId: string(session.id),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function safeToken(row) {
|
|
69
|
+
return {
|
|
70
|
+
id: string(row.id) ?? '',
|
|
71
|
+
name: string(row.name) ?? '',
|
|
72
|
+
lastFour: string(row.lastFour) ?? '',
|
|
73
|
+
scopes: Array.isArray(row.scopes)
|
|
74
|
+
? row.scopes.filter((v) => typeof v === 'string')
|
|
75
|
+
: [],
|
|
76
|
+
expiresAt: string(row.expiresAt),
|
|
77
|
+
lastUsedAt: string(row.lastUsedAt),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** A single read of the three owner-only Security Center surfaces. */
|
|
81
|
+
class SecurityOverview extends base_command_1.BaseCommand {
|
|
82
|
+
static description = 'Review active sessions, recent security activity, and access tokens';
|
|
83
|
+
static examples = [
|
|
84
|
+
'<%= config.bin %> security overview',
|
|
85
|
+
'<%= config.bin %> security overview --limit 50 --json',
|
|
86
|
+
];
|
|
87
|
+
static flags = {
|
|
88
|
+
limit: core_1.Flags.integer({
|
|
89
|
+
description: 'Sessions and events to show (default 25, maximum 100)',
|
|
90
|
+
min: 1,
|
|
91
|
+
max: 100,
|
|
92
|
+
}),
|
|
93
|
+
json: core_1.Flags.boolean({ description: 'Output JSON' }),
|
|
94
|
+
};
|
|
95
|
+
async run() {
|
|
96
|
+
this.requireAuth();
|
|
97
|
+
const { flags } = await this.parse(SecurityOverview);
|
|
98
|
+
const limit = flags.limit ?? 25;
|
|
99
|
+
let sessionPage;
|
|
100
|
+
let activityPage;
|
|
101
|
+
let tokenPage;
|
|
102
|
+
try {
|
|
103
|
+
// All three reads must succeed. A partial response could claim an
|
|
104
|
+
// account has no sessions or tokens when a Security Center read failed.
|
|
105
|
+
[sessionPage, activityPage, tokenPage] = await Promise.all([
|
|
106
|
+
(0, api_fetch_1.apiFetch)(`/api/auth/sessions/refresh?limit=${limit}`, {
|
|
107
|
+
credential: this.resolvedCredential,
|
|
108
|
+
}),
|
|
109
|
+
(0, api_fetch_1.apiFetch)(`/api/user/security-activity?limit=${limit}`, {
|
|
110
|
+
credential: this.resolvedCredential,
|
|
111
|
+
}),
|
|
112
|
+
(0, api_fetch_1.apiFetch)('/api/user/access-tokens', {
|
|
113
|
+
credential: this.resolvedCredential,
|
|
114
|
+
}),
|
|
115
|
+
]);
|
|
116
|
+
const sessions = rows(sessionPage?.sessions, 'sessions').map(safeSession);
|
|
117
|
+
const activity = rows(activityPage?.events, 'activity').map(safeActivity);
|
|
118
|
+
const accessTokens = activeTokens(tokenPage?.accessTokens).map(safeToken);
|
|
119
|
+
const overview = {
|
|
120
|
+
sessions,
|
|
121
|
+
sessionsHaveMore: hasMore(sessionPage?.hasMore, 'sessions'),
|
|
122
|
+
activity,
|
|
123
|
+
activityHasMore: hasMore(activityPage?.hasMore, 'activity'),
|
|
124
|
+
accessTokens,
|
|
125
|
+
};
|
|
126
|
+
if (flags.json) {
|
|
127
|
+
this.log(JSON.stringify(overview, null, 2));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this.log(`Active sessions (${sessions.length}${overview.sessionsHaveMore ? '+' : ''}):`);
|
|
131
|
+
for (const s of sessions) {
|
|
132
|
+
this.log(` ${s.id} ${s.current ? '[this session] ' : ''}${s.label || s.device} ${s.kind} ${s.country || s.network || 'unknown location'} last active ${s.lastActiveAt || 'unknown'}`);
|
|
133
|
+
}
|
|
134
|
+
if (!sessions.length)
|
|
135
|
+
this.log(' None.');
|
|
136
|
+
this.log(`Recent security activity (${activity.length}${overview.activityHasMore ? '+' : ''}):`);
|
|
137
|
+
for (const event of activity) {
|
|
138
|
+
this.log(` ${event.createdAt} ${event.action} (${event.outcome}) ${event.device || 'unknown device'} ${event.country || event.network || 'unknown location'}${event.newDevice ? ' NEW DEVICE' : ''}${event.reportedNotMe ? ' REPORTED' : ''} ${event.id}`);
|
|
139
|
+
}
|
|
140
|
+
if (!activity.length)
|
|
141
|
+
this.log(' None.');
|
|
142
|
+
this.log(`Active access tokens (${accessTokens.length}):`);
|
|
143
|
+
for (const token of accessTokens) {
|
|
144
|
+
this.log(` ${token.id} ${token.name} …${token.lastFour} expires ${token.expiresAt || 'unknown'}`);
|
|
145
|
+
}
|
|
146
|
+
if (!accessTokens.length)
|
|
147
|
+
this.log(' None.');
|
|
148
|
+
if (overview.sessionsHaveMore || overview.activityHasMore) {
|
|
149
|
+
this.log('More sessions or events are available in Account settings; increase --limit to see up to 100 here.');
|
|
150
|
+
}
|
|
151
|
+
this.log('To review an unfamiliar sign-in and secure your account, open Account settings > Security activity.');
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.handleApiError(err);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.default = SecurityOverview;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
export default class SpacesClaim extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|