@wabery/cli 0.2.0 → 0.4.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 +94 -12
- package/dist/api-client.d.ts +7 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +74 -22
- package/dist/api-client.js.map +1 -1
- package/dist/cli.js +494 -23
- 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,6 +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
|
+
## 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):
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
wabery env --project-id "proj_id"
|
|
78
|
+
```
|
|
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
|
+
|
|
48
100
|
For local repository development, build the workspace package:
|
|
49
101
|
|
|
50
102
|
```sh
|
|
@@ -149,7 +201,9 @@ Installed CLI:
|
|
|
149
201
|
- `wabery_preview_config_diff`
|
|
150
202
|
- `wabery_apply_config`
|
|
151
203
|
- `wabery_list_projects`
|
|
152
|
-
- `
|
|
204
|
+
- `wabery_get_project`
|
|
205
|
+
- `wabery_update_project`
|
|
206
|
+
- `wabery_rotate_webhook_secret`
|
|
153
207
|
- `wabery_list_channels`
|
|
154
208
|
- `wabery_list_flows`
|
|
155
209
|
- `wabery_get_flow`
|
|
@@ -165,13 +219,12 @@ Installed CLI:
|
|
|
165
219
|
|
|
166
220
|
## Typical Developer Workflow
|
|
167
221
|
|
|
168
|
-
1. List
|
|
222
|
+
1. List the project your key is scoped to:
|
|
169
223
|
|
|
170
224
|
```sh
|
|
171
225
|
wabery login
|
|
172
226
|
wabery doctor
|
|
173
227
|
wabery projects list
|
|
174
|
-
wabery projects create --name "Demo project"
|
|
175
228
|
```
|
|
176
229
|
|
|
177
230
|
2. Enroll a WhatsApp phone number and capture the returned `contact_id` and `channel_id`:
|
|
@@ -194,11 +247,33 @@ wabery config diff wabery.config.json
|
|
|
194
247
|
wabery config apply wabery.config.json
|
|
195
248
|
```
|
|
196
249
|
|
|
197
|
-
|
|
250
|
+
`wabery.config.json` can manage the project's webhook declaratively, so
|
|
251
|
+
`config apply` keeps the webhook URL and signing secret in sync alongside flows:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"project_id": "proj_or_agent_id",
|
|
256
|
+
"webhook": {
|
|
257
|
+
"url": "https://yourapp.com/webhooks/wabery",
|
|
258
|
+
"signing_enabled": true,
|
|
259
|
+
"secret": "whsec_your_own_secret_from_your_secret_manager"
|
|
260
|
+
},
|
|
261
|
+
"flows": []
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Omit `secret` to have the server generate one; set `signing_enabled: false` to
|
|
266
|
+
clear it. The webhook setup is scriptable after the project exists: create or
|
|
267
|
+
select the project in the dashboard, create the project-scoped API key with
|
|
268
|
+
`wabery login`, run `config apply` to point the webhook, and run `wabery env` to
|
|
269
|
+
emit the secret for your deploy.
|
|
270
|
+
|
|
271
|
+
4. Publish a flow explicitly after review (`--wait` blocks until Meta reports it
|
|
272
|
+
published, or fails with the rejection reason):
|
|
198
273
|
|
|
199
274
|
```sh
|
|
200
275
|
wabery flows list
|
|
201
|
-
wabery flows publish "flow_id"
|
|
276
|
+
wabery flows publish "flow_id" --wait
|
|
202
277
|
```
|
|
203
278
|
|
|
204
279
|
5. Send a published flow:
|
|
@@ -228,10 +303,17 @@ your webhook secret and POSTing it to a local URL:
|
|
|
228
303
|
wabery webhooks send-test --url http://localhost:3000/webhooks \
|
|
229
304
|
--secret "$WABERY_WEBHOOK_SECRET" --event flow.completed
|
|
230
305
|
|
|
231
|
-
# Drive your data-exchange endpoint (
|
|
306
|
+
# Drive your data-exchange endpoint (one screen advance):
|
|
232
307
|
wabery flows test --url http://localhost:3000/flow \
|
|
233
308
|
--secret "$WABERY_WEBHOOK_SECRET" \
|
|
234
309
|
--action data_exchange --screen SELECT --data '{"workspace":"ws_1"}'
|
|
310
|
+
|
|
311
|
+
# Walk the whole journey, then fire flow.completed at your webhook:
|
|
312
|
+
wabery flows simulate --url http://localhost:3000/flow \
|
|
313
|
+
--webhook-url http://localhost:3000/webhooks \
|
|
314
|
+
--secret "$WABERY_WEBHOOK_SECRET" \
|
|
315
|
+
--steps '[{"action":"INIT"},{"action":"data_exchange","screen":"SELECT","data":{"workspace":"ws_1"}}]' \
|
|
316
|
+
--submission '{"workspace":"ws_1","project":"proj_a"}'
|
|
235
317
|
```
|
|
236
318
|
|
|
237
319
|
Both send a valid `x-wabery-signature` so you can verify it exactly as in production.
|
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
|
}
|
|
@@ -51,23 +55,71 @@ export class WaberyApiClient {
|
|
|
51
55
|
headers["wabery-version"] = this.apiVersion;
|
|
52
56
|
if (body !== undefined)
|
|
53
57
|
headers["content-type"] = "application/json";
|
|
54
|
-
const
|
|
58
|
+
const init = {
|
|
55
59
|
method,
|
|
56
60
|
headers,
|
|
57
61
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
};
|
|
63
|
+
// Transient gateway errors (Cloudflare 502/503/504) and network blips are
|
|
64
|
+
// retryable — the request usually never reached the origin. Back off and
|
|
65
|
+
// retry a few times so callers don't have to hand-roll the loop.
|
|
66
|
+
for (let attempt = 0;; attempt++) {
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
69
|
+
let response;
|
|
70
|
+
try {
|
|
71
|
+
response = await fetch(url, { ...init, signal: controller.signal });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// A timeout (abort) or network blip is transient — retry it.
|
|
75
|
+
if (attempt < MAX_RETRIES) {
|
|
76
|
+
await sleep(retryDelayMs(attempt, null));
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
if (RETRYABLE_STATUS.has(response.status) && attempt < MAX_RETRIES) {
|
|
85
|
+
const delay = retryDelayMs(attempt, response.headers.get("retry-after"));
|
|
86
|
+
process.stderr.write(`Wabery API returned ${response.status}; retrying in ${Math.round(delay / 1000)}s (attempt ${attempt + 1}/${MAX_RETRIES})...\n`);
|
|
87
|
+
await sleep(delay);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const responseBody = await readResponse(response);
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const message = typeof responseBody === "object" &&
|
|
93
|
+
responseBody !== null &&
|
|
94
|
+
"message" in responseBody
|
|
95
|
+
? String(responseBody.message)
|
|
96
|
+
: `Wabery API request failed with HTTP ${response.status}`;
|
|
97
|
+
throw new WaberyApiError(message, response.status, responseBody);
|
|
98
|
+
}
|
|
99
|
+
return responseBody;
|
|
67
100
|
}
|
|
68
|
-
return responseBody;
|
|
69
101
|
}
|
|
70
102
|
}
|
|
103
|
+
/** Gateway/transient statuses worth retrying (Cloudflare origin errors). */
|
|
104
|
+
const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
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;
|
|
108
|
+
const MAX_RETRY_AFTER_MS = 15_000;
|
|
109
|
+
/** Honor a `Retry-After` (seconds), else exponential backoff with jitter. */
|
|
110
|
+
function retryDelayMs(attempt, retryAfter) {
|
|
111
|
+
if (retryAfter) {
|
|
112
|
+
const seconds = Number(retryAfter);
|
|
113
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
114
|
+
return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const base = Math.min(1000 * 2 ** attempt, 8000);
|
|
118
|
+
return base + Math.floor(Math.random() * 250);
|
|
119
|
+
}
|
|
120
|
+
function sleep(ms) {
|
|
121
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
122
|
+
}
|
|
71
123
|
function normalizeBaseUrl(value) {
|
|
72
124
|
const trimmed = value.replace(/\/+$/, "");
|
|
73
125
|
if (trimmed.endsWith("/v1") || trimmed.endsWith("/api/v1"))
|
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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"}
|