@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 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("No websites yet. Add one with `1db init <domain>`.");
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("Not logged in. Run `1db login` first.");
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("\nOr run `1db mcp --install` to do it for you."));
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 \`1db help\`.`);
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("Not logged in. Run `1db login` (or set ONEDB_TOKEN).");
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("The sign-in code expired. Run `1db login` again.");
48
+ throw new CliError(`The sign-in code expired. Run \`${CMD} login\` again.`);
49
49
  }
50
50
 
51
51
  export async function promptToken() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1db/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Connect websites to 1DB from your terminal or an AI agent: onboard a domain, get its lead keys, send and list leads.",
5
5
  "type": "module",
6
6
  "bin": {