@skrr-ai/cli 0.1.35 → 0.1.36

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 (41) hide show
  1. package/dist/commands/balance/index.js +2 -1
  2. package/dist/commands/balance/overage.d.ts +12 -0
  3. package/dist/commands/balance/overage.js +43 -0
  4. package/dist/commands/balance/plan.d.ts +11 -0
  5. package/dist/commands/balance/plan.js +59 -0
  6. package/dist/commands/balance/usage/events.d.ts +15 -0
  7. package/dist/commands/balance/usage/events.js +69 -0
  8. package/dist/commands/balance/usage.js +13 -2
  9. package/dist/commands/commitments/export.d.ts +3 -0
  10. package/dist/commands/commitments/export.js +59 -14
  11. package/dist/commands/learning/export.d.ts +5 -1
  12. package/dist/commands/learning/export.js +85 -21
  13. package/dist/commands/tasks/export.d.ts +6 -2
  14. package/dist/commands/tasks/export.js +69 -21
  15. package/dist/commands/tones/export.d.ts +3 -1
  16. package/dist/commands/tones/export.js +50 -21
  17. package/dist/lib/artifact-export.d.ts +84 -0
  18. package/dist/lib/artifact-export.js +122 -0
  19. package/dist/lib/balance.d.ts +13 -1
  20. package/dist/lib/balance.js +30 -0
  21. package/dist/lib/web-url.d.ts +2 -0
  22. package/dist/lib/web-url.js +2 -0
  23. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +15 -10
  24. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +18 -11
  25. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +9 -3
  26. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +15 -10
  27. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +18 -11
  28. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +9 -3
  29. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  30. package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
  31. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/loopback-server.d.ts +2 -0
  32. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/loopback-server.js +8 -3
  33. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.d.ts +2 -0
  34. package/dist/node_modules/@skrr-ai/inference-broker/dist/cjs/managed-inference-broker.js +6 -1
  35. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/loopback-server.d.ts +2 -0
  36. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/loopback-server.js +9 -4
  37. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.d.ts +2 -0
  38. package/dist/node_modules/@skrr-ai/inference-broker/dist/esm/managed-inference-broker.js +7 -2
  39. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  40. package/oclif.manifest.json +6974 -6657
  41. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const promises_1 = require("node:fs/promises");
4
3
  const core_1 = require("@oclif/core");
5
4
  const data_provider_1 = require("@skrr-ai/data-provider");
6
5
  const base_command_1 = require("../../base-command");
7
6
  const list_1 = require("./list");
8
7
  const tasks_1 = require("../../lib/tasks");
9
8
  const task_transfer_1 = require("../../lib/task-transfer");
9
+ const artifact_export_1 = require("../../lib/artifact-export");
10
10
  /** The list filter vocabulary is intentionally mirrored here; request construction stays shared. */
