@skrr-ai/cli 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/base-command.d.ts +12 -9
  2. package/dist/base-command.js +27 -26
  3. package/dist/commands/commitments/action-proposals/propose.d.ts +32 -0
  4. package/dist/commands/commitments/action-proposals/propose.js +111 -0
  5. package/dist/commands/commitments/create.d.ts +2 -0
  6. package/dist/commands/commitments/create.js +42 -2
  7. package/dist/commands/commitments/delivery.js +1 -0
  8. package/dist/commands/commitments/doctor.js +11 -0
  9. package/dist/commands/commitments/mode.js +1 -0
  10. package/dist/commands/commitments/update.d.ts +2 -0
  11. package/dist/commands/commitments/update.js +40 -5
  12. package/dist/commands/store/browse.d.ts +14 -0
  13. package/dist/commands/store/browse.js +71 -0
  14. package/dist/commands/store/install.d.ts +16 -0
  15. package/dist/commands/store/install.js +54 -0
  16. package/dist/commands/store/releases.d.ts +19 -0
  17. package/dist/commands/store/releases.js +60 -0
  18. package/dist/commands/store/update.d.ts +15 -0
  19. package/dist/commands/store/update.js +69 -0
  20. package/dist/commands/store/updates.d.ts +24 -0
  21. package/dist/commands/store/updates.js +113 -0
  22. package/dist/commands/subscriptions/cancel.d.ts +14 -0
  23. package/dist/commands/subscriptions/cancel.js +47 -0
  24. package/dist/commands/subscriptions/health.d.ts +24 -0
  25. package/dist/commands/subscriptions/health.js +62 -0
  26. package/dist/commands/subscriptions/list.d.ts +9 -0
  27. package/dist/commands/subscriptions/list.js +52 -0
  28. package/dist/commands/subscriptions/status.d.ts +12 -0
  29. package/dist/commands/subscriptions/status.js +49 -0
  30. package/dist/commands/subscriptions/subscribe.d.ts +15 -0
  31. package/dist/commands/subscriptions/subscribe.js +59 -0
  32. package/dist/commands/tasks/complete.d.ts +10 -4
  33. package/dist/commands/tasks/complete.js +7 -5
  34. package/dist/commands/tasks/self-schedule.d.ts +30 -0
  35. package/dist/commands/tasks/self-schedule.js +114 -0
  36. package/dist/commands/triggers/disable.js +3 -1
  37. package/dist/commands/triggers/enable.js +3 -1
  38. package/dist/commands/triggers/rotate-secret.js +5 -0
  39. package/dist/commands/triggers/show.js +3 -1
  40. package/dist/lib/commitments.d.ts +17 -0
  41. package/dist/lib/commitments.js +59 -0
  42. package/dist/lib/node-adapter.js +4 -0
  43. package/dist/lib/task-extras.d.ts +18 -0
  44. package/dist/lib/task-extras.js +19 -1
  45. package/dist/lib/triggers.d.ts +21 -2
  46. package/dist/lib/triggers.js +48 -1
  47. package/dist/node_modules/@skrr-ai/data-provider/index.js +4063 -3902
  48. package/oclif.manifest.json +2475 -1413
  49. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * What versions of a published agent exist.
