@wabery/cli 0.3.0 → 0.5.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 +93 -12
- package/dist/api-client.d.ts +7 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +24 -12
- package/dist/api-client.js.map +1 -1
- package/dist/cli.js +372 -27
- package/dist/cli.js.map +1 -1
- package/dist/config-example.d.ts +4 -0
- package/dist/config-example.d.ts.map +1 -1
- package/dist/config-example.js +6 -0
- package/dist/config-example.js.map +1 -1
- package/dist/diagnostics.js +1 -1
- package/dist/local-config.d.ts +86 -0
- package/dist/local-config.d.ts.map +1 -1
- package/dist/local-config.js +259 -15
- package/dist/local-config.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +43 -11
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,12 +28,12 @@ npx wabery doctor
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
`wabery login` prints an authorization URL and tries to open it in your
|
|
31
|
-
browser. Approve the request on `app.wabery.com`,
|
|
32
|
-
shows you, and paste it back into the
|
|
33
|
-
|
|
34
|
-
local callback server or open browser is required — if the browser does
|
|
35
|
-
open, just open the printed URL yourself. Use `wabery login --no-open` to
|
|
36
|
-
the open attempt. You can still override credentials explicitly:
|
|
31
|
+
browser. Approve the request on `app.wabery.com`, choose the project the CLI
|
|
32
|
+
should manage, copy the API key the page shows you, and paste it back into the
|
|
33
|
+
terminal. The key is scoped to that project and stored in your local CLI config
|
|
34
|
+
file. No local callback server or open browser is required — if the browser does
|
|
35
|
+
not open, just open the printed URL yourself. Use `wabery login --no-open` to
|
|
36
|
+
skip the open attempt. You can still override credentials explicitly:
|
|
37
37
|
|
|
38
38
|
```sh
|
|
39
39
|
export WABERY_API_KEY="wab_live_..."
|
|
@@ -45,14 +45,58 @@ use a placeholder so credentials can live in your CLI config, shell, MCP client
|
|
|
45
45
|
secret storage, or another local secret manager. Public API keys support scoped
|
|
46
46
|
permissions. Use the smallest scope set the integration needs.
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
## Working across multiple projects
|
|
49
|
+
|
|
50
|
+
API keys are scoped to a single project, so the CLI stores **one key per
|
|
51
|
+
project** and auto-selects the right one based on the repository you run from.
|
|
52
|
+
|
|
53
|
+
- `wabery login` (run once per project) stores that project's key in the global
|
|
54
|
+
config and, the first time, writes a committed, secret-free `.wabery.json`
|
|
55
|
+
(`{"project_id": "..."}`) at the repo root.
|
|
56
|
+
- When you `cd` into a repo, the CLI walks up from the current directory to find
|
|
57
|
+
`.wabery.json` (falling back to a `wabery.config.json`'s `project_id`) and uses
|
|
58
|
+
the matching stored key automatically — no flags needed.
|
|
59
|
+
- `wabery use <project-id-or-name>` binds the current repo to a different
|
|
60
|
+
project you've logged into (rewrites `.wabery.json`).
|
|
61
|
+
- `wabery whoami` shows the active key (masked), org, project, and how it was
|
|
62
|
+
resolved.
|
|
63
|
+
- `wabery logout [--project <id|name>]` removes one stored key;
|
|
64
|
+
`wabery logout --all` clears them all.
|
|
65
|
+
|
|
66
|
+
`.wabery.json` is safe to commit — it never contains a key. A teammate who
|
|
67
|
+
clones the repo runs `wabery login` once to add their own key for that project.
|
|
68
|
+
|
|
69
|
+
Resolution precedence for which key is used: `--api-key` / `WABERY_API_KEY` >
|
|
70
|
+
the stored key for the project from `--project-id` > `WABERY_PROJECT_ID` >
|
|
71
|
+
`.wabery.json` > (if exactly one project is stored) that one.
|
|
72
|
+
|
|
73
|
+
Print a ready-to-paste env block for a project (API key, base URL, project id,
|
|
74
|
+
channel id, webhook URL, and the webhook signing secret):
|
|
51
75
|
|
|
52
76
|
```sh
|
|
53
77
|
wabery env --project-id "proj_id"
|
|
54
78
|
```
|
|
55
79
|
|
|
80
|
+
The signing secret is read from `GET /projects/{id}`, which reveals it for a
|
|
81
|
+
single project (the list never does). If a project has no secret yet, set one
|
|
82
|
+
from your own secret manager (bring-your-own) so both sides stay in sync:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# Set name / webhook URL / a known signing secret on an existing project
|
|
86
|
+
wabery projects update "proj_id" \
|
|
87
|
+
--webhook-url "https://yourapp.com/webhooks/wabery" \
|
|
88
|
+
--webhook-secret "$WABERY_WEBHOOK_SECRET"
|
|
89
|
+
|
|
90
|
+
# Or rotate to a fresh server-generated secret (returned once)
|
|
91
|
+
wabery projects rotate-secret "proj_id"
|
|
92
|
+
|
|
93
|
+
# Reveal a project (includes webhook_secret)
|
|
94
|
+
wabery projects get "proj_id"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The same secret signs both inbound webhooks and outbound WhatsApp Flow
|
|
98
|
+
data-exchange calls.
|
|
99
|
+
|
|
56
100
|
For local repository development, build the workspace package:
|
|
57
101
|
|
|
58
102
|
```sh
|
|
@@ -157,7 +201,9 @@ Installed CLI:
|
|
|
157
201
|
- `wabery_preview_config_diff`
|
|
158
202
|
- `wabery_apply_config`
|
|
159
203
|
- `wabery_list_projects`
|
|
160
|
-
- `
|
|
204
|
+
- `wabery_get_project`
|
|
205
|
+
- `wabery_update_project`
|
|
206
|
+
- `wabery_rotate_webhook_secret`
|
|
161
207
|
- `wabery_list_channels`
|
|
162
208
|
- `wabery_list_flows`
|
|
163
209
|
- `wabery_get_flow`
|
|
@@ -173,13 +219,12 @@ Installed CLI:
|
|
|
173
219
|
|
|
174
220
|
## Typical Developer Workflow
|
|
175
221
|
|
|
176
|
-
1. List
|
|
222
|
+
1. List the project your key is scoped to:
|
|
177
223
|
|
|
178
224
|
```sh
|
|
179
225
|
wabery login
|
|
180
226
|
wabery doctor
|
|
181
227
|
wabery projects list
|
|
182
|
-
wabery projects create --name "Demo project"
|
|
183
228
|
```
|
|
184
229
|
|
|
185
230
|
2. Enroll a WhatsApp phone number and capture the returned `contact_id` and `channel_id`:
|
|
@@ -189,8 +234,14 @@ wabery contacts enroll \
|
|
|
189
234
|
--project-id "proj_or_agent_id" \
|
|
190
235
|
--phone "+15555550123" \
|
|
191
236
|
--name "Jane Developer" \
|
|
237
|
+
--preferred-language "es" \
|
|
192
238
|
--reference-id "user_123"
|
|
193
239
|
wabery contacts get "contact_id"
|
|
240
|
+
|
|
241
|
+
# Update language / metadata later (metadata is shallow-merged):
|
|
242
|
+
wabery contacts update "contact_id" \
|
|
243
|
+
--preferred-language "id" \
|
|
244
|
+
--metadata '{"linked": true}'
|
|
194
245
|
```
|
|
195
246
|
|
|
196
247
|
3. Generate and validate `wabery.config.json`:
|
|
@@ -202,6 +253,27 @@ wabery config diff wabery.config.json
|
|
|
202
253
|
wabery config apply wabery.config.json
|
|
203
254
|
```
|
|
204
255
|
|
|
256
|
+
`wabery.config.json` can manage the project's webhook declaratively, so
|
|
257
|
+
`config apply` keeps the webhook URL and signing secret in sync alongside flows:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"project_id": "proj_or_agent_id",
|
|
262
|
+
"webhook": {
|
|
263
|
+
"url": "https://yourapp.com/webhooks/wabery",
|
|
264
|
+
"signing_enabled": true,
|
|
265
|
+
"secret": "whsec_your_own_secret_from_your_secret_manager"
|
|
266
|
+
},
|
|
267
|
+
"flows": []
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Omit `secret` to have the server generate one; set `signing_enabled: false` to
|
|
272
|
+
clear it. The webhook setup is scriptable after the project exists: create or
|
|
273
|
+
select the project in the dashboard, create the project-scoped API key with
|
|
274
|
+
`wabery login`, run `config apply` to point the webhook, and run `wabery env` to
|
|
275
|
+
emit the secret for your deploy.
|
|
276
|
+
|
|
205
277
|
4. Publish a flow explicitly after review (`--wait` blocks until Meta reports it
|
|
206
278
|
published, or fails with the rejection reason):
|
|
207
279
|
|
|
@@ -217,6 +289,15 @@ wabery channels list
|
|
|
217
289
|
wabery flows send "flow_id" --channel-id "channel_id" --contact-id "contact_id"
|
|
218
290
|
```
|
|
219
291
|
|
|
292
|
+
For locale-aware sends, publish one flow per language sharing a `config_key`
|
|
293
|
+
(omit `locale` for the default) and send by key — Wabery resolves the variant for
|
|
294
|
+
the contact's `preferred_language`, falling back to the default:
|
|
295
|
+
|
|
296
|
+
```sh
|
|
297
|
+
wabery flows send --config-key "lead_intake" --locale "es" \
|
|
298
|
+
--channel-id "channel_id" --contact-id "contact_id"
|
|
299
|
+
```
|
|
300
|
+
|
|
220
301
|
For sandbox projects, `wabery contacts enroll` returns the shared sandbox `channel_id` that is authorized for that phone number. Use that `channel_id` when sending a flow to the returned `contact_id`.
|
|
221
302
|
|
|
222
303
|
To remove a phone's sandbox authorization:
|
package/dist/api-client.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export interface WaberyClientOptions {
|
|
|
2
2
|
apiKey?: string;
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
apiVersion?: string;
|
|
5
|
+
/** Target project (selects which stored credential to use). */
|
|
6
|
+
projectId?: string;
|
|
7
|
+
/** Working directory for repo-local `.wabery.json` discovery. */
|
|
8
|
+
cwd?: string;
|
|
5
9
|
}
|
|
6
10
|
export declare class WaberyApiError extends Error {
|
|
7
11
|
readonly status: number;
|
|
@@ -12,10 +16,13 @@ export declare class WaberyApiClient {
|
|
|
12
16
|
private readonly apiKey;
|
|
13
17
|
private readonly baseUrl;
|
|
14
18
|
private readonly apiVersion?;
|
|
19
|
+
/** The project this client's key is scoped to, when known. */
|
|
20
|
+
readonly projectId?: string;
|
|
15
21
|
constructor(options?: WaberyClientOptions);
|
|
16
22
|
get(path: string, query?: Record<string, string | number | undefined>): Promise<unknown>;
|
|
17
23
|
post(path: string, body?: unknown): Promise<unknown>;
|
|
18
24
|
put(path: string, body?: unknown): Promise<unknown>;
|
|
25
|
+
patch(path: string, body?: unknown): Promise<unknown>;
|
|
19
26
|
delete(path: string): Promise<unknown>;
|
|
20
27
|
private request;
|
|
21
28
|
}
|
package/dist/api-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGvB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO;CAI9B;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEhB,OAAO,GAAE,mBAAwB;IAgBvC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAIrE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIjC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIhC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIlC,MAAM,CAAC,IAAI,EAAE,MAAM;YAIX,OAAO;CA0ErB"}
|
package/dist/api-client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveCredential } from "./local-config.js";
|
|
2
2
|
export class WaberyApiError extends Error {
|
|
3
3
|
status;
|
|
4
4
|
body;
|
|
@@ -12,17 +12,18 @@ export class WaberyApiClient {
|
|
|
12
12
|
apiKey;
|
|
13
13
|
baseUrl;
|
|
14
14
|
apiVersion;
|
|
15
|
+
/** The project this client's key is scoped to, when known. */
|
|
16
|
+
projectId;
|
|
15
17
|
constructor(options = {}) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"https://api.wabery.com/v1");
|
|
18
|
+
const resolved = resolveCredential({
|
|
19
|
+
apiKey: options.apiKey,
|
|
20
|
+
baseUrl: options.baseUrl,
|
|
21
|
+
projectId: options.projectId,
|
|
22
|
+
cwd: options.cwd,
|
|
23
|
+
});
|
|
24
|
+
this.apiKey = resolved.apiKey;
|
|
25
|
+
this.projectId = resolved.projectId;
|
|
26
|
+
this.baseUrl = normalizeBaseUrl(resolved.baseUrl ?? "https://api.wabery.com/v1");
|
|
26
27
|
this.apiVersion = options.apiVersion ?? process.env.WABERY_API_VERSION;
|
|
27
28
|
}
|
|
28
29
|
async get(path, query) {
|
|
@@ -34,6 +35,9 @@ export class WaberyApiClient {
|
|
|
34
35
|
async put(path, body) {
|
|
35
36
|
return this.request("PUT", path, body);
|
|
36
37
|
}
|
|
38
|
+
async patch(path, body) {
|
|
39
|
+
return this.request("PATCH", path, body);
|
|
40
|
+
}
|
|
37
41
|
async delete(path) {
|
|
38
42
|
return this.request("DELETE", path);
|
|
39
43
|
}
|
|
@@ -60,17 +64,23 @@ export class WaberyApiClient {
|
|
|
60
64
|
// retryable — the request usually never reached the origin. Back off and
|
|
61
65
|
// retry a few times so callers don't have to hand-roll the loop.
|
|
62
66
|
for (let attempt = 0;; attempt++) {
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
63
69
|
let response;
|
|
64
70
|
try {
|
|
65
|
-
response = await fetch(url, init);
|
|
71
|
+
response = await fetch(url, { ...init, signal: controller.signal });
|
|
66
72
|
}
|
|
67
73
|
catch (error) {
|
|
74
|
+
// A timeout (abort) or network blip is transient — retry it.
|
|
68
75
|
if (attempt < MAX_RETRIES) {
|
|
69
76
|
await sleep(retryDelayMs(attempt, null));
|
|
70
77
|
continue;
|
|
71
78
|
}
|
|
72
79
|
throw error;
|
|
73
80
|
}
|
|
81
|
+
finally {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
74
84
|
if (RETRYABLE_STATUS.has(response.status) && attempt < MAX_RETRIES) {
|
|
75
85
|
const delay = retryDelayMs(attempt, response.headers.get("retry-after"));
|
|
76
86
|
process.stderr.write(`Wabery API returned ${response.status}; retrying in ${Math.round(delay / 1000)}s (attempt ${attempt + 1}/${MAX_RETRIES})...\n`);
|
|
@@ -93,6 +103,8 @@ export class WaberyApiClient {
|
|
|
93
103
|
/** Gateway/transient statuses worth retrying (Cloudflare origin errors). */
|
|
94
104
|
const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
95
105
|
const MAX_RETRIES = 3;
|
|
106
|
+
/** Abort a hung request after this long (matches the origin gateway), then retry. */
|
|
107
|
+
const REQUEST_TIMEOUT_MS = 120_000;
|
|
96
108
|
const MAX_RETRY_AFTER_MS = 15_000;
|
|
97
109
|
/** Honor a `Retry-After` (seconds), else exponential backoff with jitter. */
|
|
98
110
|
function retryDelayMs(attempt, retryAfter) {
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|