@skrr-ai/cli 0.1.83 → 0.1.84
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 +2 -2
- package/dist/base-command.js +2 -2
- package/dist/commands/agents/endpoint/connect.d.ts +24 -0
- package/dist/commands/agents/endpoint/connect.js +93 -0
- package/dist/commands/agents/endpoint/disable.d.ts +18 -0
- package/dist/commands/agents/endpoint/disable.js +44 -0
- package/dist/commands/agents/endpoint/enable.d.ts +18 -0
- package/dist/commands/agents/endpoint/enable.js +46 -0
- package/dist/commands/agents/endpoint/show.d.ts +16 -0
- package/dist/commands/agents/endpoint/show.js +45 -0
- package/dist/commands/harnesses/install-token.d.ts +4 -4
- package/dist/commands/harnesses/install-token.js +4 -4
- package/dist/commands/store/acquisitions/list.d.ts +18 -0
- package/dist/commands/store/acquisitions/list.js +58 -0
- package/dist/commands/store/governance/requests.js +6 -0
- package/dist/commands/store/maintainers/remove.d.ts +8 -1
- package/dist/commands/store/maintainers/remove.js +29 -4
- package/dist/commands/store/reports/confirm-ban.d.ts +19 -0
- package/dist/commands/store/reports/confirm-ban.js +57 -0
- package/dist/commands/store/reports/disposition.d.ts +21 -0
- package/dist/commands/store/reports/disposition.js +76 -0
- package/dist/commands/store/reports/list.d.ts +17 -0
- package/dist/commands/store/reports/list.js +79 -0
- package/dist/commands/store/reports/withdraw-ban.d.ts +18 -0
- package/dist/commands/store/reports/withdraw-ban.js +48 -0
- package/dist/commands/store/updates.js +9 -5
- package/dist/commands/workspaces/audit.d.ts +30 -0
- package/dist/commands/workspaces/audit.js +145 -0
- package/dist/lib/agent-endpoints.d.ts +62 -0
- package/dist/lib/agent-endpoints.js +64 -0
- package/dist/lib/auth-storage.d.ts +1 -1
- package/dist/lib/auth-storage.js +1 -1
- package/dist/lib/daemonBroker.d.ts +1 -1
- package/dist/lib/daemonBroker.js +3 -4
- package/dist/lib/harness-context.js +1 -5
- package/dist/lib/store-acquisition.d.ts +2 -0
- package/dist/lib/store-acquisition.js +18 -1
- package/dist/lib/store-moderation.d.ts +95 -0
- package/dist/lib/store-moderation.js +51 -0
- package/dist/lib/workspaces.d.ts +73 -0
- package/dist/lib/workspaces.js +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonPair.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonPair.js +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4324 -4294
- package/oclif.manifest.json +22000 -21244
- package/package.json +4 -3
- package/dist/commands/pair.d.ts +0 -67
- package/dist/commands/pair.js +0 -351
|
@@ -0,0 +1,76 @@
|
|
|
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 store_moderation_1 = require("../../../lib/store-moderation");
|
|
6
|
+
/**
|
|
7
|
+
* Disposition one Store listing report as a `store_curation` curator
|
|
8
|
+
* (OSK-12597). warn / delist / ban are the same disposition the admin control
|
|
9
|
+
* plane's governed command applies: the listing changes and the publisher is
|
|
10
|
+
* told the outcome with per-reason counts, never who reported or what they
|
|
11
|
+
* wrote. The server refuses a curator conflicted on the listing.
|
|
12
|
+
*/
|
|
13
|
+
class StoreReportsDisposition extends base_command_1.BaseCommand {
|
|
14
|
+
static description = 'Disposition a Store listing report: dismiss, warn, delist or ban (store_curation curators only)';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> store reports disposition 6abdd6fde81800ed380ad3b2 --action warn --note "Confirmed calendar spam"',
|
|
17
|
+
'<%= config.bin %> store reports disposition 6abdd6fde81800ed380ad3b2 --action dismiss --note "Not reproducible on 1.1"',
|
|
18
|
+
];
|
|
19
|
+
static args = {
|
|
20
|
+
reportId: core_1.Args.string({
|
|
21
|
+
description: 'The report (from `store reports list`)',
|
|
22
|
+
required: true,
|
|
23
|
+
ignoreStdin: true,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
static flags = {
|
|
27
|
+
action: core_1.Flags.string({
|
|
28
|
+
description: store_moderation_1.STORE_REPORT_DISPOSITIONS.map((action) => `${action}: ${store_moderation_1.STORE_REPORT_DISPOSITION_EFFECTS[action]}`).join(' · '),
|
|
29
|
+
options: [...store_moderation_1.STORE_REPORT_DISPOSITIONS],
|
|
30
|
+
required: true,
|
|
31
|
+
}),
|
|
32
|
+
note: core_1.Flags.string({
|
|
33
|
+
description: 'Why (kept on the record; not shown to the publisher)',
|
|
34
|
+
required: true,
|
|
35
|
+
}),
|
|
36
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
37
|
+
};
|
|
38
|
+
async run() {
|
|
39
|
+
this.requireAuth();
|
|
40
|
+
const { args, flags } = await this.parse(StoreReportsDisposition);
|
|
41
|
+
const action = flags.action;
|
|
42
|
+
let result;
|
|
43
|
+
try {
|
|
44
|
+
result = await (0, store_moderation_1.dispositionStoreReport)(args.reportId, { action, note: flags.note });
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
this.handleApiError(error);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (flags.json) {
|
|
51
|
+
this.log(JSON.stringify(result, null, 2));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const d = result.disposition;
|
|
55
|
+
if (d.pendingConfirmation) {
|
|
56
|
+
// A ban is never applied by one curator. Say so plainly, so nobody reads
|
|
57
|
+
// "Recorded" as "banned".
|
|
58
|
+
this.log(`Ban requested on report ${d.reportId} (${d.agentId}). NOT applied yet: the listing is unchanged and the publisher was not told.`);
|
|
59
|
+
this.log(`A different Store curator must run \`store reports confirm-ban ${d.reportId} --note ...\`; \`store reports withdraw-ban ${d.reportId}\` cancels it.`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.log(`${d.replayed ? 'Already recorded' : 'Recorded'}: report ${d.reportId} on ${d.agentId} -> ${d.action} (report ${d.reportStatus}${d.listingState ? `, listing ${d.listingState}` : ''}).`);
|
|
63
|
+
if (d.action === 'dismiss') {
|
|
64
|
+
this.log('The listing is unchanged and the publisher was not told.');
|
|
65
|
+
}
|
|
66
|
+
else if (d.replayed) {
|
|
67
|
+
this.log('Nothing new was sent to the publisher.');
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.log(d.publisherNotified
|
|
71
|
+
? 'The publisher was told the outcome (counts only, never who reported).'
|
|
72
|
+
: 'The listing change is in effect, but the publisher notice could not be confirmed.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.default = StoreReportsDisposition;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* The Store report moderation queue (OSK-12597) — every report still waiting
|
|
4
|
+
* on a disposition, for a `store_curation` curator. `skrr store reports` (no
|
|
5
|
+
* subcommand) is something else: the reports YOU filed.
|
|
6
|
+
*/
|
|
7
|
+
export default class StoreReportsList extends BaseCommand {
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static flags: {
|
|
11
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
skip: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
const format_1 = require("../../../lib/format");
|
|
7
|
+
const store_moderation_1 = require("../../../lib/store-moderation");
|
|
8
|
+
/**
|
|
9
|
+
* The Store report moderation queue (OSK-12597) — every report still waiting
|
|
10
|
+
* on a disposition, for a `store_curation` curator. `skrr store reports` (no
|
|
11
|
+
* subcommand) is something else: the reports YOU filed.
|
|
12
|
+
*/
|
|
13
|
+
class StoreReportsList extends base_command_1.BaseCommand {
|
|
14
|
+
static description = 'List Store listing reports awaiting a disposition (store_curation curators only). For the reports you filed, run `store reports`.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> store reports list',
|
|
17
|
+
'<%= config.bin %> store reports list --agent agent_abc --json',
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
agent: core_1.Flags.string({ description: 'Only reports on this listing' }),
|
|
21
|
+
limit: core_1.Flags.integer({ description: 'At most this many (1-100)', default: 50 }),
|
|
22
|
+
skip: core_1.Flags.integer({ description: 'Skip this many (paging)', default: 0 }),
|
|
23
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
this.requireAuth();
|
|
27
|
+
const { flags } = await this.parse(StoreReportsList);
|
|
28
|
+
let queue;
|
|
29
|
+
try {
|
|
30
|
+
queue = await (0, store_moderation_1.listCuratorStoreReports)({
|
|
31
|
+
agentId: flags.agent,
|
|
32
|
+
limit: flags.limit,
|
|
33
|
+
skip: flags.skip,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
this.handleApiError(err);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (flags.json) {
|
|
41
|
+
this.log(JSON.stringify(queue, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const reports = queue?.reports ?? [];
|
|
45
|
+
if (reports.length === 0) {
|
|
46
|
+
this.log('No Store reports are waiting for a disposition.');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
(0, format_1.renderTable)(reports.map((report) => ({
|
|
50
|
+
report: report.reportId,
|
|
51
|
+
agent: report.agentId,
|
|
52
|
+
reason: (0, data_provider_1.storeReportReasonLabel)(report.reason),
|
|
53
|
+
version: report.versionLabel ?? '-',
|
|
54
|
+
filed: report.createdAt ? String(report.createdAt).slice(0, 10) : '-',
|
|
55
|
+
// A conflicted curator is told why they cannot act, not the report text.
|
|
56
|
+
detail: [
|
|
57
|
+
report.banRequest
|
|
58
|
+
? `BAN WAITING for a second curator (requested by ${report.banRequest.requestedBy})`
|
|
59
|
+
: null,
|
|
60
|
+
report.conflict
|
|
61
|
+
? `conflicted (${report.conflict}) - another curator must decide`
|
|
62
|
+
: (report.description ?? '-'),
|
|
63
|
+
]
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join(' | '),
|
|
66
|
+
})), [
|
|
67
|
+
{ key: 'report', header: 'REPORT', verbatim: true },
|
|
68
|
+
{ key: 'agent', header: 'LISTING', verbatim: true },
|
|
69
|
+
{ key: 'reason', header: 'REASON' },
|
|
70
|
+
{ key: 'version', header: 'VERSION' },
|
|
71
|
+
{ key: 'filed', header: 'FILED' },
|
|
72
|
+
{ key: 'detail', header: 'DETAIL', maxWidth: 60 },
|
|
73
|
+
], (line) => this.log(line));
|
|
74
|
+
if (queue && queue.total > queue.skip + reports.length) {
|
|
75
|
+
this.log(`${queue.total - queue.skip - reports.length} more; page with --skip ${queue.skip + reports.length}.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.default = StoreReportsList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* Withdraw a Store report ban that is still waiting for a second curator
|
|
4
|
+
* (OSK-12597). The listing was never touched, and the report goes back to
|
|
5
|
+
* waiting for a disposition.
|
|
6
|
+
*/
|
|
7
|
+
export default class StoreReportsWithdrawBan extends BaseCommand {
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static args: {
|
|
11
|
+
reportId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
static flags: {
|
|
14
|
+
note: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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 store_moderation_1 = require("../../../lib/store-moderation");
|
|
6
|
+
/**
|
|
7
|
+
* Withdraw a Store report ban that is still waiting for a second curator
|
|
8
|
+
* (OSK-12597). The listing was never touched, and the report goes back to
|
|
9
|
+
* waiting for a disposition.
|
|
10
|
+
*/
|
|
11
|
+
class StoreReportsWithdrawBan extends base_command_1.BaseCommand {
|
|
12
|
+
static description = 'Withdraw a Store report ban still waiting for a second curator (store_curation curators only)';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> store reports withdraw-ban 6abdd6fde81800ed380ad3b2 --note "Requested on the wrong listing"',
|
|
15
|
+
];
|
|
16
|
+
static args = {
|
|
17
|
+
reportId: core_1.Args.string({
|
|
18
|
+
description: 'The report whose ban is waiting',
|
|
19
|
+
required: true,
|
|
20
|
+
ignoreStdin: true,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
static flags = {
|
|
24
|
+
note: core_1.Flags.string({ description: 'Why (kept on the record)' }),
|
|
25
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
this.requireAuth();
|
|
29
|
+
const { args, flags } = await this.parse(StoreReportsWithdrawBan);
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
result = await (0, store_moderation_1.withdrawStoreReportBan)(args.reportId, flags.note ? { note: flags.note } : {});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
this.handleApiError(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (flags.json) {
|
|
39
|
+
this.log(JSON.stringify(result, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const w = result.withdrawal;
|
|
43
|
+
this.log(w.withdrawn
|
|
44
|
+
? `Withdrawn: the ban on report ${w.reportId} (requested by ${w.withdrawnRequest?.requestedBy ?? '-'}) will not be applied. The report is waiting for a disposition again.`
|
|
45
|
+
: `No ban was waiting on report ${w.reportId}; nothing changed.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.default = StoreReportsWithdrawBan;
|
|
@@ -60,11 +60,15 @@ class StoreUpdates extends base_command_1.BaseCommand {
|
|
|
60
60
|
waiting: update.versionLabel ?? '-',
|
|
61
61
|
detail: pending?.reason === 'conflict'
|
|
62
62
|
? `you both changed ${(pending.conflicts ?? []).join(', ') || 'a field'}`
|
|
63
|
-
: pending?.reason === '
|
|
64
|
-
? `
|
|
65
|
-
.map((
|
|
66
|
-
.join('
|
|
67
|
-
:
|
|
63
|
+
: pending?.reason === 'dependency_unresolved'
|
|
64
|
+
? `requires a Skill you have not installed: ${(pending.missingDependencies ?? [])
|
|
65
|
+
.map((dep) => dep.name || dep.entityId)
|
|
66
|
+
.join(', ') || 'a required Skill'}`
|
|
67
|
+
: pending?.reason === 'capability_widened'
|
|
68
|
+
? `adds capabilities: ${(pending.capabilities ?? [])
|
|
69
|
+
.map((row) => `${row.field} (${row.added.join(', ')})`)
|
|
70
|
+
.join('; ') || 'new powers'}`
|
|
71
|
+
: 'the version your copy came from is unknown',
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
74
|
if (update?.state === 'buy') {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
import { type WorkspaceAgentAuditEvent } from '../../lib/workspaces';
|
|
3
|
+
/**
|
|
4
|
+
* `skrr workspaces audit <workspace-id>` — the workspace's third-party (Store)
|
|
5
|
+
* agent audit trail: installs, updates and rollbacks, uninstalls, licenses that
|
|
6
|
+
* lapsed or were restored, and members' decisions on purchases an agent asked
|
|
7
|
+
* for (OSK-12588).
|
|
8
|
+
*
|
|
9
|
+
* Owner/admin only, and it needs no audit export configured: it reads the same
|
|
10
|
+
* enterprise audit rows the S3 export would ship.
|
|
11
|
+
*/
|
|
12
|
+
export default class WorkspacesAudit extends BaseCommand {
|
|
13
|
+
static description: string;
|
|
14
|
+
static examples: string[];
|
|
15
|
+
static args: {
|
|
16
|
+
workspaceId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
17
|
+
};
|
|
18
|
+
static flags: {
|
|
19
|
+
kind: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
since: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
until: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
cursor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
25
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
};
|
|
27
|
+
run(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare function describeAuditKind(event: WorkspaceAgentAuditEvent): string;
|
|
30
|
+
export declare function describeAuditActor(event: WorkspaceAgentAuditEvent): string;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeAuditKind = describeAuditKind;
|
|
4
|
+
exports.describeAuditActor = describeAuditActor;
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const base_command_1 = require("../../base-command");
|
|
7
|
+
const format_1 = require("../../lib/format");
|
|
8
|
+
const workspaces_1 = require("../../lib/workspaces");
|
|
9
|
+
/** `--all` follows cursors, but never without bound. */
|
|
10
|
+
const MAX_PAGES = 50;
|
|
11
|
+
/**
|
|
12
|
+
* `skrr workspaces audit <workspace-id>` — the workspace's third-party (Store)
|
|
13
|
+
* agent audit trail: installs, updates and rollbacks, uninstalls, licenses that
|
|
14
|
+
* lapsed or were restored, and members' decisions on purchases an agent asked
|
|
15
|
+
* for (OSK-12588).
|
|
16
|
+
*
|
|
17
|
+
* Owner/admin only, and it needs no audit export configured: it reads the same
|
|
18
|
+
* enterprise audit rows the S3 export would ship.
|
|
19
|
+
*/
|
|
20
|
+
class WorkspacesAudit extends base_command_1.BaseCommand {
|
|
21
|
+
static description = "List a workspace's third-party agent audit events (install, update/rollback, uninstall, lapse/restore, purchase approvals). Owner or admin only.";
|
|
22
|
+
static examples = [
|
|
23
|
+
'<%= config.bin %> workspaces audit <workspace-id>',
|
|
24
|
+
'<%= config.bin %> workspaces audit <workspace-id> --kind installed --kind updated',
|
|
25
|
+
'<%= config.bin %> workspaces audit <workspace-id> --since 2026-09-01T00:00:00Z --all --json',
|
|
26
|
+
];
|
|
27
|
+
static args = {
|
|
28
|
+
workspaceId: core_1.Args.string({ description: 'Workspace ID', required: true, ignoreStdin: true }),
|
|
29
|
+
};
|
|
30
|
+
static flags = {
|
|
31
|
+
kind: core_1.Flags.string({
|
|
32
|
+
description: 'Only these kinds (repeatable or comma-separated): installed, updated, uninstalled, lapsed, restored, acquisition_decided',
|
|
33
|
+
multiple: true,
|
|
34
|
+
}),
|
|
35
|
+
since: core_1.Flags.string({ description: 'Events at or after this ISO-8601 time' }),
|
|
36
|
+
until: core_1.Flags.string({ description: 'Events before this ISO-8601 time' }),
|
|
37
|
+
limit: core_1.Flags.integer({ description: 'Events per page (1-200, default 50)' }),
|
|
38
|
+
cursor: core_1.Flags.string({ description: 'Continue from a previous page (nextCursor)' }),
|
|
39
|
+
all: core_1.Flags.boolean({ description: `Follow every page (up to ${MAX_PAGES})` }),
|
|
40
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
this.requireAuth();
|
|
44
|
+
const { args, flags } = await this.parse(WorkspacesAudit);
|
|
45
|
+
const kinds = (flags.kind ?? [])
|
|
46
|
+
.flatMap((value) => value.split(','))
|
|
47
|
+
.map((value) => value.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
const events = [];
|
|
50
|
+
let last;
|
|
51
|
+
let cursor = flags.cursor;
|
|
52
|
+
try {
|
|
53
|
+
for (let page = 0; page < (flags.all ? MAX_PAGES : 1); page += 1) {
|
|
54
|
+
last = await (0, workspaces_1.listWorkspaceAgentAudit)(args.workspaceId, {
|
|
55
|
+
kinds,
|
|
56
|
+
since: flags.since,
|
|
57
|
+
until: flags.until,
|
|
58
|
+
limit: flags.limit,
|
|
59
|
+
cursor,
|
|
60
|
+
});
|
|
61
|
+
events.push(...last.events);
|
|
62
|
+
if (!last.hasMore || !last.nextCursor || last.nextCursor === cursor)
|
|
63
|
+
break;
|
|
64
|
+
cursor = last.nextCursor;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
this.handleApiError(err);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const nextCursor = last?.hasMore ? (last.nextCursor ?? null) : null;
|
|
72
|
+
if (flags.json) {
|
|
73
|
+
this.log(JSON.stringify({
|
|
74
|
+
workspaceId: args.workspaceId,
|
|
75
|
+
events,
|
|
76
|
+
hasMore: Boolean(last?.hasMore),
|
|
77
|
+
nextCursor,
|
|
78
|
+
coverage: last?.coverage ?? null,
|
|
79
|
+
}, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (events.length === 0) {
|
|
83
|
+
this.log('No third-party agent audit events match.');
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
(0, format_1.renderTable)(events.map(toRow), [
|
|
87
|
+
{ key: 'at', header: 'AT' },
|
|
88
|
+
{ key: 'kind', header: 'KIND' },
|
|
89
|
+
{ key: 'actor', header: 'ACTOR' },
|
|
90
|
+
{ key: 'member', header: 'MEMBER' },
|
|
91
|
+
{ key: 'listing', header: 'LISTING' },
|
|
92
|
+
{ key: 'copy', header: 'COPY' },
|
|
93
|
+
{ key: 'release', header: 'RELEASE' },
|
|
94
|
+
], (line) => this.log(line));
|
|
95
|
+
}
|
|
96
|
+
// Say what the trail does not record, so silence is not read as "nothing happened".
|
|
97
|
+
const notRecorded = last?.coverage?.notRecorded ?? [];
|
|
98
|
+
if (notRecorded.length) {
|
|
99
|
+
this.log('');
|
|
100
|
+
this.log(`Not recorded in this trail: ${notRecorded.join(', ')}.`);
|
|
101
|
+
}
|
|
102
|
+
if (nextCursor) {
|
|
103
|
+
this.log(`More events: --cursor ${nextCursor}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.default = WorkspacesAudit;
|
|
108
|
+
function describeAuditKind(event) {
|
|
109
|
+
if (event.kind === 'updated' && event.release?.direction === 'rollback')
|
|
110
|
+
return 'rolled back';
|
|
111
|
+
if (event.kind === 'acquisition_decided') {
|
|
112
|
+
const decision = event.outcome?.decision;
|
|
113
|
+
return typeof decision === 'string' ? `purchase ${decision}` : 'purchase decided';
|
|
114
|
+
}
|
|
115
|
+
return event.kind;
|
|
116
|
+
}
|
|
117
|
+
function describeAuditActor(event) {
|
|
118
|
+
const { actor } = event;
|
|
119
|
+
let who;
|
|
120
|
+
if (actor.kind === 'user')
|
|
121
|
+
who = `user ${actor.userId ?? '?'}`;
|
|
122
|
+
else if (actor.kind === 'agent')
|
|
123
|
+
who = `agent ${actor.agentId ?? '?'}`;
|
|
124
|
+
else
|
|
125
|
+
who = `system ${actor.process ?? ''}`.trim();
|
|
126
|
+
if (actor.confirmedBy?.userId)
|
|
127
|
+
who += `, confirmed by user ${actor.confirmedBy.userId}`;
|
|
128
|
+
return who;
|
|
129
|
+
}
|
|
130
|
+
function toRow(event) {
|
|
131
|
+
const subject = event.subject;
|
|
132
|
+
const release = event.release;
|
|
133
|
+
const version = release?.fromVersionLabel && release.versionLabel
|
|
134
|
+
? `${release.fromVersionLabel} -> ${release.versionLabel}`
|
|
135
|
+
: (release?.versionLabel ?? '');
|
|
136
|
+
return {
|
|
137
|
+
at: event.at,
|
|
138
|
+
kind: describeAuditKind(event),
|
|
139
|
+
actor: describeAuditActor(event),
|
|
140
|
+
member: subject.memberUserId ?? '',
|
|
141
|
+
listing: subject.listingName ?? subject.listingId ?? subject.teamOrderId ?? '',
|
|
142
|
+
copy: subject.installedAgentName ?? subject.installedAgentId ?? '',
|
|
143
|
+
release: version,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-endpoints.ts — request client for `/api/agent-endpoints` (OSK-12611).
|
|
3
|
+
*
|
|
4
|
+
* An agent's hosted endpoint is `/v1/agents/:id/mcp`: an MCP server (Streamable
|
|
5
|
+
* HTTP) that runs YOUR agent — one you published to the Managed Agents API, or a
|
|
6
|
+
* Store agent you installed — in skrr's cloud, as you. These routes are the
|
|
7
|
+
* owner's control surface for it; the endpoint itself is called with an `osk_*`
|
|
8
|
+
* key carrying the `agents:invoke` scope.
|
|
9
|
+
*
|
|
10
|
+
* Paths are inline literals on purpose: `domain-api-parity` resolves a command
|
|
11
|
+
* to an endpoint by reading these strings.
|
|
12
|
+
*/
|
|
13
|
+
/** The API's own snake_case projection (`routes/agentEndpoints.js`). */
|
|
14
|
+
export interface AgentEndpoint {
|
|
15
|
+
agent_id: string;
|
|
16
|
+
agent_name: string | null;
|
|
17
|
+
/** `self_authored` | `store_instance` | `not_eligible` */
|
|
18
|
+
kind: string;
|
|
19
|
+
url: string | null;
|
|
20
|
+
transport: string;
|
|
21
|
+
scope: string;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
enabled_at?: string | null;
|
|
24
|
+
switchable: boolean;
|
|
25
|
+
ready: boolean;
|
|
26
|
+
/** The gate that would refuse a turn right now (see docs/cli/SKRR_AGENT_ENDPOINTS.md). */
|
|
27
|
+
blocked_by: {
|
|
28
|
+
code: string;
|
|
29
|
+
message: string;
|
|
30
|
+
} | null;
|
|
31
|
+
release?: {
|
|
32
|
+
id: string;
|
|
33
|
+
version_label: string | null;
|
|
34
|
+
number?: number;
|
|
35
|
+
} | null;
|
|
36
|
+
payer?: {
|
|
37
|
+
label: string;
|
|
38
|
+
payerKind: string;
|
|
39
|
+
summary: string;
|
|
40
|
+
} | null;
|
|
41
|
+
capabilities?: {
|
|
42
|
+
status: string;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
};
|
|
45
|
+
workspace_id?: string | null;
|
|
46
|
+
}
|
|
47
|
+
export declare function getAgentEndpoint(agentId: string): Promise<AgentEndpoint>;
|
|
48
|
+
/** Turn a Store instance's endpoint on or off. Owner only, enforced server-side. */
|
|
49
|
+
export declare function setAgentEndpointEnabled(agentId: string, enabled: boolean): Promise<AgentEndpoint>;
|
|
50
|
+
/** The scope a key needs to call an endpoint — and the only thing it grants. */
|
|
51
|
+
export declare const AGENT_ENDPOINT_SCOPE = "agents:invoke";
|
|
52
|
+
/**
|
|
53
|
+
* What to tell an MCP client. Claude Code takes a remote HTTP server with a
|
|
54
|
+
* header; any other client needs the same two facts (URL, bearer key).
|
|
55
|
+
*/
|
|
56
|
+
export declare function mcpClientCommand(endpoint: AgentEndpoint, secret: string): string;
|
|
57
|
+
/** One human line for the state an endpoint is in. */
|
|
58
|
+
export declare function describeEndpointState(endpoint: AgentEndpoint): string;
|
|
59
|
+
/** The human rendering every `agents endpoint` command prints. */
|
|
60
|
+
export declare function printAgentEndpoint(cmd: {
|
|
61
|
+
log: (line: string) => void;
|
|
62
|
+
}, endpoint: AgentEndpoint): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENT_ENDPOINT_SCOPE = void 0;
|
|
4
|
+
exports.getAgentEndpoint = getAgentEndpoint;
|
|
5
|
+
exports.setAgentEndpointEnabled = setAgentEndpointEnabled;
|
|
6
|
+
exports.mcpClientCommand = mcpClientCommand;
|
|
7
|
+
exports.describeEndpointState = describeEndpointState;
|
|
8
|
+
exports.printAgentEndpoint = printAgentEndpoint;
|
|
9
|
+
const api_fetch_1 = require("./api-fetch");
|
|
10
|
+
async function getAgentEndpoint(agentId) {
|
|
11
|
+
return (0, api_fetch_1.apiFetch)(`/api/agent-endpoints/${encodeURIComponent(agentId)}`);
|
|
12
|
+
}
|
|
13
|
+
/** Turn a Store instance's endpoint on or off. Owner only, enforced server-side. */
|
|
14
|
+
async function setAgentEndpointEnabled(agentId, enabled) {
|
|
15
|
+
return (0, api_fetch_1.apiFetch)(`/api/agent-endpoints/${encodeURIComponent(agentId)}`, {
|
|
16
|
+
method: 'PUT',
|
|
17
|
+
body: { enabled },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** The scope a key needs to call an endpoint — and the only thing it grants. */
|
|
21
|
+
exports.AGENT_ENDPOINT_SCOPE = 'agents:invoke';
|
|
22
|
+
/**
|
|
23
|
+
* What to tell an MCP client. Claude Code takes a remote HTTP server with a
|
|
24
|
+
* header; any other client needs the same two facts (URL, bearer key).
|
|
25
|
+
*/
|
|
26
|
+
function mcpClientCommand(endpoint, secret) {
|
|
27
|
+
const name = `skrr-${endpoint.agent_id}`.replace(/[^A-Za-z0-9_-]/g, '-');
|
|
28
|
+
return `claude mcp add --transport http ${name} ${endpoint.url} --header "Authorization: Bearer ${secret}"`;
|
|
29
|
+
}
|
|
30
|
+
/** One human line for the state an endpoint is in. */
|
|
31
|
+
function describeEndpointState(endpoint) {
|
|
32
|
+
if (endpoint.kind === 'not_eligible') {
|
|
33
|
+
return endpoint.blocked_by?.message || 'This agent has no hosted endpoint.';
|
|
34
|
+
}
|
|
35
|
+
if (!endpoint.enabled) {
|
|
36
|
+
return 'off — turn it on with `skrr agents endpoint enable`';
|
|
37
|
+
}
|
|
38
|
+
if (endpoint.blocked_by) {
|
|
39
|
+
return `on, but turns are refused: ${endpoint.blocked_by.code} — ${endpoint.blocked_by.message}`;
|
|
40
|
+
}
|
|
41
|
+
return 'on';
|
|
42
|
+
}
|
|
43
|
+
/** The human rendering every `agents endpoint` command prints. */
|
|
44
|
+
function printAgentEndpoint(cmd, endpoint) {
|
|
45
|
+
cmd.log(`Agent ${endpoint.agent_id}${endpoint.agent_name ? ` (${endpoint.agent_name})` : ''}`);
|
|
46
|
+
cmd.log(`Kind ${endpoint.kind.replace(/_/g, ' ')}`);
|
|
47
|
+
cmd.log(`Endpoint ${describeEndpointState(endpoint)}`);
|
|
48
|
+
if (endpoint.url) {
|
|
49
|
+
cmd.log(`URL ${endpoint.url} (MCP, ${endpoint.transport})`);
|
|
50
|
+
cmd.log(`Key scope ${endpoint.scope}`);
|
|
51
|
+
}
|
|
52
|
+
if (endpoint.release) {
|
|
53
|
+
cmd.log(`Release ${endpoint.release.version_label || 'unlabelled'} (${endpoint.release.id})`);
|
|
54
|
+
}
|
|
55
|
+
if (endpoint.payer) {
|
|
56
|
+
cmd.log(`Who pays ${endpoint.payer.summary}`);
|
|
57
|
+
}
|
|
58
|
+
else if (endpoint.kind !== 'not_eligible') {
|
|
59
|
+
cmd.log('Who pays unknown right now (the billing resolver could not be read)');
|
|
60
|
+
}
|
|
61
|
+
if (endpoint.capabilities) {
|
|
62
|
+
cmd.log(`Manifest ${endpoint.capabilities.status}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -22,7 +22,7 @@ export interface AuthBundle {
|
|
|
22
22
|
* How a stored refresh family came to exist.
|
|
23
23
|
*
|
|
24
24
|
* - `user_login`: a person signed in on purpose (`skrr login` in any of its
|
|
25
|
-
* flows
|
|
25
|
+
* flows). On a Dedicated guest this is the one stored family
|
|
26
26
|
* the broker-mode retirement must never touch: the person asked for it.
|
|
27
27
|
* - `daemon_broker`: the local daemon minted it through its hand-off.
|
|
28
28
|
*
|
package/dist/lib/auth-storage.js
CHANGED
|
@@ -779,7 +779,7 @@ function writeToBackend(bundle, options = {}) {
|
|
|
779
779
|
persistedBundle = { ...bundle, serverOrigin: normalized };
|
|
780
780
|
}
|
|
781
781
|
// Always stamp THIS config as the owner — a bundle read back and re-written
|
|
782
|
-
//
|
|
782
|
+
// must not carry a record forward from wherever it came from.
|
|
783
783
|
persistedBundle = { ...persistedBundle, owner: ownerFor(persistedBundle) };
|
|
784
784
|
// Provenance: an explicit one wins (a login or a broker mint creates a new
|
|
785
785
|
// family); otherwise this is a rotation of the stored family and keeps what
|
|
@@ -405,7 +405,7 @@ export declare function findBrokeredHandoffDescriptor(profile?: string): LocalBo
|
|
|
405
405
|
* 2. `~/.skrr/cli-auth.json` reads cleanly and carries a refresh token,
|
|
406
406
|
* 3. its stored `serverOrigin` matches the descriptor's `serverUrl`,
|
|
407
407
|
* 4. it is not marked `provenance: 'user_login'`. Since OSK-12293 every
|
|
408
|
-
* explicit sign-in (`skrr login`,
|
|
408
|
+
* explicit sign-in (`skrr login`, in any of its flows) records that marker in
|
|
409
409
|
* the file, so a login the person made seconds ago is kept and used;
|
|
410
410
|
* an unmarked file was written by a CLI that predates the marker.
|
|
411
411
|
*
|
package/dist/lib/daemonBroker.js
CHANGED
|
@@ -1024,7 +1024,7 @@ function findBrokeredHandoffDescriptor(profile = 'default') {
|
|
|
1024
1024
|
* 2. `~/.skrr/cli-auth.json` reads cleanly and carries a refresh token,
|
|
1025
1025
|
* 3. its stored `serverOrigin` matches the descriptor's `serverUrl`,
|
|
1026
1026
|
* 4. it is not marked `provenance: 'user_login'`. Since OSK-12293 every
|
|
1027
|
-
* explicit sign-in (`skrr login`,
|
|
1027
|
+
* explicit sign-in (`skrr login`, in any of its flows) records that marker in
|
|
1028
1028
|
* the file, so a login the person made seconds ago is kept and used;
|
|
1029
1029
|
* an unmarked file was written by a CLI that predates the marker.
|
|
1030
1030
|
*
|
|
@@ -1070,7 +1070,7 @@ function retirableCliAuthFamily(descriptorServerUrl) {
|
|
|
1070
1070
|
if (!stored?.refreshToken)
|
|
1071
1071
|
return null;
|
|
1072
1072
|
// A family a person signed in for on purpose is never a pre-broker
|
|
1073
|
-
// leftover: `skrr login`
|
|
1073
|
+
// leftover: `skrr login` stamps it, and a refresh carries the
|
|
1074
1074
|
// stamp forward. Retiring it undid an explicit guest login on the very next
|
|
1075
1075
|
// command (OSK-12293). Only an UNMARKED file — written by a CLI that
|
|
1076
1076
|
// predates the marker — or a daemon-minted one is retired.
|
|
@@ -1223,7 +1223,7 @@ async function attemptDaemonBrokerLoginAndPersist(opts) {
|
|
|
1223
1223
|
// ---------------------------------------------------------------------
|
|
1224
1224
|
/**
|
|
1225
1225
|
* Commands that manage their own auth lifecycle. The auto-broker MUST
|
|
1226
|
-
* NOT fire for these — `login`
|
|
1226
|
+
* NOT fire for these — `login` runs its own minting flows
|
|
1227
1227
|
* (auto-brokering first would burn a needless server round-trip and
|
|
1228
1228
|
* mint a token that `cliLogin()` would immediately supersede), and
|
|
1229
1229
|
* `logout` is about CLEARING auth (auto-brokering first would mint a
|
|
@@ -1237,7 +1237,6 @@ async function attemptDaemonBrokerLoginAndPersist(opts) {
|
|
|
1237
1237
|
const AUTH_SELF_MANAGED_COMMANDS = new Set([
|
|
1238
1238
|
'login',
|
|
1239
1239
|
'logout',
|
|
1240
|
-
'pair',
|
|
1241
1240
|
'generate-recovery-code',
|
|
1242
1241
|
]);
|
|
1243
1242
|
/**
|
|
@@ -48,8 +48,4 @@ class HarnessCredentialPersistRefused extends Error {
|
|
|
48
48
|
}
|
|
49
49
|
exports.HarnessCredentialPersistRefused = HarnessCredentialPersistRefused;
|
|
50
50
|
/** The commands a harness-context process may not run (they own the stored session). */
|
|
51
|
-
exports.HARNESS_REFUSED_AUTH_COMMANDS = new Set([
|
|
52
|
-
'login',
|
|
53
|
-
'logout',
|
|
54
|
-
'pair',
|
|
55
|
-
]);
|
|
51
|
+
exports.HARNESS_REFUSED_AUTH_COMMANDS = new Set(['login', 'logout']);
|
|
@@ -9,3 +9,5 @@ import type { StoreAcquisitionRequest } from '@skrr-ai/data-provider';
|
|
|
9
9
|
* terminal and the card cannot describe one request two ways.
|
|
10
10
|
*/
|
|
11
11
|
export declare function describeAcquisitionRequestLines(request: StoreAcquisitionRequest): string[];
|
|
12
|
+
/** One request as one line, for `skrr store acquisitions list`. */
|
|
13
|
+
export declare function describeAcquisitionRequestRow(request: StoreAcquisitionRequest): string;
|