@purveyors/cli 0.33.2 → 0.35.0

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 (87) hide show
  1. package/README.md +176 -13
  2. package/dist/commands/auth.d.ts.map +1 -1
  3. package/dist/commands/auth.js +21 -0
  4. package/dist/commands/auth.js.map +1 -1
  5. package/dist/commands/catalog.d.ts.map +1 -1
  6. package/dist/commands/catalog.js +40 -13
  7. package/dist/commands/catalog.js.map +1 -1
  8. package/dist/commands/context.d.ts.map +1 -1
  9. package/dist/commands/context.js +2 -0
  10. package/dist/commands/context.js.map +1 -1
  11. package/dist/commands/inventory.d.ts.map +1 -1
  12. package/dist/commands/inventory.js +34 -11
  13. package/dist/commands/inventory.js.map +1 -1
  14. package/dist/commands/manifest.d.ts.map +1 -1
  15. package/dist/commands/manifest.js +2 -0
  16. package/dist/commands/manifest.js.map +1 -1
  17. package/dist/commands/market.d.ts.map +1 -1
  18. package/dist/commands/market.js +40 -2
  19. package/dist/commands/market.js.map +1 -1
  20. package/dist/commands/price-index.d.ts +4 -2
  21. package/dist/commands/price-index.d.ts.map +1 -1
  22. package/dist/commands/price-index.js +148 -5
  23. package/dist/commands/price-index.js.map +1 -1
  24. package/dist/commands/reference-profile.d.ts +4 -0
  25. package/dist/commands/reference-profile.d.ts.map +1 -0
  26. package/dist/commands/reference-profile.js +200 -0
  27. package/dist/commands/reference-profile.js.map +1 -0
  28. package/dist/commands/roast.d.ts.map +1 -1
  29. package/dist/commands/roast.js +42 -1
  30. package/dist/commands/roast.js.map +1 -1
  31. package/dist/lib/catalog.d.ts +26 -18
  32. package/dist/lib/catalog.d.ts.map +1 -1
  33. package/dist/lib/catalog.js +38 -46
  34. package/dist/lib/catalog.js.map +1 -1
  35. package/dist/lib/cherry.d.ts +2 -16
  36. package/dist/lib/cherry.d.ts.map +1 -1
  37. package/dist/lib/cherry.js +19 -5
  38. package/dist/lib/cherry.js.map +1 -1
  39. package/dist/lib/interactive/watch.d.ts +2 -3
  40. package/dist/lib/interactive/watch.d.ts.map +1 -1
  41. package/dist/lib/interactive/watch.js +4 -26
  42. package/dist/lib/interactive/watch.js.map +1 -1
  43. package/dist/lib/inventory.d.ts +8 -1
  44. package/dist/lib/inventory.d.ts.map +1 -1
  45. package/dist/lib/inventory.js +19 -5
  46. package/dist/lib/inventory.js.map +1 -1
  47. package/dist/lib/manifest.d.ts +13 -0
  48. package/dist/lib/manifest.d.ts.map +1 -1
  49. package/dist/lib/manifest.js +498 -18
  50. package/dist/lib/manifest.js.map +1 -1
  51. package/dist/lib/market.d.ts.map +1 -1
  52. package/dist/lib/numeric-contracts.d.ts +8 -0
  53. package/dist/lib/numeric-contracts.d.ts.map +1 -1
  54. package/dist/lib/numeric-contracts.js +2 -0
  55. package/dist/lib/numeric-contracts.js.map +1 -1
  56. package/dist/lib/parchment.d.ts +10 -1
  57. package/dist/lib/parchment.d.ts.map +1 -1
  58. package/dist/lib/parchment.js +11 -1
  59. package/dist/lib/parchment.js.map +1 -1
  60. package/dist/lib/reference-profiles.d.ts +182 -0
  61. package/dist/lib/reference-profiles.d.ts.map +1 -0
  62. package/dist/lib/reference-profiles.js +236 -0
  63. package/dist/lib/reference-profiles.js.map +1 -0
  64. package/dist/lib/roast.d.ts +36 -2
  65. package/dist/lib/roast.d.ts.map +1 -1
  66. package/dist/lib/roast.js +23 -0
  67. package/dist/lib/roast.js.map +1 -1
  68. package/dist/program.d.ts.map +1 -1
  69. package/dist/program.js +23 -0
  70. package/dist/program.js.map +1 -1
  71. package/package.json +2 -2
  72. package/dist/lib/artisan/parser.d.ts +0 -19
  73. package/dist/lib/artisan/parser.d.ts.map +0 -1
  74. package/dist/lib/artisan/parser.js +0 -376
  75. package/dist/lib/artisan/parser.js.map +0 -1
  76. package/dist/lib/artisan/temperature.d.ts +0 -52
  77. package/dist/lib/artisan/temperature.d.ts.map +0 -1
  78. package/dist/lib/artisan/temperature.js +0 -101
  79. package/dist/lib/artisan/temperature.js.map +0 -1
  80. package/dist/lib/artisan/types.d.ts +0 -195
  81. package/dist/lib/artisan/types.d.ts.map +0 -1
  82. package/dist/lib/artisan/types.js +0 -35
  83. package/dist/lib/artisan/types.js.map +0 -1
  84. package/dist/lib/artisan/validator.d.ts +0 -14
  85. package/dist/lib/artisan/validator.d.ts.map +0 -1
  86. package/dist/lib/artisan/validator.js +0 -228
  87. package/dist/lib/artisan/validator.js.map +0 -1
