@purveyors/cli 0.9.6 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +476 -159
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +4 -3
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/catalog.d.ts.map +1 -1
- package/dist/commands/catalog.js +55 -31
- package/dist/commands/catalog.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +61 -12
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +34 -234
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/manifest.d.ts +3 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +32 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/roast.d.ts.map +1 -1
- package/dist/commands/roast.js +105 -2
- package/dist/commands/roast.js.map +1 -1
- package/dist/commands/sales.d.ts.map +1 -1
- package/dist/commands/sales.js +87 -40
- package/dist/commands/sales.js.map +1 -1
- package/dist/index.js +6 -105
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +13 -1
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +153 -10
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/interactive/watch.d.ts +37 -3
- package/dist/lib/interactive/watch.d.ts.map +1 -1
- package/dist/lib/interactive/watch.js +237 -81
- package/dist/lib/interactive/watch.js.map +1 -1
- package/dist/lib/manifest.d.ts +107 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +977 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/output.d.ts +10 -0
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +33 -18
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/roast.d.ts +2 -0
- package/dist/lib/roast.d.ts.map +1 -1
- package/dist/lib/roast.js +5 -2
- package/dist/lib/roast.js.map +1 -1
- package/dist/lib/sales.d.ts +15 -2
- package/dist/lib/sales.d.ts.map +1 -1
- package/dist/lib/sales.js +77 -15
- package/dist/lib/sales.js.map +1 -1
- package/dist/program.d.ts +4 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +135 -0
- package/dist/program.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +7 -1
package/dist/commands/context.js
CHANGED
|
@@ -1,244 +1,44 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
PURVEY CLI - Agent Reference
|
|
6
|
-
============================
|
|
7
|
-
Official CLI for purveyors.io. Node.js 20+.
|
|
8
|
-
Credentials file: ~/.config/purvey/credentials.json
|
|
9
|
-
Config file: ~/.config/purvey/config.json
|
|
10
|
-
|
|
11
|
-
ROLES
|
|
12
|
-
-----
|
|
13
|
-
viewer valid authenticated session; required for catalog commands
|
|
14
|
-
member required for inventory, roast, sales, and tasting commands
|
|
15
|
-
|
|
16
|
-
AUTH
|
|
17
|
-
----
|
|
18
|
-
Interactive login:
|
|
19
|
-
purvey auth login
|
|
20
|
-
|
|
21
|
-
Headless login:
|
|
22
|
-
purvey auth login --headless
|
|
23
|
-
1. CLI prints a Google OAuth URL
|
|
24
|
-
2. User opens it in any browser and signs in
|
|
25
|
-
3. User pastes the full callback URL back into the terminal
|
|
26
|
-
|
|
27
|
-
Status:
|
|
28
|
-
purvey auth status
|
|
29
|
-
purvey auth status --pretty
|
|
30
|
-
|
|
31
|
-
Logout:
|
|
32
|
-
purvey auth logout
|
|
33
|
-
|
|
34
|
-
OUTPUT
|
|
35
|
-
------
|
|
36
|
-
Most commands emit compact JSON to stdout by default.
|
|
37
|
-
Use --json to request compact JSON explicitly.
|
|
38
|
-
Use --pretty for indented JSON.
|
|
39
|
-
Use --csv for array-shaped results that support CSV output.
|
|
40
|
-
User feedback goes to stderr.
|
|
41
|
-
|
|
42
|
-
Important exception:
|
|
43
|
-
- auth status prints human-readable output in an interactive TTY unless --json, --pretty, or --csv is passed; otherwise it emits structured output
|
|
44
|
-
|
|
45
|
-
ID MAP
|
|
46
|
-
------
|
|
47
|
-
catalog_id coffee_catalog row; used by catalog get, inventory add --catalog-id, tasting get, roast list --catalog-id
|
|
48
|
-
inventory_id green_coffee_inv row; used by inventory get/update/delete, roast --coffee-id, roast list --coffee-id, tasting rate
|
|
49
|
-
roast_id roast_data row; used by roast get/delete, roast list --roast-id, sales --roast-id
|
|
50
|
-
sale_id coffee_sales row; used by sales update/delete
|
|
51
|
-
|
|
52
|
-
COMMANDS
|
|
53
|
-
--------
|
|
54
|
-
auth
|
|
55
|
-
login [--headless]
|
|
56
|
-
status
|
|
57
|
-
logout
|
|
58
|
-
|
|
59
|
-
catalog
|
|
60
|
-
search [options]
|
|
61
|
-
--origin <text>
|
|
62
|
-
--process <method>
|
|
63
|
-
--price-min <n>
|
|
64
|
-
--price-max <n>
|
|
65
|
-
--flavor <keywords>
|
|
66
|
-
--name <text>
|
|
67
|
-
--supplier <name>
|
|
68
|
-
--ids <n,n,...>
|
|
69
|
-
--variety <text>
|
|
70
|
-
--drying-method <text>
|
|
71
|
-
--stocked-days <n>
|
|
72
|
-
--stocked
|
|
73
|
-
--sort <price|price-desc|name|origin|newest>
|
|
74
|
-
--offset <n>
|
|
75
|
-
--limit <n>
|
|
76
|
-
get <catalog_id>
|
|
77
|
-
stats
|
|
78
|
-
similar <catalog_id>
|
|
79
|
-
--threshold <0-1> default 0.70
|
|
80
|
-
--limit <n> default 10
|
|
81
|
-
--stocked-only
|
|
82
|
-
|
|
83
|
-
inventory (member)
|
|
84
|
-
list
|
|
85
|
-
--stocked
|
|
86
|
-
--catalog-id <catalog_id>
|
|
87
|
-
--purchase-date-start <YYYY-MM-DD>
|
|
88
|
-
--purchase-date-end <YYYY-MM-DD>
|
|
89
|
-
--origin <country>
|
|
90
|
-
--limit <n>
|
|
91
|
-
--offset <n>
|
|
92
|
-
get <inventory_id>
|
|
93
|
-
add
|
|
94
|
-
--catalog-id <id> required in flag mode
|
|
95
|
-
--qty <lbs> required in flag mode
|
|
96
|
-
--cost <dollars>
|
|
97
|
-
--tax-ship <dollars>
|
|
98
|
-
--notes <text>
|
|
99
|
-
--purchase-date <YYYY-MM-DD>
|
|
100
|
-
--form
|
|
101
|
-
update <inventory_id>
|
|
102
|
-
--qty <lbs>
|
|
103
|
-
--cost <dollars>
|
|
104
|
-
--tax-ship <dollars>
|
|
105
|
-
--notes <text>
|
|
106
|
-
--stocked <true|false>
|
|
107
|
-
delete <inventory_id> [--yes] [--force]
|
|
108
|
-
--force cascade delete dependent roast profiles and sales records before deleting the item
|
|
109
|
-
without --force, delete fails with DEPENDENCY_CONFLICT if dependents exist
|
|
110
|
-
|
|
111
|
-
roast (member)
|
|
112
|
-
list
|
|
113
|
-
--coffee-id <inventory_id>
|
|
114
|
-
--roast-id <roast_id>
|
|
115
|
-
--batch-name <text>
|
|
116
|
-
--coffee-name <text>
|
|
117
|
-
--date-start <YYYY-MM-DD>
|
|
118
|
-
--date-end <YYYY-MM-DD>
|
|
119
|
-
--stocked
|
|
120
|
-
--catalog-id <catalog_id>
|
|
121
|
-
--limit <n>
|
|
122
|
-
--offset <n>
|
|
123
|
-
get <roast_id>
|
|
124
|
-
--include-temps
|
|
125
|
-
--include-events
|
|
126
|
-
create
|
|
127
|
-
--coffee-id <inventory_id> required in flag mode
|
|
128
|
-
--batch-name <name>
|
|
129
|
-
--oz-in <oz>
|
|
130
|
-
--oz-out <oz>
|
|
131
|
-
--roast-date <YYYY-MM-DD>
|
|
132
|
-
--notes <text>
|
|
133
|
-
--form
|
|
134
|
-
update <roast_id>
|
|
135
|
-
--notes <text>
|
|
136
|
-
--oz-out <oz>
|
|
137
|
-
--batch-name <name>
|
|
138
|
-
--targets <text>
|
|
139
|
-
delete <roast_id> [--yes]
|
|
140
|
-
import [file]
|
|
141
|
-
--coffee-id <inventory_id> required in flag mode
|
|
142
|
-
--batch-name <name>
|
|
143
|
-
--oz-in <oz>
|
|
144
|
-
--roast-notes <text>
|
|
145
|
-
--form
|
|
146
|
-
watch [directory]
|
|
147
|
-
--coffee-id <inventory_id> required unless --auto-match
|
|
148
|
-
--batch-prefix <name>
|
|
149
|
-
--prompt-each
|
|
150
|
-
--auto-match mutually exclusive with --coffee-id
|
|
151
|
-
--resume
|
|
152
|
-
--form
|
|
153
|
-
|
|
154
|
-
sales (member)
|
|
155
|
-
list
|
|
156
|
-
--roast-id <roast_id>
|
|
157
|
-
--date-start <YYYY-MM-DD>
|
|
158
|
-
--date-end <YYYY-MM-DD>
|
|
159
|
-
--buyer <name>
|
|
160
|
-
--limit <n>
|
|
161
|
-
--offset <n>
|
|
162
|
-
record
|
|
163
|
-
--roast-id <roast_id> required in flag mode
|
|
164
|
-
--oz <amount> required in flag mode
|
|
165
|
-
--price <dollars> required in flag mode
|
|
166
|
-
--buyer <name>
|
|
167
|
-
--sell-date <YYYY-MM-DD>
|
|
168
|
-
--form
|
|
169
|
-
update <sale_id>
|
|
170
|
-
--oz <amount>
|
|
171
|
-
--price <dollars>
|
|
172
|
-
--buyer <name>
|
|
173
|
-
--sell-date <YYYY-MM-DD>
|
|
174
|
-
delete <sale_id> [--yes]
|
|
175
|
-
|
|
176
|
-
tasting (member)
|
|
177
|
-
get <catalog_id>
|
|
178
|
-
--filter <user|supplier|both> default both
|
|
179
|
-
rate [inventory_id]
|
|
180
|
-
--aroma <1-5> required in flag mode
|
|
181
|
-
--body <1-5> required in flag mode
|
|
182
|
-
--acidity <1-5> required in flag mode
|
|
183
|
-
--sweetness <1-5> required in flag mode
|
|
184
|
-
--aftertaste <1-5> required in flag mode
|
|
185
|
-
--brew-method <method>
|
|
186
|
-
--notes <text>
|
|
187
|
-
--form
|
|
188
|
-
|
|
189
|
-
config
|
|
190
|
-
list
|
|
191
|
-
get <key>
|
|
192
|
-
set <key> <value>
|
|
193
|
-
reset
|
|
194
|
-
|
|
195
|
-
Current config keys:
|
|
196
|
-
form-mode true|false
|
|
197
|
-
|
|
198
|
-
WORKFLOWS
|
|
199
|
-
---------
|
|
200
|
-
Catalog -> inventory:
|
|
201
|
-
purvey catalog search --origin "Ethiopia" --stocked --pretty
|
|
202
|
-
purvey inventory add --catalog-id 128 --qty 10 --cost 8.50
|
|
203
|
-
|
|
204
|
-
Import a roast from Artisan:
|
|
205
|
-
purvey inventory list --stocked --pretty
|
|
206
|
-
purvey roast import ~/artisan/ethiopia.alog --coffee-id 7 --pretty
|
|
207
|
-
|
|
208
|
-
Watch a folder for new roasts:
|
|
209
|
-
purvey roast watch ~/artisan/ --coffee-id 7
|
|
210
|
-
purvey roast watch ~/artisan/ --auto-match
|
|
211
|
-
purvey roast watch --resume
|
|
212
|
-
|
|
213
|
-
Rate coffee and record a sale:
|
|
214
|
-
purvey tasting rate 7 --aroma 4 --body 3 --acidity 5 --sweetness 4 --aftertaste 4
|
|
215
|
-
purvey sales record --roast-id 123 --oz 12 --price 22.00 --buyer "Jane Smith"
|
|
216
|
-
|
|
217
|
-
ERROR PATTERNS
|
|
218
|
-
--------------
|
|
219
|
-
Not logged in:
|
|
220
|
-
run purvey auth login or purvey auth login --headless
|
|
221
|
-
|
|
222
|
-
Wrong ID type:
|
|
223
|
-
verify whether the command wants catalog_id, inventory_id, roast_id, or sale_id
|
|
224
|
-
|
|
225
|
-
Missing required args in write commands:
|
|
226
|
-
pass the required flags or use --form
|
|
227
|
-
|
|
228
|
-
Mutually exclusive watch flags:
|
|
229
|
-
roast watch forbids using --auto-match together with --coffee-id
|
|
230
|
-
`.trim();
|
|
2
|
+
import { PrvrsError, withErrorHandling } from '../lib/errors.js';
|
|
3
|
+
import { getCliManifest, renderContextText } from '../lib/manifest.js';
|
|
4
|
+
import { outputData } from '../lib/output.js';
|
|
231
5
|
export function buildContextCommand() {
|
|
232
6
|
return new Command('context')
|
|
233
|
-
.description('Output
|
|
7
|
+
.description('Output the dense human-readable operator reference; use --json/--pretty only for manifest parity')
|
|
8
|
+
.option('--json', 'Emit the machine-readable manifest contract as compact JSON')
|
|
9
|
+
.option('--pretty', 'Emit the machine-readable manifest contract as indented JSON')
|
|
234
10
|
.addHelpText('after', `
|
|
11
|
+
Default output is the dense human-readable operator reference.
|
|
12
|
+
Preferred machine contract: \`purvey manifest\`.
|
|
13
|
+
Use --json or --pretty only when an existing \`purvey context\` caller needs manifest parity.
|
|
14
|
+
Use \`@purveyors/cli/manifest\` for the same contract inside Node.js or agent runtimes.
|
|
15
|
+
|
|
235
16
|
Examples:
|
|
236
17
|
purvey context
|
|
237
18
|
purvey context | head -50
|
|
238
|
-
purvey context
|
|
19
|
+
purvey context --json
|
|
20
|
+
purvey context --pretty
|
|
21
|
+
purvey context --json > cli-manifest.json
|
|
22
|
+
purvey manifest
|
|
239
23
|
`)
|
|
240
|
-
.action(() => {
|
|
241
|
-
|
|
242
|
-
|
|
24
|
+
.action(withErrorHandling(async (opts, cmd) => {
|
|
25
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
26
|
+
const manifest = getCliManifest();
|
|
27
|
+
const wantsJson = Boolean(opts.json) ||
|
|
28
|
+
Boolean(opts.pretty) ||
|
|
29
|
+
Boolean(globalOpts.json) ||
|
|
30
|
+
Boolean(globalOpts.pretty);
|
|
31
|
+
if (globalOpts.csv) {
|
|
32
|
+
throw new PrvrsError('INVALID_ARGUMENT', 'The context command does not support --csv. Use text, --json, or --pretty.');
|
|
33
|
+
}
|
|
34
|
+
if (wantsJson) {
|
|
35
|
+
outputData(manifest, {
|
|
36
|
+
json: true,
|
|
37
|
+
pretty: Boolean(opts.pretty) || Boolean(globalOpts.pretty),
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
console.log(renderContextText(manifest));
|
|
42
|
+
}));
|
|
243
43
|
}
|
|
244
44
|
//# sourceMappingURL=context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;SAC1B,WAAW,CACV,kGAAkG,CACnG;SACA,MAAM,CAAC,QAAQ,EAAE,6DAA6D,CAAC;SAC/E,MAAM,CAAC,UAAU,EAAE,8DAA8D,CAAC;SAClF,WAAW,CACV,OAAO,EACP;;;;;;;;;;;;;CAaL,CACI;SACA,MAAM,CACL,iBAAiB,CAAC,KAAK,EAAE,IAA6B,EAAE,GAAY,EAAE,EAAE;QACtE,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAmB,CAAC;QAC1D,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;QAClC,MAAM,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YACpB,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YACxB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAE7B,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;YACnB,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,UAAU,CAAC,QAAQ,EAAE;gBACnB,IAAI,EAAE,IAAI;gBACV,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;aAC3D,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CACH,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,oBAAoB,IAAI,OAAO,CAsC9C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { PrvrsError, withErrorHandling } from '../lib/errors.js';
|
|
3
|
+
import { getCliManifest } from '../lib/manifest.js';
|
|
4
|
+
import { outputData } from '../lib/output.js';
|
|
5
|
+
export function buildManifestCommand() {
|
|
6
|
+
return new Command('manifest')
|
|
7
|
+
.description('Output the preferred stable machine-readable CLI manifest contract for shells and agents')
|
|
8
|
+
.option('--json', 'Emit the manifest as compact JSON (default)')
|
|
9
|
+
.option('--pretty', 'Emit the manifest as indented JSON')
|
|
10
|
+
.addHelpText('after', `
|
|
11
|
+
Preferred machine-readable entrypoint for shells, scripts, and agents.
|
|
12
|
+
Use \`purvey context --json\` only for compatibility with existing context-based callers.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
purvey manifest
|
|
16
|
+
purvey manifest --pretty
|
|
17
|
+
purvey manifest > cli-manifest.json
|
|
18
|
+
purvey manifest | jq '.commandGroups[].name'
|
|
19
|
+
node --input-type=module -e "import { getCliManifest } from '@purveyors/cli/manifest'; console.log(JSON.stringify(getCliManifest()))"
|
|
20
|
+
`)
|
|
21
|
+
.action(withErrorHandling(async (opts, cmd) => {
|
|
22
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
23
|
+
if (globalOpts.csv) {
|
|
24
|
+
throw new PrvrsError('INVALID_ARGUMENT', 'The manifest command does not support --csv. Use --json or --pretty.');
|
|
25
|
+
}
|
|
26
|
+
outputData(getCliManifest(), {
|
|
27
|
+
json: true,
|
|
28
|
+
pretty: Boolean(opts.pretty) || Boolean(globalOpts.pretty),
|
|
29
|
+
});
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/commands/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;SAC3B,WAAW,CACV,0FAA0F,CAC3F;SACA,MAAM,CAAC,QAAQ,EAAE,6CAA6C,CAAC;SAC/D,MAAM,CAAC,UAAU,EAAE,oCAAoC,CAAC;SACxD,WAAW,CACV,OAAO,EACP;;;;;;;;;;CAUL,CACI;SACA,MAAM,CACL,iBAAiB,CAAC,KAAK,EAAE,IAA6B,EAAE,GAAY,EAAE,EAAE;QACtE,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAmB,CAAC;QAE1D,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;YACnB,MAAM,IAAI,UAAU,CAClB,kBAAkB,EAClB,sEAAsE,CACvE,CAAC;QACJ,CAAC;QAED,UAAU,CAAC,cAAc,EAAE,EAAE;YAC3B,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;AACN,CAAC"}
|
|
@@ -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;AAgBpC,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEhB,MAAM,iBAAiB,CAAC;AAOzB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAIhE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"roast.d.ts","sourceRoot":"","sources":["../../src/commands/roast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEhB,MAAM,iBAAiB,CAAC;AAOzB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAIhE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAk6B3C"}
|
package/dist/commands/roast.js
CHANGED
|
@@ -342,12 +342,14 @@ Notes:
|
|
|
342
342
|
.option('--batch-name <name>', 'Batch name (auto-generated from coffee name + date if omitted)')
|
|
343
343
|
.option('--oz-in <oz>', 'Green weight in ounces (extracted from .alog if omitted)')
|
|
344
344
|
.option('--roast-notes <notes>', 'Additional roast notes')
|
|
345
|
+
.option('--roast-targets <targets>', 'Roast targets to store with the import')
|
|
345
346
|
.option('--form', 'Interactive form mode (browse and select bean)')
|
|
346
347
|
.addHelpText('after', `
|
|
347
348
|
Examples:
|
|
348
349
|
purvey roast import ~/artisan/ethiopia-guji.alog --coffee-id 7 --pretty
|
|
349
350
|
purvey roast import roast.alog --coffee-id 42 --oz-in 16
|
|
350
351
|
purvey roast import roast.alog --coffee-id 7 --batch-name "Ethiopia Guji #3" --roast-notes "Faster development"
|
|
352
|
+
purvey roast import roast.alog --coffee-id 7 --roast-targets "Aim for 18% development"
|
|
351
353
|
purvey roast import --form # interactive wizard (browse files + select bean)
|
|
352
354
|
|
|
353
355
|
Required: <file> path and --coffee-id (unless using --form)
|
|
@@ -410,6 +412,11 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
410
412
|
placeholder: 'optional',
|
|
411
413
|
});
|
|
412
414
|
guardCancel(roastNotesRaw);
|
|
415
|
+
const roastTargetsRaw = await p.text({
|
|
416
|
+
message: 'Roast targets',
|
|
417
|
+
placeholder: 'optional',
|
|
418
|
+
});
|
|
419
|
+
guardCancel(roastTargetsRaw);
|
|
413
420
|
const confirmed = await p.confirm({ message: 'Import this roast?' });
|
|
414
421
|
guardCancel(confirmed);
|
|
415
422
|
if (!confirmed) {
|
|
@@ -422,6 +429,7 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
422
429
|
const ozIn = ozInStr !== '' ? parseFloat(ozInStr) : undefined;
|
|
423
430
|
const batchName = String(batchNameRaw).trim() || defaultBatch;
|
|
424
431
|
const notesStr = String(roastNotesRaw).trim();
|
|
432
|
+
const targetsStr = String(roastTargetsRaw).trim();
|
|
425
433
|
const spin = p.spinner();
|
|
426
434
|
spin.start('Importing roast data...');
|
|
427
435
|
const result = await importRoastFromFile(supabase, userId, {
|
|
@@ -431,6 +439,7 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
431
439
|
batchName,
|
|
432
440
|
ozIn,
|
|
433
441
|
roastNotes: notesStr !== '' ? notesStr : undefined,
|
|
442
|
+
roastTargets: targetsStr !== '' ? targetsStr : undefined,
|
|
434
443
|
});
|
|
435
444
|
spin.stop('Done');
|
|
436
445
|
p.outro(`Roast imported! Profile #${result.roast_id} created.`);
|
|
@@ -469,6 +478,9 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
469
478
|
throw new PrvrsError('INVALID_ARGUMENT', `Invalid --oz-in: "${opts.ozIn}".`);
|
|
470
479
|
}
|
|
471
480
|
}
|
|
481
|
+
const roastTargets = typeof opts.roastTargets === 'string' && opts.roastTargets.trim() !== ''
|
|
482
|
+
? opts.roastTargets.trim()
|
|
483
|
+
: undefined;
|
|
472
484
|
// 6. Run the import
|
|
473
485
|
const result = await importRoastFromFile(supabase, userId, {
|
|
474
486
|
fileContent,
|
|
@@ -477,6 +489,7 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
477
489
|
batchName: opts.batchName,
|
|
478
490
|
ozIn,
|
|
479
491
|
roastNotes: opts.roastNotes,
|
|
492
|
+
roastTargets,
|
|
480
493
|
});
|
|
481
494
|
// 7. Output
|
|
482
495
|
if (globalOpts.pretty) {
|
|
@@ -490,12 +503,16 @@ Required: <file> path and --coffee-id (unless using --form)
|
|
|
490
503
|
// ── roast watch <directory> ───────────────────────────────────────────────
|
|
491
504
|
roast
|
|
492
505
|
.command('watch')
|
|
493
|
-
.description('Watch a directory for new .alog files and auto-import them')
|
|
506
|
+
.description('Watch a directory for new .alog files and queue or auto-import them')
|
|
494
507
|
.argument('[directory]', 'Directory to watch for .alog files')
|
|
495
508
|
.option('--coffee-id <id>', '[REQUIRED unless --auto-match] green_coffee_inv ID for all imports')
|
|
496
509
|
.option('--batch-prefix <name>', 'Batch name prefix for auto-named batches (defaults to coffee name)')
|
|
497
510
|
.option('--prompt-each', 'Prompt for bean selection on each new file (instead of using --coffee-id)')
|
|
498
511
|
.option('--auto-match', 'Use AI to auto-match beans per file (mutually exclusive with --coffee-id)')
|
|
512
|
+
.option('--commit-mode <mode>', 'Commit mode: batch (default) or individual')
|
|
513
|
+
.option('--oz-in <oz>', 'Green weight in ounces for watched imports')
|
|
514
|
+
.option('--roast-notes <notes>', 'Roast notes to apply to watched imports')
|
|
515
|
+
.option('--roast-targets <targets>', 'Roast targets to apply to watched imports')
|
|
499
516
|
.option('--resume', 'Resume a previous watch session from where it left off')
|
|
500
517
|
.option('--form', 'Interactive form mode (prompts for directory + bean selection)')
|
|
501
518
|
.addHelpText('after', `
|
|
@@ -503,19 +520,41 @@ Examples:
|
|
|
503
520
|
purvey roast watch ~/artisan/ --coffee-id 7
|
|
504
521
|
purvey roast watch ~/artisan/ --coffee-id 42 --batch-prefix "Colombia Huila"
|
|
505
522
|
purvey roast watch ~/artisan/ --auto-match
|
|
523
|
+
purvey roast watch ~/artisan/ --coffee-id 7 --commit-mode individual
|
|
506
524
|
purvey roast watch ~/artisan/ --prompt-each
|
|
507
525
|
purvey roast watch --resume # continue a previous session
|
|
508
526
|
purvey roast watch --form # interactive setup wizard
|
|
509
527
|
|
|
510
528
|
Notes:
|
|
511
529
|
Runs continuously until Ctrl+C. New .alog files detected in the directory
|
|
512
|
-
are
|
|
530
|
+
are queued or imported as roast profiles depending on commit mode.
|
|
513
531
|
--auto-match and --coffee-id are mutually exclusive.
|
|
532
|
+
--commit-mode defaults to batch.
|
|
514
533
|
Session state is saved for --resume.
|
|
515
534
|
Requires authentication (member role).
|
|
516
535
|
`)
|
|
517
536
|
.action(withErrorHandling(async (directory, opts) => {
|
|
518
537
|
const { supabase, userId } = await requireAuth('member');
|
|
538
|
+
const parseCommitMode = (value, fallback = 'batch') => {
|
|
539
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
540
|
+
return fallback;
|
|
541
|
+
}
|
|
542
|
+
const normalized = String(value).trim().toLowerCase();
|
|
543
|
+
if (normalized === 'batch' || normalized === 'individual') {
|
|
544
|
+
return normalized;
|
|
545
|
+
}
|
|
546
|
+
throw new PrvrsError('INVALID_ARGUMENT', `Invalid --commit-mode: "${value}". Use "batch" or "individual".`);
|
|
547
|
+
};
|
|
548
|
+
const parseOptionalPositiveNumber = (value, flagName) => {
|
|
549
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
const parsed = parseFloat(String(value));
|
|
553
|
+
if (isNaN(parsed) || parsed <= 0) {
|
|
554
|
+
throw new PrvrsError('INVALID_ARGUMENT', `Invalid ${flagName}: "${value}".`);
|
|
555
|
+
}
|
|
556
|
+
return parsed;
|
|
557
|
+
};
|
|
519
558
|
// ── Resume mode ──────────────────────────────────────────────────────
|
|
520
559
|
if (opts.resume) {
|
|
521
560
|
const saved = await loadWatchSession();
|
|
@@ -526,7 +565,15 @@ Notes:
|
|
|
526
565
|
coffeeId: saved.coffeeId,
|
|
527
566
|
coffeeName: saved.coffeeName,
|
|
528
567
|
batchPrefix: saved.batchPrefix,
|
|
568
|
+
promptEach: Boolean(saved.promptEach),
|
|
569
|
+
autoMatch: Boolean(saved.autoMatch),
|
|
570
|
+
commitMode: saved.commitMode ?? 'batch',
|
|
571
|
+
startedAt: saved.startedAt,
|
|
572
|
+
resumeImports: saved.imports,
|
|
529
573
|
startSequence: saved.imports.length,
|
|
574
|
+
ozIn: saved.ozIn,
|
|
575
|
+
roastNotes: saved.roastNotes,
|
|
576
|
+
roastTargets: saved.roastTargets,
|
|
530
577
|
});
|
|
531
578
|
return;
|
|
532
579
|
}
|
|
@@ -592,12 +639,56 @@ Notes:
|
|
|
592
639
|
});
|
|
593
640
|
if (typeof promptEachRaw !== 'boolean')
|
|
594
641
|
guardCancel(promptEachRaw);
|
|
642
|
+
const commitModeRaw = await p.select({
|
|
643
|
+
message: 'Commit mode',
|
|
644
|
+
initialValue: 'batch',
|
|
645
|
+
options: [
|
|
646
|
+
{
|
|
647
|
+
value: 'batch',
|
|
648
|
+
label: 'Batch commit on Ctrl+C',
|
|
649
|
+
hint: 'Default. Queue imports and commit them together when the session ends.',
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
value: 'individual',
|
|
653
|
+
label: 'Commit each roast immediately',
|
|
654
|
+
hint: 'Import each new file as soon as it appears.',
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
});
|
|
658
|
+
guardCancel(commitModeRaw);
|
|
659
|
+
const commitMode = parseCommitMode(commitModeRaw);
|
|
660
|
+
const ozInRaw = await p.text({
|
|
661
|
+
message: 'Green weight (oz)',
|
|
662
|
+
placeholder: 'optional',
|
|
663
|
+
validate: (v) => {
|
|
664
|
+
if (!v || v.trim() === '')
|
|
665
|
+
return;
|
|
666
|
+
const n = parseFloat(v);
|
|
667
|
+
if (isNaN(n) || n <= 0)
|
|
668
|
+
return 'Must be a positive number.';
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
guardCancel(ozInRaw);
|
|
672
|
+
const roastTargetsRaw = await p.text({
|
|
673
|
+
message: 'Roast targets',
|
|
674
|
+
placeholder: 'optional',
|
|
675
|
+
});
|
|
676
|
+
guardCancel(roastTargetsRaw);
|
|
677
|
+
const roastNotesRaw = await p.text({
|
|
678
|
+
message: 'Roast notes',
|
|
679
|
+
placeholder: 'optional',
|
|
680
|
+
});
|
|
681
|
+
guardCancel(roastNotesRaw);
|
|
595
682
|
await startWatch(supabase, userId, watchDir, {
|
|
596
683
|
coffeeId: watchCoffeeId,
|
|
597
684
|
coffeeName: watchCoffeeName,
|
|
598
685
|
batchPrefix,
|
|
686
|
+
commitMode,
|
|
599
687
|
promptEach: useAutoMatch ? false : Boolean(promptEachRaw),
|
|
600
688
|
autoMatch: useAutoMatch,
|
|
689
|
+
ozIn: parseOptionalPositiveNumber(ozInRaw, 'green weight'),
|
|
690
|
+
roastTargets: String(roastTargetsRaw).trim() !== '' ? String(roastTargetsRaw).trim() : undefined,
|
|
691
|
+
roastNotes: String(roastNotesRaw).trim() !== '' ? String(roastNotesRaw).trim() : undefined,
|
|
601
692
|
});
|
|
602
693
|
return;
|
|
603
694
|
}
|
|
@@ -606,6 +697,14 @@ Notes:
|
|
|
606
697
|
throw new PrvrsError('INVALID_ARGUMENT', 'Missing directory argument. Use --form for interactive mode or --resume to continue.');
|
|
607
698
|
}
|
|
608
699
|
const autoMatch = Boolean(opts.autoMatch);
|
|
700
|
+
const commitMode = parseCommitMode(opts.commitMode);
|
|
701
|
+
const ozIn = parseOptionalPositiveNumber(opts.ozIn, '--oz-in');
|
|
702
|
+
const roastNotes = typeof opts.roastNotes === 'string' && opts.roastNotes.trim() !== ''
|
|
703
|
+
? opts.roastNotes.trim()
|
|
704
|
+
: undefined;
|
|
705
|
+
const roastTargets = typeof opts.roastTargets === 'string' && opts.roastTargets.trim() !== ''
|
|
706
|
+
? opts.roastTargets.trim()
|
|
707
|
+
: undefined;
|
|
609
708
|
// --auto-match and --coffee-id are mutually exclusive (auto-match picks the bean)
|
|
610
709
|
if (autoMatch && opts.coffeeId) {
|
|
611
710
|
throw new PrvrsError('INVALID_ARGUMENT', '--auto-match and --coffee-id are mutually exclusive. Use one or the other.');
|
|
@@ -644,8 +743,12 @@ Notes:
|
|
|
644
743
|
coffeeId,
|
|
645
744
|
coffeeName,
|
|
646
745
|
batchPrefix,
|
|
746
|
+
commitMode,
|
|
647
747
|
promptEach: Boolean(opts.promptEach),
|
|
648
748
|
autoMatch,
|
|
749
|
+
ozIn,
|
|
750
|
+
roastNotes,
|
|
751
|
+
roastTargets,
|
|
649
752
|
});
|
|
650
753
|
}));
|
|
651
754
|
return roast;
|