@1db/cli 0.1.0 → 0.2.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 +1 -0
- package/bin/1db.mjs +46 -4
- package/lib/core.mjs +3 -1
- package/lib/login.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ npx @1db/cli init acme.com # scan + add the site, write DB_* vars to .env.lo
|
|
|
13
13
|
npx @1db/cli send acme.com # send a test lead
|
|
14
14
|
npx @1db/cli leads acme.com # see it arrive
|
|
15
15
|
npx @1db/cli keys acme.com # print the site's endpoint, key and form slug
|
|
16
|
+
npx @1db/cli form acme.com --name "Quote request" # add another form, print its slug
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
Run `npx @1db/cli help` for every command, or `npm i -g @1db/cli` to get a plain `1db` command. Zero dependencies, Node 18+.
|
package/bin/1db.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// 1DB CLI: zero dependencies, talks to the agent API (/v1/agent/*) with an
|
|
3
3
|
// agent token. Run `1db help` for commands.
|
|
4
4
|
import {
|
|
5
|
+
CMD,
|
|
5
6
|
CliError,
|
|
6
7
|
api,
|
|
7
8
|
bold,
|
|
@@ -50,7 +51,7 @@ const commands = {
|
|
|
50
51
|
async websites({ flags }) {
|
|
51
52
|
const { websites } = await api("/v1/agent/websites");
|
|
52
53
|
if (flags.json) return json(websites);
|
|
53
|
-
if (websites.length === 0) return print(
|
|
54
|
+
if (websites.length === 0) return print(`No websites yet. Add one with \`${CMD} init <domain>\`.`);
|
|
54
55
|
table(websites, ["slug", "name", "domain", "framework"]);
|
|
55
56
|
},
|
|
56
57
|
|
|
@@ -71,6 +72,31 @@ const commands = {
|
|
|
71
72
|
writeCredentials(result, { ...flags, env: flags["env-file"] ? flags.env : false });
|
|
72
73
|
},
|
|
73
74
|
|
|
75
|
+
async form({ positional, flags }) {
|
|
76
|
+
const website = positional[0];
|
|
77
|
+
if (!website || typeof flags.name !== "string") {
|
|
78
|
+
throw new CliError('Usage: 1db form <website> --name "Quote request" [--fields "email*,name,phone:phone,message:textarea"] [--preset "B2B"] [--slug quote]');
|
|
79
|
+
}
|
|
80
|
+
const result = await api("/v1/agent/forms", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: {
|
|
83
|
+
website,
|
|
84
|
+
name: flags.name,
|
|
85
|
+
slug: typeof flags.slug === "string" ? flags.slug : undefined,
|
|
86
|
+
preset: typeof flags.preset === "string" ? flags.preset : undefined,
|
|
87
|
+
fields: typeof flags.fields === "string" ? parseFieldList(flags.fields) : undefined,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (flags.json) return json(result);
|
|
91
|
+
const form = result.forms.find((f) => f.slug === result.formSlug);
|
|
92
|
+
print(`${result.created ? green("Created") : "Found"} ${bold(form.name)} on ${result.website.name}`);
|
|
93
|
+
print(dim(`Fields: ${form.fields.map((f) => f.key + (f.required ? "*" : "")).join(", ")}`));
|
|
94
|
+
// Its own env var, so DB_FORM_SLUG keeps pointing at the site's existing form
|
|
95
|
+
print(`\n${result.formEnvVar}=${form.slug}`);
|
|
96
|
+
print(dim(`\nPost its leads to ${result.leadsUrl} with {"formSlug": "${form.slug}", ...} and the site's DB_API_KEY.`));
|
|
97
|
+
print(dim(`Test it: ${CMD} send ${result.website.slug} --form ${form.slug}`));
|
|
98
|
+
},
|
|
99
|
+
|
|
74
100
|
async send({ positional, flags }) {
|
|
75
101
|
const [website, ...pairs] = positional;
|
|
76
102
|
if (!website) throw new CliError("Usage: 1db send <website> [email=jane@acme.com message=\"Hi\" ...] [--form slug]");
|
|
@@ -104,10 +130,10 @@ const commands = {
|
|
|
104
130
|
},
|
|
105
131
|
|
|
106
132
|
async mcp({ flags }) {
|
|
107
|
-
if (!settings().token) throw new CliError(
|
|
133
|
+
if (!settings().token) throw new CliError(`Not logged in. Run \`${CMD} login\` first.`);
|
|
108
134
|
if (flags.install) return installMcp();
|
|
109
135
|
printMcpCommand();
|
|
110
|
-
print(dim(
|
|
136
|
+
print(dim(`\nOr run \`${CMD} mcp --install\` to do it for you.`));
|
|
111
137
|
},
|
|
112
138
|
|
|
113
139
|
/** One step for a new machine: sign in, then add the MCP server to Claude Code. */
|
|
@@ -128,6 +154,9 @@ const commands = {
|
|
|
128
154
|
1db init <domain> Scan + add a website, write its keys to .env.local
|
|
129
155
|
[--name "Acme"] [--env-file .env] [--prefix VITE_|NEXT_PUBLIC_|PUBLIC_] [--no-env]
|
|
130
156
|
1db keys <website> Print a website's endpoint, key and form slug
|
|
157
|
+
1db form <website> --name "Quote request"
|
|
158
|
+
Add another form to a website and print its slug
|
|
159
|
+
[--fields "email*,name,phone:phone,message:textarea"] [--preset "B2B"] [--slug quote]
|
|
131
160
|
1db send <website> [k=v ...] Send a (test) lead through the real ingest endpoint
|
|
132
161
|
1db leads [website] [--limit 20] Most recent leads
|
|
133
162
|
1db mcp [--install] Print (or run) the command that adds 1DB's MCP server to Claude Code
|
|
@@ -161,11 +190,24 @@ function writeCredentials(result, flags) {
|
|
|
161
190
|
print(dim(`Test it: 1db send ${site.slug}`));
|
|
162
191
|
}
|
|
163
192
|
|
|
193
|
+
/** "email*,name,phone:phone" → [{ key: "email", required: true }, { key: "name" }, { key: "phone", type: "phone" }] */
|
|
194
|
+
function parseFieldList(spec) {
|
|
195
|
+
return spec
|
|
196
|
+
.split(",")
|
|
197
|
+
.map((s) => s.trim())
|
|
198
|
+
.filter(Boolean)
|
|
199
|
+
.map((s) => {
|
|
200
|
+
const required = s.endsWith("*");
|
|
201
|
+
const [key, type] = s.replace(/\*$/, "").split(":");
|
|
202
|
+
return { key, ...(type ? { type } : {}), ...(required ? { required } : {}) };
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
164
206
|
// ---------- main ----------
|
|
165
207
|
const [name = "help", ...rest] = process.argv.slice(2);
|
|
166
208
|
const command = commands[name === "--help" || name === "-h" ? "help" : name];
|
|
167
209
|
if (!command) {
|
|
168
|
-
console.error(`Unknown command "${name}". Run
|
|
210
|
+
console.error(`Unknown command "${name}". Run \`${CMD} help\`.`);
|
|
169
211
|
process.exit(1);
|
|
170
212
|
}
|
|
171
213
|
try {
|
package/lib/core.mjs
CHANGED
|
@@ -8,6 +8,8 @@ export const DEFAULT_API_URL = "https://gallant-goshawk-513.convex.site";
|
|
|
8
8
|
export const DEFAULT_APP_URL = "https://www.1-db.com";
|
|
9
9
|
export const CONFIG_DIR = join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "1db");
|
|
10
10
|
export const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
11
|
+
// How to name a command in hints: npx runs us with npm_command=exec, a global install as plain 1db.
|
|
12
|
+
export const CMD = process.env.npm_command === "exec" ? "npx @1db/cli" : "1db";
|
|
11
13
|
|
|
12
14
|
// ---------- config ----------
|
|
13
15
|
export function readConfig() {
|
|
@@ -58,7 +60,7 @@ export class CliError extends Error {}
|
|
|
58
60
|
|
|
59
61
|
export async function api(path, { method = "GET", body } = {}) {
|
|
60
62
|
const { token, apiUrl } = settings();
|
|
61
|
-
if (!token) throw new CliError(
|
|
63
|
+
if (!token) throw new CliError(`Not logged in. Run \`${CMD} login\` (or set ONEDB_TOKEN).`);
|
|
62
64
|
let res;
|
|
63
65
|
try {
|
|
64
66
|
res = await fetch(`${apiUrl}${path}`, {
|
package/lib/login.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { execFileSync, spawn } from "node:child_process";
|
|
5
|
-
import { CliError, CONFIG_FILE, api, bold, dim, green, print, readConfig, settings, writeConfig } from "./core.mjs";
|
|
5
|
+
import { CMD, CliError, CONFIG_FILE, api, bold, dim, green, print, readConfig, settings, writeConfig } from "./core.mjs";
|
|
6
6
|
|
|
7
7
|
// ---------- login ----------
|
|
8
8
|
export function openBrowser(url) {
|
|
@@ -45,7 +45,7 @@ export async function browserLogin() {
|
|
|
45
45
|
if (result.status === "denied") throw new CliError("Sign-in was denied in the browser.");
|
|
46
46
|
if (result.status === "expired") break;
|
|
47
47
|
}
|
|
48
|
-
throw new CliError(
|
|
48
|
+
throw new CliError(`The sign-in code expired. Run \`${CMD} login\` again.`);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export async function promptToken() {
|