@mapled/cli 0.6.0 → 0.7.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.
- package/README.md +22 -3
- package/dist/args.js +1 -1
- package/dist/commands.d.ts +8 -3
- package/dist/commands.js +55 -0
- package/dist/index.js +10 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ npx @mapled/cli doctor
|
|
|
14
14
|
|
|
15
15
|
Node 18 or newer. No dependencies. Prefer a global install? `npm install -g @mapled/cli` puts `mapled` on your PATH, and every command below works as `mapled …`.
|
|
16
16
|
|
|
17
|
+
Connecting a site without an AI agent? [Integrate by hand](https://www.npmjs.com/package/@mapled/next#integrate-by-hand) in the `@mapled/next` README walks through the whole integration — modeling the content, reading it, the publish webhook, preview, forms — and uses these commands along the way.
|
|
18
|
+
|
|
17
19
|
## Sign in
|
|
18
20
|
|
|
19
21
|
```bash
|
|
@@ -127,6 +129,23 @@ npx @mapled/cli bindings pull
|
|
|
127
129
|
|
|
128
130
|
`bindings pull` writes the pushed manifest into `mapled/manifest.json` — say, after your agent pushed one from a chat and you want the repository's copy. It refuses to overwrite a local file that differs unless you pass `--force`.
|
|
129
131
|
|
|
132
|
+
## Create a form
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx @mapled/cli forms create "Contact"
|
|
136
|
+
npx @mapled/cli forms list
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`forms create` makes a form the site can post submissions to — the same call your AI agent's `create_form` makes, so an integration done by hand needs no agent for it:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
Created the form “Contact” (contact) in Dolphin Landing.
|
|
143
|
+
The site posts submissions to https://api.mapled.io/v1/delivery/forms/contact — flat JSON with the delivery key in X-Mapled-Key, an empty hidden _gotcha field, and the consent label as _consent when the form has a consent checkbox.
|
|
144
|
+
Submissions show up in Mapled under Forms.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The key comes from the name; `--key <key>` chooses it (lowercase letters, digits and dashes), `--no-spam-protection` turns the honeypot off. Creating a form changes structure, so it needs the builder plan. `forms list` shows every form with the URL the site posts to (`--json` for scripts). The component that posts to it is in the [`@mapled/next` README](https://www.npmjs.com/package/@mapled/next#forms).
|
|
148
|
+
|
|
130
149
|
## Leave a guide for the next agent
|
|
131
150
|
|
|
132
151
|
```bash
|
|
@@ -155,8 +174,8 @@ Mapled doctor — Dolphin Landing
|
|
|
155
174
|
✓ MAPLED.md MAPLED.md matches the schema (3f9a1c2b4d5e) and manifest v3
|
|
156
175
|
✓ Environment MAPLED_KEY and MAPLED_WEBHOOK_SECRET in .env.local
|
|
157
176
|
✓ Secrets in git No env files or Mapled secrets are tracked.
|
|
158
|
-
✓ @mapled/next 0.7.
|
|
159
|
-
✓ mapled CLI 0.
|
|
177
|
+
✓ @mapled/next 0.7.1 (current)
|
|
178
|
+
✓ mapled CLI 0.7.0 (current)
|
|
160
179
|
✓ Revalidation route app/api/mapled/revalidate/route.ts
|
|
161
180
|
⚠ Preview route app/api/mapled/preview/route.ts is missing — mount createPreviewHandler from "@mapled/next/server" so Preview from Mapled works.
|
|
162
181
|
✓ Site reads content Last read 3m ago
|
|
@@ -175,7 +194,7 @@ Exit code 1 when something is marked ✗; `--json` prints the checks for scripts
|
|
|
175
194
|
|
|
176
195
|
- `--api <origin>` — a Mapled API other than `https://api.mapled.io` (or set `MAPLED_API_URL`); `project link` remembers it in `mapled.json`
|
|
177
196
|
- `--no-browser` — print the sign-in link instead of opening a browser
|
|
178
|
-
- `--json` — machine-readable output (`doctor`, `schema diff`, `scan`, `manifest validate`)
|
|
197
|
+
- `--json` — machine-readable output (`doctor`, `schema diff`, `scan`, `manifest validate`, `forms list`)
|
|
179
198
|
- `MAPLED_CONFIG_DIR` — where credentials live (default: `$XDG_CONFIG_HOME/mapled` or `~/.config/mapled`)
|
|
180
199
|
|
|
181
200
|
## Files in the repository
|
package/dist/args.js
CHANGED
|
@@ -2,7 +2,7 @@ import { CliError } from "./errors.js";
|
|
|
2
2
|
/** Minimal argument parsing: the leading words name the command;
|
|
3
3
|
`--flag`, `--flag=value` and `--flag value` (for the flags that take
|
|
4
4
|
one) follow. Nothing here is user content beyond the command line. */
|
|
5
|
-
const VALUE_FLAGS = new Set(["api", "project", "out"]);
|
|
5
|
+
const VALUE_FLAGS = new Set(["api", "project", "out", "key"]);
|
|
6
6
|
export function parseArgs(argv) {
|
|
7
7
|
const words = [];
|
|
8
8
|
const flags = {};
|
package/dist/commands.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { type ParsedArgs } from "./args.js";
|
|
|
2
2
|
import { type FoundConfig } from "./config.js";
|
|
3
3
|
import { type Connection } from "./credentials.js";
|
|
4
4
|
import { type Fetch } from "./oauth.js";
|
|
5
|
-
/** The commands of waves 1 and 2 (§31)
|
|
6
|
-
world comes in through `Ctx`, so the
|
|
7
|
-
fake API and a temp directory. */
|
|
5
|
+
/** The commands of waves 1 and 2 (§31), MAPLED.md (§21.3) and forms.
|
|
6
|
+
Everything that talks to the world comes in through `Ctx`, so the
|
|
7
|
+
commands run in tests against a fake API and a temp directory. */
|
|
8
8
|
export type Ctx = {
|
|
9
9
|
cwd: string;
|
|
10
10
|
env: NodeJS.ProcessEnv;
|
|
@@ -29,6 +29,11 @@ export declare function manifestValidate(ctx: Ctx, flags: Flags): Promise<number
|
|
|
29
29
|
export declare function bindingsPush(ctx: Ctx, flags: Flags): Promise<void>;
|
|
30
30
|
export declare function bindingsPull(ctx: Ctx, flags: Flags): Promise<void>;
|
|
31
31
|
export declare function mdPull(ctx: Ctx, flags: Flags): Promise<void>;
|
|
32
|
+
/** `mapled forms create "Contact"` — a form the site posts submissions to.
|
|
33
|
+
The same call an AI agent's `create_form` makes, so an integration
|
|
34
|
+
done by hand needs no agent for it. */
|
|
35
|
+
export declare function formsCreate(ctx: Ctx, flags: Flags, words: string[]): Promise<void>;
|
|
36
|
+
export declare function formsList(ctx: Ctx, flags: Flags): Promise<void>;
|
|
32
37
|
export declare function scan(ctx: Ctx, flags: Flags): Promise<void>;
|
|
33
38
|
export declare function doctor(ctx: Ctx, flags: Flags): Promise<number>;
|
|
34
39
|
export {};
|
package/dist/commands.js
CHANGED
|
@@ -390,6 +390,61 @@ export async function mdPull(ctx, flags) {
|
|
|
390
390
|
else
|
|
391
391
|
ctx.out(`Updated ${MD_FILE} — ${summary}; the notes below the notes line were kept.`);
|
|
392
392
|
}
|
|
393
|
+
const FORM_KEY = /^[a-z][a-z0-9-]{0,63}$/;
|
|
394
|
+
const submitUrl = (api, key) => `${api}/v1/delivery/forms/${key}`;
|
|
395
|
+
/** `mapled forms create "Contact"` — a form the site posts submissions to.
|
|
396
|
+
The same call an AI agent's `create_form` makes, so an integration
|
|
397
|
+
done by hand needs no agent for it. */
|
|
398
|
+
export async function formsCreate(ctx, flags, words) {
|
|
399
|
+
const displayName = words.join(" ").trim();
|
|
400
|
+
if (!displayName)
|
|
401
|
+
throw new CliError('Name the form: `mapled forms create "Contact"`.');
|
|
402
|
+
if (displayName.length > 80)
|
|
403
|
+
throw new CliError("A form name is up to 80 characters.");
|
|
404
|
+
const key = stringFlag(flags, "key");
|
|
405
|
+
if (flags.key !== undefined && (!key || !FORM_KEY.test(key))) {
|
|
406
|
+
throw new CliError("A form key is lowercase letters, digits and dashes, starting with a letter (up to 64 characters).");
|
|
407
|
+
}
|
|
408
|
+
const { session } = await openSession(ctx, flags);
|
|
409
|
+
let created;
|
|
410
|
+
try {
|
|
411
|
+
created = await session.send("POST", "/v1/agent/forms", {
|
|
412
|
+
displayName,
|
|
413
|
+
...(key ? { key } : {}),
|
|
414
|
+
...(flags["no-spam-protection"] ? { spamProtection: false } : {}),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
if (err instanceof ApiError && err.code === "BUILDER_PLAN_REQUIRED") {
|
|
419
|
+
throw new CliError("Creating a form changes the project's structure, which needs a builder plan — ask the project owner to upgrade.");
|
|
420
|
+
}
|
|
421
|
+
throw err;
|
|
422
|
+
}
|
|
423
|
+
const form = created.form;
|
|
424
|
+
ctx.out(`Created the form “${form.displayName}” (${form.key}) in ${session.conn.projectName}.`);
|
|
425
|
+
ctx.out(`The site posts submissions to ${submitUrl(session.conn.api, form.key)} — flat JSON with the delivery key in X-Mapled-Key` +
|
|
426
|
+
`${form.spamProtection ? ", an empty hidden _gotcha field" : ""}, and the consent label as _consent when the form has a consent checkbox.`);
|
|
427
|
+
ctx.out("Submissions show up in Mapled under Forms.");
|
|
428
|
+
}
|
|
429
|
+
export async function formsList(ctx, flags) {
|
|
430
|
+
const { session } = await openSession(ctx, flags);
|
|
431
|
+
const { forms } = await session.get("/v1/agent/forms");
|
|
432
|
+
if (flags.json) {
|
|
433
|
+
ctx.out(JSON.stringify({ forms: forms.map((f) => ({ ...f, submitUrl: submitUrl(session.conn.api, f.key) })) }, null, 2));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (forms.length === 0) {
|
|
437
|
+
ctx.out(`No forms in ${session.conn.projectName} yet. Create one with \`mapled forms create "Contact"\`.`);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const keyWidth = Math.max(...forms.map((f) => f.key.length));
|
|
441
|
+
const nameWidth = Math.max(...forms.map((f) => f.displayName.length));
|
|
442
|
+
ctx.out(`Forms of ${session.conn.projectName}`);
|
|
443
|
+
ctx.out("");
|
|
444
|
+
for (const f of forms) {
|
|
445
|
+
ctx.out(`${f.key.padEnd(keyWidth)} ${f.displayName.padEnd(nameWidth)} spam protection ${f.spamProtection ? "on " : "off"} ${submitUrl(session.conn.api, f.key)}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
393
448
|
/* ---- wave 2: the scanner ---- */
|
|
394
449
|
function describeScan(result, schema) {
|
|
395
450
|
const singles = new Set(schema.collections.filter((c) => c.kind === "single").map((c) => c.key));
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { parseArgs } from "./args.js";
|
|
5
5
|
import { openBrowser } from "./browser.js";
|
|
6
|
-
import { bindingsPull, bindingsPush, doctor, generate, link, login, logout, manifestValidate, mdPull, scan, schemaDiff, schemaPull, } from "./commands.js";
|
|
6
|
+
import { bindingsPull, bindingsPush, doctor, formsCreate, formsList, generate, link, login, logout, manifestValidate, mdPull, scan, schemaDiff, schemaPull, } from "./commands.js";
|
|
7
7
|
import { credentialsPath } from "./credentials.js";
|
|
8
8
|
import { CliError } from "./errors.js";
|
|
9
9
|
/** `mapled` — sign in, link a repository to its project, generate types
|
|
@@ -24,6 +24,8 @@ Usage
|
|
|
24
24
|
mapled bindings push [--dry-run] Push mapled/manifest.json so Mapled grades every binding
|
|
25
25
|
mapled bindings pull [--force] Write the pushed manifest into mapled/manifest.json
|
|
26
26
|
mapled md pull [--force] Write MAPLED.md, the guide for the next agent, from the project
|
|
27
|
+
mapled forms create <name> [--key <key>] Create a form the site posts submissions to (--no-spam-protection to skip the honeypot)
|
|
28
|
+
mapled forms list [--json] List the project's forms and where the site posts to
|
|
27
29
|
mapled doctor [--json] Check the integration end to end
|
|
28
30
|
|
|
29
31
|
Options
|
|
@@ -108,6 +110,12 @@ async function main(argv) {
|
|
|
108
110
|
case "md pull":
|
|
109
111
|
await mdPull(ctx, flags);
|
|
110
112
|
return 0;
|
|
113
|
+
case "forms create":
|
|
114
|
+
await formsCreate(ctx, flags, words.slice(2));
|
|
115
|
+
return 0;
|
|
116
|
+
case "forms list":
|
|
117
|
+
await formsList(ctx, flags);
|
|
118
|
+
return 0;
|
|
111
119
|
case "doctor":
|
|
112
120
|
return doctor(ctx, flags);
|
|
113
121
|
case "auth":
|
|
@@ -117,6 +125,7 @@ async function main(argv) {
|
|
|
117
125
|
case "manifest":
|
|
118
126
|
case "bindings":
|
|
119
127
|
case "md":
|
|
128
|
+
case "forms":
|
|
120
129
|
console.log(HELP);
|
|
121
130
|
return 2;
|
|
122
131
|
default:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapled/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Mapled CLI — sign in, link a project, generate TypeScript types for its content and check the site's integration.",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Mapled CLI — sign in, link a project, generate TypeScript types for its content, create forms and check the site's integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mapled.io",
|
|
7
7
|
"keywords": [
|