@@ -1,9 +1,11 @@
1
1
  import { Command } from 'commander';
2
2
  import { outputData } from '../lib/output.js';
3
3
  import { withErrorHandling, PrvrsError } from '../lib/errors.js';
4
- import { createParchmentClient, unwrapParchment } from '../lib/parchment.js';
4
+ import { createOptionalParchmentClient, createParchmentClient, unwrapParchment, } from '../lib/parchment.js';
5
5
  import { CLI_NUMERIC_BOUNDS } from '../lib/numeric-contracts.js';
6
6
  import { parseStrictPositiveCount } from '../lib/strict-number.js';
7
+ const COMPARISONS_WHOLESALE = ['true', 'false', 'all'];
8
+ const ORDERS = ['asc', 'desc'];
7
9
  function parsePositiveInt(rawValue, flag, max) {
8
10
  const parsed = parseStrictPositiveCount(rawValue, max);
9
11
  if (!Number.isFinite(parsed)) {
@@ -20,13 +22,148 @@ function parseWholesale(rawValue) {
20
22
  return 'false';
21
23
  throw new PrvrsError('INVALID_ARGUMENT', `Invalid --wholesale: "${rawValue}". Must be "true" or "false".`);
22
24
  }
25
+ function parseEnum(rawValue, flag, values) {
26
+ const value = rawValue.trim().toLowerCase();
27
+ if (values.includes(value))
28
+ return value;
29
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid ${flag}: "${rawValue}". Must be one of: ${values.join(', ')}.`);
30
+ }
31
+ function parseIsoDate(rawValue, flag) {
32
+ const value = rawValue.trim();
33
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
34
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid ${flag}: "${rawValue}". Must be an ISO date (YYYY-MM-DD).`);
35
+ }
36
+ return value;
37
+ }
38
+ function toFlag(attributeName) {
39
+ return `--${attributeName.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
40
+ }
41
+ /**
42
+ * Resolve a `price-index <subcommand>` invocation's own options.
43
+ *
44
+ * The parent `price-index` command keeps its snapshot filters (`--origin`,
45
+ * `--from`, `--wholesale`, `--page`, `--limit`, ...), and Commander assigns a
46
+ * flag to the parent whenever both levels declare it. Subcommands therefore
47
+ * read their declared options from both levels and reject parent-only filters
48
+ * instead of silently ignoring them.
49
+ */
50
+ function resolveSubcommandOptions(cmd) {
51
+ const own = new Set(cmd.options.map((option) => option.attributeName()));
52
+ const parentOptions = cmd.parent?.opts() ?? {};
53
+ for (const key of Object.keys(parentOptions)) {
54
+ if (!own.has(key)) {
55
+ throw new PrvrsError('INVALID_ARGUMENT', `${toFlag(key)} is not supported by \`purvey price-index ${cmd.name()}\`.`);
56
+ }
57
+ }
58
+ return { ...parentOptions, ...cmd.opts() };
59
+ }
60
+ function requireOption(opts, key, cmd) {
61
+ const value = opts[key];
62
+ if (typeof value !== 'string' || value.trim() === '') {
63
+ throw new PrvrsError('INVALID_ARGUMENT', `\`purvey price-index ${cmd.name()}\` requires ${toFlag(key)}.`);
64
+ }
65
+ return value;
66
+ }
67
+ function buildComparisonsCommand() {
68
+ return new Command('comparisons')
69
+ .description('Available exact 30-day matched price comparisons with significance')
70
+ .option('--wholesale <true|false|all>', 'Market scope (API default when omitted)')
71
+ .addHelpText('after', `
72
+ Examples:
73
+ purvey price-index comparisons --pretty
74
+ purvey price-index comparisons --wholesale all --json
75
+
76
+ Notes:
77
+ The API discovers origins with an exact 30-day matched comparison ending on the
78
+ latest completed observation day; an empty list means none qualify.
79
+ Each comparison carries its significance read verbatim. classification is null
80
+ until eight baseline windows exist, which means "not enough history", not "quiet".
81
+ Requires Parchment Intelligence access, enforced by the API.`)
82
+ .action(withErrorHandling(async (_opts, cmd) => {
83
+ const globalOpts = cmd.optsWithGlobals();
84
+ const opts = resolveSubcommandOptions(cmd);
85
+ const query = {};
86
+ if (opts.wholesale !== undefined)
87
+ query.wholesale = parseEnum(opts.wholesale, '--wholesale', COMPARISONS_WHOLESALE);
88
+ const client = await createParchmentClient('member');
89
+ const data = unwrapParchment(await client.priceIndex.comparisons(query), 'price-index comparisons');
90
+ outputData(data, globalOpts);
91
+ }));
92
+ }
93
+ function buildComparisonCommand() {
94
+ return new Command('comparison')
95
+ .description('Matched-listing price comparison for one origin between two exact dates')
96
+ .option('--origin <origin>', 'Origin to compare (required)')
97
+ .option('--from <date>', 'Starting UTC date, YYYY-MM-DD (required)')
98
+ .option('--to <date>', 'Ending UTC date, YYYY-MM-DD, within 365 days of --from (required)')
99
+ .option('--wholesale <true|false>', 'Market scope (API default when omitted)')
100
+ .addHelpText('after', `
101
+ Examples:
102
+ purvey price-index comparison --origin "Ethiopia" --from 2026-08-31 --to 2026-09-30 --pretty
103
+ purvey price-index comparison --origin "Colombia" --from 2026-06-01 --to 2026-09-01 --wholesale true --json
104
+
105
+ Notes:
106
+ Supplier-balanced fresh matched prices only; status "insufficient_fresh_coverage"
107
+ returns a null changePercent, never zero.
108
+ Requires Parchment Intelligence access, enforced by the API.`)
109
+ .action(withErrorHandling(async (_opts, cmd) => {
110
+ const globalOpts = cmd.optsWithGlobals();
111
+ const opts = resolveSubcommandOptions(cmd);
112
+ const query = {
113
+ origin: requireOption(opts, 'origin', cmd),
114
+ from: parseIsoDate(requireOption(opts, 'from', cmd), '--from'),
115
+ to: parseIsoDate(requireOption(opts, 'to', cmd), '--to'),
116
+ };
117
+ if (opts.wholesale !== undefined)
118
+ query.wholesale = parseWholesale(opts.wholesale);
119
+ const client = await createParchmentClient('member');
120
+ const data = unwrapParchment(await client.priceIndex.comparison(query), 'price-index comparison');
121
+ outputData(data, globalOpts);
122
+ }));
123
+ }
124
+ function buildHistoryCommand() {
125
+ const { priceIndexHistoryLimit, priceIndexHistoryWindowDays } = CLI_NUMERIC_BOUNDS;
126
+ return new Command('history')
127
+ .description('Tier-one price-index chart history (public up to 90 days)')
128
+ .option('--window-days <n>', `Trailing window in days (${priceIndexHistoryWindowDays.minimum}-${priceIndexHistoryWindowDays.maximum})`)
129
+ .option('--page <n>', '1-based page number')
130
+ .option('--limit <n>', `Results per page (${priceIndexHistoryLimit.minimum}-${priceIndexHistoryLimit.maximum})`)
131
+ .option('--order <asc|desc>', 'Snapshot ordering (API default asc)')
132
+ .addHelpText('after', `
133
+ Examples:
134
+ purvey price-index history --pretty
135
+ purvey price-index history --window-days 30 --order desc --json
136
+ purvey price-index history --window-days 365 --limit 500 --json
137
+
138
+ Notes:
139
+ Windows up to 90 days are public and run without credentials; 91-365 days
140
+ require Parchment Intelligence access, enforced by the API.`)
141
+ .action(withErrorHandling(async (_opts, cmd) => {
142
+ const globalOpts = cmd.optsWithGlobals();
143
+ const opts = resolveSubcommandOptions(cmd);
144
+ const query = {};
145
+ if (opts.windowDays !== undefined)
146
+ query.windowDays = parsePositiveInt(opts.windowDays, '--window-days', priceIndexHistoryWindowDays.maximum);
147
+ if (opts.page !== undefined)
148
+ query.page = parsePositiveInt(opts.page, '--page');
149
+ if (opts.limit !== undefined)
150
+ query.limit = parsePositiveInt(opts.limit, '--limit', priceIndexHistoryLimit.maximum);
151
+ if (opts.order !== undefined)
152
+ query.order = parseEnum(opts.order, '--order', ORDERS);
153
+ const client = await createOptionalParchmentClient();
154
+ const data = unwrapParchment(await client.priceIndex.history(query), 'price-index history');
155
+ outputData(data, globalOpts);
156
+ }));
157
+ }
23
158
  /**
24
- * `purvey price-index` — Parchment Price Index aggregate snapshots.
25
- * Requires an authenticated session or API key with price-index entitlement.
159
+ * `purvey price-index` — Parchment Price Index reads.
160
+ * The bare command returns aggregate snapshots (price-index entitlement);
161
+ * subcommands expose matched comparisons (Parchment Intelligence) and chart
162
+ * history (public up to 90 days). Entitlements are enforced by the API.
26
163
  */
