gufi-cli 0.1.50 → 0.1.51

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 (35) hide show
  1. package/dist/commands/docs.js +1 -5
  2. package/dist/lib/docs-resolver.d.ts +8 -0
  3. package/dist/lib/docs-resolver.js +27 -0
  4. package/dist/mcp.js +5 -31
  5. package/docs/dev-guide/1-01-architecture.md +358 -0
  6. package/docs/dev-guide/1-02-multi-tenant.md +415 -0
  7. package/docs/dev-guide/1-03-column-types.md +594 -0
  8. package/docs/dev-guide/1-04-json-config.md +442 -0
  9. package/docs/dev-guide/1-05-authentication.md +427 -0
  10. package/docs/dev-guide/2-01-api-reference.md +564 -0
  11. package/docs/dev-guide/2-02-automations.md +508 -0
  12. package/docs/dev-guide/2-03-gufi-cli.md +568 -0
  13. package/docs/dev-guide/2-04-realtime.md +401 -0
  14. package/docs/dev-guide/2-05-permissions.md +497 -0
  15. package/docs/dev-guide/2-06-integrations-overview.md +104 -0
  16. package/docs/dev-guide/2-07-stripe.md +173 -0
  17. package/docs/dev-guide/2-08-nayax.md +297 -0
  18. package/docs/dev-guide/2-09-ourvend.md +226 -0
  19. package/docs/dev-guide/2-10-tns.md +177 -0
  20. package/docs/dev-guide/2-11-custom-http.md +268 -0
  21. package/docs/dev-guide/3-01-custom-views.md +555 -0
  22. package/docs/dev-guide/3-02-webhooks-api.md +446 -0
  23. package/docs/mcp/00-overview.md +329 -0
  24. package/docs/mcp/01-architecture.md +226 -0
  25. package/docs/mcp/02-modules.md +285 -0
  26. package/docs/mcp/03-fields.md +357 -0
  27. package/docs/mcp/04-views.md +613 -0
  28. package/docs/mcp/05-automations.md +461 -0
  29. package/docs/mcp/06-api.md +531 -0
  30. package/docs/mcp/07-packages.md +246 -0
  31. package/docs/mcp/08-common-errors.md +284 -0
  32. package/docs/mcp/09-examples.md +453 -0
  33. package/docs/mcp/README.md +71 -0
  34. package/docs/mcp/tool-descriptions.json +64 -0
  35. package/package.json +3 -2
@@ -9,12 +9,8 @@
9
9
  */
10
10
  import * as fs from "fs";
11
11
  import * as path from "path";
12
- import { fileURLToPath } from "url";
13
12
  import chalk from "chalk";
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = path.dirname(__filename);
16
- // docs/mcp path relative to CLI
17
- const DOCS_MCP_PATH = path.resolve(__dirname, "../../../../docs/mcp");
13
+ import { DOCS_MCP_PATH } from "../lib/docs-resolver.js";
18
14
  // Topic to file mapping