4
+ *
5
+ * Operator-relevant rather than store chrome: pinning an install to a specific
6
+ * release, or reading what changed before taking an update, both need this and
7
+ * neither is a browsing activity.
8
+ */
9
+ export default class StoreReleases extends BaseCommand {
10
+ static description: string;
11
+ static examples: string[];
12
+ static args: {
13
+ agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
+ };
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,60 @@
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
+ /**
8
+ * What versions of a published agent exist.
9
+ *
10
+ * Operator-relevant rather than store chrome: pinning an install to a specific
11
+ * release, or reading what changed before taking an update, both need this and
12
+ * neither is a browsing activity.
13
+ */
14
+ class StoreReleases extends base_command_1.BaseCommand {
15
+ static description = 'List the published releases of a store agent';
16
+ static examples = [
17
+ '<%= config.bin %> store releases agent_abc123',
18
+ '<%= config.bin %> store releases agent_abc123 --json',
19
+ ];
20
+ static args = {
21
+ agentId: core_1.Args.string({ description: 'The store agent', required: true }),
22
+ };
23
+ static flags = {
24
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
25
+ };
26
+ async run() {
27
+ this.requireAuth();
28
+ const { args, flags } = await this.parse(StoreReleases);
29
+ let response;
30
+ try {
31
+ response = await data_provider_1.dataService.getStoreAgentReleases(args.agentId);
32
+ }
33
+ catch (err) {
34
+ this.handleApiError(err);
35
+ }
36
+ if (flags.json) {
37
+ this.log(JSON.stringify(response, null, 2));
38
+ return;
39
+ }
40
+ const items = response?.data ?? [];
41
+ if (items.length === 0) {
42
+ this.log('No published releases.');
43
+ return;
44
+ }
45
+ (0, format_1.renderTable)(items.map((r) => ({
46
+ id: String(r.id ?? '-'),
47
+ version: String(r.versionLabel ?? '-'),
48
+ price: r.price ? String(r.price) : 'free',
49
+ published: r.publishedAt ? String(r.publishedAt).slice(0, 10) : '-',
50
+ changelog: String(r.changelog ?? ''),
51
+ })), [
52
+ { key: 'id', header: 'RELEASE ID' },
53
+ { key: 'version', header: 'VERSION' },
54
+ { key: 'price', header: 'PRICE' },
55
+ { key: 'published', header: 'PUBLISHED' },
56
+ { key: 'changelog', header: 'CHANGELOG', maxWidth: 44 },
57
+ ], (line) => this.log(line));
58
+ }
59
+ }
60
+ exports.default = StoreReleases;
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class StoreUpdate extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ release: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ installed: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ strategy: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,69 @@
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
+ class StoreUpdate extends base_command_1.BaseCommand {
7
+ static description = 'Update an installed agent to a published release, keeping the edits you made to your copy';
8
+ static examples = [
9
+ '<%= config.bin %> store update agent_abc123',
10
+ '<%= config.bin %> store update agent_abc123 --release ar_def456',
11
+ '<%= config.bin %> store update agent_abc123 --strategy replace',
12
+ ];
13
+ static args = {
14
+ agentId: core_1.Args.string({ description: 'The store agent you installed', required: true }),
15
+ };
16
+ static flags = {
17
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
+ release: core_1.Flags.string({ description: 'Target release id (defaults to the latest)' }),
19
+ installed: core_1.Flags.string({ description: 'Your installed copy (defaults to the purchase)' }),
20
+ strategy: core_1.Flags.string({
21
+ description: 'merge (default) keeps your edits and refuses on a conflict; replace takes the publisher version',
22
+ options: ['merge', 'replace'],
23
+ }),
24
+ };
25
+ async run() {
26
+ this.requireAuth();
27
+ const { args, flags } = await this.parse(StoreUpdate);
28
+ let response;
29
+ try {
30
+ response = await data_provider_1.dataService.updateInstalledStoreAgent({
31
+ agentId: args.agentId,
32
+ ...(flags.installed ? { installedAgentId: flags.installed } : {}),
33
+ ...(flags.release ? { targetReleaseId: flags.release } : {}),
34
+ ...(flags.strategy ? { strategy: flags.strategy } : {}),
35
+ });
36
+ }
37
+ catch (err) {
38
+ // A conflict is not a failure of the update; it is the update asking a
39
+ // question. Print the fields, and the one command that answers it.
40
+ const conflict = err;
41
+ const body = conflict?.body;
42
+ if (body?.code === 'UPDATE_CONFLICT' || body?.code === 'UPDATE_NO_BASE') {
43
+ this.log(body.error ?? 'This update needs a decision.');
44
+ if (body.conflicts?.length) {
45
+ this.log(`Both of you changed: ${body.conflicts.join(', ')}`);
46
+ }
47
+ this.log('');
48
+ this.log(`Take the publisher version: skrr store update ${args.agentId} --strategy replace`);
49
+ this.error('Update not applied.', { exit: 1 });
50
+ }
51
+ this.handleApiError(err);
52
+ }
53
+ if (flags.json) {
54
+ this.log(JSON.stringify(response, null, 2));
55
+ return;
56
+ }
57
+ const merge = response
58
+ ?.merge;
59
+ this.log(`Updated to ${response?.targetVersionLabel ?? response?.targetReleaseId ?? ''}.`);
60
+ if (merge?.kept?.length) {
61
+ // The promise that makes an automatic update worth having, made visible.
62
+ this.log(`Kept your changes to: ${merge.kept.join(', ')}`);
63
+ }
64
+ if (merge?.taken?.length) {
65
+ this.log(`Applied: ${merge.taken.join(', ')}`);
66
+ }
67
+ }
68
+ }
69
+ exports.default = StoreUpdate;
@@ -0,0 +1,24 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * What is waiting on you across everything you installed.
4
+ *
5
+ * Three states, kept apart because they ask for three different things:
6
+ *
7
+ * ready a same-major release that will apply cleanly
8
+ * review one that could not be applied on its own — you and the publisher
9
+ * both changed the same field, or the version your copy came from is
10
+ * unknown so your edits cannot be told apart from theirs
11
+ * buy a new major, which under the channel decision is a fresh purchase
12
+ * rather than an update
13
+ *
14
+ * Collapsing them into "update available" is what makes a person ignore the
15
+ * whole column: two of the three cannot be resolved by pressing update.
16
+ */
17
+ export default class StoreUpdates extends BaseCommand {
18
+ static description: string;
19
+ static examples: string[];
20
+ static flags: {
21
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
22
+ };
23
+ run(): Promise<void>;
24
+ }
@@ -0,0 +1,113 @@
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
+ /**
8
+ * What is waiting on you across everything you installed.
9
+ *
10
+ * Three states, kept apart because they ask for three different things:
11
+ *
12
+ * ready a same-major release that will apply cleanly
13
+ * review one that could not be applied on its own — you and the publisher
14
+ * both changed the same field, or the version your copy came from is
15
+ * unknown so your edits cannot be told apart from theirs
16
+ * buy a new major, which under the channel decision is a fresh purchase
17
+ * rather than an update
18
+ *
19
+ * Collapsing them into "update available" is what makes a person ignore the
20
+ * whole column: two of the three cannot be resolved by pressing update.
21
+ */
22
+ class StoreUpdates extends base_command_1.BaseCommand {
23
+ static description = 'Show what is waiting across the agents you installed';
24
+ static examples = [
25
+ '<%= config.bin %> store updates',
26
+ '<%= config.bin %> store updates --json',
27
+ ];
28
+ static flags = {
29
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
30
+ };
31
+ async run() {
32
+ this.requireAuth();
33
+ const { flags } = await this.parse(StoreUpdates);
34
+ let purchased;
35
+ try {
36
+ purchased = await data_provider_1.dataService.getPurchasedAgents();
37
+ }
38
+ catch (err) {
39
+ this.handleApiError(err);
40
+ }
41
+ const items = purchased?.data ?? [];
42
+ const rows = [];
43
+ for (const item of items) {
44
+ const agentId = String(item.sourceAgentId ?? item.id ?? '');
45
+ if (!agentId) {
46
+ continue;
47
+ }
48
+ let status;
49
+ try {
50
+ status = await data_provider_1.dataService.getAgentPurchaseStatus(agentId);
51
+ }
52
+ catch {
53
+ // One unreadable listing must not hide the rest. A row we could not
54
+ // read is reported as such rather than silently dropped.
55
+ rows.push({ agent: agentId, state: 'unreadable', have: '-', waiting: '-', detail: '' });
56
+ continue;
57
+ }
58
+ if (!status?.purchased) {
59
+ continue;
60
+ }
61
+ const have = status.installedVersionLabel ?? '-';
62
+ if (status.pendingUpdate) {
63
+ rows.push({
64
+ agent: agentId,
65
+ state: 'review',
66
+ have,
67
+ waiting: status.pendingUpdate.versionLabel ?? '-',
68
+ detail: status.pendingUpdate.reason === 'conflict'
69
+ ? `you both changed ${(status.pendingUpdate.conflicts ?? []).join(', ') || 'a field'}`
70
+ : 'the version your copy came from is unknown',
71
+ });
72
+ }
73
+ if (status.availableMajor) {
74
+ rows.push({
75
+ agent: agentId,
76
+ state: 'buy',
77
+ have,
78
+ waiting: status.availableMajor.versionLabel ?? '-',
79
+ detail: 'a new major is a separate purchase',
80
+ });
81
+ }
82
+ if (!status.pendingUpdate && status.updateAvailable && status.updateKind === 'minor') {
83
+ rows.push({
84
+ agent: agentId,
85
+ state: 'ready',
86
+ have,
87
+ waiting: status.latestVersionLabel ?? '-',
88
+ detail: 'applies cleanly',
89
+ });
90
+ }
91
+ }
92
+ if (flags.json) {
93
+ this.log(JSON.stringify({ object: 'list', data: rows }, null, 2));
94
+ return;
95
+ }
96
+ if (rows.length === 0) {
97
+ this.log('Nothing waiting.');
98
+ return;
99
+ }
100
+ (0, format_1.renderTable)(rows, [
101
+ { key: 'agent', header: 'AGENT' },
102
+ { key: 'state', header: 'STATE' },
103
+ { key: 'have', header: 'YOU HAVE' },
104
+ { key: 'waiting', header: 'WAITING' },
105
+ { key: 'detail', header: 'WHY', maxWidth: 46 },
106
+ ], (line) => this.log(line));
107
+ this.log('');
108
+ this.log('ready → skrr store update <agent>');
109
+ this.log('review → skrr store update <agent> --strategy replace (takes the publisher version)');
110
+ this.log('buy → skrr store install <agent>');
111
+ }
112
+ }
113
+ exports.default = StoreUpdates;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class SubscriptionsCancel extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ 'expected-version': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,47 @@
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
+ class SubscriptionsCancel extends base_command_1.BaseCommand {
7
+ static description = 'End a service subscription. Stops future work; keeps everything already delivered';
8
+ static examples = [
9
+ '<%= config.bin %> subscriptions cancel agent_abc123',
10
+ '<%= config.bin %> subscriptions cancel agent_abc123 --reason "no longer needed"',
11
+ '<%= config.bin %> subscriptions cancel agent_abc123 --expected-version 3',
12
+ ];
13
+ static args = {
14
+ agentId: core_1.Args.string({ description: 'The service to unsubscribe from', required: true }),
15
+ };
16
+ static flags = {
17
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
+ reason: core_1.Flags.string({ description: 'Why it is being cancelled (recorded on the lease)' }),
19
+ 'expected-version': core_1.Flags.integer({
20
+ description: 'Fence — refuse if the lease has changed since you read it. Pass it when a program read the subscription first.',
21
+ }),
22
+ };
23
+ async run() {
24
+ this.requireAuth();
25
+ const { args, flags } = await this.parse(SubscriptionsCancel);
26
+ let response;
27
+ try {
28
+ response = await data_provider_1.dataService.unsubscribeFromStoreAgent(args.agentId, {
29
+ ...(flags.reason ? { reason: flags.reason } : {}),
30
+ ...(flags['expected-version'] !== undefined
31
+ ? { expectedVersion: flags['expected-version'] }
32
+ : {}),
33
+ });
34
+ }
35
+ catch (err) {
36
+ this.handleApiError(err);
37
+ }
38
+ if (flags.json) {
39
+ this.log(JSON.stringify(response, null, 2));
40
+ return;
41
+ }
42
+ this.log(`Ended subscription ${response?.entitlementId} (${response?.status}).`);
43
+ // Said plainly, because it is the thing people fear about cancelling.
44
+ this.log(response?.retained ?? '');
45
+ }
46
+ }
47
+ exports.default = SubscriptionsCancel;
@@ -0,0 +1,24 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * Has this service actually delivered?
4
+ *
5
+ * The question every recurring charge has to answer, and the reason a
6
+ * subscription is modelled as a Commitment rather than a billing schedule.
7
+ *
8
+ * `unverified` — the work is done and nobody has looked — is counted as
9
+ * DELIVERED and reported separately as "waiting on you". It is not a failure:
10
+ * counting it as one would let a subscriber who stops confirming make a working
11
+ * service read as broken. It is surfaced anyway, because it is the subscriber's
12
+ * own evidence in a refund conversation.
13
+ */
14
+ export default class SubscriptionsHealth extends BaseCommand {
15
+ static description: string;
16
+ static examples: string[];
17
+ static args: {
18
+ agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
19
+ };
20
+ static flags: {
21
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
22
+ };
23
+ run(): Promise<void>;
24
+ }
@@ -0,0 +1,62 @@
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
+ /**
7
+ * Has this service actually delivered?
8
+ *
9
+ * The question every recurring charge has to answer, and the reason a
10
+ * subscription is modelled as a Commitment rather than a billing schedule.
11
+ *
12
+ * `unverified` — the work is done and nobody has looked — is counted as
13
+ * DELIVERED and reported separately as "waiting on you". It is not a failure:
14
+ * counting it as one would let a subscriber who stops confirming make a working
15
+ * service read as broken. It is surfaced anyway, because it is the subscriber's
16
+ * own evidence in a refund conversation.
17
+ */
18
+ class SubscriptionsHealth extends base_command_1.BaseCommand {
19
+ static description = 'Show what a service has actually delivered, and what awaits you';
20
+ static examples = [
21
+ '<%= config.bin %> subscriptions health agent_abc123',
22
+ '<%= config.bin %> subscriptions health agent_abc123 --json',
23
+ ];
24
+ static args = {
25
+ agentId: core_1.Args.string({ description: 'The service to check', required: true }),
26
+ };
27
+ static flags = {
28
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
29
+ };
30
+ async run() {
31
+ this.requireAuth();
32
+ const { args, flags } = await this.parse(SubscriptionsHealth);
33
+ let response;
34
+ try {
35
+ response = await data_provider_1.dataService.getStoreAgentSubscriptionHealth(args.agentId);
36
+ }
37
+ catch (err) {
38
+ this.handleApiError(err);
39
+ }
40
+ if (flags.json) {
41
+ this.log(JSON.stringify(response, null, 2));
42
+ return;
43
+ }
44
+ this.log(response?.summary ?? '');
45
+ if (!response?.read) {
46
+ // "We could not look" and "nothing arrived" lead somewhere different, so
47
+ // they are never printed the same way.
48
+ return;
49
+ }
50
+ this.log('');
51
+ this.log(`delivered ${response.delivered} / ${response.tasks}`);
52
+ this.log(`waiting on you ${response.awaitingConfirmation}`);
53
+ this.log(`still owed ${response.owed}`);
54
+ this.log(`blocked ${response.blocked}`);
55
+ if (response.noContract > 0) {
56
+ // "Declared nothing" and "contract met" are different things to know
57
+ // about what you are paying for.
58
+ this.log(`no stated contract ${response.noContract}`);
59
+ }
60
+ }
61
+ }
62
+ exports.default = SubscriptionsHealth;
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class SubscriptionsList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,52 @@
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
+ class SubscriptionsList extends base_command_1.BaseCommand {
8
+ static description = 'List the service subscriptions you hold';
9
+ static examples = [
10
+ '<%= config.bin %> subscriptions list',
11
+ '<%= config.bin %> subscriptions list --json',
12
+ ];
13
+ static flags = {
14
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
15
+ };
16
+ async run() {
17
+ this.requireAuth();
18
+ const { flags } = await this.parse(SubscriptionsList);
19
+ let response;
20
+ try {
21
+ response = await data_provider_1.dataService.getStoreSubscriptions();
22
+ }
23
+ catch (err) {
24
+ this.handleApiError(err);
25
+ }
26
+ if (flags.json) {
27
+ this.log(JSON.stringify(response, null, 2));
28
+ return;
29
+ }
30
+ const items = response?.data ?? [];
31
+ if (items.length === 0) {
32
+ this.log('No active subscriptions.');
33
+ return;
34
+ }
35
+ (0, format_1.renderTable)(items.map((s) => ({
36
+ entitlementId: s.entitlementId,
37
+ service: s.sourceAgentId,
38
+ instance: s.instanceAgentId ?? '-',
39
+ // Who pays for the work is part of what you bought, so it is a column
40
+ // rather than something you have to ask for with --json.
41
+ pays: s.billingMode,
42
+ expires: s.expiresAt ? String(s.expiresAt).slice(0, 10) : 'no end date',
43
+ })), [
44
+ { key: 'entitlementId', header: 'ENTITLEMENT' },
45
+ { key: 'service', header: 'SERVICE' },
46
+ { key: 'instance', header: 'YOUR INSTANCE' },
47
+ { key: 'pays', header: 'COMPUTE PAID BY' },
48
+ { key: 'expires', header: 'EXPIRES' },
49
+ ], (line) => this.log(line));
50
+ }
51
+ }
52
+ exports.default = SubscriptionsList;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class SubscriptionsStatus extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ agentId: 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
+ }
@@ -0,0 +1,49 @@
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
+ class SubscriptionsStatus extends base_command_1.BaseCommand {
7
+ static description = 'Show whether a service subscription is live, and until when';
8
+ static examples = [
9
+ '<%= config.bin %> subscriptions status agent_abc123',
10
+ '<%= config.bin %> subscriptions status agent_abc123 --json',
11
+ ];
12
+ static args = {
13
+ agentId: core_1.Args.string({ description: 'The service to check', required: true }),
14
+ };
15
+ static flags = {
16
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
17
+ };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { args, flags } = await this.parse(SubscriptionsStatus);
21
+ let response;
22
+ try {
23
+ response = await data_provider_1.dataService.getStoreAgentSubscriptionStatus(args.agentId);
24
+ }
25
+ catch (err) {
26
+ this.handleApiError(err);
27
+ }
28
+ if (flags.json) {
29
+ this.log(JSON.stringify(response, null, 2));
30
+ return;
31
+ }
32
+ if (!response?.subscribed) {
33
+ // The two reasons want different next actions, so they are printed
34
+ // differently rather than collapsed into "not subscribed".
35
+ this.log(response?.reason === 'expired'
36
+ ? 'Not live — the subscription expired. Renewing resumes it.'
37
+ : 'Not subscribed.');
38
+ return;
39
+ }
40
+ this.log(`Live. Entitlement ${response.entitlementId} (v${response.version})`);
41
+ this.log(`Your instance: ${response.instanceAgentId ?? '-'}`);
42
+ this.log(`Compute paid by: ${response.billingMode}`);
43
+ this.log(`Expires: ${response.expiresAt ? String(response.expiresAt) : 'no end date'}`);
44
+ if (response.commitmentId) {
45
+ this.log(`Obligation: ${response.commitmentId}`);
46
+ }
47
+ }
48
+ }
49
+ exports.default = SubscriptionsStatus;
@@ -0,0 +1,15 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class SubscriptionsSubscribe extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ agentId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ 'billing-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ 'expires-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,59 @@
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
+ class SubscriptionsSubscribe extends base_command_1.BaseCommand {
7
+ static description = 'Subscribe to a published service — installs your own managed instance and opens the obligation it is delivered through';
8
+ static examples = [
9
+ '<%= config.bin %> subscriptions subscribe agent_abc123',
10
+ '<%= config.bin %> subscriptions subscribe agent_abc123 --billing-mode per-user',
11
+ '<%= config.bin %> subscriptions subscribe agent_abc123 --space space_xyz --json',
12
+ ];
13
+ static args = {
14
+ agentId: core_1.Args.string({ description: 'The published service to subscribe to', required: true }),
15
+ };
16
+ static flags = {
17
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
+ 'billing-mode': core_1.Flags.string({
19
+ description: 'Who pays for the inference this service consumes',
20
+ options: ['owner', 'per-user', 'shared'],
21
+ }),
22
+ 'expires-at': core_1.Flags.string({ description: 'Paid-through date (ISO 8601)' }),
23
+ space: core_1.Flags.string({ description: 'Space the service should work in' }),
24
+ };
25
+ async run() {
26
+ this.requireAuth();
27
+ const { args, flags } = await this.parse(SubscriptionsSubscribe);
28
+ let response;
29
+ try {
30
+ response = await data_provider_1.dataService.subscribeToStoreAgent(args.agentId, {
31
+ ...(flags['billing-mode']
32
+ ? { billingMode: flags['billing-mode'] }
33
+ : {}),
34
+ ...(flags['expires-at'] ? { expiresAt: flags['expires-at'] } : {}),
35
+ ...(flags.space ? { spaceId: flags.space } : {}),
36
+ });
37
+ }
38
+ catch (err) {
39
+ this.handleApiError(err);
40
+ }
41
+ if (flags.json) {
42
+ this.log(JSON.stringify(response, null, 2));
43
+ return;
44
+ }
45
+ this.log(`Subscribed. Entitlement ${response?.entitlementId}`);
46
+ this.log(`Your instance: ${response?.instanceAgentId ?? '-'}`);
47
+ this.log(`Compute paid by: ${response?.billingMode}`);
48
+ if (response?.commitmentId) {
49
+ // The commitment is the thing you can hold the service to — surface it,
50
+ // because "is it working" is answered there and nowhere else.
51
+ this.log(`Obligation: ${response.commitmentId}`);
52
+ }
53
+ if (response?.degraded === 'commitment_pending') {
54
+ this.log('');
55
+ this.log('Note: the subscription is live but its obligation has not been created yet. It is resumable and refundable; retry to finish opening it.');
56
+ }
57
+ }
58
+ }
59
+ exports.default = SubscriptionsSubscribe;