trinity-method-sdk 2.0.9 → 2.2.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/CHANGELOG.md +702 -267
- package/README.md +550 -540
- package/dist/cli/commands/deploy/agents.js +1 -1
- package/dist/cli/commands/deploy/ci-cd.d.ts +4 -3
- package/dist/cli/commands/deploy/ci-cd.js +10 -9
- package/dist/cli/commands/deploy/claude-setup.js +28 -35
- package/dist/cli/commands/deploy/configuration.js +10 -11
- package/dist/cli/commands/deploy/directories.js +13 -14
- package/dist/cli/commands/deploy/gitignore.js +3 -5
- package/dist/cli/commands/deploy/index.d.ts +1 -1
- package/dist/cli/commands/deploy/index.js +7 -3
- package/dist/cli/commands/deploy/knowledge-base.js +3 -3
- package/dist/cli/commands/deploy/pre-flight.js +1 -1
- package/dist/cli/commands/deploy/root-files.js +3 -18
- package/dist/cli/commands/deploy/sdk-install.js +1 -1
- package/dist/cli/commands/deploy/summary.js +3 -3
- package/dist/cli/commands/deploy/templates.js +33 -20
- package/dist/cli/commands/update/agents.js +1 -1
- package/dist/cli/commands/update/backup.js +6 -12
- package/dist/cli/commands/update/commands.d.ts +1 -0
- package/dist/cli/commands/update/commands.js +18 -45
- package/dist/cli/commands/update/knowledge-base.js +2 -2
- package/dist/cli/commands/update/pre-flight.js +11 -11
- package/dist/cli/commands/update/summary.js +5 -5
- package/dist/cli/commands/update/templates.js +35 -13
- package/dist/cli/commands/update/verification.js +5 -5
- package/dist/cli/commands/update/version.js +1 -1
- package/dist/cli/utils/deploy-ci.d.ts +3 -2
- package/dist/cli/utils/deploy-ci.js +24 -24
- package/dist/cli/utils/deploy-linting.js +101 -6
- package/dist/cli/utils/error-classes.d.ts +2 -2
- package/dist/cli/utils/linting-tools.js +14 -6
- package/dist/cli/utils/template-processor.js +2 -4
- package/dist/templates/{claude → .claude}/EMPLOYEE-DIRECTORY.md.template +16 -22
- package/dist/templates/{agents → .claude/agents}/aj-team/apo-documentation-specialist.md.template +10 -10
- package/dist/templates/{agents → .claude/agents}/aj-team/bas-quality-gate.md.template +13 -15
- package/dist/templates/{agents → .claude/agents}/aj-team/bon-dependency-manager.md.template +4 -4
- package/dist/templates/{agents → .claude/agents}/aj-team/cap-configuration-specialist.md.template +3 -3
- package/dist/templates/{agents → .claude/agents}/aj-team/dra-code-reviewer.md.template +8 -10
- package/dist/templates/{agents → .claude/agents}/aj-team/kil-task-executor.md.template +11 -13
- package/dist/templates/{agents → .claude/agents}/aj-team/uro-refactoring-specialist.md.template +3 -3
- package/dist/templates/{agents → .claude/agents}/audit/juno-auditor.md.template +46 -42
- package/dist/templates/{agents → .claude/agents}/deployment/ein-cicd.md.template +59 -164
- package/dist/templates/{agents → .claude/agents}/deployment/ino-context.md.template +25 -22
- package/dist/templates/{agents → .claude/agents}/deployment/tan-structure.md.template +32 -30
- package/dist/templates/{agents → .claude/agents}/deployment/zen-knowledge.md.template +28 -23
- package/dist/templates/{agents → .claude/agents}/leadership/aj-maestro.md.template +10 -6
- package/dist/templates/{agents → .claude/agents}/leadership/aly-cto.md.template +22 -19
- package/dist/templates/{agents → .claude/agents}/planning/eus-decomposer.md.template +8 -4
- package/dist/templates/{agents → .claude/agents}/planning/mon-requirements.md.template +8 -4
- package/dist/templates/{agents → .claude/agents}/planning/ror-design.md.template +8 -4
- package/dist/templates/{agents → .claude/agents}/planning/tra-planner.md.template +10 -4
- package/dist/templates/{shared/claude-commands → .claude/commands/execution}/trinity-audit.md.template +15 -15
- package/dist/templates/.claude/commands/execution/trinity-breakdown.md.template +535 -0
- package/dist/templates/{shared/claude-commands → .claude/commands/execution}/trinity-orchestrate.md.template +48 -48
- package/dist/templates/{shared/claude-commands → .claude/commands/infrastructure}/trinity-init.md.template +32 -54
- package/dist/templates/{shared/claude-commands → .claude/commands/investigation}/trinity-create-investigation.md.template +13 -7
- package/dist/templates/{shared/claude-commands → .claude/commands/investigation}/trinity-investigate-templates.md.template +19 -15
- package/dist/templates/{shared/claude-commands → .claude/commands/investigation}/trinity-plan-investigation.md.template +12 -6
- package/dist/templates/{shared/claude-commands → .claude/commands/maintenance}/trinity-changelog.md.template +9 -8
- package/dist/templates/.claude/commands/maintenance/trinity-docs-update.md.template +279 -0
- package/dist/templates/.claude/commands/maintenance/trinity-docs.md.template +2828 -0
- package/dist/templates/{shared/claude-commands → .claude/commands/maintenance}/trinity-readme.md.template +21 -20
- package/dist/templates/{shared/claude-commands → .claude/commands/planning}/trinity-decompose.md.template +6 -4
- package/dist/templates/{shared/claude-commands → .claude/commands/planning}/trinity-design.md.template +6 -4
- package/dist/templates/{shared/claude-commands → .claude/commands/planning}/trinity-plan.md.template +7 -5
- package/dist/templates/{shared/claude-commands → .claude/commands/planning}/trinity-requirements.md.template +6 -4
- package/dist/templates/{shared/claude-commands → .claude/commands/session}/trinity-continue.md.template +30 -24
- package/dist/templates/{shared/claude-commands → .claude/commands/session}/trinity-end.md.template +403 -397
- package/dist/templates/{shared/claude-commands → .claude/commands/session}/trinity-start.md.template +9 -4
- package/dist/templates/{shared/claude-commands → .claude/commands/utility}/trinity-agents.md.template +11 -8
- package/dist/templates/{shared/claude-commands → .claude/commands/utility}/trinity-verify.md.template +56 -57
- package/dist/templates/{shared/claude-commands → .claude/commands/utility}/trinity-workorder.md.template +13 -9
- package/dist/templates/ci/ci.yml.template +2 -2
- package/dist/templates/root/CLAUDE.md.template +9 -9
- package/dist/templates/root/linting/nodejs/.husky-pre-commit.template +5 -0
- package/dist/templates/source/base-CLAUDE.md.template +310 -310
- package/dist/templates/source/flutter-CLAUDE.md.template +593 -593
- package/dist/templates/source/nodejs-CLAUDE.md.template +531 -531
- package/dist/templates/source/python-CLAUDE.md.template +510 -510
- package/dist/templates/source/react-CLAUDE.md.template +513 -513
- package/dist/templates/source/rust-CLAUDE.md.template +653 -653
- package/dist/templates/trinity/CLAUDE.md.template +14 -14
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/AI-DEVELOPMENT-GUIDE.md.template +1 -1
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/ARCHITECTURE.md.template +5 -5
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/CODING-PRINCIPLES.md.template +1 -1
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/DOCUMENTATION-CRITERIA.md.template +1 -1
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/ISSUES.md.template +9 -9
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/TESTING-PRINCIPLES.md.template +1 -1
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/Technical-Debt.md.template +2 -4
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/To-do.md.template +2 -2
- package/dist/templates/{knowledge-base → trinity/knowledge-base}/Trinity.md.template +6 -6
- package/dist/templates/trinity/templates/documentation/api-docs/README.md.template +218 -0
- package/dist/templates/trinity/templates/documentation/configuration/documentation-structure.md.template +71 -0
- package/dist/templates/trinity/templates/documentation/configuration/env-example-generator.md.template +387 -0
- package/dist/templates/trinity/templates/documentation/discovery/api-endpoint-scanner.md.template +343 -0
- package/dist/templates/trinity/templates/documentation/discovery/component-discovery.md.template +254 -0
- package/dist/templates/trinity/templates/documentation/discovery/env-variable-extraction.md.template +316 -0
- package/dist/templates/trinity/templates/documentation/discovery/framework-detection.md.template +205 -0
- package/dist/templates/trinity/templates/documentation/guides/api-development.md.template +375 -0
- package/dist/templates/trinity/templates/documentation/guides/contributing.md.template +488 -0
- package/dist/templates/trinity/templates/documentation/guides/deployment.md.template +565 -0
- package/dist/templates/trinity/templates/documentation/guides/getting-started.md.template +118 -0
- package/dist/templates/trinity/templates/documentation/mermaid-diagrams/api-endpoint-map.md.template +56 -0
- package/dist/templates/trinity/templates/documentation/mermaid-diagrams/component-hierarchy.md.template +60 -0
- package/dist/templates/trinity/templates/documentation/mermaid-diagrams/database-er.md.template +49 -0
- package/dist/templates/trinity/templates/documentation/mermaid-diagrams/mvc-flow.md.template +41 -0
- package/dist/templates/trinity/templates/documentation/processes/error-handling-protocol.md.template +166 -0
- package/dist/templates/trinity/templates/documentation/processes/fallback-mechanism.md.template +88 -0
- package/dist/templates/trinity/templates/documentation/reports/apo-docs-update-checklist.md.template +343 -0
- package/dist/templates/trinity/templates/documentation/reports/juno-docs-update-checklist.md.template +1337 -0
- package/dist/templates/trinity/templates/documentation/reports/juno-final-report.md.template +237 -0
- package/dist/templates/trinity/templates/documentation/reports/juno-internal-report.md.template +461 -0
- package/dist/templates/trinity/templates/documentation/validation/documentation-verification-rules.md.template +379 -0
- package/dist/templates/trinity/templates/documentation/validation/juno-quality-gates.md.template +282 -0
- package/dist/templates/{investigations → trinity/templates/investigations}/bug.md.template +14 -14
- package/dist/templates/{investigations → trinity/templates/investigations}/feature.md.template +14 -14
- package/dist/templates/{investigations → trinity/templates/investigations}/performance.md.template +14 -14
- package/dist/templates/{investigations → trinity/templates/investigations}/security.md.template +14 -14
- package/dist/templates/{investigations → trinity/templates/investigations}/technical.md.template +14 -14
- package/dist/templates/{work-orders → trinity/templates/work-orders}/ANALYSIS-TEMPLATE.md.template +10 -13
- package/dist/templates/{work-orders → trinity/templates/work-orders}/AUDIT-TEMPLATE.md.template +10 -26
- package/dist/templates/{work-orders → trinity/templates/work-orders}/IMPLEMENTATION-TEMPLATE.md.template +10 -26
- package/dist/templates/{work-orders → trinity/templates/work-orders}/INVESTIGATION-TEMPLATE.md.template +10 -25
- package/dist/templates/{work-orders → trinity/templates/work-orders}/PATTERN-TEMPLATE.md.template +10 -26
- package/dist/templates/{work-orders → trinity/templates/work-orders}/VERIFICATION-TEMPLATE.md.template +10 -26
- package/package.json +99 -94
- package/dist/templates/agents/leadership/aj-cc.md.template +0 -462
- package/dist/templates/ci/cd.yml.template +0 -175
- package/dist/templates/ci/github-actions.yml +0 -86
- package/dist/templates/root/TRINITY.md.template +0 -52
- package/dist/templates/shared/claude-commands/trinity-docs.md.template +0 -2577
- /package/dist/templates/{linting → root/linting}/flutter/.pre-commit-config.yaml.template +0 -0
- /package/dist/templates/{linting → root/linting}/flutter/analysis_options.yaml.template +0 -0
- /package/dist/templates/{linting → root/linting}/nodejs/.eslintrc-commonjs.json.template +0 -0
- /package/dist/templates/{linting → root/linting}/nodejs/.eslintrc-esm.json.template +0 -0
- /package/dist/templates/{linting → root/linting}/nodejs/.eslintrc-typescript.json.template +0 -0
- /package/dist/templates/{linting → root/linting}/nodejs/.pre-commit-config.yaml.template +0 -0
- /package/dist/templates/{linting → root/linting}/nodejs/.prettierrc.json.template +0 -0
- /package/dist/templates/{linting → root/linting}/python/.flake8.template +0 -0
- /package/dist/templates/{linting → root/linting}/python/.pre-commit-config.yaml.template +0 -0
- /package/dist/templates/{linting → root/linting}/python/pyproject.toml.template +0 -0
- /package/dist/templates/{linting → root/linting}/rust/.pre-commit-config.yaml.template +0 -0
- /package/dist/templates/{linting → root/linting}/rust/clippy.toml.template +0 -0
- /package/dist/templates/{linting → root/linting}/rust/rustfmt.toml.template +0 -0
- /package/dist/templates/{documentation → trinity/templates/documentation}/ROOT-README.md.template +0 -0
- /package/dist/templates/{documentation → trinity/templates/documentation}/SUBDIRECTORY-README.md.template +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# .env.example Generator Template
|
|
2
|
+
|
|
3
|
+
**Purpose:** Rule-based engine for generating sensible .env.example values from discovered environment variables.
|
|
4
|
+
|
|
5
|
+
**Usage:** APO-3 reads this template and applies rules to each environment variable key discovered in the codebase.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Instructions for APO-3
|
|
10
|
+
|
|
11
|
+
This template provides a **rule-based engine** for generating sensible .env.example values. You MUST apply these rules in priority order to each discovered environment variable.
|
|
12
|
+
|
|
13
|
+
**Critical Requirements:**
|
|
14
|
+
- Apply rules in the order listed (Rule 1 has highest priority)
|
|
15
|
+
- Stop at the first matching rule for each variable
|
|
16
|
+
- NEVER create empty values (KEY= with no value)
|
|
17
|
+
- All values must be sensible examples or placeholders
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Rule Priority Order
|
|
22
|
+
|
|
23
|
+
### Rule 1: Database URLs (Highest Priority)
|
|
24
|
+
|
|
25
|
+
**Pattern Match:**
|
|
26
|
+
- Key contains "DATABASE" AND "URL"
|
|
27
|
+
- Key contains "DB_URL"
|
|
28
|
+
- Key equals "DATABASE_URL"
|
|
29
|
+
|
|
30
|
+
**Generation Logic:**
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
if (original_value.includes("file:")) {
|
|
34
|
+
// SQLite detected
|
|
35
|
+
example_value = original_value; // Keep SQLite paths (not sensitive)
|
|
36
|
+
}
|
|
37
|
+
else if (original_value.includes("postgresql") || original_value.includes("postgres")) {
|
|
38
|
+
example_value = "postgresql://user:password@localhost:5432/{{PROJECT_NAME}}_db";
|
|
39
|
+
}
|
|
40
|
+
else if (original_value.includes("mongodb") || original_value.includes("mongo")) {
|
|
41
|
+
example_value = "mongodb://localhost:27017/{{PROJECT_NAME}}_db";
|
|
42
|
+
}
|
|
43
|
+
else if (original_value.includes("mysql")) {
|
|
44
|
+
example_value = "mysql://user:password@localhost:3306/{{PROJECT_NAME}}_db";
|
|
45
|
+
}
|
|
46
|
+
else if (original_value.includes("redis")) {
|
|
47
|
+
example_value = "redis://localhost:6379";
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Generic database URL fallback
|
|
51
|
+
example_value = "file:./dev.db"; // Safe SQLite default
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Examples:**
|
|
56
|
+
- `DATABASE_URL=postgresql://prod@aws.com/db` → `postgresql://user:password@localhost:5432/myapp_db`
|
|
57
|
+
- `DATABASE_URL=file:./dev.db` → `file:./dev.db` (kept as-is)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Rule 2: Port Numbers
|
|
62
|
+
|
|
63
|
+
**Pattern Match:**
|
|
64
|
+
- Key equals "PORT"
|
|
65
|
+
- Key ends with "_PORT"
|
|
66
|
+
|
|
67
|
+
**Generation Logic:**
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
if (/^\d+$/.test(original_value)) {
|
|
71
|
+
// Original value is numeric - keep it (ports aren't sensitive)
|
|
72
|
+
example_value = original_value;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Non-numeric or missing - use sensible default
|
|
76
|
+
example_value = "3001"; // Common backend port
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Examples:**
|
|
81
|
+
- `PORT=3000` → `3000` (kept as-is)
|
|
82
|
+
- `API_PORT=8080` → `8080` (kept as-is)
|
|
83
|
+
- `PORT=` → `3001` (default)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### Rule 3: Frontend/CORS URLs
|
|
88
|
+
|
|
89
|
+
**Pattern Match:**
|
|
90
|
+
- Key contains "FRONTEND"
|
|
91
|
+
- Key contains "ORIGIN"
|
|
92
|
+
- Key contains "CLIENT_URL"
|
|
93
|
+
- Key contains "CORS"
|
|
94
|
+
|
|
95
|
+
**Generation Logic:**
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// Check if Next.js project
|
|
99
|
+
if ({{FRAMEWORK}} === "Next.js" || exists("next.config.js")) {
|
|
100
|
+
example_value = "http://localhost:3000"; // Next.js default
|
|
101
|
+
}
|
|
102
|
+
// Check if Vite project
|
|
103
|
+
else if (exists("vite.config.ts") || exists("vite.config.js")) {
|
|
104
|
+
example_value = "http://localhost:5173"; // Vite default
|
|
105
|
+
}
|
|
106
|
+
// Check if Create React App
|
|
107
|
+
else if (exists("react-scripts") in package.json) {
|
|
108
|
+
example_value = "http://localhost:3000"; // CRA default
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
example_value = "http://localhost:5173"; // Modern default (Vite)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Examples:**
|
|
116
|
+
- `FRONTEND_URL=https://prod.app.com` → `http://localhost:5173`
|
|
117
|
+
- `CORS_ORIGIN=*` → `http://localhost:3000` (if Next.js detected)
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### Rule 4: API Keys
|
|
122
|
+
|
|
123
|
+
**Pattern Match:**
|
|
124
|
+
- Key contains "API_KEY"
|
|
125
|
+
- Key contains "APIKEY"
|
|
126
|
+
- Key ends with "_KEY" (but not SECRET or JWT)
|
|
127
|
+
|
|
128
|
+
**Generation Logic:**
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// Extract service name from key (first word before underscore)
|
|
132
|
+
const service_name = key.split('_')[0].toLowerCase();
|
|
133
|
+
example_value = `your_${service_name}_api_key_here`;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Examples:**
|
|
137
|
+
- `STRIPE_API_KEY=sk_live_xxx` → `your_stripe_api_key_here`
|
|
138
|
+
- `OPENAI_API_KEY=sk-xxx` → `your_openai_api_key_here`
|
|
139
|
+
- `GITHUB_APIKEY=ghp_xxx` → `your_github_api_key_here`
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### Rule 5: Client IDs (OAuth)
|
|
144
|
+
|
|
145
|
+
**Pattern Match:**
|
|
146
|
+
- Key contains "CLIENT_ID"
|
|
147
|
+
- Key contains "APP_ID"
|
|
148
|
+
|
|
149
|
+
**Generation Logic:**
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Extract service name from key
|
|
153
|
+
const service_name = key.split('_')[0].toLowerCase();
|
|
154
|
+
example_value = `your_${service_name}_client_id_here`;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Examples:**
|
|
158
|
+
- `GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com` → `your_google_client_id_here`
|
|
159
|
+
- `GITHUB_CLIENT_ID=Iv1.xxx` → `your_github_client_id_here`
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Rule 6: Client Secrets (OAuth)
|
|
164
|
+
|
|
165
|
+
**Pattern Match:**
|
|
166
|
+
- Key contains "SECRET"
|
|
167
|
+
- Key contains "CLIENT_SECRET"
|
|
168
|
+
- Key ends with "_SECRET" (but not JWT_SECRET)
|
|
169
|
+
|
|
170
|
+
**Generation Logic:**
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
// Extract service name from key
|
|
174
|
+
const service_name = key.split('_')[0].toLowerCase();
|
|
175
|
+
example_value = `your_${service_name}_client_secret_here`;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Examples:**
|
|
179
|
+
- `GOOGLE_CLIENT_SECRET=GOCSPX-xxx` → `your_google_client_secret_here`
|
|
180
|
+
- `AWS_SECRET_ACCESS_KEY=xxx` → `your_aws_secret_access_key_here`
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### Rule 7: OAuth Redirect URIs
|
|
185
|
+
|
|
186
|
+
**Pattern Match:**
|
|
187
|
+
- Key contains "REDIRECT" AND "URI"
|
|
188
|
+
- Key contains "CALLBACK" AND "URL"
|
|
189
|
+
|
|
190
|
+
**Generation Logic:**
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
// Extract service name from key
|
|
194
|
+
const service_name = key.match(/^([A-Z]+)_/)?.[1]?.toLowerCase() || "oauth";
|
|
195
|
+
|
|
196
|
+
// Check discovered endpoints for matching callback route
|
|
197
|
+
const callback_endpoint = {{DISCOVERED_ENDPOINTS}}.find(ep =>
|
|
198
|
+
ep.path.includes(service_name) && ep.path.includes("callback")
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (callback_endpoint) {
|
|
202
|
+
example_value = `http://localhost:{{PORT}}${callback_endpoint.path}`;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
example_value = `http://localhost:{{PORT}}/api/${service_name}/callback`;
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Examples:**
|
|
210
|
+
- `GOOGLE_REDIRECT_URI=https://prod.com/auth/google/callback` → `http://localhost:3001/api/auth/google/callback`
|
|
211
|
+
- `OAUTH_CALLBACK_URL=https://prod.com/callback` → `http://localhost:3001/api/oauth/callback`
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### Rule 8: JWT Secrets
|
|
216
|
+
|
|
217
|
+
**Pattern Match:**
|
|
218
|
+
- Key contains "JWT" AND "SECRET"
|
|
219
|
+
- Key equals "JWT_SECRET"
|
|
220
|
+
- Key equals "ACCESS_TOKEN_SECRET"
|
|
221
|
+
- Key equals "REFRESH_TOKEN_SECRET"
|
|
222
|
+
|
|
223
|
+
**Generation Logic:**
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
example_value = "your_jwt_secret_key_here_min_32_chars";
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Examples:**
|
|
230
|
+
- `JWT_SECRET=prod-secret-xxx` → `your_jwt_secret_key_here_min_32_chars`
|
|
231
|
+
- `ACCESS_TOKEN_SECRET=xxx` → `your_jwt_secret_key_here_min_32_chars`
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### Rule 9: Boolean Flags
|
|
236
|
+
|
|
237
|
+
**Pattern Match:**
|
|
238
|
+
- Original value is exactly "true" or "false" (case-insensitive)
|
|
239
|
+
|
|
240
|
+
**Generation Logic:**
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
// Keep original boolean value (not sensitive)
|
|
244
|
+
example_value = original_value.toLowerCase();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Examples:**
|
|
248
|
+
- `DEBUG=true` → `true`
|
|
249
|
+
- `ENABLE_LOGGING=false` → `false`
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### Rule 10: Node Environment
|
|
254
|
+
|
|
255
|
+
**Pattern Match:**
|
|
256
|
+
- Key equals "NODE_ENV"
|
|
257
|
+
|
|
258
|
+
**Generation Logic:**
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
example_value = "development";
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Examples:**
|
|
265
|
+
- `NODE_ENV=production` → `development`
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### Rule 11: Default Fallback (Lowest Priority)
|
|
270
|
+
|
|
271
|
+
**Pattern Match:**
|
|
272
|
+
- No other rule matched
|
|
273
|
+
|
|
274
|
+
**Generation Logic:**
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
// Generate helpful TODO comment
|
|
278
|
+
example_value = `# TODO: Add example value for ${key}`;
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Examples:**
|
|
282
|
+
- `CUSTOM_CONFIG=xxx` → `# TODO: Add example value for CUSTOM_CONFIG`
|
|
283
|
+
- `MY_SPECIAL_KEY=xxx` → `# TODO: Add example value for MY_SPECIAL_KEY`
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Enforcement Requirements
|
|
288
|
+
|
|
289
|
+
**APO-3 MUST:**
|
|
290
|
+
1. ✅ Apply these rules to EVERY environment variable
|
|
291
|
+
2. ✅ Apply rules in priority order (stop at first match)
|
|
292
|
+
3. ✅ NEVER create empty values (KEY= with no value)
|
|
293
|
+
4. ✅ Use {{PROJECT_NAME}}, {{PORT}}, {{FRAMEWORK}} variables where applicable
|
|
294
|
+
5. ✅ Check {{DISCOVERED_ENDPOINTS}} for OAuth callback routes
|
|
295
|
+
|
|
296
|
+
**APO-3 MUST NOT:**
|
|
297
|
+
1. ❌ Skip any environment variable keys
|
|
298
|
+
2. ❌ Create keys with empty values
|
|
299
|
+
3. ❌ Use production URLs or secrets in examples
|
|
300
|
+
4. ❌ Ignore the rule priority order
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Validation
|
|
305
|
+
|
|
306
|
+
**Phase 2.5 Validation 7 will verify:**
|
|
307
|
+
- No empty values in .env.example
|
|
308
|
+
- All keys from .env are present in .env.example
|
|
309
|
+
- All values are sensible examples or placeholders
|
|
310
|
+
- No production secrets leaked
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Variables from JUNO SECTION C
|
|
315
|
+
|
|
316
|
+
APO-3 will receive these pre-filled variables from JUNO's audit report:
|
|
317
|
+
|
|
318
|
+
- `{{PROJECT_NAME}}` - Project name
|
|
319
|
+
- `{{PORT}}` - Discovered backend port
|
|
320
|
+
- `{{FRAMEWORK}}` - Frontend framework (if detected)
|
|
321
|
+
- `{{DISCOVERED_ENDPOINTS}}` - Array of API endpoint objects with path and method
|
|
322
|
+
- `{{ENV_KEYS}}` - Array of environment variable keys discovered
|
|
323
|
+
|
|
324
|
+
**Example JUNO SECTION C Structure:**
|
|
325
|
+
|
|
326
|
+
```markdown
|
|
327
|
+
## File 1: .env.example
|
|
328
|
+
|
|
329
|
+
**Template:** .claude/trinity/templates/documentation/config/env-example-generator.md.template
|
|
330
|
+
|
|
331
|
+
**Variables to Replace:**
|
|
332
|
+
- {{PROJECT_NAME}}: "dnd-tool"
|
|
333
|
+
- {{PORT}}: "3001"
|
|
334
|
+
- {{FRAMEWORK}}: "React (Vite)"
|
|
335
|
+
- {{DISCOVERED_ENDPOINTS}}: [...]
|
|
336
|
+
- {{ENV_KEYS}}: ["DATABASE_URL", "PORT", "JWT_SECRET", "STRIPE_API_KEY", ...]
|
|
337
|
+
|
|
338
|
+
**Output:** .env.example
|
|
339
|
+
**Required:** YES
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Usage Example
|
|
345
|
+
|
|
346
|
+
**Input (from .env):**
|
|
347
|
+
```
|
|
348
|
+
DATABASE_URL=postgresql://prod@aws.rds.com/mydb
|
|
349
|
+
PORT=3001
|
|
350
|
+
FRONTEND_URL=https://app.production.com
|
|
351
|
+
STRIPE_API_KEY=sk_live_xxxxxxxxxx
|
|
352
|
+
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
|
|
353
|
+
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxx
|
|
354
|
+
JWT_SECRET=prod-secret-key-do-not-share
|
|
355
|
+
DEBUG=true
|
|
356
|
+
NODE_ENV=production
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Output (.env.example after applying rules):**
|
|
360
|
+
```
|
|
361
|
+
# Database Configuration
|
|
362
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/dnd_tool_db
|
|
363
|
+
|
|
364
|
+
# Server Configuration
|
|
365
|
+
PORT=3001
|
|
366
|
+
NODE_ENV=development
|
|
367
|
+
|
|
368
|
+
# Frontend Configuration
|
|
369
|
+
FRONTEND_URL=http://localhost:5173
|
|
370
|
+
|
|
371
|
+
# Stripe Integration
|
|
372
|
+
STRIPE_API_KEY=your_stripe_api_key_here
|
|
373
|
+
|
|
374
|
+
# Google OAuth
|
|
375
|
+
GOOGLE_CLIENT_ID=your_google_client_id_here
|
|
376
|
+
GOOGLE_CLIENT_SECRET=your_google_client_secret_here
|
|
377
|
+
|
|
378
|
+
# Security
|
|
379
|
+
JWT_SECRET=your_jwt_secret_key_here_min_32_chars
|
|
380
|
+
|
|
381
|
+
# Development
|
|
382
|
+
DEBUG=true
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
**Last Updated:** {{GENERATION_DATE}}
|
package/dist/templates/trinity/templates/documentation/discovery/api-endpoint-scanner.md.template
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# API Endpoint Scanner Patterns
|
|
2
|
+
**Category:** Discovery
|
|
3
|
+
**Purpose:** Discover API routes and endpoints for Express, Fastify, NestJS, and Koa
|
|
4
|
+
**Used By:** JUNO (Phase 1), APO-1 (API Diagrams), APO-2 (API Docs)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This template provides patterns for discovering API endpoints across popular Node.js backend frameworks.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Route File Discovery
|
|
15
|
+
|
|
16
|
+
### Common Route Locations
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const route_directories = [
|
|
20
|
+
"server/routes/",
|
|
21
|
+
"src/routes/",
|
|
22
|
+
"api/",
|
|
23
|
+
"server/api/",
|
|
24
|
+
"src/api/",
|
|
25
|
+
"app/routes/",
|
|
26
|
+
"routes/"
|
|
27
|
+
];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Route File Patterns
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
const route_file_patterns = [
|
|
34
|
+
"**/*.routes.{ts,js}",
|
|
35
|
+
"**/*.route.{ts,js}",
|
|
36
|
+
"**/*.controller.{ts,js}",
|
|
37
|
+
"**/routes/**/*.{ts,js}",
|
|
38
|
+
"**/api/**/*.{ts,js}"
|
|
39
|
+
];
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Framework-Specific Patterns
|
|
45
|
+
|
|
46
|
+
### Express.js
|
|
47
|
+
|
|
48
|
+
**HTTP Method Patterns:**
|
|
49
|
+
```javascript
|
|
50
|
+
// Grep patterns for Express routes:
|
|
51
|
+
- router.get('...')
|
|
52
|
+
- router.post('...')
|
|
53
|
+
- router.put('...')
|
|
54
|
+
- router.patch('...')
|
|
55
|
+
- router.delete('...')
|
|
56
|
+
- app.get('...')
|
|
57
|
+
- app.post('...')
|
|
58
|
+
- router.route('...').get|post|put|patch|delete()
|
|
59
|
+
- app.route('...').get|post|put|patch|delete()
|
|
60
|
+
- router.all('...')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example extraction:**
|
|
64
|
+
```javascript
|
|
65
|
+
// From: router.get('/users/:id', userController.getUser);
|
|
66
|
+
// Extract:
|
|
67
|
+
{
|
|
68
|
+
method: 'GET',
|
|
69
|
+
path: '/users/:id',
|
|
70
|
+
file: 'server/routes/users.js'
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Fastify
|
|
75
|
+
|
|
76
|
+
**HTTP Method Patterns:**
|
|
77
|
+
```javascript
|
|
78
|
+
// Grep patterns for Fastify routes:
|
|
79
|
+
- fastify.get('...')
|
|
80
|
+
- fastify.post('...')
|
|
81
|
+
- fastify.put('...')
|
|
82
|
+
- fastify.patch('...')
|
|
83
|
+
- fastify.delete('...')
|
|
84
|
+
- app.get('...')
|
|
85
|
+
- server.route({ method: 'GET', url: '...' })
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### NestJS
|
|
89
|
+
|
|
90
|
+
**Controller Decorator Patterns:**
|
|
91
|
+
```javascript
|
|
92
|
+
// Grep patterns for NestJS:
|
|
93
|
+
- @Controller('...')
|
|
94
|
+
- @Get('...')
|
|
95
|
+
- @Post('...')
|
|
96
|
+
- @Put('...')
|
|
97
|
+
- @Patch('...')
|
|
98
|
+
- @Delete('...')
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Example extraction:**
|
|
102
|
+
```javascript
|
|
103
|
+
// From:
|
|
104
|
+
// @Controller('users')
|
|
105
|
+
// class UserController {
|
|
106
|
+
// @Get(':id')
|
|
107
|
+
// getUser() {}
|
|
108
|
+
// }
|
|
109
|
+
//
|
|
110
|
+
// Extract:
|
|
111
|
+
{
|
|
112
|
+
method: 'GET',
|
|
113
|
+
path: '/users/:id',
|
|
114
|
+
file: 'src/users/users.controller.ts'
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Koa
|
|
119
|
+
|
|
120
|
+
**HTTP Method Patterns:**
|
|
121
|
+
```javascript
|
|
122
|
+
// Grep patterns for Koa routes:
|
|
123
|
+
- router.get('...')
|
|
124
|
+
- router.post('...')
|
|
125
|
+
- router.put('...')
|
|
126
|
+
- router.patch('...')
|
|
127
|
+
- router.delete('...')
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Endpoint Extraction Logic
|
|
133
|
+
|
|
134
|
+
### Step 1: Find Route Files
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// Use Glob to find all route files
|
|
138
|
+
const route_files = Glob(route_file_patterns);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Step 2: Parse Each File for HTTP Methods
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Use Grep to find HTTP method declarations
|
|
145
|
+
const methods = ['get', 'post', 'put', 'patch', 'delete'];
|
|
146
|
+
const endpoint_patterns = methods.map(m => `router\\.${m}\\(`);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Step 3: Extract Path and Method
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Example line: router.get('/users/:id', userController.getUser);
|
|
153
|
+
// Parse with regex:
|
|
154
|
+
const match = line.match(/router\.(get|post|put|patch|delete)\(['"](.+?)['"]/);
|
|
155
|
+
if (match) {
|
|
156
|
+
const method = match[1].toUpperCase(); // "GET"
|
|
157
|
+
const path = match[2]; // "/users/:id"
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Step 4: Group by Resource
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
// Group endpoints by base resource:
|
|
165
|
+
// /users, /users/:id, /users/:id/posts → "users" resource
|
|
166
|
+
const resource = path.split('/')[1]; // First path segment
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Template Variable Mapping
|
|
172
|
+
|
|
173
|
+
**Variables to populate:**
|
|
174
|
+
- `{{API_ENDPOINT_COUNT}}` → Total number of endpoints found
|
|
175
|
+
- `{{API_RESOURCES}}` → Unique resource names (e.g., "users, posts, auth")
|
|
176
|
+
- `{{API_ENDPOINTS}}` → List of [METHOD /path] pairs
|
|
177
|
+
- `{{API_BASE_URL}}` → Base URL if detectable (e.g., "/api/v1")
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Endpoint Categorization
|
|
182
|
+
|
|
183
|
+
### By HTTP Method
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const by_method = {
|
|
187
|
+
GET: [], // Read operations
|
|
188
|
+
POST: [], // Create operations
|
|
189
|
+
PUT: [], // Update (full) operations
|
|
190
|
+
PATCH: [], // Update (partial) operations
|
|
191
|
+
DELETE: [] // Delete operations
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### By Resource
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
const by_resource = {
|
|
199
|
+
users: ['GET /users', 'POST /users', 'GET /users/:id'],
|
|
200
|
+
posts: ['GET /posts', 'POST /posts', 'DELETE /posts/:id'],
|
|
201
|
+
auth: ['POST /auth/login', 'POST /auth/logout']
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Verification Requirements
|
|
208
|
+
|
|
209
|
+
### For APO-1 (API Endpoint Map Diagram)
|
|
210
|
+
|
|
211
|
+
**Must provide:**
|
|
212
|
+
1. HTTP method (GET/POST/PUT/PATCH/DELETE)
|
|
213
|
+
2. Full endpoint path
|
|
214
|
+
3. Resource grouping
|
|
215
|
+
4. File location (for verification)
|
|
216
|
+
|
|
217
|
+
**Example output for JUNO report:**
|
|
218
|
+
```
|
|
219
|
+
API Endpoints (12 total):
|
|
220
|
+
|
|
221
|
+
Users Resource:
|
|
222
|
+
- GET /users (list users)
|
|
223
|
+
- POST /users (create user)
|
|
224
|
+
- GET /users/:id (get user)
|
|
225
|
+
- PUT /users/:id (update user)
|
|
226
|
+
- DELETE /users/:id (delete user)
|
|
227
|
+
|
|
228
|
+
Auth Resource:
|
|
229
|
+
- POST /auth/login
|
|
230
|
+
- POST /auth/logout
|
|
231
|
+
- POST /auth/refresh
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### For APO-2 (API Documentation)
|
|
235
|
+
|
|
236
|
+
**Must provide:**
|
|
237
|
+
1. Endpoint count
|
|
238
|
+
2. Resource list
|
|
239
|
+
3. Example endpoints for documentation
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Fallback Detection
|
|
244
|
+
|
|
245
|
+
If no endpoints found with primary patterns:
|
|
246
|
+
|
|
247
|
+
1. **Search for common route keywords:**
|
|
248
|
+
- Grep for: `app.use`, `router.use`, `@Controller`
|
|
249
|
+
2. **Check server entry points:**
|
|
250
|
+
- Look for: `server.js`, `app.js`, `main.ts`, `index.ts`
|
|
251
|
+
3. **Parse server configuration:**
|
|
252
|
+
- Find route mounting: `app.use('/api', apiRouter)`
|
|
253
|
+
4. **Log warning:** "⚠️ No API endpoints found with standard patterns - may be frontend-only project"
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Base URL Detection
|
|
258
|
+
|
|
259
|
+
### Common Patterns
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
// Detect base URL from route mounting:
|
|
263
|
+
// app.use('/api/v1', routes) → base = "/api/v1"
|
|
264
|
+
// app.use('/api', routes) → base = "/api"
|
|
265
|
+
|
|
266
|
+
const base_url_pattern = /app\.use\(['"](\/api[^'"]*)['"]/;
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Prefix Application
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
// If base URL detected, prepend to all endpoints:
|
|
273
|
+
const full_path = `${base_url}${endpoint_path}`;
|
|
274
|
+
// Example: "/api/v1" + "/users" = "/api/v1/users"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Examples
|
|
280
|
+
|
|
281
|
+
### Example 1: Express REST API
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
// Route file: server/routes/users.js
|
|
285
|
+
router.get('/users', userController.list);
|
|
286
|
+
router.post('/users', userController.create);
|
|
287
|
+
router.get('/users/:id', userController.get);
|
|
288
|
+
router.put('/users/:id', userController.update);
|
|
289
|
+
router.delete('/users/:id', userController.delete);
|
|
290
|
+
|
|
291
|
+
// Extracted endpoints:
|
|
292
|
+
API_ENDPOINT_COUNT = 5
|
|
293
|
+
API_RESOURCES = "users"
|
|
294
|
+
API_ENDPOINTS = [
|
|
295
|
+
"GET /users",
|
|
296
|
+
"POST /users",
|
|
297
|
+
"GET /users/:id",
|
|
298
|
+
"PUT /users/:id",
|
|
299
|
+
"DELETE /users/:id"
|
|
300
|
+
]
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Example 2: NestJS API
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
// Controller: src/users/users.controller.ts
|
|
307
|
+
@Controller('users')
|
|
308
|
+
export class UsersController {
|
|
309
|
+
@Get()
|
|
310
|
+
findAll() {}
|
|
311
|
+
|
|
312
|
+
@Get(':id')
|
|
313
|
+
findOne() {}
|
|
314
|
+
|
|
315
|
+
@Post()
|
|
316
|
+
create() {}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Extracted endpoints:
|
|
320
|
+
API_ENDPOINT_COUNT = 3
|
|
321
|
+
API_RESOURCES = "users"
|
|
322
|
+
API_BASE_URL = "/users"
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Error Handling
|
|
328
|
+
|
|
329
|
+
**No endpoints found:**
|
|
330
|
+
- Log: "ℹ️ No API endpoints discovered - may be frontend-only project or static site"
|
|
331
|
+
- Set `API_ENDPOINT_COUNT = 0`
|
|
332
|
+
- Continue processing (not critical for frontend projects)
|
|
333
|
+
|
|
334
|
+
**Too many endpoints (>50):**
|
|
335
|
+
- Log: "ℹ️ Large API surface detected: [count] endpoints"
|
|
336
|
+
- Provide full list to APO-1, summarize by resource in JUNO report
|
|
337
|
+
|
|
338
|
+
**Malformed route syntax:**
|
|
339
|
+
- Log: "⚠️ Could not parse route in [file]:[line] - skipping"
|
|
340
|
+
- Continue with other endpoints
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|