@ketrics/ketrics-cli 0.5.0 → 0.6.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/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +6 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/create.d.ts +1 -0
- package/dist/src/commands/create.d.ts.map +1 -1
- package/dist/src/commands/create.js +44 -13
- package/dist/src/commands/create.js.map +1 -1
- package/dist/src/services/local-template-service.d.ts +52 -0
- package/dist/src/services/local-template-service.d.ts.map +1 -0
- package/dist/src/services/local-template-service.js +216 -0
- package/dist/src/services/local-template-service.js.map +1 -0
- package/dist/src/services/remote-template-service.d.ts +41 -0
- package/dist/src/services/remote-template-service.d.ts.map +1 -0
- package/dist/src/services/remote-template-service.js +232 -0
- package/dist/src/services/remote-template-service.js.map +1 -0
- package/dist/src/services/template-cache-service.d.ts +44 -0
- package/dist/src/services/template-cache-service.d.ts.map +1 -0
- package/dist/src/services/template-cache-service.js +193 -0
- package/dist/src/services/template-cache-service.js.map +1 -0
- package/dist/src/services/template-service.d.ts +25 -31
- package/dist/src/services/template-service.d.ts.map +1 -1
- package/dist/src/services/template-service.js +136 -132
- package/dist/src/services/template-service.js.map +1 -1
- package/dist/src/types/index.d.ts +46 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +5 -1
- package/templates/HelloWorld/.claude/skills/ketrics-app/BACKEND_REFERENCE.md +693 -0
- package/templates/HelloWorld/.claude/skills/ketrics-app/CONFIG_AND_DEPLOY.md +278 -0
- package/templates/HelloWorld/.claude/skills/ketrics-app/FRONTEND_REFERENCE.md +325 -0
- package/templates/HelloWorld/.claude/skills/ketrics-app/SKILL.md +348 -0
- package/templates/HelloWorld/.env.example +20 -0
- package/templates/HelloWorld/.github/workflows/deploy.yml +51 -0
- package/templates/HelloWorld/backend/package.json +1 -1
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ketrics-app
|
|
3
|
+
description: Scaffolds and builds Ketrics tenant applications with backend handlers, frontend React UI, and platform SDK integrations. Use when creating a new Ketrics app, adding backend handlers, setting up database connections, DocumentDB storage, Excel exports, Volume file storage, messaging, comments, environment variables, or deploying to the Ketrics platform.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ketrics Application Builder
|
|
7
|
+
|
|
8
|
+
Build tenant applications on the Ketrics platform. This skill covers project scaffolding, backend handler development, frontend React integration, and deployment.
|
|
9
|
+
|
|
10
|
+
## Architecture overview
|
|
11
|
+
|
|
12
|
+
A Ketrics app has two parts:
|
|
13
|
+
|
|
14
|
+
1. **Backend** (`backend/src/index.ts`): Single TypeScript file exporting async handler functions. Uses the global `ketrics` object (typed by `@ketrics/sdk-backend`) with no imports needed. Built with esbuild into a single bundle.
|
|
15
|
+
2. **Frontend** (`frontend/src/`): React app (Vite + TypeScript) embedded as an iframe. Uses `@ketrics/sdk-frontend` for auth. Calls backend handlers via a service layer.
|
|
16
|
+
|
|
17
|
+
A `ketrics.config.json` at the project root declares the app name, runtime, action names, entry point, and resources.
|
|
18
|
+
|
|
19
|
+
## Project structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
my-ketrics-app/
|
|
23
|
+
├── ketrics.config.json # App config (actions, resources)
|
|
24
|
+
├── CLAUDE.md # Dev instructions
|
|
25
|
+
├── backend/
|
|
26
|
+
│ ├── package.json # devDeps: @ketrics/sdk-backend, esbuild, typescript
|
|
27
|
+
│ ├── tsconfig.json
|
|
28
|
+
│ └── src/
|
|
29
|
+
│ └── index.ts # All handler functions (single file)
|
|
30
|
+
├── frontend/
|
|
31
|
+
│ ├── package.json # deps: react, @ketrics/sdk-frontend, vite
|
|
32
|
+
│ ├── tsconfig.json
|
|
33
|
+
│ ├── vite.config.ts
|
|
34
|
+
│ ├── index.html
|
|
35
|
+
│ └── src/
|
|
36
|
+
│ ├── App.tsx # Main component
|
|
37
|
+
│ ├── main.tsx # React entry point
|
|
38
|
+
│ ├── types.ts # Shared TypeScript interfaces
|
|
39
|
+
│ ├── services/
|
|
40
|
+
│ │ └── index.ts # callFunction service layer
|
|
41
|
+
│ └── mocks/
|
|
42
|
+
│ └── handlers.ts # Dev-mode mock handlers
|
|
43
|
+
└── .github/
|
|
44
|
+
└── workflows/
|
|
45
|
+
└── deploy.yml # CI/CD pipeline
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Step-by-step: Create a new app
|
|
49
|
+
|
|
50
|
+
### 1. Initialize the project
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mkdir my-ketrics-app && cd my-ketrics-app
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Create `ketrics.config.json`
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"name": "my-ketrics-app",
|
|
61
|
+
"version": "1.0.0",
|
|
62
|
+
"description": "My Ketrics application",
|
|
63
|
+
"runtime": "nodejs18",
|
|
64
|
+
"actions": [
|
|
65
|
+
"listItems",
|
|
66
|
+
"getItem",
|
|
67
|
+
"createItem",
|
|
68
|
+
"updateItem",
|
|
69
|
+
"deleteItem"
|
|
70
|
+
],
|
|
71
|
+
"entry": "dist/index.js",
|
|
72
|
+
"include": ["dist/**/*"],
|
|
73
|
+
"exclude": ["node_modules", "*.test.js"],
|
|
74
|
+
"resources": {
|
|
75
|
+
"documentdb": [
|
|
76
|
+
{ "code": "app-data", "description": "Main data store" }
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Key rules:**
|
|
83
|
+
- `actions` array MUST match the exported handler names in `backend/src/index.ts`
|
|
84
|
+
- `resources` declares DocumentDB collections and Volumes the app needs
|
|
85
|
+
- Add `"volume"` entries under resources when the app needs file storage
|
|
86
|
+
|
|
87
|
+
### 3. Set up the backend
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
mkdir -p backend/src
|
|
91
|
+
cd backend
|
|
92
|
+
npm init -y
|
|
93
|
+
npm install -D @ketrics/sdk-backend@0.11.0 esbuild typescript @types/node
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**`backend/package.json` scripts:**
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"scripts": {
|
|
100
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=es2020 --outfile=dist/index.js"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Write backend handlers
|
|
106
|
+
|
|
107
|
+
See [BACKEND_REFERENCE.md](BACKEND_REFERENCE.md) for the complete SDK API and patterns.
|
|
108
|
+
|
|
109
|
+
Every handler is an `async` function that receives a typed payload and returns a result object:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const myHandler = async (payload: { id: string }) => {
|
|
113
|
+
const userId = ketrics.requestor.userId;
|
|
114
|
+
// ... handler logic using ketrics.* SDK
|
|
115
|
+
return { result: "value" };
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export { myHandler };
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 5. Set up the frontend
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mkdir -p frontend/src/services frontend/src/mocks
|
|
125
|
+
cd frontend
|
|
126
|
+
npm init -y
|
|
127
|
+
npm install react react-dom @ketrics/sdk-frontend
|
|
128
|
+
npm install -D @vitejs/plugin-react vite typescript @types/react @types/react-dom
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See [FRONTEND_REFERENCE.md](FRONTEND_REFERENCE.md) for the service layer, auth, and mock handler patterns.
|
|
132
|
+
|
|
133
|
+
### 6. Deploy
|
|
134
|
+
|
|
135
|
+
See [CONFIG_AND_DEPLOY.md](CONFIG_AND_DEPLOY.md) for ketrics.config.json details and GitHub Actions CI/CD.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Manual deploy (requires .env with KETRICS_TOKEN)
|
|
139
|
+
ketrics deploy --env .env
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## SDK quick reference
|
|
143
|
+
|
|
144
|
+
The `ketrics` global object is available in all backend handlers with no imports. Here's a quick overview of each subsystem:
|
|
145
|
+
|
|
146
|
+
### Environment variables
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const value = ketrics.environment["MY_VAR"];
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Read app-specific configuration. Common pattern: store JSON arrays for connection configs, resource codes, feature flags.
|
|
153
|
+
|
|
154
|
+
### Requestor context
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
ketrics.requestor.userId // Current user ID
|
|
158
|
+
ketrics.requestor.name // Display name
|
|
159
|
+
ketrics.requestor.email // Email
|
|
160
|
+
ketrics.requestor.applicationPermissions // ["editor", ...]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Database (SQL queries)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const db = await ketrics.Database.connect(connectionCode);
|
|
167
|
+
try {
|
|
168
|
+
const result = await db.query<Record<string, unknown>>(sql, params);
|
|
169
|
+
// result.rows, result.rowCount
|
|
170
|
+
} finally {
|
|
171
|
+
await db.close();
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### DocumentDB (NoSQL storage)
|
|
176
|
+
|
|
177
|
+
DynamoDB-style pk/sk model with `put`, `get`, `delete`, `list` operations.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const docdb = await ketrics.DocumentDb.connect(resourceCode);
|
|
181
|
+
await docdb.put(pk, sk, item);
|
|
182
|
+
const item = await docdb.get(pk, sk);
|
|
183
|
+
const result = await docdb.list(pk, { skPrefix: "PREFIX#" });
|
|
184
|
+
await docdb.delete(pk, sk);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Excel generation
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const excel = ketrics.Excel.create();
|
|
191
|
+
const sheet = excel.addWorksheet("Sheet1");
|
|
192
|
+
sheet.columns = columns.map(col => ({ header: col, key: col, width: 15 }));
|
|
193
|
+
sheet.addRows(rows);
|
|
194
|
+
const buffer = await excel.toBuffer();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Volume (file storage)
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const volume = await ketrics.Volume.connect(volumeCode);
|
|
201
|
+
await volume.put(fileKey, buffer);
|
|
202
|
+
const { url } = await volume.generateDownloadUrl(fileKey);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Messages (notifications)
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
await ketrics.Messages.sendBulk({
|
|
209
|
+
userIds: ["user1", "user2"],
|
|
210
|
+
type: "CUSTOM_TYPE",
|
|
211
|
+
subject: "Subject line",
|
|
212
|
+
body: "**Markdown** body",
|
|
213
|
+
priority: "MEDIUM",
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Users
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const users = await ketrics.Users.list();
|
|
221
|
+
// [{ id, firstName, lastName, email }, ...]
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Logging
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
ketrics.console.error("Non-critical error message");
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Application context
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
ketrics.application.id // Application UUID
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Common patterns
|
|
237
|
+
|
|
238
|
+
### Permission checking
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
function requireEditor(): void {
|
|
242
|
+
if (!ketrics.requestor.applicationPermissions.includes("editor")) {
|
|
243
|
+
throw new Error("Permission denied: editor role required");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Ownership verification
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const existing = await docdb.get(pk, sk);
|
|
252
|
+
if (!existing) throw new Error("Not found");
|
|
253
|
+
if (existing.createdBy !== userId) {
|
|
254
|
+
throw new Error("You can only modify your own resources");
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### DocumentDB key design
|
|
259
|
+
|
|
260
|
+
Use prefixed composite keys for multi-entity storage in a single DocumentDB:
|
|
261
|
+
|
|
262
|
+
| Entity | pk | sk |
|
|
263
|
+
|--------|----|----|
|
|
264
|
+
| User items | `USER#${userId}` | `ITEM#${itemId}` |
|
|
265
|
+
| Tenant-wide | `TENANT_ITEMS` | `ITEM#${itemId}` |
|
|
266
|
+
| Comments | `COMMENTS#${targetId}` | `COMMENT#${createdAt}#${commentId}` |
|
|
267
|
+
| Index | `INDEX#${scope}` | `KEY#${lookupKey}` |
|
|
268
|
+
|
|
269
|
+
### Export to Excel via Volume
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const exportToExcel = async (payload: { data: unknown[] }) => {
|
|
273
|
+
const volumeCode = ketrics.environment["EXPORTS_VOLUME"];
|
|
274
|
+
if (!volumeCode) throw new Error("EXPORTS_VOLUME not configured");
|
|
275
|
+
|
|
276
|
+
// Generate Excel
|
|
277
|
+
const excel = ketrics.Excel.create();
|
|
278
|
+
const sheet = excel.addWorksheet("Export");
|
|
279
|
+
// ... add columns and rows
|
|
280
|
+
const buffer = await excel.toBuffer();
|
|
281
|
+
|
|
282
|
+
// Save to Volume and get download URL
|
|
283
|
+
const filename = `export_${Date.now()}.xlsx`;
|
|
284
|
+
const fileKey = `${ketrics.application.id}/${filename}`;
|
|
285
|
+
const volume = await ketrics.Volume.connect(volumeCode);
|
|
286
|
+
await volume.put(fileKey, buffer);
|
|
287
|
+
const { url } = await volume.generateDownloadUrl(fileKey);
|
|
288
|
+
|
|
289
|
+
return { url, filename };
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Comment system
|
|
294
|
+
|
|
295
|
+
Store comments with composite keys for efficient queries. Use a separate index for fast count lookups:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Store comment
|
|
299
|
+
await docdb.put(
|
|
300
|
+
`COMMENTS#${targetId}`,
|
|
301
|
+
`COMMENT#${createdAt}#${commentId}`,
|
|
302
|
+
commentData
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
// Update count index
|
|
306
|
+
const indexItem = await docdb.get(`INDEX#${scope}`, `KEY#${targetId}`);
|
|
307
|
+
const count = indexItem ? (indexItem.count as number) : 0;
|
|
308
|
+
await docdb.put(`INDEX#${scope}`, `KEY#${targetId}`, {
|
|
309
|
+
targetId,
|
|
310
|
+
count: count + 1,
|
|
311
|
+
lastCommentAt: now,
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Shared resources with access control
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// Filter: owned by me OR shared with me OR shared with all
|
|
319
|
+
const items = result.items.filter((item) => {
|
|
320
|
+
if (item.owner === userId) return true;
|
|
321
|
+
if (item.visibility !== "shared") return false;
|
|
322
|
+
const sw = item.sharedWith;
|
|
323
|
+
if (sw === "all") return true;
|
|
324
|
+
if (Array.isArray(sw) && sw.includes(userId)) return true;
|
|
325
|
+
return false;
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Additional resources
|
|
330
|
+
|
|
331
|
+
- **Backend SDK reference**: See [BACKEND_REFERENCE.md](BACKEND_REFERENCE.md) for complete API docs and all handler patterns
|
|
332
|
+
- **Frontend patterns**: See [FRONTEND_REFERENCE.md](FRONTEND_REFERENCE.md) for the service layer, auth manager, mock handlers, and type definitions
|
|
333
|
+
- **Config and deployment**: See [CONFIG_AND_DEPLOY.md](CONFIG_AND_DEPLOY.md) for ketrics.config.json schema, GitHub Actions workflow, and environment setup
|
|
334
|
+
|
|
335
|
+
## Checklist for new apps
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
- [ ] Create ketrics.config.json with correct actions and resources
|
|
339
|
+
- [ ] Set up backend with @ketrics/sdk-backend and esbuild
|
|
340
|
+
- [ ] Write handler functions using ketrics.* global
|
|
341
|
+
- [ ] Export all handlers and sync with config actions array
|
|
342
|
+
- [ ] Set up frontend with React, Vite, and @ketrics/sdk-frontend
|
|
343
|
+
- [ ] Create callFunction service layer with dev/prod branching
|
|
344
|
+
- [ ] Write mock handlers for local development
|
|
345
|
+
- [ ] Define TypeScript interfaces in types.ts
|
|
346
|
+
- [ ] Set up GitHub Actions deploy workflow
|
|
347
|
+
- [ ] Configure environment variables in Ketrics dashboard
|
|
348
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Ketrics Deployment Configuration
|
|
2
|
+
# Copy this file to .env and fill in your values
|
|
3
|
+
|
|
4
|
+
# API URL (default for production)
|
|
5
|
+
KETRICS_API_URL=https://api.ketrics.io/api/v1
|
|
6
|
+
|
|
7
|
+
# RUNTIME API URL (default for production)
|
|
8
|
+
KETRICS_RUNTIME_URL=https://runtime.ketrics.io
|
|
9
|
+
|
|
10
|
+
# Your tenant ID
|
|
11
|
+
KETRICS_TENANT_ID=TENANT_ID_HERE
|
|
12
|
+
|
|
13
|
+
# Application ID for this backend
|
|
14
|
+
KETRICS_APPLICATION_ID=APPLICATION_ID_HERE
|
|
15
|
+
|
|
16
|
+
# Deployment token from Ketrics dashboard
|
|
17
|
+
KETRICS_TOKEN=KETRICS_TOKEN_HERE
|
|
18
|
+
|
|
19
|
+
# Optional: Custom authentication token (JWT) for Ketrics API
|
|
20
|
+
KETRICS_AUTH_TOKEN=KETRICS_AUTH_TOKEN_HERE
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Deploy to Ketrics
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
KETRICS_API_URL: https://api.ketrics.io/api/v1
|
|
9
|
+
KETRICS_RUNTIME_URL: https://runtime.ketrics.io
|
|
10
|
+
KETRICS_TENANT_ID: KETRICS_TENANT_ID
|
|
11
|
+
KETRICS_APPLICATION_ID: KETRICS_APPLICATION_ID
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
deploy:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: prod
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: 18
|
|
23
|
+
|
|
24
|
+
# Install Ketrics CLI
|
|
25
|
+
- run: npm install -g @ketrics/ketrics-cli
|
|
26
|
+
|
|
27
|
+
# Generate .env file (only KETRICS_TOKEN is a secret)
|
|
28
|
+
- name: Generate .env file
|
|
29
|
+
run: |
|
|
30
|
+
cat <<EOF > .env
|
|
31
|
+
KETRICS_API_URL=$KETRICS_API_URL
|
|
32
|
+
KETRICS_RUNTIME_URL=$KETRICS_RUNTIME_URL
|
|
33
|
+
KETRICS_TENANT_ID=$KETRICS_TENANT_ID
|
|
34
|
+
KETRICS_APPLICATION_ID=$KETRICS_APPLICATION_ID
|
|
35
|
+
KETRICS_TOKEN=${{ secrets.KETRICS_TOKEN }}
|
|
36
|
+
EOF
|
|
37
|
+
|
|
38
|
+
# Backend: install + build
|
|
39
|
+
- run: npm ci
|
|
40
|
+
working-directory: backend
|
|
41
|
+
- run: npm run build
|
|
42
|
+
working-directory: backend
|
|
43
|
+
|
|
44
|
+
# Frontend: install + build
|
|
45
|
+
- run: npm ci
|
|
46
|
+
working-directory: frontend
|
|
47
|
+
- run: npm run build
|
|
48
|
+
working-directory: frontend
|
|
49
|
+
|
|
50
|
+
# Deploy
|
|
51
|
+
- run: ketrics deploy --env .env
|