19
15
  const TOPIC_FILES = {
20
16
  overview: { file: "00-overview.md", description: "Overview and workflow" },
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolves documentation paths for both development (monorepo) and global install.
3
+ *
4
+ * - Global install: docs are bundled at <pkg>/docs/
5
+ * - Development: docs live at <monorepo>/docs/
6
+ */
7
+ export declare const DOCS_MCP_PATH: string;
8
+ export declare const DOCS_DEV_GUIDE_PATH: string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Resolves documentation paths for both development (monorepo) and global install.
3
+ *
4
+ * - Global install: docs are bundled at <pkg>/docs/
5
+ * - Development: docs live at <monorepo>/docs/
6
+ */
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import { fileURLToPath } from "url";
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ // Package root: from dist/lib/ go up 2 levels
13
+ const PKG_ROOT = path.resolve(__dirname, "../..");
14
+ function resolveDocsPath(subdir) {
15
+ // 1. Package-local (global install): <pkg>/docs/<subdir>
16
+ const localPath = path.join(PKG_ROOT, "docs", subdir);
17
+ if (fs.existsSync(localPath))
18
+ return localPath;
19
+ // 2. Monorepo (development): <pkg>/../../docs/<subdir>
20
+ const monorepoPath = path.resolve(PKG_ROOT, "../../docs", subdir);
21
+ if (fs.existsSync(monorepoPath))
22
+ return monorepoPath;
23
+ // Fallback to local path (will fail gracefully downstream)
24
+ return localPath;
25
+ }
26
+ export const DOCS_MCP_PATH = resolveDocsPath("mcp");
27
+ export const DOCS_DEV_GUIDE_PATH = resolveDocsPath("dev-guide");
package/dist/mcp.js CHANGED
@@ -48,10 +48,8 @@ const COLUMN_TYPE_NAMES = [
48
48
  // For ES modules __dirname equivalent
49
49
  const __filename = fileURLToPath(import.meta.url);
50
50
  const __dirname = path.dirname(__filename);
51
- // docs/mcp path relative to CLI
52
- const DOCS_MCP_PATH = path.resolve(__dirname, "../../../docs/mcp");
53
- // docs/dev-guide path for integration docs
54
- const DOCS_DEV_GUIDE_PATH = path.resolve(__dirname, "../../../docs/dev-guide");
51
+ // Docs paths - resolved for both global install and monorepo dev
52
+ import { DOCS_MCP_PATH, DOCS_DEV_GUIDE_PATH } from "./lib/docs-resolver.js";
55
53
  /**
56
54
  * Parse frontmatter from markdown content
57
55
  */
@@ -1806,11 +1804,6 @@ const toolHandlers = {
1806
1804
  // 💜 CLI: Save to ~/gufi-dev/company_<id>/view_<id>/
1807
1805
  const viewDir = path.join(LOCAL_VIEWS_DIR, `company_${companyId}`, `view_${viewId}`);
1808
1806
  fs.mkdirSync(viewDir, { recursive: true });
1809
- // 💜 Migrate: if old path exists (~/gufi-dev/view_<id>/), remove it
1810
- const oldViewDir = path.join(LOCAL_VIEWS_DIR, `view_${viewId}`);
1811
- if (fs.existsSync(oldViewDir)) {
1812
- fs.rmSync(oldViewDir, { recursive: true, force: true });
1813
- }
1814
1807
  // 💜 Get latest snapshot for version tracking
1815
1808
  let latestSnapshot;
1816
1809
  try {
@@ -1890,7 +1883,6 @@ const toolHandlers = {
1890
1883
  }
1891
1884
  // 💜 Try to get company_id from view metadata if not provided
1892
1885
  if (!companyId && useLocal) {
1893
- // Try new path first (company_*/view_*)
1894
1886
  const companyDirs = fs.existsSync(LOCAL_VIEWS_DIR)
1895
1887
  ? fs.readdirSync(LOCAL_VIEWS_DIR).filter(d => d.startsWith("company_"))
1896
1888
  : [];
@@ -1906,18 +1898,6 @@ const toolHandlers = {
1906
1898
  break;
1907
1899
  }
1908
1900
  }
1909
- // Fallback: old path (view_*)
1910
- if (!companyId) {
1911
- const oldMetaPath = path.join(LOCAL_VIEWS_DIR, `view_${viewId}`, ".gufi-view.json");
1912
- if (fs.existsSync(oldMetaPath)) {
1913
- try {
1914
- const meta = JSON.parse(fs.readFileSync(oldMetaPath, "utf-8"));
1915
- if (meta.company_id)
1916
- companyId = String(meta.company_id);
1917
- }
1918
- catch { /* ignore */ }
1919
- }
1920
- }
1921
1901
  }
1922
1902
  if (!companyId) {
1923
1903
  throw new Error("company_id is required. Pass it as parameter or pull the view first.");
@@ -1925,12 +1905,8 @@ const toolHandlers = {
1925
1905
  let files = [];
1926
1906
  let lastPulledSnapshot;
1927
1907
  if (useLocal) {
1928
- // 💜 CLI: Read from ~/gufi-dev/company_<id>/view_<id>/ (or legacy ~/gufi-dev/view_<id>/)
1929
- let viewDir = path.join(LOCAL_VIEWS_DIR, `company_${companyId}`, `view_${viewId}`);
1930
- if (!fs.existsSync(viewDir)) {
1931
- // Fallback to old path
1932
- viewDir = path.join(LOCAL_VIEWS_DIR, `view_${viewId}`);
1933
- }
1908
+ // 💜 CLI: Read from ~/gufi-dev/company_<id>/view_<id>/
1909
+ const viewDir = path.join(LOCAL_VIEWS_DIR, `company_${companyId}`, `view_${viewId}`);
1934
1910
  if (!fs.existsSync(viewDir)) {
1935
1911
  throw new Error(`View directory not found. Run gufi_view_pull({ view_id: ${viewId}, company_id: '${companyId}' }) first.`);
1936
1912
  }
@@ -2055,9 +2031,7 @@ const toolHandlers = {
2055
2031
  }
2056
2032
  // 💜 Update local metadata with new snapshot number (for version tracking)
2057
2033
  if (useLocal && result.snapshot) {
2058
- let viewDir = path.join(LOCAL_VIEWS_DIR, `company_${companyId}`, `view_${viewId}`);
2059
- if (!fs.existsSync(viewDir))
2060
- viewDir = path.join(LOCAL_VIEWS_DIR, `view_${viewId}`);
2034
+ const viewDir = path.join(LOCAL_VIEWS_DIR, `company_${companyId}`, `view_${viewId}`);
2061
2035
  const metaPath = path.join(viewDir, ".gufi-view.json");
2062
2036
  if (fs.existsSync(metaPath)) {
2063
2037
  try {
@@ -0,0 +1,358 @@
1
+ ---
2
+ id: architecture
3
+ title: "Architecture Overview"
4
+ description: "How Gufi is built and why"
5
+ icon: Layers
6
+ category: dev
7
+ part: 1
8
+ ---
9
+
10
+ # Architecture Overview
11
+
12
+ How Gufi is built and why
13
+
14
+ ## System Architecture
15
+
16
+ Gufi follows a modern microservices architecture optimized for multi-tenancy, real-time updates, and developer experience.
17
+
18
+ ```
19
+ ┌─────────────────────────────────────────────────────────────────┐
20
+ │ CLIENTS │
21
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
22
+ │ │ Web App │ │ Mobile │ │ CLI │ │ API │ │
23
+ │ │ (React) │ │ (PWA) │ │ (Node) │ │ Clients │ │
24
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
25
+ │ │ │ │ │ │
26
+ └───────┼─────────────┼─────────────┼─────────────┼───────────────┘
27
+ │ │ │ │
28
+ ▼ ▼ ▼ ▼
29
+ ┌─────────────────────────────────────────────────────────────────┐
30
+ │ LOAD BALANCER │
31
+ └───────────────────────────┬─────────────────────────────────────┘
32
+
33
+ ┌───────────────────┼───────────────────┐
34
+ │ │ │
35
+ ▼ ▼ ▼
36
+ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
37
+ │ Backend ERP │ │ Marketplace │ │ WebSocket │
38
+ │ (Express) │ │ Backend │ │ Server │
39
+ │ :3000 │ │ :3003 │ │ :4000 │
40
+ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘
41
+ │ │ │
42
+ └──────────────────┼───────────────────┘
43
+
44
+ ┌──────────────────┴──────────────────┐
45
+ │ │
46
+ ▼ ▼
47
+ ┌───────────────┐ ┌───────────────┐
48
+ │ PostgreSQL │ │ Redis │
49
+ │ (Primary DB) │ │ (Cache) │
50
+ └───────────────┘ └───────────────┘
51
+
52
+
53
+ ┌───────────────┐
54
+ │ Worker │
55
+ │ (pg-boss) │
56
+ └───────────────┘
57
+ ```
58
+
59
+ ## Core Services
60
+
61
+ ### Backend ERP (Port 3000)
62
+
63
+ The main API server handling:
64
+
65
+ - Authentication and authorization
66
+ - CRUD operations for all entities
67
+ - Module and schema management
68
+ - File uploads and storage
69
+ - Business logic and validations
70
+
71
+ **Tech Stack:**
72
+ - Node.js with Express
73
+ - JWT + HttpOnly cookie authentication
74
+ - Zod for validation
75
+ - pg (node-postgres) for database
76
+
77
+ ### Marketplace Backend (Port 3003)
78
+
79
+ Handles marketplace-specific operations:
80
+
81
+ - Package publishing and versioning
82
+ - View code storage and retrieval
83
+ - Developer accounts and billing
84
+ - Cross-company module installations
85
+
86
+ **Key Endpoints:**
87
+ - `/packages` - Package CRUD
88
+ - `/views` - Custom view management
89
+ - `/developer` - Developer portal API
90
+
91
+ ### WebSocket Server (Port 4000)
92
+
93
+ Real-time communication:
94
+
95
+ - LISTEN/NOTIFY from PostgreSQL
96
+ - Broadcasts changes to connected clients
97
+ - Room-based subscriptions per company/table
98
+ - Presence tracking
99
+
100
+ **Protocol:**
101
+ - Native WebSocket (not Socket.IO)
102
+ - JSON message format
103
+ - Heartbeat for connection health
104
+
105
+ ### Worker (pg-boss)
106
+
107
+ Background job processing:
108
+
109
+ - Scheduled automations
110
+ - Email sending
111
+ - Heavy computations
112
+ - Data synchronization
113
+ - External API calls
114
+
115
+ **Features:**
116
+ - Persistent job queue in PostgreSQL
117
+ - Retry with exponential backoff
118
+ - Dead letter queue for failures
119
+ - Monitoring and alerting
120
+
121
+ ## Frontend Architecture
122
+
123
+ ### React Application
124
+
125
+ Built with modern React patterns:
126
+
127
+ ```
128
+ frontend/src/
129
+ ├── features/ # Feature-based organization
130
+ │ ├── auth/ # Login, registration
131
+ │ ├── tables/ # Table views and editing
132
+ │ ├── modules/ # Module management
133
+ │ └── ...
134
+ ├── shared/ # Shared components
135
+ │ ├── ui/ # Base UI components
136
+ │ ├── views/ # View system components
137
+ │ ├── dialogs/ # Dialog components
138
+ │ └── hooks/ # Custom hooks
139
+ ├── stores/ # Zustand state stores
140
+ ├── contexts/ # React contexts
141
+ └── sdk/ # Gufi SDK for views
142
+ ```
143
+
144
+ ### State Management
145
+
146
+ | Store | Purpose |
147
+ |---|---|
148
+ | **authStore** | User session, tokens |
149
+ | **schemaStore** | Company schema cache |
150
+ | **permissionsStore** | Permission cache |
151
+ | **uiStore** | UI state (sidebar, dialogs) |
152
+
153
+ Using Zustand for simple, type-safe state management.
154
+
155
+ ### Data Fetching
156
+
157
+ Using Refine framework:
158
+
159
+ - Automatic CRUD hooks
160
+ - Caching and invalidation
161
+ - Optimistic updates
162
+ - Pagination helpers
163
+
164
+ ## Database Architecture
165
+
166
+ ### PostgreSQL as Core
167
+
168
+ Why PostgreSQL:
169
+
170
+ - JSONB for flexible schemas
171
+ - Row-level security
172
+ - LISTEN/NOTIFY for real-time
173
+ - Excellent performance
174
+ - Strong ACID compliance
175
+
176
+ ### Multi-Tenant Schema
177
+
178
+ ```
179
+ PostgreSQL Instance
180
+ ├── core (schema) # Platform data
181
+ │ ├── users # All users
182
+ │ ├── companies # All companies
183
+ │ ├── modules # Module definitions
184
+ │ ├── entities # Entity definitions
185
+ │ └── automation_scripts # Shared scripts
186
+
187
+ ├── company_1 (schema) # Company 1 data
188
+ │ ├── m1_t1 # Module 1, Entity 1
189
+ │ ├── m1_t2 # Module 1, Entity 2
190
+ │ ├── __audit_log__ # Audit trail
191
+ │ └── ...
192
+
193
+ ├── company_2 (schema) # Company 2 data
194
+ │ └── ...
195
+ └── ...
196
+ ```
197
+
198
+ ### Table Naming Convention
199
+
200
+ Physical tables use IDs:
201
+
202
+ ```
203
+ m{moduleId}_t{entityId}
204
+
205
+ Example: m360_t4589
206
+ └── Module 360, Entity 4589
207
+ ```
208
+
209
+ Logical names (products, customers) map to physical names via schema.
210
+
211
+ ## API Design
212
+
213
+ ### RESTful Endpoints
214
+
215
+ ```
216
+ GET /api/tables/:tableId # List records
217
+ GET /api/tables/:tableId/:rowId # Get record
218
+ POST /api/tables/:tableId # Create record
219
+ PUT /api/tables/:tableId/:rowId # Update record
220
+ DELETE /api/tables/:tableId/:rowId # Delete record
221
+ ```
222
+
223
+ ### Authentication Flow
224
+
225
+ ```
226
+ 1. Login → POST /api/auth/login
227
+ ← Access token (15min) + Refresh cookie (7days)
228
+
229
+ 2. API Request → GET /api/... + Authorization: Bearer {token}
230
+ ← Data response
231
+
232
+ 3. Token Expiring → POST /api/auth/refresh
233
+ ← New access token
234
+ ```
235
+
236
+ ### Response Format
237
+
238
+ Success:
239
+ ```json
240
+ {
241
+ "data": { ... },
242
+ "meta": {
243
+ "total": 100,
244
+ "page": 1,
245
+ "pageSize": 20
246
+ }
247
+ }
248
+ ```
249
+
250
+ Error:
251
+ ```json
252
+ {
253
+ "error": {
254
+ "code": "VALIDATION_ERROR",
255
+ "message": "Price must be positive",
256
+ "details": { "field": "price" }
257
+ }
258
+ }
259
+ ```
260
+
261
+ ## Security Model
262
+
263
+ ### Layers of Security
264
+
265
+ 1. **Network**: HTTPS, WAF, Rate limiting
266
+ 2. **Authentication**: JWT, refresh tokens, 2FA
267
+ 3. **Authorization**: Role-based, entity-level
268
+ 4. **Data**: Row-level security, tenant isolation
269
+ 5. **Audit**: Complete change tracking
270
+
271
+ ### Tenant Isolation
272
+
273
+ Each request:
274
+
275
+ ```
276
+ 1. Extract company_id from JWT
277
+ 2. SET search_path TO company_{id}
278
+ 3. Execute query (sees only company data)
279
+ 4. RESET search_path
280
+ ```
281
+
282
+ No cross-tenant queries possible at database level.
283
+
284
+ ## Caching Strategy
285
+
286
+ ### Redis Cache Layers
287
+
288
+ | Layer | TTL | Purpose |
289
+ |---|---|---|
290
+ | **Schema** | 5 min | Company schemas |
291
+ | **Permissions** | 1 min | User permissions |
292
+ | **Queries** | 30 sec | Expensive queries |
293
+ | **Sessions** | 7 days | User sessions |
294
+
295
+ ### Cache Invalidation
296
+
297
+ Event-driven invalidation:
298
+
299
+ ```
300
+ Record Changed → Invalidate related caches
301
+ → Notify WebSocket
302
+ → Clients refetch
303
+ ```
304
+
305
+ ## Deployment
306
+
307
+ ### Container Architecture
308
+
309
+ ```yaml
310
+ services:
311
+ frontend:
312
+ image: gufi/frontend
313
+ ports: [5173, 8080]
314
+
315
+ backend:
316
+ image: gufi/backend
317
+ ports: [3000]
318
+ depends_on: [db, redis]
319
+
320
+ websocket:
321
+ image: gufi/websocket
322
+ ports: [4000]
323
+ depends_on: [db, redis]
324
+
325
+ worker:
326
+ image: gufi/worker
327
+ depends_on: [db, redis]
328
+
329
+ db:
330
+ image: postgres:15
331
+ volumes: [db_data]
332
+
333
+ redis:
334
+ image: redis:7
335
+ ```
336
+
337
+ ### Scaling
338
+
339
+ | Component | Scale Strategy |
340
+ |---|---|
341
+ | Frontend | Horizontal (CDN + replicas) |
342
+ | Backend | Horizontal (load balanced) |
343
+ | WebSocket | Horizontal (Redis pub/sub) |
344
+ | Worker | Horizontal (pg-boss handles) |
345
+ | PostgreSQL | Vertical + Read replicas |
346
+ | Redis | Cluster mode |
347
+
348
+ ## Performance Targets
349
+
350
+ | Metric | Target |
351
+ |---|---|
352
+ | API Response (P95) | < 100ms |
353
+ | Page Load | < 2s |
354
+ | Time to Interactive | < 3s |
355
+ | WebSocket Latency | < 50ms |
356
+ | Database Query | < 50ms |
357
+
358
+ Monitored continuously with alerting on degradation.