@commercelayer/agent-cli 1.0.0-dev.10 → 1.0.0-dev.11
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 +2 -2
- package/dist/builtins/extensions.js +22 -5
- package/dist/inputs.js +21 -1
- package/dist/schema.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ This is the 1.0.0 line, in development on the `clean-slate` branch. It replaces
|
|
|
18
18
|
- A dotted flag sets part of an object: `--customer.email ada@example.com`. An `in` filter takes several values as the flag repeated or a JSON array; on enums, numbers and dates also separated by commas, while a name or text is one value even with a comma in it (`--tax-calculator.in "UK VAT, manual"`), and a list that has one is sent as a JSON array. Any other array takes a JSON array, values separated by commas, or the flag repeated; an array of objects takes `key=value` pairs, one object per flag: `--items sku=COFFEE,quantity=2`. `--data` takes the fields as one JSON object, or `@` and a file name.
|
|
19
19
|
- Amounts are in major units: `--amount 12.50`.
|
|
20
20
|
- A secret field the schema marks `secret_input` (provider credentials) has no flag of its own: `--credentials-file <file>` reads it, as JSON or `KEY=value` lines, from a plain file only its owner can read (mode 600). Without the file, a terminal asks for it without echo; with neither, the command stops with `secret_required`. It is refused as a flag value or in `--data`, and never printed.
|
|
21
|
-
- A document field the schema marks `file_input` (price rules, rule filters) also reads from a JSON file: `--file <file>`, or `--<field>-file` when a verb has more than one.
|
|
21
|
+
- A document field the schema marks `file_input` (price rules, rule filters) also reads from a JSON file: `--file <file>`, or `--<field>-file` when a verb has more than one. Where the verb takes `--format csv` (`imports create`), the file goes as CSV text with `--format csv`, or when its name ends in `.csv` and no format is given; otherwise it is JSON.
|
|
22
22
|
- A verb whose schema lists `id_alternative` takes the id or those flags in its place: `c11r prices get pr_1`, or `c11r prices get --sku BEANS-1KG --price-list eur`. Both at once, or half the flags, stop before sending.
|
|
23
23
|
|
|
24
24
|
## Built-in nouns
|
|
@@ -76,7 +76,7 @@ Destructive verbs (deletes, `orders cancel`, `payments void` and `refund`, `sand
|
|
|
76
76
|
|
|
77
77
|
## The store's setup in files
|
|
78
78
|
|
|
79
|
-
`c11r config export` writes the organization's setup into the project folder: `store.yml` (the Configure nouns, refs by key, no ids, no secrets; its first key, `meta`, names the organization and the time) and `price_lists/<code>.csv
|
|
79
|
+
`c11r config export` writes the organization's setup into the project folder: `store.yml` (the Configure nouns, refs by key, no ids, no secrets; its first key, `meta`, names the organization and the time) and `price_lists/<code>.csv` (in a row, an empty `compare_at_amount` means equal to `amount`, and one below `amount` is refused). It records what it wrote in `.c11r/export.json`, and never overwrites a file changed since the last export (or one no export wrote) without `--yes`: the answer lists them. A price list file the organization no longer has, unchanged since, goes. `c11r config plan` sends the files and answers what `config apply` would change; `apply` makes the organization match them, and with `--prune` (confirmed) also deletes what the files lack. While imports run, `apply` answers `status: running` with each noun's state, `retry_after` and the run's token, `run`; `--wait` applies again with that token and the same files until done or failed, and the done answer counts everything the run wrote. The `next` an unfinished apply suggests carries `--run <token> --wait`. `plan` lists in `needs` the new records the files can't make alone (a webhook without its `callback_url`); `apply` skips them and warns `needs_input` with the command that creates each.
|
|
80
80
|
|
|
81
81
|
Records that share a key (two shipping methods with the same name in one market, say) can't be matched to the files. `export` and `plan` list them in `ambiguous` (an empty list when there are none), each with the command that makes its key differ, and `export` puts the first one at the top of `next`. `apply` refuses with `conflict` and the same list in `error.details.ambiguous`, before it writes anything.
|
|
82
82
|
|
|
@@ -169,14 +169,18 @@ export const config = {
|
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
if (name === 'plan' || name === 'apply') {
|
|
172
|
-
const
|
|
173
|
-
let r = await run.send(
|
|
172
|
+
const values = run.parsed.values;
|
|
173
|
+
let r = await run.send(resourceRequest('config', run.verb, undefined, values));
|
|
174
174
|
const started = Date.now();
|
|
175
|
-
while (name === 'apply' &&
|
|
175
|
+
while (name === 'apply' && values.wait && r.data?.status === 'running' && Date.now() - started < WAIT_MAX_MS) {
|
|
176
176
|
const ms = waitMs(r.data);
|
|
177
177
|
process.stderr.write(JSON.stringify({ running: { nouns: states(r.data), next_in_ms: ms } }) + '\n');
|
|
178
178
|
await sleep(ms);
|
|
179
|
-
|
|
179
|
+
// The same run carries on (45.9): its token goes back with the same files, so done counts
|
|
180
|
+
// everything the run wrote.
|
|
181
|
+
if (typeof r.data?.run === 'string')
|
|
182
|
+
values.run = r.data.run;
|
|
183
|
+
r = await run.send(resourceRequest('config', run.verb, undefined, values));
|
|
180
184
|
}
|
|
181
185
|
const warnings = [...r.warnings];
|
|
182
186
|
const here = storeFileOrg();
|
|
@@ -192,12 +196,25 @@ export const config = {
|
|
|
192
196
|
...r,
|
|
193
197
|
data: running ? { ...data, retry_after: waitMs(data) / 1000, states: states(data) } : data,
|
|
194
198
|
warnings,
|
|
195
|
-
next: running
|
|
199
|
+
next: running ? [{ command: carryOn(run.line, run.verb.flags.run ? data.run : undefined), why: 'carry on with the same files, and wait until every noun is done or failed' }, ...r.next] : r.next,
|
|
196
200
|
};
|
|
197
201
|
}
|
|
198
202
|
return undefined;
|
|
199
203
|
},
|
|
200
204
|
};
|
|
205
|
+
/**
|
|
206
|
+
* The apply that carries this run on: the same line with its --run token and --wait. Without --yes:
|
|
207
|
+
* a yes covers the run the human saw, so carrying on a prune is confirmed again.
|
|
208
|
+
*/
|
|
209
|
+
function carryOn(line, token) {
|
|
210
|
+
let out = line
|
|
211
|
+
.replace(/\s--run(=|\s+)("[^"]*"|'[^']*'|\S+)/g, '')
|
|
212
|
+
.replace(/\s--wait\b/g, '')
|
|
213
|
+
.replace(/\s(--yes|-y)(?=\s|$)/g, '');
|
|
214
|
+
if (typeof token === 'string' && token)
|
|
215
|
+
out += ` --run ${/^[\w.~:+\/=-]+$/.test(token) ? token : `'${token.replace(/'/g, `'\\''`)}'`}`;
|
|
216
|
+
return `${out} --wait`;
|
|
217
|
+
}
|
|
201
218
|
/** Each noun's state while apply runs: done, running (with its import), waiting, failed or unchanged. */
|
|
202
219
|
function states(data) {
|
|
203
220
|
const out = {};
|
package/dist/inputs.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// KEY=value lines, from a plain file only its owner can read; or, in a terminal, typed without
|
|
5
5
|
// echo. Never from a flag value or --data, never printed: an error names the file, not the value.
|
|
6
6
|
// A document field (file_input: price rules, rule filters) is also read from a JSON file, --file.
|
|
7
|
+
// Where the verb takes --format csv (imports), the file is sent as CSV text with --format csv, or
|
|
8
|
+
// when it ends in .csv and no format is given; JSON otherwise.
|
|
7
9
|
import fs from 'node:fs';
|
|
8
10
|
import { toFlag } from './argv.js';
|
|
9
11
|
import { fail } from './errors.js';
|
|
@@ -29,7 +31,12 @@ export async function readInputs(noun, verb, parsed, prompter) {
|
|
|
29
31
|
continue;
|
|
30
32
|
if (parsed.values[input.target] !== undefined)
|
|
31
33
|
fail('invalid_value', `${cmd} takes ${input.target} from ${toFlag(input.target)} or from ${toFlag(key)}, not both.`, { path: key });
|
|
32
|
-
|
|
34
|
+
if (isCsv(verb, parsed, file)) {
|
|
35
|
+
parsed.values.format ??= 'csv';
|
|
36
|
+
parsed.values[input.target] = csvOf(file, key);
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
parsed.values[input.target] = documentOf(file, key, spec);
|
|
33
40
|
continue;
|
|
34
41
|
}
|
|
35
42
|
if (file !== undefined) {
|
|
@@ -51,6 +58,19 @@ export async function readInputs(noun, verb, parsed, prompter) {
|
|
|
51
58
|
});
|
|
52
59
|
}
|
|
53
60
|
}
|
|
61
|
+
const isCsv = (verb, parsed, file) => Boolean(verb.flags.format?.values?.includes('csv')) && (parsed.values.format === 'csv' || (parsed.values.format === undefined && /\.csv$/i.test(file)));
|
|
62
|
+
function csvOf(file, key) {
|
|
63
|
+
let text;
|
|
64
|
+
try {
|
|
65
|
+
text = fs.readFileSync(file, 'utf8');
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
fail('invalid_value', `${toFlag(key)} ${file}: ${e.code === 'ENOENT' ? 'no such file' : e.message}.`, { path: key });
|
|
69
|
+
}
|
|
70
|
+
if (!text.trim())
|
|
71
|
+
fail('invalid_value', `${file} is empty: a CSV file starts with its header row.`, { path: key });
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
54
74
|
function documentOf(file, key, spec) {
|
|
55
75
|
let text;
|
|
56
76
|
try {
|
package/dist/schema.js
CHANGED
|
@@ -103,7 +103,9 @@ function fileInputs(flags, fields, body) {
|
|
|
103
103
|
type: 'string',
|
|
104
104
|
to: 'local',
|
|
105
105
|
input: { kind: 'document', target: k, required: Boolean(flags[k].required), target_type: flags[k].type },
|
|
106
|
-
description:
|
|
106
|
+
description: flags.format?.values?.includes('csv')
|
|
107
|
+
? `A file with ${k}, in place of ${toFlag(k)}: JSON, or CSV with --format csv or a name ending in .csv.`
|
|
108
|
+
: `A JSON file with ${k}, in place of ${toFlag(k)}.`,
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercelayer/agent-cli",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.11",
|
|
4
4
|
"description": "c11r: the Commerce Layer CLI for coding agents. One grammar, c11r <noun> <verb>, from a sandbox to a running organization.",
|
|
5
5
|
"homepage": "https://commercelayer.ai",
|
|
6
6
|
"keywords": [
|