@1db/cli 0.2.0 → 0.3.1
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 +3 -0
- package/bin/1db.mjs +6 -14
- package/lib/config-commands.mjs +161 -0
- package/lib/core.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,10 @@ 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
16
|
npx @1db/cli form acme.com --name "Quote request" # add another form, print its slug
|
|
17
|
+
npx @1db/cli site acme.com --product-type pallets # website settings
|
|
18
|
+
npx @1db/cli forwarding # lead forwarding rules
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
Run `npx @1db/cli help` for every command, or `npm i -g @1db/cli` to get a plain `1db` command. Zero dependencies, Node 18+.
|
|
22
|
+
What a token may do depends on its scopes (`npx @1db/cli whoami`); see the [docs](https://www.1-db.com/docs/guides/ai-agents#scopes).
|
|
20
23
|
Credentials live in `~/.config/1db/config.json` (mode 600); `ONEDB_TOKEN` overrides.
|
package/bin/1db.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
writeConfig,
|
|
21
21
|
} from "../lib/core.mjs";
|
|
22
22
|
import { browserLogin, installMcp, printMcpCommand, promptToken, saveToken } from "../lib/login.mjs";
|
|
23
|
+
import { CONFIG_HELP, configCommands, parseFieldList } from "../lib/config-commands.mjs";
|
|
23
24
|
|
|
24
25
|
// ---------- commands ----------
|
|
25
26
|
const commands = {
|
|
@@ -45,6 +46,7 @@ const commands = {
|
|
|
45
46
|
const me = await api("/v1/agent/me");
|
|
46
47
|
if (flags.json) return json(me);
|
|
47
48
|
print(`${me.organization?.name} ${dim(`(${me.user?.email ?? me.user?.name})`)}`);
|
|
49
|
+
print(dim(`Scopes: ${(me.scopes ?? []).join(", ")}`));
|
|
48
50
|
print(dim(`API ${settings().apiUrl}`));
|
|
49
51
|
},
|
|
50
52
|
|
|
@@ -162,6 +164,8 @@ const commands = {
|
|
|
162
164
|
1db mcp [--install] Print (or run) the command that adds 1DB's MCP server to Claude Code
|
|
163
165
|
1db logout
|
|
164
166
|
|
|
167
|
+
${CONFIG_HELP}
|
|
168
|
+
|
|
165
169
|
Every command takes --json. Env: ONEDB_TOKEN, ONEDB_API_URL, ONEDB_APP_URL.`);
|
|
166
170
|
},
|
|
167
171
|
};
|
|
@@ -190,22 +194,10 @@ function writeCredentials(result, flags) {
|
|
|
190
194
|
print(dim(`Test it: 1db send ${site.slug}`));
|
|
191
195
|
}
|
|
192
196
|
|
|
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
|
-
|
|
206
197
|
// ---------- main ----------
|
|
207
198
|
const [name = "help", ...rest] = process.argv.slice(2);
|
|
208
|
-
const
|
|
199
|
+
const all = { ...commands, ...configCommands };
|
|
200
|
+
const command = all[name === "--help" || name === "-h" ? "help" : name];
|
|
209
201
|
if (!command) {
|
|
210
202
|
console.error(`Unknown command "${name}". Run \`${CMD} help\`.`);
|
|
211
203
|
process.exit(1);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Configuration commands: website settings, form edits and notifications,
|
|
2
|
+
// lead forwarding, deletes. Each maps to a route in convex/http/agentConfig.ts.
|
|
3
|
+
import { CliError, api, dim, green, json, print, table } from "./core.mjs";
|
|
4
|
+
|
|
5
|
+
const s = (value) => (typeof value === "string" ? value : undefined);
|
|
6
|
+
const list = (value) => (typeof value === "string" ? value.split(",").map((x) => x.trim()).filter(Boolean) : undefined);
|
|
7
|
+
const post = (path, body) => api(path, { method: "POST", body });
|
|
8
|
+
|
|
9
|
+
/** "email*,name,phone:phone" → [{ key: "email", required: true }, { key: "name" }, { key: "phone", type: "phone" }] */
|
|
10
|
+
export function parseFieldList(spec) {
|
|
11
|
+
return spec
|
|
12
|
+
.split(",")
|
|
13
|
+
.map((x) => x.trim())
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.map((x) => {
|
|
16
|
+
const required = x.endsWith("*");
|
|
17
|
+
const [key, type] = x.replace(/\*$/, "").split(":");
|
|
18
|
+
return { key, ...(type ? { type } : {}), ...(required ? { required } : {}) };
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function printForm(result) {
|
|
23
|
+
const { form, notifications: n } = result;
|
|
24
|
+
print(`${form.name} ${dim(`(${form.slug}${result.website ? `, ${result.website.slug}` : ""})`)}`);
|
|
25
|
+
print(dim(`Fields: ${form.fields.map((f) => f.key + (f.required ? "*" : "")).join(", ")}`));
|
|
26
|
+
if (!n) return;
|
|
27
|
+
print(dim(`Email: ${n.email.enabled ? n.email.recipients.join(", ") : "off"}`));
|
|
28
|
+
print(dim(`Webhook: ${n.webhook.enabled ? n.webhook.url : "off"}`));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** --enable / --disable → true / false / undefined */
|
|
32
|
+
const enabledFlag = (flags) => (flags.enable ? true : flags.disable ? false : undefined);
|
|
33
|
+
|
|
34
|
+
function ruleBody(flags) {
|
|
35
|
+
return {
|
|
36
|
+
name: s(flags.name),
|
|
37
|
+
endpoint: s(flags.endpoint),
|
|
38
|
+
authType: s(flags["auth-type"]),
|
|
39
|
+
authHeader: s(flags["auth-header"]),
|
|
40
|
+
authToken: s(flags["auth-token"]),
|
|
41
|
+
sourceIdentifier: s(flags.source),
|
|
42
|
+
enabled: enabledFlag(flags),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const configCommands = {
|
|
47
|
+
async "product-types"({ flags }) {
|
|
48
|
+
const { productTypes } = await api("/v1/agent/product-types");
|
|
49
|
+
if (flags.json) return json(productTypes);
|
|
50
|
+
if (productTypes.length === 0) return print("No product types in this organization's catalog.");
|
|
51
|
+
table(productTypes, ["slug", "name", "active"]);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
async site({ positional, flags }) {
|
|
55
|
+
const website = positional[0];
|
|
56
|
+
if (!website) {
|
|
57
|
+
throw new CliError("Usage: 1db site <website> [--name ..] [--domain ..] [--status active|development|maintenance|archived] [--product-type slug] [--product-types a,b]");
|
|
58
|
+
}
|
|
59
|
+
const result = await post("/v1/agent/website/update", {
|
|
60
|
+
website,
|
|
61
|
+
name: s(flags.name),
|
|
62
|
+
domain: s(flags.domain),
|
|
63
|
+
status: s(flags.status),
|
|
64
|
+
defaultProductType: s(flags["product-type"]),
|
|
65
|
+
productTypes: list(flags["product-types"]),
|
|
66
|
+
});
|
|
67
|
+
if (flags.json) return json(result);
|
|
68
|
+
const w = result.website;
|
|
69
|
+
print(`${green("Saved")} ${w.name} ${dim(`(${w.slug}${w.domain ? `, ${w.domain}` : ""})`)}`);
|
|
70
|
+
print(dim(`Status: ${w.status ?? "active"} · Default product type: ${w.defaultProductType ?? "none"}`));
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async "form-edit"({ positional, flags }) {
|
|
74
|
+
const form = positional[0];
|
|
75
|
+
if (!form) throw new CliError('Usage: 1db form-edit <form slug> [--name ..] [--slug ..] [--fields "email*,name,message:textarea"]');
|
|
76
|
+
const result = s(flags.name) || s(flags.slug) || s(flags.fields)
|
|
77
|
+
? await post("/v1/agent/forms/update", {
|
|
78
|
+
form,
|
|
79
|
+
name: s(flags.name),
|
|
80
|
+
slug: s(flags.slug),
|
|
81
|
+
fields: s(flags.fields) ? parseFieldList(flags.fields) : undefined,
|
|
82
|
+
})
|
|
83
|
+
: await api(`/v1/agent/form?form=${encodeURIComponent(form)}`);
|
|
84
|
+
if (flags.json) return json(result);
|
|
85
|
+
printForm(result);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async notify({ positional, flags }) {
|
|
89
|
+
const form = positional[0];
|
|
90
|
+
if (!form || (flags.emails === undefined && flags.webhook === undefined)) {
|
|
91
|
+
throw new CliError("Usage: 1db notify <form slug> [--emails a@x.com,b@x.com | --no-emails] [--webhook https://... | --no-webhook]");
|
|
92
|
+
}
|
|
93
|
+
const result = await post("/v1/agent/forms/notifications", {
|
|
94
|
+
form,
|
|
95
|
+
email: flags.emails === undefined ? undefined : flags.emails === false ? { enabled: false, recipients: [] } : { enabled: true, recipients: list(flags.emails) },
|
|
96
|
+
webhook: flags.webhook === undefined ? undefined : flags.webhook === false ? { enabled: false } : { enabled: true, url: s(flags.webhook) },
|
|
97
|
+
});
|
|
98
|
+
if (flags.json) return json(result);
|
|
99
|
+
printForm(result);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async forwarding({ positional, flags }) {
|
|
103
|
+
const [action = "list", id] = positional;
|
|
104
|
+
if (action === "list") {
|
|
105
|
+
const { rules, warnings = [] } = await api("/v1/agent/forwarding");
|
|
106
|
+
if (flags.json) return json(rules);
|
|
107
|
+
if (rules.length === 0) print("No forwarding rules.");
|
|
108
|
+
else {
|
|
109
|
+
table(
|
|
110
|
+
rules.map((r) => ({ ...r, on: r.enabled ? "yes" : "no", auth: r.hasAuthToken ? r.authType : "none" })),
|
|
111
|
+
["id", "name", "endpoint", "on", "auth"],
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
for (const warning of warnings) print(`! ${warning}`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let result;
|
|
118
|
+
if (action === "add") {
|
|
119
|
+
if (!s(flags.name) || !s(flags.endpoint)) {
|
|
120
|
+
throw new CliError("Usage: 1db forwarding add --name .. --endpoint https://.. [--auth-token ..] [--auth-header X-API-Key] [--auth-type api_key|bearer|none] [--source 1db] [--enable]");
|
|
121
|
+
}
|
|
122
|
+
result = await post("/v1/agent/forwarding", ruleBody(flags));
|
|
123
|
+
} else if (action === "update" && id) {
|
|
124
|
+
result = await post("/v1/agent/forwarding/update", { id, ...ruleBody(flags) });
|
|
125
|
+
} else if (action === "delete" && id) {
|
|
126
|
+
result = await post("/v1/agent/forwarding/delete", { id, confirm: s(flags.confirm) ?? "" });
|
|
127
|
+
} else {
|
|
128
|
+
throw new CliError("Usage: 1db forwarding [list | add .. | update <id> .. | delete <id> --confirm <rule name>]");
|
|
129
|
+
}
|
|
130
|
+
if (flags.json) return json(result);
|
|
131
|
+
if (result.deleted) return print(green(`Deleted rule ${result.deleted.rule}`));
|
|
132
|
+
const r = result.rule;
|
|
133
|
+
print(`${green("Saved")} ${r.name} ${dim(`(${r.id})`)} → ${r.endpoint}`);
|
|
134
|
+
print(dim(r.enabled ? "Enabled: new leads are forwarded." : "Disabled. Turn it on with --enable when ready."));
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async delete({ positional, flags }) {
|
|
138
|
+
const [kind, target] = positional;
|
|
139
|
+
if (!["form", "site"].includes(kind) || !target || typeof flags.confirm !== "string") {
|
|
140
|
+
throw new CliError("Usage: 1db delete form <slug> --confirm <slug> | 1db delete site <website> --confirm <website slug>");
|
|
141
|
+
}
|
|
142
|
+
const result =
|
|
143
|
+
kind === "form"
|
|
144
|
+
? await post("/v1/agent/forms/delete", { form: target, confirm: flags.confirm })
|
|
145
|
+
: await post("/v1/agent/website/delete", { website: target, confirm: flags.confirm });
|
|
146
|
+
if (flags.json) return json(result);
|
|
147
|
+
print(green(`Deleted ${kind} ${Object.values(result.deleted)[0]}`));
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const CONFIG_HELP = `
|
|
152
|
+
Configure (needs a token with the right scope; see \`1db whoami\`)
|
|
153
|
+
1db site <website> [--status ..] [--product-type slug] [--name ..] [--domain ..]
|
|
154
|
+
1db product-types List the organization's product types
|
|
155
|
+
1db form-edit <form> [--name ..] [--slug ..] [--fields "email*,name"]
|
|
156
|
+
Show a form, or change it
|
|
157
|
+
1db notify <form> [--emails a@x.com,b@x.com | --no-emails] [--webhook https://.. | --no-webhook]
|
|
158
|
+
1db forwarding [list | add | update <id> | delete <id>]
|
|
159
|
+
add --name .. --endpoint https://.. [--auth-token ..] [--auth-header ..] [--enable]
|
|
160
|
+
1db delete form <slug> --confirm <slug>
|
|
161
|
+
1db delete site <website> --confirm <website slug>`;
|
package/lib/core.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export function settings() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// ---------- args ----------
|
|
39
|
-
export const BOOLEAN_FLAGS = new Set(["json", "help", "install", "paste", "relogin"]);
|
|
39
|
+
export const BOOLEAN_FLAGS = new Set(["json", "help", "install", "paste", "relogin", "enable", "disable"]);
|
|
40
40
|
|
|
41
41
|
export function parseArgs(argv) {
|
|
42
42
|
const positional = [];
|