@commercelayer/agent-cli 1.0.0-dev.3 → 1.0.0-dev.4
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 +1 -1
- package/dist/builtins/loops.js +36 -14
- package/dist/builtins/sandbox.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ This is the 1.0.0 line, in development on the `clean-slate` branch. It replaces
|
|
|
34
34
|
| `c11r auth login [--client-id] [--secret-file] [--keep-file]` | The human's own Commerce Layer login, from their Provisioning API application. See below. |
|
|
35
35
|
| `c11r auth logout`, `auth switch <slug> [--global]` | Forgetting the login; the organization commands act on. |
|
|
36
36
|
|
|
37
|
-
A loop policy allows verbs: a bare verb is the skill's own noun (`approve`), `<noun>.<verb>` another Manage noun's verb on the same item (`payments.capture`). Verbs that delete or return money are never open to a policy.
|
|
37
|
+
A loop runs a skill a Manage noun's schema names in `loop.skill` (`manage-orders`, `manage-payments`, `playbook-mail-to-order` for carts); create and update refuse any other skill, and any verb that loop does not list, with the allowed values. A loop policy allows verbs: a bare verb is the skill's own noun (`approve`), `<noun>.<verb>` another Manage noun's verb on the same item (`payments.capture`). Verbs that delete or return money are never open to a policy.
|
|
38
38
|
|
|
39
39
|
## Login, organization tokens and secrets
|
|
40
40
|
|
package/dist/builtins/loops.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
// schedules the agent repeats it every `every`.
|
|
5
5
|
//
|
|
6
6
|
// A policy allows verbs: a bare verb is the skill's own noun (approve), <noun>.<verb> another
|
|
7
|
-
// Manage noun's verb on the same item (payments.capture).
|
|
8
|
-
// loop.
|
|
7
|
+
// Manage noun's verb on the same item (payments.capture). The skill must be one a Manage noun's
|
|
8
|
+
// schema names in loop.skill (a noun's own skill is not always its loop's: carts loops run
|
|
9
|
+
// playbook-mail-to-order), and each verb in that loop.verbs; the API checks again on every pass.
|
|
9
10
|
import fs from 'node:fs';
|
|
10
11
|
import path from 'node:path';
|
|
11
12
|
import YAML from 'yaml';
|
|
@@ -13,11 +14,11 @@ import { projectDir } from '../config.js';
|
|
|
13
14
|
import { CliError, closest, fail, fixWith } from '../errors.js';
|
|
14
15
|
import { envelope } from '../http.js';
|
|
15
16
|
import { parseSchema } from '../schema.js';
|
|
16
|
-
import { apiSchema } from '../schemas.js';
|
|
17
|
+
import { apiNouns, apiSchema } from '../schemas.js';
|
|
17
18
|
const NAME = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
18
19
|
const EVERY = /^[1-9]\d*(m|h|d)$/;
|
|
19
20
|
const policyFlags = (required) => [
|
|
20
|
-
{ name: 'skill', type: 'string', required, description: 'The
|
|
21
|
+
{ name: 'skill', type: 'string', required, description: 'The skill or playbook the loop runs, as in manage-orders or playbook-mail-to-order: one a noun\'s schema names in loop.skill.' },
|
|
21
22
|
{ name: 'every', type: 'string', required, description: 'How often a pass should run, as in 15m, 2h or 1d. The scheduler reads it; the CLI does not wait.' },
|
|
22
23
|
{ name: 'allow', type: 'array', required, description: 'A verb the policy allows, repeated: approve, payments.capture. Only verbs the noun\'s schema lists in loop.verbs.' },
|
|
23
24
|
{ name: 'max_total', type: 'array', description: 'The largest order total acted on, in major units: 500 for every currency, or EUR=500, repeated per currency. Above it, the item waits for the human.' },
|
|
@@ -166,24 +167,45 @@ function maxTotal(items) {
|
|
|
166
167
|
return [ccy, amount(n)];
|
|
167
168
|
}));
|
|
168
169
|
}
|
|
169
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* The skill and the policy's verbs against the same source loops run uses: each noun schema's loop,
|
|
172
|
+
* { skill, verbs }. A skill no schema's loop names is refused with the ones that are, and so is a
|
|
173
|
+
* verb its loop does not list.
|
|
174
|
+
*/
|
|
170
175
|
async function checkPolicy(def) {
|
|
171
176
|
if (!def.policy.allow.length)
|
|
172
177
|
fail('missing_argument', 'A loop needs at least one --allow.', { path: 'allow' });
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
const skills = await loopSkills();
|
|
179
|
+
const allowed = skills.get(def.skill);
|
|
180
|
+
if (!allowed) {
|
|
181
|
+
const names = [...skills.keys()].sort();
|
|
182
|
+
fail('invalid_value', `--skill ${def.skill} runs no loop. A loop runs ${names.length ? names.join(', ') : 'no skill yet'}.`, {
|
|
183
|
+
path: 'skill',
|
|
184
|
+
did_you_mean: closest(def.skill, names),
|
|
185
|
+
details: { values: names },
|
|
186
|
+
next_step: fixWith('c11r loops create --help'),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
180
189
|
for (const verb of def.policy.allow) {
|
|
181
|
-
if (!allowed.includes(verb)) {
|
|
182
|
-
fail('policy_forbidden', `A ${def.skill} loop may not allow ${verb}. It may allow ${allowed.join(', ')}.`, { path: 'allow', did_you_mean: closest(verb, allowed), details: { loopable: allowed }, next_step: fixWith(`c11r schema ${noun}`) });
|
|
190
|
+
if (!allowed.verbs.includes(verb)) {
|
|
191
|
+
fail('policy_forbidden', `A ${def.skill} loop may not allow ${verb}. It may allow ${allowed.verbs.join(', ')}.`, { path: 'allow', did_you_mean: closest(verb, allowed.verbs), details: { loopable: allowed.verbs }, next_step: fixWith(`c11r schema ${allowed.noun}`) });
|
|
183
192
|
}
|
|
184
193
|
}
|
|
185
194
|
return [];
|
|
186
195
|
}
|
|
196
|
+
/** The skills a loop can run, from the schemas of the nouns that have a loop: skill to its noun and verbs. */
|
|
197
|
+
async function loopSkills() {
|
|
198
|
+
const nouns = (await apiNouns()).filter((n) => n.group === 'manage' && n.served !== false);
|
|
199
|
+
const schemas = await Promise.all(nouns.map((n) => apiSchema(n.noun, []).catch(() => null)));
|
|
200
|
+
const out = new Map();
|
|
201
|
+
for (const s of schemas) {
|
|
202
|
+
if (!s?.loop?.skill)
|
|
203
|
+
continue;
|
|
204
|
+
const seen = out.get(s.loop.skill);
|
|
205
|
+
out.set(s.loop.skill, { noun: seen?.noun ?? s.noun, verbs: [...new Set([...(seen?.verbs ?? []), ...s.loop.verbs])] });
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
187
209
|
async function remove(run, name, file) {
|
|
188
210
|
one(file, name);
|
|
189
211
|
if (run.globals.dryRun)
|
package/dist/builtins/sandbox.js
CHANGED
|
@@ -28,7 +28,7 @@ export const sandbox = {
|
|
|
28
28
|
live: false,
|
|
29
29
|
body: [
|
|
30
30
|
{ name: 'name', type: 'string', description: 'The organization name; without it, an invented one. A name starting zztest marks a test organization.' },
|
|
31
|
-
{ name: 'seed', type: 'boolean', description: 'Loads the demo dataset: two markets, six products, shipping and a
|
|
31
|
+
{ name: 'seed', type: 'boolean', description: 'Loads the demo dataset: two markets, six products, shipping and a manual payment setting (Wire transfer), enough to place a test order at once.' },
|
|
32
32
|
],
|
|
33
33
|
effect: 'Creates a sandbox, stores its token on this machine (never printed) and links this folder to it with .c11r.json. With --seed it answers once the seed is applied, in about 20 seconds.',
|
|
34
34
|
},
|
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.4",
|
|
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": [
|