11
11
  const FILTER_FLAGS = {
12
12
  space: core_1.Flags.string({ description: 'Filter by space ID' }),
@@ -49,26 +49,24 @@ const FILTER_FLAGS = {
49
49
  class TasksExport extends base_command_1.BaseCommand {
50
50
  static description = 'Export a filtered task forest as portable JSON or flat CSV';
51
51
  static examples = [
52
+ '<%= config.bin %> tasks export --space <space-id> --summary',
52
53
  '<%= config.bin %> tasks export --space <space-id> --output tasks.json',
53
54
  '<%= config.bin %> tasks export --space <space-id> --format csv --output tasks.csv',
54
- '<%= config.bin %> tasks export --space <space-id> --with-comments > tasks.json',
55
+ '<%= config.bin %> tasks export --space <space-id> --output - | jq .',
55
56
  ];
56
57
  static flags = {
57
58
  ...FILTER_FLAGS,
59
+ ...(0, artifact_export_1.paginatedExportFlags)({ defaultMaxItems: 1000 }),
58
60
  format: core_1.Flags.string({
59
61
  description: 'Portable output format',
60
62
  options: ['json', 'csv'],
61
63
  default: 'json',
62
64
  }),
63
- output: core_1.Flags.string({
64
- description: 'Write to this path instead of stdout (use "-" for stdout)',
65
- }),
66
65
  'with-comments': core_1.Flags.boolean({
67
66
  description: 'Include comment bodies. Imported comments are newly authored by the importing principal.',
68
67
  }),
69
68
  max: core_1.Flags.integer({
70
- description: 'Safety cap for exported tasks',
71
- default: 10000,
69
+ description: 'Deprecated alias for --max-items',
72
70
  min: 1,
73
71
  max: 100000,
74
72
  }),
@@ -76,6 +74,29 @@ class TasksExport extends base_command_1.BaseCommand {
76
74
  async run() {
77
75
  this.requireAuth();
78
76
  const { flags } = await this.parse(TasksExport);
77
+ let selection;
78
+ try {
79
+ selection = (0, artifact_export_1.resolveExportSelection)(flags);
80
+ }
81
+ catch (err) {
82
+ if (err instanceof artifact_export_1.ExportContractError) {
83
+ this.failWithCliError({
84
+ message: err.message,
85
+ code: err.code,
86
+ exit: 2,
87
+ retryable: false,
88
+ });
89
+ }
90
+ throw err;
91
+ }
92
+ if (flags.max !== undefined && flags['max-items'] !== undefined) {
93
+ this.failWithCliError({
94
+ message: '--max and --max-items cannot be used together.',
95
+ code: 'TASK_EXPORT_MAX_FLAG_CONFLICT',
96
+ exit: 2,
97
+ retryable: false,
98
+ });
99
+ }
79
100
  if (flags.mine && flags['assignee-user']) {
80
101
  this.failWithCliError({
81
102
  message: '--mine and --assignee-user are mutually exclusive.',
@@ -97,9 +118,9 @@ class TasksExport extends base_command_1.BaseCommand {
97
118
  const assigneeUserId = flags.mine || flags['assignee-user']?.trim().toLowerCase() === 'me'
98
119
  ? await this.resolveCurrentUserId()
99
120
  : flags['assignee-user'];
100
- const max = flags.max ?? 10000;
121
+ const max = flags.max ?? flags['max-items'] ?? 1000;
101
122
  const all = [];
102
- let cursor;
123
+ let cursor = flags.cursor;
103
124
  let hasMore = false;
104
125
  do {
105
126
  let response;
@@ -119,14 +140,6 @@ class TasksExport extends base_command_1.BaseCommand {
119
140
  cursor = response.next_cursor ?? undefined;
120
141
  hasMore = Boolean(response.has_more && cursor);
121
142
  } while (hasMore && all.length < max);
122
- if (hasMore) {
123
- this.failWithCliError({
124
- message: `Export reached the --max safety cap (${max}). Narrow filters or pass a larger --max.`,
125
- code: 'TASK_EXPORT_MAX_REACHED',
126
- exit: 2,
127
- retryable: false,
128
- });
129
- }
130
143
  const commentsByTaskId = new Map();
131
144
  if (flags['with-comments']) {
132
145
  for (const task of all) {
@@ -158,16 +171,51 @@ class TasksExport extends base_command_1.BaseCommand {
158
171
  commentsByTaskId.set(taskId, comments);
159
172
  }
160
173
  }
174
+ const commentCount = [...commentsByTaskId.values()].reduce((sum, comments) => sum + comments.length, 0);
175
+ if (selection.summary) {
176
+ const summary = (0, artifact_export_1.contentFreeSummary)({
177
+ command: 'tasks export',
178
+ artifactSchemaVersion: 1,
179
+ itemCount: all.length,
180
+ counts: { tasks: all.length, comments: commentCount },
181
+ truncated: hasMore,
182
+ nextCursor: cursor,
183
+ });
184
+ this.log(selection.json ? JSON.stringify(summary, null, 2) : (0, artifact_export_1.humanExportSummary)(summary));
185
+ return;
186
+ }
161
187
  const exported = (0, task_transfer_1.buildPortableTaskExport)(all, commentsByTaskId);
162
188
  const content = flags.format === 'csv'
163
189
  ? (0, task_transfer_1.portableTaskExportToCsv)(exported)
164
190
  : JSON.stringify(exported, null, 2);
165
- if (flags.output && flags.output !== '-') {
166
- await (0, promises_1.writeFile)(flags.output, `${content}\n`, 'utf8');
167
- this.log(`Exported ${all.length} task(s) to ${flags.output}.`);
191
+ const artifact = content.endsWith('\n') ? content : `${content}\n`;
192
+ if (selection.output === '-') {
193
+ this.log(content);
168
194
  return;
169
195
  }
170
- this.log(content);
196
+ let receipt;
197
+ try {
198
+ receipt = await (0, artifact_export_1.writeExportArtifact)({
199
+ command: 'tasks export',
200
+ artifactSchemaVersion: exported.version,
201
+ format: flags.format ?? 'json',
202
+ output: selection.output,
203
+ content: artifact,
204
+ itemCount: all.length,
205
+ truncated: hasMore,
206
+ nextCursor: cursor,
207
+ });
208
+ }
209
+ catch (err) {
210
+ this.failWithCliError({
211
+ message: `Could not write ${selection.output}: ${err.message}`,
212
+ code: 'TASK_EXPORT_WRITE_FAILED',
213
+ exit: 1,
214
+ retryable: false,
215
+ details: { path: selection.output },
216
+ });
217
+ }
218
+ this.log(selection.json ? JSON.stringify(receipt, null, 2) : (0, artifact_export_1.humanExportReceipt)(receipt));
171
219
  }
172
220
  }
173
221
  exports.default = TasksExport;
@@ -20,9 +20,11 @@ export default class TonesExport extends BaseCommand {
20
20
  id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
21
21
  };
22
22
  static flags: {
23
- json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
24
23
  mine: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
25
24
  out: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ summary: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
+ output: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
27
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
28
  };
27
29
  run(): Promise<void>;
28
30
  }
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TONE_BUNDLE_VERSION = void 0;
4
4
  const core_1 = require("@oclif/core");
5
- const promises_1 = require("node:fs/promises");
6
5
  const data_provider_1 = require("@skrr-ai/data-provider");
7
6
  const base_command_1 = require("../../base-command");
8
7
  const tone_input_1 = require("../../lib/tone-input");
8
+ const artifact_export_1 = require("../../lib/artifact-export");
9
9
  /** Shape written by `export` and accepted by `import`. */
10
10
  exports.TONE_BUNDLE_VERSION = 1;
11
11
  /**
@@ -23,9 +23,10 @@ exports.TONE_BUNDLE_VERSION = 1;
23
23
  class TonesExport extends base_command_1.BaseCommand {
24
24
  static description = 'Export tones as JSON (round-trips with `tones import`)';
25
25
  static examples = [
26
- '<%= config.bin %> tones export',
27
- '<%= config.bin %> tones export --mine --out tones.json',
28
- '<%= config.bin %> tones export brief --out brief.json',
26
+ '<%= config.bin %> tones export --summary',
27
+ '<%= config.bin %> tones export --mine --output tones.json',
28
+ '<%= config.bin %> tones export brief --output brief.json',
29
+ '<%= config.bin %> tones export --output - | jq .',
29
30
  ];
30
31
  static args = {
31
32
  id: core_1.Args.string({
@@ -35,15 +36,28 @@ class TonesExport extends base_command_1.BaseCommand {
35
36
  }),
36
37
  };
37
38
  static flags = {
38
- json: core_1.Flags.boolean({
39
- description: 'Print the bundle to stdout even when --out wrote it to a file',
40
- }),
39
+ ...(0, artifact_export_1.standardExportFlags)(),
41
40
  mine: core_1.Flags.boolean({ description: 'Export only tones you authored' }),
42
- out: core_1.Flags.string({ description: 'Write to this file instead of stdout' }),
41
+ out: core_1.Flags.string({ description: 'Deprecated alias for --output' }),
43
42
  };
44
43
  async run() {
45
44
  this.requireAuth();
46
45
  const { args, flags } = await this.parse(TonesExport);
46
+ let selection;
47
+ try {
48
+ selection = (0, artifact_export_1.resolveExportSelection)(flags);
49
+ }
50
+ catch (err) {
51
+ if (err instanceof artifact_export_1.ExportContractError) {
52
+ this.failWithCliError({
53
+ message: err.message,
54
+ code: err.code,
55
+ exit: 2,
56
+ retryable: false,
57
+ });
58
+ }
59
+ throw err;
60
+ }
47
61
  let library;
48
62
  try {
49
63
  library = await data_provider_1.dataService.listTones();
@@ -79,6 +93,19 @@ class TonesExport extends base_command_1.BaseCommand {
79
93
  else {
80
94
  selected = flags.mine ? [...library.custom] : [...library.presets, ...library.custom];
81
95
  }
96
+ if (selection.summary) {
97
+ const summary = (0, artifact_export_1.contentFreeSummary)({
98
+ command: 'tones export',
99
+ artifactSchemaVersion: exports.TONE_BUNDLE_VERSION,
100
+ itemCount: selected.length,
101
+ counts: {
102
+ presets: selected.filter((tone) => !(0, tone_input_1.isCustomTone)(tone)).length,
103
+ custom: selected.filter((tone) => (0, tone_input_1.isCustomTone)(tone)).length,
104
+ },
105
+ });
106
+ this.log(selection.json ? JSON.stringify(summary, null, 2) : (0, artifact_export_1.humanExportSummary)(summary));
107
+ return;
108
+ }
82
109
  const bundle = {
83
110
  object: 'sky.tone.bundle',
84
111
  version: exports.TONE_BUNDLE_VERSION,
@@ -97,30 +124,32 @@ class TonesExport extends base_command_1.BaseCommand {
97
124
  visibility: (0, tone_input_1.isCustomTone)(tone) ? tone.visibility : 'private',
98
125
  })),
99
126
  };
100
- const serialized = `${JSON.stringify(bundle, null, 2)}\n`;
101
- if (!flags.out) {
102
- // stdout IS the bundle here — `skrr tones export > tones.json` and
103
- // `… | jq` both work without a flag, so --json is a no-op on this path.
127
+ const serialized = (0, artifact_export_1.serializedArtifact)(bundle);
128
+ if (selection.output === '-') {
104
129
  this.log(serialized.trimEnd());
105
130
  return;
106
131
  }
132
+ let receipt;
107
133
  try {
108
- await (0, promises_1.writeFile)(flags.out, serialized, 'utf8');
134
+ receipt = await (0, artifact_export_1.writeExportArtifact)({
135
+ command: 'tones export',
136
+ artifactSchemaVersion: exports.TONE_BUNDLE_VERSION,
137
+ format: 'json',
138
+ output: selection.output,
139
+ content: serialized,
140
+ itemCount: selected.length,
141
+ });
109
142
  }
110
143
  catch (err) {
111
144
  this.failWithCliError({
112
- message: `Could not write ${flags.out}: ${err.message}`,
145
+ message: `Could not write ${selection.output}: ${err.message}`,
113
146
  code: 'TONE_BUNDLE_WRITE_FAILED',
114
147
  exit: 1,
115
- retryable: true,
116
- details: { path: flags.out },
148
+ retryable: false,
149
+ details: { path: selection.output },
117
150
  });
118
151
  }
119
- if (flags.json) {
120
- this.log(serialized.trimEnd());
121
- return;
122
- }
123
- this.log(`Wrote ${bundle.tones.length} tone(s) to ${flags.out}.`);
152
+ this.log(selection.json ? JSON.stringify(receipt, null, 2) : (0, artifact_export_1.humanExportReceipt)(receipt));
124
153
  }
125
154
  }
126
155
  exports.default = TonesExport;
@@ -0,0 +1,84 @@
1
+ export declare const EXPORT_RECEIPT_SCHEMA_VERSION = 1;
2
+ export type ExportSelection = {
3
+ output?: string;
4
+ summary: boolean;
5
+ json: boolean;
6
+ };
7
+ export type ExportReceipt = {
8
+ object: 'skrr.export.receipt';
9
+ schemaVersion: typeof EXPORT_RECEIPT_SCHEMA_VERSION;
10
+ command: string;
11
+ artifactSchemaVersion: string | number;
12
+ format: string;
13
+ output: string;
14
+ contentDigest: string;
15
+ bytes: number;
16
+ lines: number;
17
+ itemCount: number;
18
+ truncated: boolean;
19
+ nextCursor: string | null;
20
+ };
21
+ export declare class ExportContractError extends Error {
22
+ readonly code: string;
23
+ constructor(message: string, code: string);
24
+ }
25
+ export declare function standardExportFlags(): {
26
+ summary: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
27
+ output: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
28
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
29
+ };
30
+ export declare function paginatedExportFlags(options?: {
31
+ defaultMaxItems?: number;
32
+ }): {
33
+ 'max-items': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
+ cursor: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
+ summary: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
36
+ output: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
37
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
38
+ };
39
+ export declare function resolveExportSelection(flags: {
40
+ output?: string;
41
+ out?: string;
42
+ summary?: boolean;
43
+ json?: boolean;
44
+ }): ExportSelection;
45
+ export declare function serializedArtifact(value: unknown): string;
46
+ export declare function buildExportReceipt(input: {
47
+ command: string;
48
+ artifactSchemaVersion: string | number;
49
+ format: string;
50
+ output: string;
51
+ content: string;
52
+ itemCount: number;
53
+ truncated?: boolean;
54
+ nextCursor?: string | null;
55
+ }): ExportReceipt;
56
+ export declare function writeExportArtifact(input: {
57
+ command: string;
58
+ artifactSchemaVersion: string | number;
59
+ format: string;
60
+ output: string;
61
+ content: string;
62
+ itemCount: number;
63
+ truncated?: boolean;
64
+ nextCursor?: string | null;
65
+ }): Promise<ExportReceipt>;
66
+ export declare function humanExportReceipt(receipt: ExportReceipt): string;
67
+ export declare function contentFreeSummary(input: {
68
+ command: string;
69
+ artifactSchemaVersion: string | number;
70
+ itemCount: number;
71
+ truncated?: boolean;
72
+ nextCursor?: string | null;
73
+ counts?: Record<string, number>;
74
+ }): {
75
+ object: "skrr.export.summary";
76
+ schemaVersion: number;
77
+ command: string;
78
+ artifactSchemaVersion: string | number;
79
+ itemCount: number;
80
+ counts: Record<string, number>;
81
+ truncated: boolean;
82
+ nextCursor: string | null;
83
+ };
84
+ export declare function humanExportSummary(summary: ReturnType<typeof contentFreeSummary>): string;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExportContractError = exports.EXPORT_RECEIPT_SCHEMA_VERSION = void 0;
4
+ exports.standardExportFlags = standardExportFlags;
5
+ exports.paginatedExportFlags = paginatedExportFlags;
6
+ exports.resolveExportSelection = resolveExportSelection;
7
+ exports.serializedArtifact = serializedArtifact;
8
+ exports.buildExportReceipt = buildExportReceipt;
9
+ exports.writeExportArtifact = writeExportArtifact;
10
+ exports.humanExportReceipt = humanExportReceipt;
11
+ exports.contentFreeSummary = contentFreeSummary;
12
+ exports.humanExportSummary = humanExportSummary;
13
+ const node_crypto_1 = require("node:crypto");
14
+ const promises_1 = require("node:fs/promises");
15
+ const core_1 = require("@oclif/core");
16
+ exports.EXPORT_RECEIPT_SCHEMA_VERSION = 1;
17
+ class ExportContractError extends Error {
18
+ code;
19
+ constructor(message, code) {
20
+ super(message);
21
+ this.code = code;
22
+ }
23
+ }
24
+ exports.ExportContractError = ExportContractError;
25
+ function standardExportFlags() {
26
+ return {
27
+ summary: core_1.Flags.boolean({
28
+ description: 'Print a content-free summary instead of the full artifact',
29
+ }),
30
+ output: core_1.Flags.string({
31
+ char: 'o',
32
+ description: 'Write the full artifact to this path; use "-" for deliberate stdout',
33
+ }),
34
+ json: core_1.Flags.boolean({
35
+ description: 'Print summary or file receipt as machine-readable JSON',
36
+ }),
37
+ };
38
+ }
39
+ function paginatedExportFlags(options = {}) {
40
+ return {
41
+ ...standardExportFlags(),
42
+ 'max-items': core_1.Flags.integer({
43
+ description: `Maximum records in this export chunk (default ${options.defaultMaxItems ?? 1000})`,
44
+ min: 1,
45
+ max: 100000,
46
+ }),
47
+ cursor: core_1.Flags.string({
48
+ description: 'Opaque cursor returned by a previous export chunk',
49
+ }),
50
+ };
51
+ }
52
+ function resolveExportSelection(flags) {
53
+ if (flags.output && flags.out) {
54
+ throw new ExportContractError('--output and the deprecated --out alias cannot be used together.', 'EXPORT_OUTPUT_FLAG_CONFLICT');
55
+ }
56
+ const output = flags.output ?? flags.out;
57
+ const summary = Boolean(flags.summary);
58
+ if (!output && !summary) {
59
+ throw new ExportContractError('Choose --summary, --output <path>, or --output - for deliberate stdout.', 'EXPORT_DESTINATION_REQUIRED');
60
+ }
61
+ if (output && summary) {
62
+ throw new ExportContractError('--summary and --output are mutually exclusive.', 'EXPORT_MODE_CONFLICT');
63
+ }
64
+ return { output, summary, json: Boolean(flags.json) };
65
+ }
66
+ function serializedArtifact(value) {
67
+ return `${JSON.stringify(value, null, 2)}\n`;
68
+ }
69
+ function buildExportReceipt(input) {
70
+ return {
71
+ object: 'skrr.export.receipt',
72
+ schemaVersion: exports.EXPORT_RECEIPT_SCHEMA_VERSION,
73
+ command: input.command,
74
+ artifactSchemaVersion: input.artifactSchemaVersion,
75
+ format: input.format,
76
+ output: input.output,
77
+ contentDigest: (0, node_crypto_1.createHash)('sha256').update(input.content).digest('hex'),
78
+ bytes: Buffer.byteLength(input.content),
79
+ lines: input.content.length ? input.content.split('\n').length - 1 : 0,
80
+ itemCount: input.itemCount,
81
+ truncated: Boolean(input.truncated),
82
+ nextCursor: input.nextCursor ?? null,
83
+ };
84
+ }
85
+ async function writeExportArtifact(input) {
86
+ const receipt = buildExportReceipt(input);
87
+ if (input.output === '-')
88
+ return receipt;
89
+ await (0, promises_1.writeFile)(input.output, input.content, {
90
+ encoding: 'utf8',
91
+ flag: 'wx',
92
+ mode: 0o600,
93
+ });
94
+ return receipt;
95
+ }
96
+ function humanExportReceipt(receipt) {
97
+ const continuation = receipt.truncated && receipt.nextCursor ? ` Next cursor: ${receipt.nextCursor}.` : '';
98
+ return (`Wrote ${receipt.itemCount} item(s) to ${receipt.output} ` +
99
+ `(${receipt.bytes} bytes, sha256:${receipt.contentDigest}, schema ` +
100
+ `${receipt.artifactSchemaVersion}).${continuation}`);
101
+ }
102
+ function contentFreeSummary(input) {
103
+ return {
104
+ object: 'skrr.export.summary',
105
+ schemaVersion: exports.EXPORT_RECEIPT_SCHEMA_VERSION,
106
+ command: input.command,
107
+ artifactSchemaVersion: input.artifactSchemaVersion,
108
+ itemCount: input.itemCount,
109
+ counts: input.counts ?? {},
110
+ truncated: Boolean(input.truncated),
111
+ nextCursor: input.nextCursor ?? null,
112
+ };
113
+ }
114
+ function humanExportSummary(summary) {
115
+ const counts = Object.entries(summary.counts)
116
+ .map(([key, value]) => `${key}=${value}`)
117
+ .join(', ');
118
+ const continuation = summary.truncated && summary.nextCursor ? `; next cursor=${summary.nextCursor}` : '';
119
+ return (`${summary.command}: ${summary.itemCount} item(s)` +
120
+ (counts ? ` (${counts})` : '') +
121
+ `; schema ${summary.artifactSchemaVersion}${continuation}`);
122
+ }
@@ -1,4 +1,4 @@
1
- import type { TSkyCodeUsageSnapshot } from '@skrr-ai/data-provider';
1
+ import type { TSubscription, TSkyCodeUsageEvents, TSkyCodeUsageSnapshot } from '@skrr-ai/data-provider';
2
2
  /**
3
3
  * balance.ts — request client for `/api/balance`.
4
4
  *
@@ -79,6 +79,18 @@ export interface BalanceHistory {
79
79
  /** The account's current credits, limit and period usage. */
80
80
  export declare function getBalance(): Promise<BalanceStatus>;
81
81
  export declare function getSkyCodeUsage(workspaceId?: string): Promise<TSkyCodeUsageSnapshot>;
82
+ /** The personal Sky Code subscription, distinct from published agent-service subscriptions. */
83
+ export declare function getSkyCodeSubscription(): Promise<TSubscription>;
84
+ /** Explicit personal consent to use stored balance after included Sky Code allowance. */
85
+ export declare function setSkyCodeOverageConsent(enabled: boolean): Promise<{
86
+ enabled: boolean;
87
+ }>;
88
+ export declare function getSkyCodeUsageEvents(workspaceId: string | undefined, params?: {
89
+ from?: string;
90
+ to?: string;
91
+ cursor?: string;
92
+ limit?: number;
93
+ }): Promise<TSkyCodeUsageEvents>;
82
94
  /** Recent token transactions, newest first. Cursor is an ISO createdAt. */
83
95
  export declare function getBalanceHistory(limit: number, cursor?: string): Promise<BalanceHistory>;
84
96
  /**
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CREDITS_PER_USD = void 0;
4
4
  exports.getBalance = getBalance;
5
5
  exports.getSkyCodeUsage = getSkyCodeUsage;
6
+ exports.getSkyCodeSubscription = getSkyCodeSubscription;
7
+ exports.setSkyCodeOverageConsent = setSkyCodeOverageConsent;
8
+ exports.getSkyCodeUsageEvents = getSkyCodeUsageEvents;
6
9
  exports.getBalanceHistory = getBalanceHistory;
7
10
  exports.formatCredits = formatCredits;
8
11
  exports.getWorkspaceBalance = getWorkspaceBalance;
@@ -27,6 +30,33 @@ async function getSkyCodeUsage(workspaceId) {
27
30
  : '/api/balance/usage/sky-code';
28
31
  return (0, api_fetch_1.apiFetch)(path);
29
32
  }
33
+ /** The personal Sky Code subscription, distinct from published agent-service subscriptions. */
34
+ async function getSkyCodeSubscription() {
35
+ return (0, api_fetch_1.apiFetch)('/api/stripe/subscription');
36
+ }
37
+ /** Explicit personal consent to use stored balance after included Sky Code allowance. */
38
+ async function setSkyCodeOverageConsent(enabled) {
39
+ return (0, api_fetch_1.apiFetch)('/api/balance/usage/sky-code/overage', {
40
+ method: 'PUT',
41
+ body: { enabled },
42
+ });
43
+ }
44
+ async function getSkyCodeUsageEvents(workspaceId, params = {}) {
45
+ const query = new URLSearchParams();
46
+ if (params.from)
47
+ query.set('from', params.from);
48
+ if (params.to)
49
+ query.set('to', params.to);
50
+ if (params.cursor)
51
+ query.set('cursor', params.cursor);
52
+ if (params.limit !== undefined)
53
+ query.set('limit', String(params.limit));
54
+ const base = workspaceId
55
+ ? `/api/balance/usage/sky-code/workspace/${encodeURIComponent(workspaceId)}/events`
56
+ : '/api/balance/usage/sky-code/events';
57
+ const suffix = query.toString();
58
+ return (0, api_fetch_1.apiFetch)(suffix ? `${base}?${suffix}` : base);
59
+ }
30
60
  /** Recent token transactions, newest first. Cursor is an ISO createdAt. */
31
61
  async function getBalanceHistory(limit, cursor) {
32
62
  const query = new URLSearchParams({ limit: String(limit) });
@@ -16,6 +16,8 @@
16
16
  * and the `api.` subdomain are distinct origins in skrr's topology).
17
17
  */
18
18
  export type WebUrlRef = {
19
+ kind: 'billing';
20
+ } | {
19
21
  kind: 'task';
20
22
  taskId: string;
21
23
  spaceId?: string | null;
@@ -51,6 +51,8 @@ const publicEndpoints_generated_1 = require("./publicEndpoints.generated");
51
51
  */
52
52
  function webPath(ref) {
53
53
  switch (ref.kind) {
54
+ case 'billing':
55
+ return '/d/settings/balance';
54
56
  case 'task': {
55
57
  const taskId = encodeId(ref.taskId);
56
58
  if (!taskId)
@@ -19,7 +19,9 @@
19
19
  *
20
20
  * 1. set `displayName` and `provider`;
21
21
  * 2. move the old `provider` to the front of `aliases`;
22
- * 3. leave `wireProvider` on the spelling installed daemons still speak;
22
+ * 3. set `wireProvider` to the old spelling as a literal — installed daemons
23
+ * still speak it — and derive it from `provider` again only in the wire
24
+ * phase, once every peer accepts the new one;
23
25
  * 4. regenerate `first-party-harness.json`
24
26
  * (`node --import tsx scripts/emit-first-party-harness-json.mjs`);
25
27
  * 5. ship the data migration, which is generic over `aliases`.
@@ -33,9 +35,11 @@
33
35
  * canonical at the edge. Nothing new is ever written with one.
34
36
  * `wireProvider` What the server and a daemon say to EACH OTHER — the
35
37
  * capability a daemon advertises, `daemon:<p>:request|response`,
36
- * the `provider` of a session RPC. It moves LAST, because a
37
- * daemon on a user's machine may be any version and the server
38
- * cannot tell which one sent a request.
38
+ * the `provider` of a session RPC. It moves after the flip and
39
+ * BEFORE contract, because a daemon on a user's machine may be
40
+ * any version and the server cannot tell which one sent a
41
+ * request: only a released daemon that already advertises the
42
+ * canonical capability lets the old one age out of the fleet.
39
43
  *
40
44
  * The old code treated those three as one string, which is why no single
41
45
  * change could rename it safely. See
@@ -63,18 +67,19 @@ export interface FirstPartyHarnessIdentity {
63
67
  /**
64
68
  * THE identity — the only literal product name in the codebase.
65
69
  *
66
- * Phase: FLIPPED. `skrr-code` is canonical — stored, returned and rendered.
67
- * `sky-code` is still accepted on every ingress, and it is still the WIRE
68
- * spelling: daemons on users' machines may predate the rename, so the server
69
- * and a daemon keep exchanging `sky-code` until contract (OSK-8663) empties
70
- * `aliases` and moves `wireProvider`.
70
+ * Phase: WIRE MOVED. `skrr-code` is canonical — stored, returned, rendered — and
71
+ * it is now also what the server and a daemon say to each other. `sky-code` is
72
+ * still accepted on every ingress, so a daemon released before this change
73
+ * (which advertises and answers on `sky-code`) keeps working against a server
74
+ * that speaks `skrr-code`, and the reverse. Contract (OSK-8663) empties
75
+ * `aliases` once no connected daemon still advertises the old capability.
71
76
  */
72
77
  declare const IDENTITY: {
73
78
  readonly displayName: "skrr Code";
74
79
  readonly command: "skrr code";
75
80
  readonly provider: "skrr-code";
76
81
  readonly aliases: readonly ["sky-code"];
77
- readonly wireProvider: "sky-code";
82
+ readonly wireProvider: "skrr-code";
78
83
  };
79
84
  /** The canonical slug, as a literal type. */
80
85
  export type FirstPartyHarnessCanonicalProvider = typeof IDENTITY.provider;