@skrr-ai/cli 0.1.116 → 0.1.118

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 (94) hide show
  1. package/dist/base-command.d.ts +21 -0
  2. package/dist/base-command.js +95 -8
  3. package/dist/commands/actions/browse.js +2 -1
  4. package/dist/commands/actions/install.d.ts +2 -0
  5. package/dist/commands/actions/install.js +67 -2
  6. package/dist/commands/actions/list.js +2 -1
  7. package/dist/commands/actions/show.js +2 -1
  8. package/dist/commands/commitments/create.d.ts +21 -2
  9. package/dist/commands/commitments/create.js +74 -4
  10. package/dist/commands/convos/recovery.js +12 -4
  11. package/dist/commands/followups/plans/list.js +1 -1
  12. package/dist/commands/guide.d.ts +21 -0
  13. package/dist/commands/guide.js +46 -0
  14. package/dist/commands/instructions/guide.js +34 -52
  15. package/dist/commands/memory/maintain.js +1 -1
  16. package/dist/commands/skills/browse.js +2 -1
  17. package/dist/commands/skills/details.js +4 -2
  18. package/dist/commands/skills/featured.js +2 -1
  19. package/dist/commands/skills/install.d.ts +2 -0
  20. package/dist/commands/skills/install.js +62 -3
  21. package/dist/commands/skills/search.js +2 -1
  22. package/dist/commands/slack/send-as-agent.js +3 -3
  23. package/dist/commands/spaces/create.js +8 -2
  24. package/dist/commands/spaces/guide.d.ts +21 -0
  25. package/dist/commands/spaces/guide.js +40 -0
  26. package/dist/commands/spaces/index.d.ts +0 -1
  27. package/dist/commands/spaces/index.js +1 -6
  28. package/dist/commands/store/acquisitions/list.js +2 -1
  29. package/dist/commands/store/acquisitions/show.js +2 -1
  30. package/dist/commands/store/browse.js +4 -2
  31. package/dist/commands/store/install.d.ts +1 -0
  32. package/dist/commands/store/install.js +26 -2
  33. package/dist/commands/store/releases.js +4 -2
  34. package/dist/commands/store/show.js +3 -2
  35. package/dist/commands/store/tip.js +2 -2
  36. package/dist/commands/tasks/checkout.js +2 -3
  37. package/dist/commands/tasks/comments/add.js +3 -5
  38. package/dist/commands/tasks/complete.js +2 -3
  39. package/dist/commands/tasks/deliverable/add.d.ts +1 -1
  40. package/dist/commands/tasks/deliverable/add.js +4 -7
  41. package/dist/commands/tasks/expectations/assess.js +2 -2
  42. package/dist/commands/tasks/guide.d.ts +8 -0
  43. package/dist/commands/tasks/guide.js +23 -10
  44. package/dist/commands/tasks/report.js +9 -5
  45. package/dist/commands/tasks/resource/add.js +2 -3
  46. package/dist/commands/tasks/result/submit.js +2 -3
  47. package/dist/commands/tasks/resume/save.js +2 -3
  48. package/dist/commands/tasks/review.js +2 -3
  49. package/dist/commands/tasks/triage.js +2 -1
  50. package/dist/commands/tasks/updates/add.js +2 -3
  51. package/dist/generated/agent-guides.json +8312 -0
  52. package/dist/generated/managed-instruction-guides.json +1 -1
  53. package/dist/help.js +4 -3
  54. package/dist/hooks/command-not-found.js +1 -1
  55. package/dist/lib/action-listings.js +2 -1
  56. package/dist/lib/agent-guide-reference-gate.d.ts +28 -0
  57. package/dist/lib/agent-guide-reference-gate.js +186 -0
  58. package/dist/lib/agent-guides.d.ts +51 -0
  59. package/dist/lib/agent-guides.js +170 -0
  60. package/dist/lib/api-fetch.js +3 -1
  61. package/dist/lib/command-miss.d.ts +13 -2
  62. package/dist/lib/command-miss.js +134 -8
  63. package/dist/lib/guide-pointer.d.ts +15 -0
  64. package/dist/lib/guide-pointer.js +51 -0
  65. package/dist/lib/lookbackPlans.d.ts +3 -1
  66. package/dist/lib/lookbackPlans.js +9 -0
  67. package/dist/lib/node-adapter.js +4 -3
  68. package/dist/lib/outbox.d.ts +0 -14
  69. package/dist/lib/outbox.js +0 -21
  70. package/dist/lib/roles.d.ts +1 -1
  71. package/dist/lib/store-listing.d.ts +6 -1
  72. package/dist/lib/store-listing.js +6 -1
  73. package/dist/lib/task-admission.js +27 -5
  74. package/dist/lib/task-batch.d.ts +10 -7
  75. package/dist/lib/task-batch.js +5 -20
  76. package/dist/lib/task-operations.d.ts +8 -20
  77. package/dist/lib/task-operations.js +9 -94
  78. package/dist/lib/task-preflight.js +70 -33
  79. package/dist/lib/task-runtime-headers.d.ts +11 -0
  80. package/dist/lib/task-runtime-headers.js +73 -0
  81. package/dist/lib/task-write-attribution.js +1 -1
  82. package/dist/lib/usage-discovery.d.ts +4 -0
  83. package/dist/lib/usage-discovery.js +5 -0
  84. package/dist/lib/usd-micros.d.ts +27 -0
  85. package/dist/lib/usd-micros.js +67 -2
  86. package/dist/lib/voice-call-intent.js +3 -1
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +17 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +35 -2
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +17 -0
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +44 -0
  91. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  92. package/dist/node_modules/@skrr-ai/data-provider/index.js +11927 -11678
  93. package/oclif.manifest.json +251 -14
  94. package/package.json +4 -2
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Guide extends Command {
3
+ static baseFlags: {
4
+ bare: import("@oclif/core/interfaces").BooleanFlag<boolean>;
5
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ raw: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ static description: string;
11
+ static args: {
12
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
13
+ section: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ offline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ static examples: string[];
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,46 @@
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 agent_guides_1 = require("../lib/agent-guides");
7
+ // Documentation stays available when a daemon-issued Agent credential is
8
+ // unavailable. Do not run BaseCommand's mandatory credential initialization.
9
+ class Guide extends core_1.Command {
10
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
11
+ static description = 'Read Skrr operation guides on demand';
12
+ static args = {
13
+ id: core_1.Args.string({ description: 'Guide id', ignoreStdin: true }),
14
+ section: core_1.Args.string({ description: 'Section id', ignoreStdin: true }),
15
+ };
16
+ static flags = {
17
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
18
+ offline: core_1.Flags.boolean({
19
+ description: 'Read the bundled release guides without checking credentials or the server',
20
+ }),
21
+ };
22
+ static examples = [
23
+ '<%= config.bin %> guide',
24
+ '<%= config.bin %> guide tasks',
25
+ '<%= config.bin %> guide tasks updates --offline',
26
+ ];
27
+ async run() {
28
+ const { args, flags } = await this.parse(Guide);
29
+ const request = {
30
+ ...(args.id ? { id: args.id } : {}),
31
+ ...(args.section ? { section: args.section } : {}),
32
+ };
33
+ const read = await (0, agent_guides_1.readAgentGuide)(request, flags);
34
+ if (!read.found)
35
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
36
+ if (flags.json) {
37
+ process.stdout.write((0, agent_guides_1.agentGuideJson)(read));
38
+ return;
39
+ }
40
+ const notice = (0, agent_guides_1.agentGuideFallbackNotice)(read);
41
+ if (notice)
42
+ process.stderr.write(`${notice}\n`);
43
+ process.stdout.write((0, data_provider_1.renderAgentGuideText)(read.response));
44
+ }
45
+ }
46
+ exports.default = Guide;
@@ -4,19 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const core_1 = require("@oclif/core");
7
+ const data_provider_1 = require("@skrr-ai/data-provider");
7
8
  const node_crypto_1 = require("node:crypto");
8
9
  const base_command_1 = require("../../base-command");
9
10
  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");
11
+ const agent_guides_1 = require("../../lib/agent-guides");
12
+ // The installed v20 instructions still name this command; the guide body they
13
+ // ask for now lives in the tasks card. The card also dropped the membership
14
+ // and scheduling rules, so one trailing line keeps an agent pointed at where
15
+ // those went.
16
+ const TASKS_GUIDE_POINTER = 'Membership and scheduling now live in `skrr guide spaces access` for Space membership ' +
17
+ 'and `skrr guide tasks followups` for follow-ups and scheduling.';
15
18
  // Documentation stays available when a daemon-issued Agent credential is
16
19
  // unavailable. Do not run BaseCommand's mandatory credential initialization.
17
20
  class InstructionsGuide extends core_1.Command {
18
21
  static baseFlags = base_command_1.BaseCommand.baseFlags;
19
- static description = 'Read the detailed task-management guide on demand';
22
+ static description = 'Kept for installed instructions — prints the tasks guide. `skrr guide` reads every guide.';
20
23
  static args = {
21
24
  name: core_1.Args.string({
22
25
  description: 'Guide to read',
@@ -37,55 +40,34 @@ class InstructionsGuide extends core_1.Command {
37
40
  ];
38
41
  async run() {
39
42
  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
- // An integrity failure, not a network failure: the server
60
- // answered, but the body it sent does not match its own hash.
61
- // Give it its own reason before the catch below can flatten it
62
- // into 'server-unavailable', which describes the opposite world.
63
- reason = 'server-hash-mismatch';
64
- throw new Error('The server guide hash does not match its body.');
65
- }
66
- guide = response;
67
- source = 'server';
68
- reason = undefined;
69
- }
70
- }
71
- catch {
72
- // A missing/unreadable auth envelope or delegated daemon cannot make
73
- // a read-only local guide unavailable, and must never emit auth JSON.
74
- reason ??= 'server-unavailable';
75
- }
76
- }
77
- if (flags.json)
43
+ const read = await (0, agent_guides_1.readAgentGuide)({ id: 'tasks' }, flags);
44
+ if (!read.found)
45
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
46
+ const body = (0, data_provider_1.renderAgentGuideText)(read.response);
47
+ if (flags.json) {
48
+ // The ManagedInstructionGuide shape is the contract older callers read:
49
+ // keep its keys, but `body` is the tasks card text and `hash` its sha256,
50
+ // so a consumer that verifies hash against body still succeeds.
51
+ const preset = managed_instruction_guides_json_1.default['oversky-task-management'];
78
52
  this.log(JSON.stringify({
79
- ...guide,
80
- provenance: { source, fallback: source === 'bundled', ...(reason ? { reason } : {}) },
53
+ key: preset.key,
54
+ name: preset.name,
55
+ templateVersion: preset.templateVersion,
56
+ body,
57
+ hash: (0, node_crypto_1.createHash)('sha256').update(body, 'utf8').digest('hex'),
58
+ redirectedTo: 'tasks',
59
+ provenance: {
60
+ source: read.source,
61
+ fallback: read.source === 'bundled',
62
+ ...(read.reason ? { reason: read.reason } : {}),
63
+ },
81
64
  }, null, 2));
82
- else {
83
- if (source === 'bundled')
84
- this.logToStderr(reason === 'server-hash-mismatch'
85
- ? `The server guide’s body does not match its hash; using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`
86
- : `Using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`);
87
- this.log(guide.body);
65
+ return;
88
66
  }
67
+ const notice = (0, agent_guides_1.agentGuideFallbackNotice)(read);
68
+ if (notice)
69
+ process.stderr.write(`${notice}\n`);
70
+ this.log(`${body}\n${TASKS_GUIDE_POINTER}`);
89
71
  }
90
72
  }
91
73
  exports.default = InstructionsGuide;
@@ -16,7 +16,7 @@ class MemoryMaintain extends base_command_1.BaseCommand {
16
16
  static description = 'Apply an auditable canonical memory maintenance operation';
17
17
  static examples = [
18
18
  '<%= config.bin %> memory maintain remember \'{"path":"/notes","claim":"Ship notes on Fridays."}\' --agent agent_abc123',
19
- '<%= config.bin %> memory maintain archive \'{"factId":"fact_abc"}\' --agent agent_abc123',
19
+ '<%= config.bin %> memory maintain archive \'{"path":"/notes/old-note"}\' --agent agent_abc123',
20
20
  ];
21
21
  static args = {
22
22
  operation: core_1.Args.string({ required: true, options: [...OPERATIONS], ignoreStdin: true }),
@@ -6,6 +6,7 @@ const core_1 = require("@oclif/core");
6
6
  const data_provider_1 = require("@skrr-ai/data-provider");
7
7
  const base_command_1 = require("../../base-command");
8
8
  const format_1 = require("../../lib/format");
9
+ const usd_micros_1 = require("../../lib/usd-micros");
9
10
  function skillBrowseDescription(skill) {
10
11
  const description = String(skill.description ?? '').trim();
11
12
  if (description && description !== '>' && description !== '|')
@@ -64,7 +65,7 @@ class SkillsBrowse extends base_command_1.BaseCommand {
64
65
  this.handleApiError(err);
65
66
  }
66
67
  if (flags.json) {
67
- this.log(JSON.stringify(response, null, 2));
68
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(response), null, 2));
68
69
  return;
69
70
  }
70
71
  const r = response;
@@ -4,6 +4,8 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const local_skills_1 = require("../../lib/local-skills");
7
+ const store_listing_1 = require("../../lib/store-listing");
8
+ const usd_micros_1 = require("../../lib/usd-micros");
7
9
  class SkillsDetails extends base_command_1.BaseCommand {
8
10
  static description = 'Show full details for a skill';
9
11
  static examples = [
@@ -49,7 +51,7 @@ class SkillsDetails extends base_command_1.BaseCommand {
49
51
  await this.failOnStoreMiss(err, args.id);
50
52
  }
51
53
  if (flags.json) {
52
- this.log(JSON.stringify(skill, null, 2));
54
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(skill), null, 2));
53
55
  return;
54
56
  }
55
57
  const s = skill;
@@ -59,7 +61,7 @@ class SkillsDetails extends base_command_1.BaseCommand {
59
61
  this.log(`Author: ${s.authorName ?? s.author ?? '-'}`);
60
62
  this.log(`Rating: ${s.rating != null ? s.rating.toFixed(1) : '-'} (${s.ratingCount ?? 0} reviews)`);
61
63
  this.log(`Installs: ${s.installCount ?? 0}`);
62
- this.log(`Price: ${s.price ? `${s.price} credits` : 'Free'}`);
64
+ this.log(`Price: ${(0, store_listing_1.formatListingPrice)(typeof s.price === 'number' ? s.price : null)}`);
63
65
  this.log(`Public: ${s.isPublic ? 'yes' : 'no'}`);
64
66
  this.log(`Created: ${s.createdAt ?? '-'}`);
65
67
  if (s.description) {
@@ -5,6 +5,7 @@ const core_1 = require("@oclif/core");
5
5
  const data_provider_1 = require("@skrr-ai/data-provider");
6
6
  const base_command_1 = require("../../base-command");
7
7
  const format_1 = require("../../lib/format");
8
+ const usd_micros_1 = require("../../lib/usd-micros");
8
9
  const browse_1 = require("./browse");
9
10
  function featuredSkillIndex(response, limit) {
10
11
  const envelope = response;
@@ -41,7 +42,7 @@ class SkillsFeatured extends base_command_1.BaseCommand {
41
42
  }
42
43
  const index = featuredSkillIndex(response, flags.limit);
43
44
  if (flags.json)
44
- return void this.log(JSON.stringify(index, null, 2));
45
+ return void this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(index), null, 2));
45
46
  const items = index.data;
46
47
  if (items.length === 0) {
47
48
  this.log('No featured skills.');
@@ -9,6 +9,8 @@ export default class SkillsInstall extends BaseCommand {
9
9
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ 'expected-price': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
14
  };
13
15
  /** A store 404 for a name that may be a skill on the caller's machine. */
14
16
  private failOnStoreMiss;
@@ -3,12 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
+ const prompt_1 = require("../../lib/prompt");
6
7
  const local_skills_1 = require("../../lib/local-skills");
8
+ const usd_micros_1 = require("../../lib/usd-micros");
7
9
  class SkillsInstall extends base_command_1.BaseCommand {
8
- static description = 'Install a skill from the store to a service (agent)';
10
+ static description = 'Install a skill from the store to a service (agent). A paid skill shows its price and asks you to confirm first.';
9
11
  static examples = [
10
12
  '<%= config.bin %> skills install <skill-id>',
11
13
  '<%= config.bin %> skills install <skill-id> --agent <agent-id>',
14
+ '<%= config.bin %> skills install <skill-id> --yes',
12
15
  '<%= config.bin %> skills install <skill-id> --json',
13
16
  ];
14
17
  static args = {
@@ -25,6 +28,14 @@ class SkillsInstall extends base_command_1.BaseCommand {
25
28
  description: '[DEPRECATED] Use --agent. Same meaning, kept for script compatibility.',
26
29
  hidden: true,
27
30
  }),
31
+ yes: core_1.Flags.boolean({
32
+ char: 'y',
33
+ description: 'Confirm a paid install without prompting. The price is still printed.',
34
+ }),
35
+ 'expected-price': core_1.Flags.string({
36
+ description: 'Refuse unless the install would charge exactly this USD amount (e.g. 4 or 2.50; 0 pins free). ' +
37
+ 'Use it to pin the price the person named, not just the one read now.',
38
+ }),
28
39
  };
29
40
  /** A store 404 for a name that may be a skill on the caller's machine. */
30
41
  async failOnStoreMiss(err, id) {
@@ -56,18 +67,66 @@ class SkillsInstall extends base_command_1.BaseCommand {
56
67
  retryable: false,
57
68
  });
58
69
  }
70
+ // What installing would charge NOW, read before the install sends it.
71
+ // A paid skill shows its price and asks first; the install then pins what
72
+ // was shown, so a listing whose price moved in between is refused rather
73
+ // than charged (OSK-18042 — same convention as `store install`, OSK-12240).
74
+ let listing;
75
+ try {
76
+ listing = await data_provider_1.dataService.getSkill(args.id);
77
+ }
78
+ catch (err) {
79
+ return await this.failOnStoreMiss(err, args.id);
80
+ }
81
+ const charge = Number(listing.price) || 0;
82
+ // A caller-pinned price binds the install to what the PERSON named, not
83
+ // just to what this command read (OSK-18256 — same convention as
84
+ // `store install`).
85
+ const expectedMicros = (0, usd_micros_1.expectedPriceFlagMicros)(flags['expected-price']);
86
+ if (expectedMicros === null) {
87
+ this.error(`--expected-price must be a USD amount with at most 2 decimals, e.g. 4 or 2.50 (got "${flags['expected-price']}")`, { exit: 2 });
88
+ }
89
+ if (expectedMicros !== undefined && expectedMicros !== charge) {
90
+ this.failWithCliError({
91
+ message: (0, usd_micros_1.expectedPriceMismatchMessage)(expectedMicros, charge),
92
+ code: 'EXPECTED_PRICE_MISMATCH',
93
+ exit: 1,
94
+ retryable: false,
95
+ });
96
+ }
97
+ if (charge > 0) {
98
+ const name = listing.displayName || listing.name || args.id;
99
+ // Printed before any prompt and even with --yes, so a scripted purchase
100
+ // still leaves the price it agreed to in its own output.
101
+ this.logToStderr(`${name} is a paid skill.`);
102
+ this.logToStderr(` Price: ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)} USD`);
103
+ if (listing.offerVersionId) {
104
+ this.logToStderr(` Offer: ${listing.offerVersionId}`);
105
+ }
106
+ const confirmed = await (0, prompt_1.confirmDestructive)(`Buy ${name} for ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)}?`, flags.yes).catch((err) => this.error(err.message, { exit: err.exit ?? 1 }));
107
+ if (!confirmed) {
108
+ this.log('Cancelled. Nothing was bought.');
109
+ return;
110
+ }
111
+ }
59
112
  let result;
60
113
  try {
61
114
  result = await data_provider_1.dataService.installSkill({
62
115
  skillId: args.id,
63
116
  targetAgentId,
117
+ // Pin what the person was shown: the charge always, the offer when
118
+ // there is one to charge under.
119
+ expectedPrice: charge,
120
+ ...(charge > 0 && listing.offerVersionId
121
+ ? { expectedOfferVersionId: listing.offerVersionId }
122
+ : {}),
64
123
  });
65
124
  }
66
125
  catch (err) {
67
- await this.failOnStoreMiss(err, args.id);
126
+ return await this.failOnStoreMiss(err, args.id);
68
127
  }
69
128
  if (flags.json) {
70
- this.log(JSON.stringify(result, null, 2));
129
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(result), null, 2));
71
130
  return;
72
131
  }
73
132
  const r = result;
@@ -5,6 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const format_1 = require("../../lib/format");
7
7
  const search_query_1 = require("../../lib/search-query");
8
+ const usd_micros_1 = require("../../lib/usd-micros");
8
9
  class SkillsSearch extends base_command_1.BaseCommand {
9
10
  static description = 'Search skills in the skrr store by keyword (same as `search skills`)';
10
11
  static examples = [
@@ -53,7 +54,7 @@ class SkillsSearch extends base_command_1.BaseCommand {
53
54
  this.handleApiError(err);
54
55
  }
55
56
  if (flags.json) {
56
- this.log(JSON.stringify(response, null, 2));
57
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(response), null, 2));
57
58
  return;
58
59
  }
59
60
  const r = response;
@@ -181,14 +181,14 @@ class SlackSendAsAgent extends base_command_1.BaseCommand {
181
181
  exports.default = SlackSendAsAgent;
182
182
  function generatedSessionIdempotencyKey(agentId, channelId, text) {
183
183
  const requestId = process.env.OVERSKY_SESSION_REQUEST_ID || process.env.OVERSKY_REQUEST_ID;
184
- if (requestId)
185
- return `session:slack:${requestId}`;
186
184
  const digest = crypto
187
185
  .createHash('sha256')
188
186
  .update(JSON.stringify({ agentId, channelId, text }))
189
187
  .digest('hex')
190
188
  .slice(0, 32);
191
- return `session:slack:${digest}`;
189
+ if (requestId)
190
+ return `session:slack:${requestId}:${digest}`;
191
+ return `session:slack:${crypto.randomUUID()}`;
192
192
  }
193
193
  function formatSessionSlackResult(result, agentId, channelId) {
194
194
  const status = result.status || 'submitted';
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
- const outbox_1 = require("../../lib/outbox");
5
+ const node_crypto_1 = require("node:crypto");
6
6
  const api_fetch_1 = require("../../lib/api-fetch");
7
7
  const base_command_1 = require("../../base-command");
8
8
  const label_ref_1 = require("../../lib/label-ref");
@@ -124,7 +124,13 @@ class SpacesCreate extends base_command_1.BaseCommand {
124
124
  // replay must collapse onto the write that already landed. A key sent only
125
125
  // on the replay would create a second Space instead — the exact duplicate
126
126
  // the outbox exists to prevent.
127
- const idempotencyKey = (0, outbox_1.idempotencyKeyFor)('space-create', String(payload.title), JSON.stringify(payload));
127
+ //
128
+ // The key is fresh per invocation and travels with the queued write; it is
129
+ // never derived from the payload. A payload-derived key made a second
130
+ // `spaces create` with the same title and parent replay the first for 24
131
+ // hours, so a person who archived "Billing migration" and asked for a new
132
+ // one got the archived Space back, and their new work was filed inside it.
133
+ const idempotencyKey = `space-create_${(0, node_crypto_1.randomUUID)()}`;
128
134
  let created;
129
135
  try {
130
136
  // apiFetch rather than `dataService.createSpace`: the shared transport
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ /** Local documentation must remain readable without credentials or a daemon. */
3
+ export default class SpacesGuide extends Command {
4
+ static baseFlags: {
5
+ bare: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ raw: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ static description: string;
12
+ static args: {
13
+ section: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ offline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ static examples: string[];
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,40 @@
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 agent_guides_1 = require("../../lib/agent-guides");
7
+ /** Local documentation must remain readable without credentials or a daemon. */
8
+ class SpacesGuide extends core_1.Command {
9
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
10
+ static description = 'Alias of `skrr guide spaces views` — the Space perspective map. `skrr guide spaces` reads the whole guide.';
11
+ static args = {
12
+ section: core_1.Args.string({
13
+ description: 'A section of the spaces guide to read instead of `views`',
14
+ ignoreStdin: true,
15
+ }),
16
+ };
17
+ static flags = {
18
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
19
+ offline: core_1.Flags.boolean({
20
+ description: 'Read the bundled release guides without checking credentials or the server',
21
+ }),
22
+ };
23
+ static examples = [
24
+ '<%= config.bin %> spaces guide',
25
+ '<%= config.bin %> spaces guide goals',
26
+ '<%= config.bin %> spaces guide --json',
27
+ ];
28
+ async run() {
29
+ const { args, flags } = await this.parse(SpacesGuide);
30
+ const read = await (0, agent_guides_1.readAgentGuide)({ id: 'spaces', section: args.section ?? 'views' }, flags);
31
+ if (!read.found)
32
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
33
+ if (flags.json) {
34
+ process.stdout.write((0, agent_guides_1.agentGuideJson)(read));
35
+ return;
36
+ }
37
+ process.stdout.write((0, data_provider_1.renderAgentGuideText)(read.response));
38
+ }
39
+ }
40
+ exports.default = SpacesGuide;
@@ -9,7 +9,6 @@ declare const PERSPECTIVES: SpacePerspective[];
9
9
  export default class Spaces extends BaseCommand {
10
10
  static description: string;
11
11
  static examples: string[];
12
- static aliases: string[];
13
12
  static flags: {
14
13
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
14
  };
@@ -102,12 +102,7 @@ const PERSPECTIVES = [
102
102
  exports.PERSPECTIVES = PERSPECTIVES;
103
103
  class Spaces extends base_command_1.BaseCommand {
104
104
  static description = 'Discover space perspectives and the CLI commands for each one';
105
- static examples = [
106
- '<%= config.bin %> spaces',
107
- '<%= config.bin %> spaces --json',
108
- '<%= config.bin %> spaces guide',
109
- ];
110
- static aliases = ['spaces guide'];
105
+ static examples = ['<%= config.bin %> spaces', '<%= config.bin %> spaces --json'];
111
106
  static flags = {
112
107
  json: core_1.Flags.boolean({ description: 'Output the perspective guide as JSON' }),
113
108
  };
@@ -5,6 +5,7 @@ const core_1 = require("@oclif/core");
5
5
  const data_provider_1 = require("@skrr-ai/data-provider");
6
6
  const base_command_1 = require("../../../base-command");
7
7
  const store_acquisition_1 = require("../../../lib/store-acquisition");
8
+ const usd_micros_1 = require("../../../lib/usd-micros");
8
9
  /**
9
10
  * Every purchase or install an agent asked to make for you, newest first
10
11
  * (OSK-13403). Before this, a request was only reachable by its id — one from
@@ -42,7 +43,7 @@ class StoreAcquisitionsList extends base_command_1.BaseCommand {
42
43
  }
43
44
  const requests = result?.requests ?? [];
44
45
  if (flags.json) {
45
- this.printJsonResult((0, list_output_1.listResult)(result, 'requests'));
46
+ this.printJsonResult((0, list_output_1.listResult)((0, usd_micros_1.withUsdPrices)(result), 'requests'));
46
47
  return;
47
48
  }
48
49
  if (requests.length === 0) {
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../../base-command");
6
6
  const store_acquisition_1 = require("../../../lib/store-acquisition");
7
+ const usd_micros_1 = require("../../../lib/usd-micros");
7
8
  /**
8
9
  * A purchase an agent asked to make, read from the server's stored request
9
10
  * (OSK-12240) — the same data the web approval card shows.
@@ -38,7 +39,7 @@ class StoreAcquisitionsShow extends base_command_1.BaseCommand {
38
39
  this.error('The purchase request could not be read.', { exit: 1 });
39
40
  }
40
41
  if (flags.json) {
41
- this.log(JSON.stringify(request, null, 2));
42
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(request), null, 2));
42
43
  return;
43
44
  }
44
45
  for (const line of (0, store_acquisition_1.describeAcquisitionRequestLines)(request)) {
@@ -4,6 +4,8 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const format_1 = require("../../lib/format");
7
+ const store_listing_1 = require("../../lib/store-listing");
8
+ const usd_micros_1 = require("../../lib/usd-micros");
7
9
  class StoreBrowse extends base_command_1.BaseCommand {
8
10
  static description = 'Browse published agents in the store';
9
11
  static examples = [
@@ -63,7 +65,7 @@ class StoreBrowse extends base_command_1.BaseCommand {
63
65
  this.handleApiError(err);
64
66
  }
65
67
  if (flags.json) {
66
- this.log(JSON.stringify(response, null, 2));
68
+ this.log(JSON.stringify((0, usd_micros_1.withUsdPrices)(response), null, 2));
67
69
  return;
68
70
  }
69
71
  const r = response;
@@ -77,7 +79,7 @@ class StoreBrowse extends base_command_1.BaseCommand {
77
79
  name: String(a.name ?? '-'),
78
80
  // Price is a property of the LISTING, so it is read from the listing
79
81
  // row rather than from anything the agent carries.
80
- price: a.price ? String(a.price) : 'free',
82
+ price: (0, store_listing_1.formatListingPrice)(typeof a.price === 'number' ? a.price : null),
81
83
  rating: a.rating ? `${Number(a.rating).toFixed(1)} (${a.ratingCount ?? 0})` : '-',
82
84
  installs: String(a.installCount ?? 0),
83
85
  author: String(a.authorName ?? '-'),
@@ -12,6 +12,7 @@ export default class StoreInstall extends BaseCommand {
12
12
  'include-bootstrap': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
13
  'include-memory': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
14
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ 'expected-price': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
16
  'run-on': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
17
  'share-outcomes': import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
18
  };