@growthub/cli 0.3.59 → 0.4.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.
Files changed (72) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/.env.example +10 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/QUICKSTART.md +44 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/brands/NEW-CLIENT.md +10 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/brands/_template/brand-kit.md +27 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/brands/growthub/brand-kit.md +25 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +47 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/fork-sync-integration.md +32 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/starter-kit-overview.md +26 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/vite-ui-shell-guide.md +24 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/examples/workspace-sample.md +10 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/growthub-meta/README.md +3 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/growthub-meta/kit-standard.md +10 -0
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +101 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/output/README.md +3 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/output-standards.md +11 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/runtime-assumptions.md +8 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/setup/check-deps.sh +20 -0
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/setup/verify-env.mjs +20 -0
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/skills.md +14 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/index.html +12 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/package.json +20 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/serve.mjs +42 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/src/App.jsx +32 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/src/app.css +20 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/src/main.jsx +10 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/studio/vite.config.js +8 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/agent-contract.md +9 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/deployment-plan.md +22 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/workspace-brief.md +11 -0
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/validation-checklist.md +13 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/workers/custom-workspace-operator/CLAUDE.md +32 -0
  32. package/assets/worker-kits/growthub-zernio-social-v1/.env.example +5 -0
  33. package/assets/worker-kits/growthub-zernio-social-v1/QUICKSTART.md +36 -4
  34. package/assets/worker-kits/growthub-zernio-social-v1/bundles/growthub-zernio-social-v1.json +30 -1
  35. package/assets/worker-kits/growthub-zernio-social-v1/docs/growthub-agentic-social-platform-ui-shell.md +134 -0
  36. package/assets/worker-kits/growthub-zernio-social-v1/docs/local-adapters.md +2 -2
  37. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/README.md +5 -8
  38. package/assets/worker-kits/growthub-zernio-social-v1/growthub-meta/kit-standard.md +1 -1
  39. package/assets/worker-kits/growthub-zernio-social-v1/kit.json +33 -1
  40. package/assets/worker-kits/growthub-zernio-social-v1/skills.md +1 -1
  41. package/assets/worker-kits/growthub-zernio-social-v1/studio/.env.example +3 -0
  42. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/assets/index-DTmBMuXr.js +78 -0
  43. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/assets/index-gHr-nTMF.css +1 -0
  44. package/assets/worker-kits/growthub-zernio-social-v1/studio/dist/index.html +14 -0
  45. package/assets/worker-kits/growthub-zernio-social-v1/studio/index.html +13 -0
  46. package/assets/worker-kits/growthub-zernio-social-v1/studio/package-lock.json +1677 -0
  47. package/assets/worker-kits/growthub-zernio-social-v1/studio/package.json +20 -0
  48. package/assets/worker-kits/growthub-zernio-social-v1/studio/serve.mjs +60 -0
  49. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/App.jsx +130 -0
  50. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/api.js +146 -0
  51. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/app.css +558 -0
  52. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/lib/rules.js +64 -0
  53. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/lib/templates.js +207 -0
  54. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/main.jsx +10 -0
  55. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Accounts.jsx +57 -0
  56. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Agent.jsx +167 -0
  57. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Analytics.jsx +164 -0
  58. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/ApiKeys.jsx +143 -0
  59. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Automations.jsx +122 -0
  60. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/CommentRules.jsx +592 -0
  61. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Compose.jsx +185 -0
  62. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Dashboard.jsx +87 -0
  63. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Inbox.jsx +144 -0
  64. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Queues.jsx +167 -0
  65. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Scheduled.jsx +85 -0
  66. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Sequences.jsx +160 -0
  67. package/assets/worker-kits/growthub-zernio-social-v1/studio/src/views/Templates.jsx +275 -0
  68. package/assets/worker-kits/growthub-zernio-social-v1/studio/vite.config.js +7 -0
  69. package/assets/worker-kits/growthub-zernio-social-v1/workers/zernio-social-operator/CLAUDE.md +3 -3
  70. package/dist/index.js +1183 -592
  71. package/package.json +1 -1
  72. package/assets/worker-kits/growthub-zernio-social-v1/docs/postiz-ui-shell-integration.md +0 -166
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "growthub-agentic-social-media-platform",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "serve": "node serve.mjs"
11
+ },
12
+ "dependencies": {
13
+ "react": "^18.3.1",
14
+ "react-dom": "^18.3.1"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-react": "^4.3.1",
18
+ "vite": "^5.4.2"
19
+ }
20
+ }
@@ -0,0 +1,60 @@
1
+ import http from "node:http";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const studioDir = path.dirname(fileURLToPath(import.meta.url));
7
+ const distDir = path.join(studioDir, "dist");
8
+ const host = process.env.HOST || "127.0.0.1";
9
+ const port = Number.parseInt(process.env.PORT || "4173", 10);
10
+
11
+ const MIME_TYPES = new Map([
12
+ [".html", "text/html; charset=utf-8"],
13
+ [".js", "application/javascript; charset=utf-8"],
14
+ [".css", "text/css; charset=utf-8"],
15
+ [".json", "application/json; charset=utf-8"],
16
+ [".svg", "image/svg+xml"],
17
+ [".png", "image/png"],
18
+ [".jpg", "image/jpeg"],
19
+ [".jpeg", "image/jpeg"],
20
+ [".ico", "image/x-icon"],
21
+ [".txt", "text/plain; charset=utf-8"],
22
+ ]);
23
+
24
+ function safeResolve(requestPath) {
25
+ const relativePath = requestPath === "/" ? "index.html" : requestPath.replace(/^\/+/, "");
26
+ const resolvedPath = path.resolve(distDir, relativePath);
27
+ if (!resolvedPath.startsWith(distDir + path.sep) && resolvedPath !== path.join(distDir, "index.html")) {
28
+ return null;
29
+ }
30
+ return resolvedPath;
31
+ }
32
+
33
+ function sendFile(res, filePath) {
34
+ const ext = path.extname(filePath).toLowerCase();
35
+ const contentType = MIME_TYPES.get(ext) || "application/octet-stream";
36
+ res.writeHead(200, { "Content-Type": contentType });
37
+ fs.createReadStream(filePath).pipe(res);
38
+ }
39
+
40
+ const server = http.createServer((req, res) => {
41
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
42
+ const filePath = safeResolve(url.pathname);
43
+
44
+ if (!filePath) {
45
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
46
+ res.end("Forbidden");
47
+ return;
48
+ }
49
+
50
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
51
+ sendFile(res, filePath);
52
+ return;
53
+ }
54
+
55
+ sendFile(res, path.join(distDir, "index.html"));
56
+ });
57
+
58
+ server.listen(port, host, () => {
59
+ process.stdout.write(`Zernio studio bundle running at http://${host}:${port}\n`);
60
+ });
@@ -0,0 +1,130 @@
1
+ import { useState, useEffect, createContext, useContext } from 'react';
2
+ import { api, PROFILE_ID } from './api.js';
3
+ import Dashboard from './views/Dashboard.jsx';
4
+ import Accounts from './views/Accounts.jsx';
5
+ import Compose from './views/Compose.jsx';
6
+ import Scheduled from './views/Scheduled.jsx';
7
+ import Queues from './views/Queues.jsx';
8
+ import Analytics from './views/Analytics.jsx';
9
+ import Agent from './views/Agent.jsx';
10
+ import ApiKeys from './views/ApiKeys.jsx';
11
+ import Automations from './views/Automations.jsx';
12
+ import Templates from './views/Templates.jsx';
13
+ import CommentRules from './views/CommentRules.jsx';
14
+ import Sequences from './views/Sequences.jsx';
15
+
16
+ export const AppCtx = createContext({});
17
+ export const useApp = () => useContext(AppCtx);
18
+
19
+ const NAV = [
20
+ { section: 'Publishing' },
21
+ { id: 'dashboard', label: 'Dashboard', icon: '🏠' },
22
+ { id: 'accounts', label: 'Accounts', icon: '🔗' },
23
+ { id: 'compose', label: 'Compose', icon: '✏️' },
24
+ { id: 'scheduled', label: 'Scheduled', icon: '📅' },
25
+ { id: 'queues', label: 'Queues', icon: '🔄' },
26
+ { id: 'analytics', label: 'Analytics', icon: '📊' },
27
+ { section: 'Comment Automation' },
28
+ { id: 'templates', label: 'Templates', icon: '📝' },
29
+ { id: 'commentrules', label: 'Comment Rules', icon: '💬' },
30
+ { id: 'sequences', label: 'Sequences', icon: '🔀' },
31
+ { id: 'automations', label: 'Automations', icon: '⚡' },
32
+ { section: 'Agent' },
33
+ { id: 'agent', label: 'Agent / Swarm', icon: '🤖' },
34
+ { id: 'apikeys', label: 'API Keys', icon: '🔑' },
35
+ ];
36
+
37
+ const VIEWS = {
38
+ dashboard: Dashboard,
39
+ accounts: Accounts,
40
+ compose: Compose,
41
+ scheduled: Scheduled,
42
+ queues: Queues,
43
+ analytics: Analytics,
44
+ agent: Agent,
45
+ automations: Automations,
46
+ apikeys: ApiKeys,
47
+ templates: Templates,
48
+ commentrules: CommentRules,
49
+ sequences: Sequences,
50
+ };
51
+
52
+ export default function App() {
53
+ const [view, setView] = useState('dashboard');
54
+ const [profile, setProfile] = useState(null);
55
+ const [accounts, setAccounts] = useState([]);
56
+ const [toast, setToast] = useState(null);
57
+ const [toastOk, setToastOk] = useState(true);
58
+
59
+ const showToast = (msg, ok = true) => {
60
+ setToast(msg);
61
+ setToastOk(ok);
62
+ setTimeout(() => setToast(null), 3500);
63
+ };
64
+
65
+ const reload = () => {
66
+ if (!PROFILE_ID) return;
67
+ Promise.all([api.getProfile(PROFILE_ID), api.getAccounts(PROFILE_ID)])
68
+ .then(([prof, accs]) => {
69
+ setProfile(prof);
70
+ setAccounts(accs.accounts || []);
71
+ })
72
+ .catch(e => showToast(e.message, false));
73
+ };
74
+
75
+ useEffect(reload, []);
76
+
77
+ const Current = VIEWS[view] || Dashboard;
78
+ const navLabel = NAV.find(n => n.id === view)?.label || '';
79
+
80
+ return (
81
+ <AppCtx.Provider value={{ profile, accounts, PROFILE_ID, showToast, reload }}>
82
+ <div className="layout">
83
+ <nav className="sidebar">
84
+ <div className="logo">⚡ Growthub</div>
85
+
86
+ {NAV.map((item, i) =>
87
+ item.section
88
+ ? <div key={i} className="nav-section">{item.section}</div>
89
+ : (
90
+ <button
91
+ key={item.id}
92
+ className={`nav-item ${view === item.id ? 'active' : ''}`}
93
+ onClick={() => setView(item.id)}
94
+ >
95
+ <span className="nav-icon">{item.icon}</span>
96
+ {item.label}
97
+ </button>
98
+ )
99
+ )}
100
+
101
+ <div className="sidebar-bottom">
102
+ <div className="account-badge">
103
+ <span className="dot" />
104
+ <span>{profile?.name || (PROFILE_ID ? 'Loading…' : 'No profile set')}</span>
105
+ </div>
106
+ </div>
107
+ </nav>
108
+
109
+ <div className="main">
110
+ <div className="topbar">
111
+ <span className="topbar-title">{navLabel}</span>
112
+ <div className="row">
113
+ <button className="btn btn-ghost btn-sm" onClick={reload}>↻ Refresh</button>
114
+ <button className="btn btn-primary btn-sm" onClick={() => setView('compose')}>+ New Post</button>
115
+ </div>
116
+ </div>
117
+ <div className="content">
118
+ <Current onNavigate={setView} />
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ {toast && (
124
+ <div className={`toast ${toastOk ? 'toast-ok' : 'toast-err'}`}>
125
+ {toast}
126
+ </div>
127
+ )}
128
+ </AppCtx.Provider>
129
+ );
130
+ }
@@ -0,0 +1,146 @@
1
+ const BASE = import.meta.env.VITE_ZERNIO_API_URL || 'https://zernio.com/api/v1';
2
+ const KEY = import.meta.env.VITE_ZERNIO_API_KEY || '';
3
+ export const PROFILE_ID = import.meta.env.VITE_ZERNIO_PROFILE_ID || '';
4
+
5
+ function authHeaders(extra = {}) {
6
+ return { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json', ...extra };
7
+ }
8
+
9
+ async function request(method, path, body, idempotencyKey) {
10
+ const h = authHeaders(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {});
11
+ const opts = { method, headers: h };
12
+ if (body !== undefined) opts.body = JSON.stringify(body);
13
+
14
+ for (let attempt = 0; attempt < 3; attempt++) {
15
+ const r = await fetch(`${BASE}${path}`, opts);
16
+ if (r.status === 429) {
17
+ const wait = (parseInt(r.headers.get('Retry-After') || '5', 10) * 1000) + 500;
18
+ await new Promise(res => setTimeout(res, wait));
19
+ continue;
20
+ }
21
+ if (!r.ok) {
22
+ const err = await r.json().catch(() => ({}));
23
+ const code = err?.error?.code || '';
24
+ const msg = err?.error?.message || `HTTP ${r.status}`;
25
+ const e = new Error(msg);
26
+ e.code = code;
27
+ e.status = r.status;
28
+ throw e;
29
+ }
30
+ return r.json();
31
+ }
32
+ throw new Error('Rate limited: max retries reached');
33
+ }
34
+
35
+ const get = (path) => request('GET', path);
36
+ const post = (path, body, ikey) => request('POST', path, body, ikey);
37
+ const put = (path, body) => request('PUT', path, body);
38
+ const del = (path) => request('DELETE', path);
39
+
40
+ export const api = {
41
+ // ── Profiles ────────────────────────────────────────────────────────────────
42
+ getProfiles: () => get('/profiles'),
43
+ getProfile: (id) => get(`/profiles/${id}`),
44
+
45
+ // ── Accounts ────────────────────────────────────────────────────────────────
46
+ getAccounts: (profileId) => get(`/accounts?profileId=${profileId}`),
47
+
48
+ // ── Posts ───────────────────────────────────────────────────────────────────
49
+ getPosts: (profileId, status = 'scheduled') => get(`/posts?profileId=${profileId}&status=${status}`),
50
+ getAllPosts: (profileId) => get(`/posts?profileId=${profileId}`),
51
+ getPost: (id) => get(`/posts/${id}`),
52
+ // Normalises the response: API returns { post: {...} } on create
53
+ createPost: async (body, ikey) => {
54
+ const d = await post('/posts', body, ikey);
55
+ return d.post || d;
56
+ },
57
+ deletePost: (id) => del(`/posts/${id}`),
58
+
59
+ // ── Queues ──────────────────────────────────────────────────────────────────
60
+ getQueues: (profileId) => get(`/queues?profileId=${profileId}`),
61
+ createQueue: (body) => post('/queues', body),
62
+ updateQueue: (id, body) => put(`/queues/${id}`, body),
63
+ deleteQueue: (id) => del(`/queues/${id}`),
64
+
65
+ // ── Media ───────────────────────────────────────────────────────────────────
66
+ uploadMedia: async (file) => {
67
+ const fd = new FormData();
68
+ fd.append('file', file);
69
+ const r = await fetch(`${BASE}/media`, {
70
+ method: 'POST',
71
+ headers: { 'Authorization': `Bearer ${KEY}` },
72
+ body: fd,
73
+ });
74
+ if (!r.ok) {
75
+ const e = await r.json().catch(() => ({}));
76
+ throw new Error(e?.error?.message || `HTTP ${r.status}`);
77
+ }
78
+ return r.json();
79
+ },
80
+ getMedia: (id) => get(`/media/${id}`),
81
+
82
+ // ── Inbox ───────────────────────────────────────────────────────────────────
83
+ getInbox: (profileId) => get(`/inbox?profileId=${profileId}`),
84
+ getConversation: (id) => get(`/inbox/${id}`),
85
+ replyConversation: (id, body) => post(`/inbox/${id}/reply`, body),
86
+
87
+ // ── Analytics ───────────────────────────────────────────────────────────────
88
+ getPostAnalytics: (profileId, from, to) => get(`/analytics/posts?profileId=${profileId}&from=${from}&to=${to}`),
89
+ getAccountAnalytics: (profileId, from, to) => get(`/analytics/accounts?profileId=${profileId}&from=${from}&to=${to}`),
90
+
91
+ // ── API Keys ─────────────────────────────────────────────────────────────────
92
+ getApiKeys: () => get('/api-keys'),
93
+ createApiKey: (body) => post('/api-keys', body),
94
+ deleteApiKey: (id) => del(`/api-keys/${id}`),
95
+
96
+ // ── Platforms ────────────────────────────────────────────────────────────────
97
+ getPlatforms: () => get('/platforms'),
98
+
99
+ // ── Connect ──────────────────────────────────────────────────────────────────
100
+ connectPlatform: (platform) => get(`/connect/${platform}`),
101
+
102
+ // ── Contacts ─────────────────────────────────────────────────────────────────
103
+ getContacts: () => get('/contacts'),
104
+ createContact:(body) => post('/contacts', body),
105
+
106
+ // ── Broadcasts ───────────────────────────────────────────────────────────────
107
+ getBroadcasts: () => get('/broadcasts'),
108
+ createBroadcast: (body) => post('/broadcasts', body),
109
+ getBroadcast: (id) => get(`/broadcasts/${id}`),
110
+
111
+ // ── Sequences ────────────────────────────────────────────────────────────────
112
+ getSequences: () => get('/sequences'),
113
+ getSequence: (id) => get(`/sequences/${id}`),
114
+ activateSequence: (id) => post(`/sequences/${id}/activate`, {}),
115
+ pauseSequence: (id) => post(`/sequences/${id}/pause`, {}),
116
+
117
+ // ── Comment-to-DM Automations (Instagram + Facebook only) ───────────────────
118
+ // Real endpoint: POST /api/v1/comment-automations
119
+ // Required: name, profileId, accountId, platformPostId, dmMessage
120
+ // Optional: keywords (comma-separated string), commentReply, isActive
121
+ getCommentAutomations: (profileId) => get(`/comment-automations${profileId ? '?profileId=' + profileId : ''}`),
122
+ getCommentAutomation: (id) => get(`/comment-automations/${id}`),
123
+ createCommentAutomation: (body) => post('/comment-automations', body),
124
+ updateCommentAutomation: (id, body) => put(`/comment-automations/${id}`, body),
125
+ deleteCommentAutomation: (id) => del(`/comment-automations/${id}`),
126
+ getCommentAutomationLogs: (id) => get(`/comment-automations/${id}/logs`),
127
+
128
+ // ── Platform OAuth connect ────────────────────────────────────────────────
129
+ getConnectUrl: (platform, profileId) => get(`/connect/${platform}?profileId=${profileId || PROFILE_ID}`),
130
+
131
+ // ── Comments on posts ─────────────────────────────────────────────────────
132
+ getPostComments: (postId, accountId) => get(`/posts/${postId}/comments?accountId=${accountId}`),
133
+ replyToComment: (postId, body) => post(`/posts/${postId}/comments`, body),
134
+ deleteComment: (postId, commentId, accountId) => del(`/posts/${postId}/comments/${commentId}?accountId=${accountId}`),
135
+ hideComment: (postId, commentId, body) => post(`/posts/${postId}/comments/${commentId}/hide`, body),
136
+
137
+ // ── Legacy stubs (kept for backward compat in Agent view) ────────────────
138
+ getAutomations: () => get('/comment-automations'),
139
+ runAutomation: (id) => post(`/comment-automations/${id}/run`, {}),
140
+
141
+ // ── Webhooks ─────────────────────────────────────────────────────────────────
142
+ getWebhooks: () => get('/webhooks'),
143
+ createWebhook: (body) => post('/webhooks', body),
144
+ updateWebhook: (id, body) => put(`/webhooks/${id}`, body),
145
+ getWebhookLogs: (id) => get(`/webhooks/${id}/logs`),
146
+ };