27
164
  export function buildPriceIndexCommand() {
28
165
  const priceIndex = new Command('price-index')
29
- .description('Fetch Parchment Price Index aggregate snapshots (requires price-index access)')
166
+ .description('Parchment Price Index snapshots, matched comparisons, and chart history')
30
167
  .option('--origin <origin>', 'Filter by origin')
31
168
  .option('--process <method>', 'Filter by process method')
32
169
  .option('--grade <grade>', 'Filter by grade')
@@ -41,9 +178,12 @@ Examples:
41
178
  purvey price-index --origin "Ethiopia" --json
42
179
  purvey price-index --from 2026-01-01 --to 2026-06-30 --pretty
43
180
  purvey price-index --wholesale true --limit 25 --json
181
+ purvey price-index comparisons --pretty
182
+ purvey price-index history --window-days 30 --json
44
183
 
45
184
  Notes:
46
- Requires a Purveyors session or API key with price-index (PPI) access.
185
+ The bare command returns aggregate snapshots and requires a Purveyors session or
186
+ API key with price-index (PPI) access.
47
187
  Set PARCHMENT_API_KEY or PURVEYORS_API_KEY to use an API key instead of the logged-in session.`)
48
188
  .action(withErrorHandling(async (opts, cmd) => {
49
189
  const globalOpts = cmd.optsWithGlobals();
@@ -69,6 +209,9 @@ Notes:
69
209
  const data = unwrapParchment(result, 'price-index');
70
210
  outputData(data, globalOpts);
71
211
  }));
212
+ priceIndex.addCommand(buildComparisonsCommand());
213
+ priceIndex.addCommand(buildComparisonCommand());
214
+ priceIndex.addCommand(buildHistoryCommand());
72
215
  return priceIndex;
73
216
  }
74
217
  //# sourceMappingURL=price-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"price-index.js","sourceRoot":"","sources":["../../src/commands/price-index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"price-index.js","sourceRoot":"","sources":["../../src/commands/price-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ /** `purvey reference-profile` — owner-scoped Studio plans through Parchment's SDK. */
3
+ export declare function buildReferenceProfileCommand(): Command;
4
+ //# sourceMappingURL=reference-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-profile.d.ts","sourceRoot":"","sources":["../../src/commands/reference-profile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC,sFAAsF;AACtF,wBAAgB,4BAA4B,IAAI,OAAO,CAsTtD"}
@@ -0,0 +1,200 @@
1
+ import { basename } from 'node:path';
2
+ import { readFile, stat } from 'node:fs/promises';
3
+ import { Command } from 'commander';
4
+ import { PrvrsError, withErrorHandling } from '../lib/errors.js';
5
+ import { compareProfiles, exportGeneratedReferenceProfile, getReferenceProfile, getReferenceProfileChart, importReferenceProfile, listReferenceProfiles, parseProfileComparisonSelector, parseReferenceProfileImportRequest, previewReferenceProfile, PROFILE_COMPARISON_TARGET_POINTS, profileComparisonUnits, readReferenceProfileGenerationRequest, REFERENCE_PROFILE_SOURCE_MAX_BYTES, saveGeneratedReferenceProfile, writeGeneratedReferenceFile, } from '../lib/reference-profiles.js';
6
+ import { normalizePathInput } from '../lib/path-input.js';
7
+ import { outputData } from '../lib/output.js';
8
+ import { parseStrictPositiveCount } from '../lib/strict-number.js';
9
+ function idempotencyKeyOption(command, description) {
10
+ return command.option('--idempotency-key <key>', `${description}; a new UUID is generated when omitted`);
11
+ }
12
+ /** `purvey reference-profile` — owner-scoped Studio plans through Parchment's SDK. */
13
+ export function buildReferenceProfileCommand() {
14
+ const referenceProfile = new Command('reference-profile').description('Compare, preview, save, and export Studio Artisan reference plans through Parchment');
15
+ referenceProfile
16
+ .command('list')
17
+ .description('List your Studio reference profiles')
18
+ .option('--include-archived', 'Include archived reference profiles')
19
+ .addHelpText('after', `
20
+ Examples:
21
+ purvey reference-profile list --pretty
22
+ purvey reference-profile list --include-archived --json
23
+
24
+ Notes:
25
+ Requires a member credential and Studio access; entitlement is enforced by Parchment.`)
26
+ .action(withErrorHandling(async (opts, cmd) => {
27
+ const globalOpts = cmd.optsWithGlobals();
28
+ const data = await listReferenceProfiles(Boolean(opts.includeArchived));
29
+ outputData(data, globalOpts);
30
+ }));
31
+ referenceProfile
32
+ .command('get <profile-id>')
33
+ .description('Get one reference profile and its current revision')
34
+ .addHelpText('after', `
35
+ Example:
36
+ purvey reference-profile get 5ea1af6f-234c-43a9-9bf8-5678dd24f854 --pretty
37
+
38
+ Use data.currentRevision.id as the revision id for chart, preview, and save.`)
39
+ .action(withErrorHandling(async (profileId, _opts, cmd) => {
40
+ const globalOpts = cmd.optsWithGlobals();
41
+ const data = await getReferenceProfile(profileId);
42
+ outputData(data, globalOpts);
43
+ }));
44
+ referenceProfile
45
+ .command('chart <profile-id> <revision-id>')
46
+ .description('Get the typed Artisan chart for a reference revision')
47
+ .addHelpText('after', `
48
+ Example:
49
+ purvey reference-profile chart 5ea1af6f-234c-43a9-9bf8-5678dd24f854 8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 --pretty`)
50
+ .action(withErrorHandling(async (profileId, revisionId, _opts, cmd) => {
51
+ const globalOpts = cmd.optsWithGlobals();
52
+ const data = await getReferenceProfileChart(profileId, revisionId);
53
+ outputData(data, globalOpts);
54
+ }));
55
+ referenceProfile
56
+ .command('compare <left> <right>')
57
+ .description('Compare two immutable roast or reference revisions with measured deltas')
58
+ .option('--unit <F|C>', 'Temperature unit for the comparison series', 'F')
59
+ .option('--target-points <n>', `Samples per aligned series (${PROFILE_COMPARISON_TARGET_POINTS.minimum}-${PROFILE_COMPARISON_TARGET_POINTS.maximum})`, String(PROFILE_COMPARISON_TARGET_POINTS.default))
60
+ .addHelpText('after', `
61
+ Examples:
62
+ purvey reference-profile compare roast:123 profile:5ea1af6f-234c-43a9-9bf8-5678dd24f854 --pretty
63
+ purvey reference-profile compare roast:123 roast:124 --unit C --json
64
+ purvey reference-profile compare revision:8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 roast:123@2026-09-30T14:22:05.123456+00:00 --pretty
65
+
66
+ Selectors:
67
+ revision:<uuid> exact reference-profile revision
68
+ profile:<uuid> the reference profile's current revision
69
+ roast:<roast-id> the executed roast's current chart revision
70
+ roast:<roast-id>@<rev> an exact executed-roast chart revision (data.metadata.revision from 'purvey roast chart')
71
+
72
+ Notes:
73
+ Parchment aligns both sides at charge and returns measured deltas and milestones unchanged.
74
+ A reference profile is a plan or comparison reference, never executed roast history.
75
+ Requires a member credential and Studio access; entitlement is enforced by Parchment.`)
76
+ .action(withErrorHandling(async (left, right, opts, cmd) => {
77
+ const globalOpts = cmd.optsWithGlobals();
78
+ const unit = String(opts.unit).trim().toUpperCase();
79
+ if (!profileComparisonUnits.includes(unit)) {
80
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --unit: "${String(opts.unit)}". Must be one of: ${profileComparisonUnits.join(', ')}.`);
81
+ }
82
+ const targetPoints = parseStrictPositiveCount(String(opts.targetPoints));
83
+ if (!Number.isFinite(targetPoints) ||
84
+ targetPoints < PROFILE_COMPARISON_TARGET_POINTS.minimum ||
85
+ targetPoints > PROFILE_COMPARISON_TARGET_POINTS.maximum) {
86
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --target-points: "${String(opts.targetPoints)}". Must be an integer between ${PROFILE_COMPARISON_TARGET_POINTS.minimum} and ${PROFILE_COMPARISON_TARGET_POINTS.maximum}.`);
87
+ }
88
+ const data = await compareProfiles({
89
+ left: parseProfileComparisonSelector(left, 'left'),
90
+ right: parseProfileComparisonSelector(right, 'right'),
91
+ targetUnit: unit,
92
+ targetPoints,
93
+ });
94
+ outputData(data, globalOpts);
95
+ }));
96
+ const importCommand = referenceProfile
97
+ .command('import <file>')
98
+ .description('Upload an Artisan reference file as a private Studio profile')
99
+ .option('--title <text>', 'Title for the reference profile')
100
+ .option('--notes <text>', 'Notes about the reference profile')
101
+ .addHelpText('after', `
102
+ Examples:
103
+ purvey reference-profile import ~/artisan/ethiopia.alog --title "Ethiopia baseline" --pretty
104
+ purvey reference-profile import ~/artisan/ethiopia.alog --idempotency-key 3f6c2a1b-8e4d-4b7a-9c05-7e1f2d3a4b5c --json
105
+
106
+ Notes:
107
+ Parchment parses and retains the private source (up to 10,000,000 bytes); this command does not create roast history.
108
+ Reuse an explicit idempotency key to safely retry the same upload.`);
109
+ idempotencyKeyOption(importCommand, 'Stable key for safe retries');
110
+ importCommand.action(withErrorHandling(async (inputPath, opts, cmd) => {
111
+ const globalOpts = cmd.optsWithGlobals();
112
+ const filePath = normalizePathInput(inputPath);
113
+ if (!filePath) {
114
+ throw new PrvrsError('INVALID_ARGUMENT', 'The Artisan reference file path cannot be blank.');
115
+ }
116
+ const fileInfo = await stat(filePath);
117
+ if (!fileInfo.isFile()) {
118
+ throw new PrvrsError('INVALID_ARGUMENT', 'The Artisan reference path must be a file.');
119
+ }
120
+ if (fileInfo.size === 0 || fileInfo.size > REFERENCE_PROFILE_SOURCE_MAX_BYTES) {
121
+ throw new PrvrsError('INVALID_ARGUMENT', `The Artisan reference must be between 1 and ${REFERENCE_PROFILE_SOURCE_MAX_BYTES} bytes.`);
122
+ }
123
+ const fileContent = await readFile(filePath, 'utf8');
124
+ const body = parseReferenceProfileImportRequest({
125
+ ...(opts.title !== undefined ? { title: String(opts.title) } : {}),
126
+ ...(opts.notes !== undefined ? { notes: String(opts.notes) } : {}),
127
+ fileName: basename(filePath),
128
+ fileContent,
129
+ fileSize: Buffer.byteLength(fileContent, 'utf8'),
130
+ });
131
+ const data = await importReferenceProfile(body, opts.idempotencyKey ? String(opts.idempotencyKey) : undefined);
132
+ outputData(data, globalOpts);
133
+ }));
134
+ const previewCommand = referenceProfile
135
+ .command('preview <profile-id> <revision-id>')
136
+ .description('Preview a bounded set of temperature adjustments without saving')
137
+ .requiredOption('--request <file>', 'JSON file with a title and bounded temperature adjustments')
138
+ .addHelpText('after', `
139
+ Example:
140
+ purvey reference-profile preview 5ea1af6f-234c-43a9-9bf8-5678dd24f854 8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 --request changes.json --pretty
141
+
142
+ The request file is also the exact body accepted by save: title, optional notes, optional
143
+ userGoal/modelRecommendation/userEdits provenance, and changes.temperatureAdjustments.
144
+ Parchment recalculates from the immutable parent; preview never changes or stores the source.`);
145
+ previewCommand.action(withErrorHandling(async (profileId, revisionId, opts, cmd) => {
146
+ const globalOpts = cmd.optsWithGlobals();
147
+ const request = await readReferenceProfileGenerationRequest(String(opts.request));
148
+ const data = await previewReferenceProfile(profileId, revisionId, request);
149
+ outputData(data, globalOpts);
150
+ }));
151
+ const saveCommand = referenceProfile
152
+ .command('save <profile-id> <revision-id>')
153
+ .description('Save the previewed changes as a new immutable generated reference')
154
+ .requiredOption('--request <file>', 'JSON file with a title and bounded temperature adjustments');
155
+ idempotencyKeyOption(saveCommand, 'Stable key for safe retries');
156
+ saveCommand.addHelpText('after', `
157
+ Example:
158
+ purvey reference-profile save 5ea1af6f-234c-43a9-9bf8-5678dd24f854 8d2c41e0-7b9a-4f3e-a6d1-2c9e5f07b3a4 --request changes.json --pretty
159
+
160
+ Notes:
161
+ Omit --idempotency-key to generate a new key for this call. Reuse an explicit key to replay a retry.
162
+ A generated reference is a plan, not executed roast history.`);
163
+ saveCommand.action(withErrorHandling(async (profileId, revisionId, opts, cmd) => {
164
+ const globalOpts = cmd.optsWithGlobals();
165
+ const request = await readReferenceProfileGenerationRequest(String(opts.request));
166
+ const data = await saveGeneratedReferenceProfile(profileId, revisionId, request, opts.idempotencyKey ? String(opts.idempotencyKey) : undefined);
167
+ outputData(data, globalOpts);
168
+ }));
169
+ const exportCommand = referenceProfile
170
+ .command('export <profile-id> <revision-id>')
171
+ .description('Download a saved generated reference as an unsigned Artisan .alog plan')
172
+ .requiredOption('--output <file>', 'Destination for the generated .alog file')
173
+ .option('--force', 'Overwrite the destination file if it already exists')
174
+ .addHelpText('after', `
175
+ Example:
176
+ purvey reference-profile export 0b7e9f52-3c61-4d8a-9e24-6f1a8c3d5b90 c43a1d7e-95b2-4e06-8f7c-1d2b3a4e5f68 --output ~/artisan/next-batch.alog
177
+
178
+ Only a saved generated revision can be exported. Export writes the .alog file locally and
179
+ prints a JSON receipt; it does not claim verified Artisan 4.2 playback compatibility.`);
180
+ exportCommand.action(withErrorHandling(async (profileId, revisionId, opts, cmd) => {
181
+ const globalOpts = cmd.optsWithGlobals();
182
+ const destination = normalizePathInput(String(opts.output ?? ''));
183
+ if (!destination) {
184
+ throw new PrvrsError('INVALID_ARGUMENT', 'The export destination path cannot be blank.');
185
+ }
186
+ const result = await exportGeneratedReferenceProfile(profileId, revisionId);
187
+ const outputFile = await writeGeneratedReferenceFile(result.data.fileContent, destination, Boolean(opts.force));
188
+ outputData({
189
+ data: {
190
+ profileId,
191
+ revisionId,
192
+ fileName: result.data.fileName,
193
+ fileSize: result.data.fileSize,
194
+ outputPath: outputFile,
195
+ },
196
+ }, globalOpts);
197
+ }));
198
+ return referenceProfile;
199
+ }
200
+ //# sourceMappingURL=reference-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-profile.js","sourceRoot":"","sources":["../../src/commands/reference-profile.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,OAAO,EAAwB,WAAW,EAA4B,MAAM,iBAAiB,CAAC;AAC9F,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAO/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAUjD,4DAA4D;AAC5D,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC;AAGvE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAsDhE;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,GACxB,iBAAiB,CAyDnB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,kBAAkB,CAwBjG;AAED,6FAA6F;AAC7F,wBAAsB,sBAAsB,CAC1C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,GACvC,OAAO,CAAC,YAAY,CAAC,CAQvB;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAm5B3C"}
1
+ {"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,OAAO,EAIL,WAAW,EAIZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAO/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAUjD,4DAA4D;AAC5D,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC;AAGvE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAkEhE;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,GACxB,iBAAiB,CAyDnB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,kBAAkB,CAwBjG;AAED,6FAA6F;AAC7F,wBAAsB,sBAAsB,CAC1C,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,GACvC,OAAO,CAAC,YAAY,CAAC,CAQvB;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA+7B3C"}
@@ -6,7 +6,7 @@ import { outputData, info, success } from '../lib/output.js';
6
6
  import { withErrorHandling, PrvrsError, AuthError } from '../lib/errors.js';
7
7
  import { requireAuth } from '../lib/auth-guard.js';
8
8
  import { confirm, todayIso } from '../lib/prompts.js';
9
- import { listRoasts, getRoast, createRoast, deleteRoast, updateRoast } from '../lib/roast.js';
9
+ import { listRoasts, getRoast, getRoastChartData, createRoast, deleteRoast, updateRoast, ROAST_CHART_TARGET_POINTS, } from '../lib/roast.js';
10
10
  import { pickBean, guardCancel } from '../lib/interactive/forms.js';
11
11
  import { normalizePathInput } from '../lib/path-input.js';
12
12
  import { startWatch, loadWatchSession } from '../lib/interactive/watch.js';
@@ -29,6 +29,14 @@ function parseRoastListCount(value, flag) {
29
29
  }
30
30
  return parsed;
31
31
  }
32
+ function parseRoastChartTargetPoints(value) {
33
+ const parsed = parseStrictPositiveCount(value);
34
+ const { minimum, maximum } = ROAST_CHART_TARGET_POINTS;
35
+ if (!Number.isFinite(parsed) || parsed < minimum || parsed > maximum) {
36
+ throw new PrvrsError('INVALID_ARGUMENT', `Invalid --target-points: "${value}". Must be an integer between ${minimum} and ${maximum}.`);
37
+ }
38
+ return parsed;
39
+ }
32
40
  function parseWatchCommitMode(value, fallback = 'batch') {
33
41
  if (value === undefined || value === null || String(value).trim() === '') {
34
42
  return fallback;
@@ -273,6 +281,34 @@ Notes:
273
281
  });
274
282
  outputData(data, globalOpts);
275
283
  }));
284
+ // ── roast chart <id> ──────────────────────────────────────────────────────
285
+ roast
286
+ .command('chart <id>')
287
+ .description('Fetch the sampled chart model for one roast (series, events, revision)')
288
+ .option('--target-points <n>', `Approximate samples per series (${ROAST_CHART_TARGET_POINTS.minimum}-${ROAST_CHART_TARGET_POINTS.maximum}; Parchment defaults to 400)`)
289
+ .addHelpText('after', `
290
+ Examples:
291
+ purvey roast chart 123 --pretty
292
+ purvey roast chart 123 --target-points 120 --json
293
+ purvey roast chart 123 | jq '.data.metadata.revision'
294
+
295
+ Notes:
296
+ <id> is roast_data.roast_id (integer).
297
+ Returns Parchment's canonical chart-data envelope unchanged: sampled series,
298
+ discrete events, and metadata. data.metadata.revision identifies the immutable
299
+ chart revision accepted by 'purvey reference-profile compare roast:<id>@<revision>'.
300
+ Requires authentication (member role); API keys need the roast:read scope.
301
+ `)
302
+ .action(withErrorHandling(async (id, opts, cmd) => {
303
+ const globalOpts = cmd.optsWithGlobals();
304
+ const data = await getRoastChartData({
305
+ id: parseRoastInt4Id(id, 'roast ID'),
306
+ targetPoints: opts.targetPoints === undefined
307
+ ? undefined
308
+ : parseRoastChartTargetPoints(String(opts.targetPoints)),
309
+ });
310
+ outputData(data, globalOpts);
311
+ }));
276
312
  // ── roast create ──────────────────────────────────────────────────────────
277
313
  roast
278
314
  .command('create')
@@ -283,6 +319,8 @@ Notes:
283
319
  .option('--oz-out <oz>', 'Roasted weight in ounces')
284
320
  .option('--roast-date <YYYY-MM-DD>', 'Roast date (defaults to today)')
285
321
  .option('--notes <text>', 'Roast notes')
322
+ .option('--targets <text>', 'Roast targets (planning goals for this roast)')
323
+ .option('--roaster-type <text>', 'Roaster model or type')
286
324
  .option('--form', 'Interactive form mode (browse and select bean)')
287
325
  .addHelpText('after', `
288
326
  Examples:
@@ -290,6 +328,7 @@ Examples:
290
328
  purvey roast create --coffee-id 7 --batch-name "Ethiopia Guji Light" --oz-in 16
291
329
  purvey roast create --coffee-id 42 --oz-in 12 --oz-out 9.8 --roast-date 2026-03-15
292
330
  purvey roast create --coffee-id 7 --notes "Extended drying phase, aimed for medium roast"
331
+ purvey roast create --coffee-id 7 --targets "FC at 390F, 18% development" --roaster-type "Aillio Bullet"
293
332
  purvey roast create --form # interactive wizard
294
333
 
295
334
  Required flags: --coffee-id (green_coffee_inv.id)
@@ -387,6 +426,8 @@ Required flags: --coffee-id (green_coffee_inv.id)
387
426
  ozOut,
388
427
  roastDate: opts.roastDate ?? todayIso(),
389
428
  notes: opts.notes,
429
+ targets: opts.targets,
430
+ roasterType: opts.roasterType,
390
431
  });
391
432
  success(`Roast profile ${data.roast_id} created.`);
392
433
  outputData(data, globalOpts);