komplian 0.3.8 → 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.
- package/README.md +24 -0
- package/komplian-onboard.mjs +11 -2
- package/komplian-postman.mjs +599 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -6,6 +6,30 @@
|
|
|
6
6
|
2. Browser login: `gh auth login -h github.com -s repo -s read:org -w`
|
|
7
7
|
3. `npx komplian onboard --yes`
|
|
8
8
|
|
|
9
|
+
### Postman (colección + entornos)
|
|
10
|
+
|
|
11
|
+
1. En [Postman](https://postman.com) usa una cuenta con email **`@komplian.com`** (o añade ese email a tu perfil).
|
|
12
|
+
2. Crea una **API key**: Settings → **API keys** → Generate.
|
|
13
|
+
3. **Una vez por máquina** (guarda la clave en `~/.komplian/postman-api-key`; no hace falta `export` después):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx komplian postman login
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
4. Cuando quieras sincronizar la colección:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx komplian postman --yes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Opcional: `export POSTMAN_API_KEY=…` solo para la sesión actual (tiene prioridad sobre el archivo).
|
|
26
|
+
|
|
27
|
+
El comando llama a `GET https://api.getpostman.com/me` y **solo continúa** si el email de la cuenta es `@komplian.com`. Crea la colección **Komplian API**, los entornos **Komplian — Local Dev** y **Komplian — Production**, y deja copia en `./komplian-postman/*.json`.
|
|
28
|
+
|
|
29
|
+
- Solo exportar archivos (sin subir por API): `npx komplian postman --yes --export-only`
|
|
30
|
+
- Otro workspace: `POSTMAN_WORKSPACE_ID=<id>`
|
|
31
|
+
- Ruta alternativa del archivo de clave: `KOMPLIAN_POSTMAN_KEY_FILE`
|
|
32
|
+
|
|
9
33
|
No OAuth App registration — `gh` uses GitHub’s built-in flow. **Default workspace:** current working directory (`process.cwd()`), not `~/komplian`. Pass a path as last argument to clone elsewhere.
|
|
10
34
|
**Dependencies:** repos with `package-lock.json` use **`npm ci`** (does not modify the lockfile, so no spurious git changes). Repos without a lockfile use **`npm install --no-package-lock`** so onboarding does not create a new `package-lock.json`. Yarn / pnpm repos use frozen lock installs when `yarn` / `pnpm` is on PATH. Unless `KOMPLIAN_NPM_AUDIT=1`, npm runs with `--no-audit --no-fund`.
|
|
11
35
|
|
package/komplian-onboard.mjs
CHANGED
|
@@ -394,8 +394,10 @@ function npmInstallEach(workspace) {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
function usage() {
|
|
397
|
-
log(`Uso: komplian onboard [opciones] [carpeta]`);
|
|
397
|
+
log(`Uso: komplian onboard [opciones] [carpeta] | komplian postman [opciones]`);
|
|
398
398
|
log(` npx komplian onboard --yes`);
|
|
399
|
+
log(` npx komplian postman login ${c.dim}(una vez · guarda API key)${c.reset}`);
|
|
400
|
+
log(` npx komplian postman --yes ${c.dim}(email @komplian.com)${c.reset}`);
|
|
399
401
|
log(``);
|
|
400
402
|
log(` Antes (una vez): gh auth login -h github.com -s repo -s read:org -w`);
|
|
401
403
|
log(` Requisitos: Node 18+, git, GitHub CLI (gh)`);
|
|
@@ -478,8 +480,15 @@ function normalizeArgv(argv) {
|
|
|
478
480
|
}
|
|
479
481
|
|
|
480
482
|
async function main() {
|
|
483
|
+
const rawArgv = process.argv.slice(2);
|
|
484
|
+
if (rawArgv[0] === "postman") {
|
|
485
|
+
const { runPostman } = await import("./komplian-postman.mjs");
|
|
486
|
+
await runPostman(rawArgv.slice(1));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
481
490
|
const configPath = join(__dirname, "komplian-team-repos.json");
|
|
482
|
-
const { argv, fromOnboardSubcommand } = normalizeArgv(
|
|
491
|
+
const { argv, fromOnboardSubcommand } = normalizeArgv(rawArgv);
|
|
483
492
|
const args = parseArgs(argv);
|
|
484
493
|
if (fromOnboardSubcommand && !args.noInstall && !args.listTeams && !args.help) {
|
|
485
494
|
args.install = true;
|
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Komplian Postman — API key + email @komplian.com (Postman /me), luego colección + entornos.
|
|
4
|
+
*
|
|
5
|
+
* Requisitos: Node 18+. Primera vez: npx komplian postman login
|
|
6
|
+
*
|
|
7
|
+
* Clave: POSTMAN_API_KEY o ~/.komplian/postman-api-key (npx komplian postman login)
|
|
8
|
+
* Opcional: POSTMAN_WORKSPACE_ID, KOMPLIAN_EMAIL_DOMAIN, KOMPLIAN_POSTMAN_KEY_FILE
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
writeFileSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
readFileSync,
|
|
15
|
+
existsSync,
|
|
16
|
+
chmodSync,
|
|
17
|
+
} from "node:fs";
|
|
18
|
+
import { join, resolve } from "node:path";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
21
|
+
import { createInterface } from "node:readline/promises";
|
|
22
|
+
|
|
23
|
+
const POSTMAN_API = "https://api.getpostman.com";
|
|
24
|
+
|
|
25
|
+
const c = {
|
|
26
|
+
reset: "\x1b[0m",
|
|
27
|
+
dim: "\x1b[2m",
|
|
28
|
+
bold: "\x1b[1m",
|
|
29
|
+
cyan: "\x1b[36m",
|
|
30
|
+
green: "\x1b[32m",
|
|
31
|
+
red: "\x1b[31m",
|
|
32
|
+
yellow: "\x1b[33m",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function log(s = "") {
|
|
36
|
+
console.log(s);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function defaultKeyPath() {
|
|
40
|
+
const override = process.env.KOMPLIAN_POSTMAN_KEY_FILE?.trim();
|
|
41
|
+
if (override) return resolve(override);
|
|
42
|
+
return join(homedir(), ".komplian", "postman-api-key");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Orden: POSTMAN_API_KEY → ~/.komplian/postman-api-key (o KOMPLIAN_POSTMAN_KEY_FILE).
|
|
47
|
+
*/
|
|
48
|
+
function resolveApiKey() {
|
|
49
|
+
const env = process.env.POSTMAN_API_KEY?.trim();
|
|
50
|
+
if (env) return { key: env, source: "POSTMAN_API_KEY" };
|
|
51
|
+
const path = defaultKeyPath();
|
|
52
|
+
if (existsSync(path)) {
|
|
53
|
+
const k = readFileSync(path, "utf8").trim();
|
|
54
|
+
if (k) return { key: k, source: path };
|
|
55
|
+
}
|
|
56
|
+
return { key: "", source: null };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function printMissingKeyHelp() {
|
|
60
|
+
log(`${c.red}✗${c.reset} No hay API key de Postman.`);
|
|
61
|
+
log(``);
|
|
62
|
+
log(` ${c.bold}Una vez por máquina:${c.reset}`);
|
|
63
|
+
log(` ${c.cyan}npx komplian postman login${c.reset}`);
|
|
64
|
+
log(` (pega la clave de Postman → Settings → API keys → Generate)`);
|
|
65
|
+
log(``);
|
|
66
|
+
log(` ${c.dim}O en la sesión actual: export POSTMAN_API_KEY=…${c.reset}`);
|
|
67
|
+
log(
|
|
68
|
+
`${c.dim} Archivo manual: ${defaultKeyPath()}${c.reset}`
|
|
69
|
+
);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function headers(apiKey) {
|
|
74
|
+
return {
|
|
75
|
+
"X-API-Key": apiKey,
|
|
76
|
+
Accept: "application/json",
|
|
77
|
+
"Content-Type": "application/json",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function pmFetch(apiKey, path, opts = {}) {
|
|
82
|
+
const url = path.startsWith("http") ? path : `${POSTMAN_API}${path}`;
|
|
83
|
+
const r = await fetch(url, {
|
|
84
|
+
...opts,
|
|
85
|
+
headers: { ...headers(apiKey), ...opts.headers },
|
|
86
|
+
});
|
|
87
|
+
const text = await r.text();
|
|
88
|
+
let body;
|
|
89
|
+
try {
|
|
90
|
+
body = text ? JSON.parse(text) : {};
|
|
91
|
+
} catch {
|
|
92
|
+
body = { _raw: text };
|
|
93
|
+
}
|
|
94
|
+
return { ok: r.ok, status: r.status, body };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function extractEmail(meBody) {
|
|
98
|
+
const b = meBody ?? {};
|
|
99
|
+
const u = b.user ?? b.data?.user ?? b;
|
|
100
|
+
const e = u?.email ?? b.email ?? b.data?.email;
|
|
101
|
+
return typeof e === "string" ? e.trim().toLowerCase() : "";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function emailAllowed(email, domain) {
|
|
105
|
+
const d = domain.toLowerCase().replace(/^@/, "");
|
|
106
|
+
return email.endsWith(`@${d}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function req(name, method, path, auth = "apiKey") {
|
|
110
|
+
const h = [];
|
|
111
|
+
if (auth === "apiKey") {
|
|
112
|
+
h.push({ key: "X-API-Key", value: "{{apiKey}}", type: "text" });
|
|
113
|
+
} else if (auth === "admin") {
|
|
114
|
+
h.push({ key: "X-API-Key", value: "{{adminApiKey}}", type: "text" });
|
|
115
|
+
}
|
|
116
|
+
const raw = path.startsWith("http") ? path : `{{baseUrl}}${path}`;
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
request: {
|
|
120
|
+
method,
|
|
121
|
+
header: h,
|
|
122
|
+
url: raw,
|
|
123
|
+
description:
|
|
124
|
+
auth === "none"
|
|
125
|
+
? "Sin cabecera X-API-Key (público)."
|
|
126
|
+
: auth === "admin"
|
|
127
|
+
? "Cabecera X-API-Key con valor de adminApiKey."
|
|
128
|
+
: "Cabecera X-API-Key con valor de apiKey (workspace).",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function folder(name, items) {
|
|
134
|
+
return { name, item: items };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildCollection() {
|
|
138
|
+
return {
|
|
139
|
+
info: {
|
|
140
|
+
name: "Komplian API",
|
|
141
|
+
description:
|
|
142
|
+
"Colección generada por `npx komplian postman`. Variables: baseUrl, apiKey, adminApiKey, workspaceId, widgetId, sessionId, leadId, flowId, sourceId.",
|
|
143
|
+
schema:
|
|
144
|
+
"https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
145
|
+
},
|
|
146
|
+
item: [
|
|
147
|
+
folder("Health", [
|
|
148
|
+
req("Root", "GET", "/", "none"),
|
|
149
|
+
req("Health check", "GET", "/health", "none"),
|
|
150
|
+
]),
|
|
151
|
+
folder("Public", [
|
|
152
|
+
req("Lead capture (web)", "POST", "/api/web/leads", "none"),
|
|
153
|
+
]),
|
|
154
|
+
folder("Widget (Public)", [
|
|
155
|
+
req("Get widget config", "GET", "/api/widget/{{widgetId}}/config", "none"),
|
|
156
|
+
req("Create session", "POST", "/api/widget/{{widgetId}}/session", "none"),
|
|
157
|
+
req("Send message", "POST", "/api/widget/{{widgetId}}/message", "none"),
|
|
158
|
+
req("Get messages", "GET", "/api/widget/{{widgetId}}/messages/{{sessionId}}", "none"),
|
|
159
|
+
req("List sessions", "GET", "/api/widget/{{widgetId}}/sessions", "none"),
|
|
160
|
+
]),
|
|
161
|
+
folder("Workspaces", [
|
|
162
|
+
req("List workspaces", "GET", "/api/workspaces", "apiKey"),
|
|
163
|
+
req("Get workspace", "GET", "/api/workspaces/{{workspaceId}}", "apiKey"),
|
|
164
|
+
req("Update workspace", "PUT", "/api/workspaces/{{workspaceId}}", "apiKey"),
|
|
165
|
+
req("Delete workspace", "DELETE", "/api/workspaces/{{workspaceId}}", "apiKey"),
|
|
166
|
+
req("Restore workspace", "POST", "/api/workspaces/{{workspaceId}}/restore", "apiKey"),
|
|
167
|
+
req("Regenerate API key", "POST", "/api/workspaces/{{workspaceId}}/api-key", "apiKey"),
|
|
168
|
+
req("Update logo", "PUT", "/api/workspaces/{{workspaceId}}/logo", "apiKey"),
|
|
169
|
+
req("Diagnostic", "GET", "/api/workspaces/{{workspaceId}}/diagnostic", "apiKey"),
|
|
170
|
+
]),
|
|
171
|
+
folder("AI & Chat", [
|
|
172
|
+
req("AI chat", "POST", "/api/ai/chat", "apiKey"),
|
|
173
|
+
req("AI chat (stream)", "POST", "/api/ai/chat/stream", "apiKey"),
|
|
174
|
+
req("Test knowledge", "POST", "/api/ai/test-knowledge", "apiKey"),
|
|
175
|
+
req("Generate flow (AI)", "POST", "/api/ai/flows/generate", "apiKey"),
|
|
176
|
+
]),
|
|
177
|
+
folder("Knowledge Base", [
|
|
178
|
+
req("Create source", "POST", "/api/knowledge/sources", "apiKey"),
|
|
179
|
+
req("List sources", "GET", "/api/knowledge/sources", "apiKey"),
|
|
180
|
+
req("Get source", "GET", "/api/knowledge/sources/{{sourceId}}", "apiKey"),
|
|
181
|
+
req("Update source", "PUT", "/api/knowledge/sources/{{sourceId}}", "apiKey"),
|
|
182
|
+
req("Delete source", "DELETE", "/api/knowledge/sources/{{sourceId}}", "apiKey"),
|
|
183
|
+
req("Scrape source", "POST", "/api/knowledge/sources/{{sourceId}}/scrape", "apiKey"),
|
|
184
|
+
req("Source jobs", "GET", "/api/knowledge/sources/{{sourceId}}/jobs", "apiKey"),
|
|
185
|
+
req("Search knowledge", "POST", "/api/knowledge/search", "apiKey"),
|
|
186
|
+
req("Knowledge stats", "GET", "/api/knowledge/stats", "apiKey"),
|
|
187
|
+
req("Upload document", "POST", "/api/knowledge/documents", "apiKey"),
|
|
188
|
+
req("Process document", "POST", "/api/knowledge/documents/{{documentId}}/process", "apiKey"),
|
|
189
|
+
req("Process document URL", "POST", "/api/knowledge/documents/{{documentId}}/process-url", "apiKey"),
|
|
190
|
+
req("Detect doc type", "GET", "/api/knowledge/documents/detect-type", "apiKey"),
|
|
191
|
+
req("Get manual entries", "GET", "/api/knowledge/manual/{{workspaceId}}", "apiKey"),
|
|
192
|
+
req("Create manual entry", "POST", "/api/knowledge/manual", "apiKey"),
|
|
193
|
+
req("Update manual entry", "PUT", "/api/knowledge/manual/{{entryId}}", "apiKey"),
|
|
194
|
+
req("Delete manual entry", "DELETE", "/api/knowledge/manual/{{entryId}}", "apiKey"),
|
|
195
|
+
]),
|
|
196
|
+
folder("Widget Config", [
|
|
197
|
+
req("Get config", "GET", "/api/widget/config/{{workspaceId}}", "apiKey"),
|
|
198
|
+
req("Update config", "PUT", "/api/widget/config/{{workspaceId}}", "apiKey"),
|
|
199
|
+
req("Publish", "POST", "/api/widget/config/{{workspaceId}}/publish", "apiKey"),
|
|
200
|
+
req("Unpublish", "POST", "/api/widget/config/{{workspaceId}}/unpublish", "apiKey"),
|
|
201
|
+
]),
|
|
202
|
+
folder("Inbox", [
|
|
203
|
+
req("List sessions", "GET", "/api/inbox/{{workspaceId}}/sessions", "apiKey"),
|
|
204
|
+
req("Get session", "GET", "/api/inbox/session/{{sessionId}}", "apiKey"),
|
|
205
|
+
req("Takeover", "POST", "/api/inbox/session/{{sessionId}}/takeover", "apiKey"),
|
|
206
|
+
req("Agent message", "POST", "/api/inbox/session/{{sessionId}}/message", "apiKey"),
|
|
207
|
+
req("Typing", "POST", "/api/inbox/session/{{sessionId}}/typing", "apiKey"),
|
|
208
|
+
req("Mark read", "POST", "/api/inbox/session/{{sessionId}}/read", "apiKey"),
|
|
209
|
+
req("Archive", "POST", "/api/inbox/session/{{sessionId}}/archive", "apiKey"),
|
|
210
|
+
req("Translate", "POST", "/api/inbox/session/{{sessionId}}/translate", "apiKey"),
|
|
211
|
+
req("Products for inbox", "GET", "/api/inbox/{{workspaceId}}/products", "apiKey"),
|
|
212
|
+
]),
|
|
213
|
+
folder("Analytics", [
|
|
214
|
+
req("Knowledge analytics", "GET", "/api/analytics/knowledge/{{workspaceId}}", "apiKey"),
|
|
215
|
+
req("Query log", "GET", "/api/analytics/knowledge/{{workspaceId}}/queries", "apiKey"),
|
|
216
|
+
req("Knowledge gaps", "GET", "/api/analytics/knowledge/{{workspaceId}}/gaps", "apiKey"),
|
|
217
|
+
req("Query chunks", "GET", "/api/analytics/knowledge/query/{{queryId}}/chunks", "apiKey"),
|
|
218
|
+
req("AI analytics", "GET", "/api/analytics/ai/{{workspaceId}}", "apiKey"),
|
|
219
|
+
]),
|
|
220
|
+
folder("Leads", [
|
|
221
|
+
req("List leads", "GET", "/api/leads", "apiKey"),
|
|
222
|
+
req("Create lead", "POST", "/api/leads", "apiKey"),
|
|
223
|
+
req("Get lead", "GET", "/api/leads/{{leadId}}", "apiKey"),
|
|
224
|
+
req("Update lead", "PATCH", "/api/leads/{{leadId}}", "apiKey"),
|
|
225
|
+
req("Delete lead", "DELETE", "/api/leads/{{leadId}}", "apiKey"),
|
|
226
|
+
req("Timeline", "GET", "/api/leads/{{leadId}}/timeline", "apiKey"),
|
|
227
|
+
req("Get notes", "GET", "/api/leads/{{leadId}}/notes", "apiKey"),
|
|
228
|
+
req("Add note", "POST", "/api/leads/{{leadId}}/notes", "apiKey"),
|
|
229
|
+
req("Lead stats", "GET", "/api/leads/stats/{{workspaceId}}", "apiKey"),
|
|
230
|
+
]),
|
|
231
|
+
folder("Flows", [
|
|
232
|
+
req("List flows", "GET", "/api/flows", "apiKey"),
|
|
233
|
+
req("Create flow", "POST", "/api/flows", "apiKey"),
|
|
234
|
+
req("Flow templates", "GET", "/api/flows/templates", "apiKey"),
|
|
235
|
+
req("Get flow", "GET", "/api/flows/{{flowId}}", "apiKey"),
|
|
236
|
+
req("Update flow", "PATCH", "/api/flows/{{flowId}}", "apiKey"),
|
|
237
|
+
req("Delete flow", "DELETE", "/api/flows/{{flowId}}", "apiKey"),
|
|
238
|
+
req("Publish", "POST", "/api/flows/{{flowId}}/publish", "apiKey"),
|
|
239
|
+
req("Pause", "POST", "/api/flows/{{flowId}}/pause", "apiKey"),
|
|
240
|
+
req("Duplicate", "POST", "/api/flows/{{flowId}}/duplicate", "apiKey"),
|
|
241
|
+
req("Flow analytics", "GET", "/api/flows/{{flowId}}/analytics", "apiKey"),
|
|
242
|
+
req("Executions", "GET", "/api/flows/{{flowId}}/executions", "apiKey"),
|
|
243
|
+
req("Test flow", "POST", "/api/flows/{{flowId}}/test", "apiKey"),
|
|
244
|
+
req("Trigger event", "POST", "/api/flows/events", "apiKey"),
|
|
245
|
+
req("Cron (flows)", "POST", "/api/flows/cron", "apiKey"),
|
|
246
|
+
]),
|
|
247
|
+
folder("Email", [req("Send email", "POST", "/api/email/send", "apiKey")]),
|
|
248
|
+
folder("Users & Plans", [
|
|
249
|
+
req("List users", "GET", "/api/users", "apiKey"),
|
|
250
|
+
req("Get user", "GET", "/api/users/{{userId}}", "apiKey"),
|
|
251
|
+
req("Plans", "GET", "/api/plans", "apiKey"),
|
|
252
|
+
]),
|
|
253
|
+
folder("Webhooks", [
|
|
254
|
+
req("WhatsApp verify", "GET", "/webhooks/whatsapp", "none"),
|
|
255
|
+
req("WhatsApp", "POST", "/webhooks/whatsapp", "none"),
|
|
256
|
+
req("Instagram verify", "GET", "/webhooks/instagram", "none"),
|
|
257
|
+
req("Instagram", "POST", "/webhooks/instagram", "none"),
|
|
258
|
+
req("Shopify GDPR data_request", "POST", "/webhooks/shopify/customers/data_request", "none"),
|
|
259
|
+
req("Shopify GDPR customers/redact", "POST", "/webhooks/shopify/customers/redact", "none"),
|
|
260
|
+
req("Shopify GDPR shop/redact", "POST", "/webhooks/shopify/shop/redact", "none"),
|
|
261
|
+
]),
|
|
262
|
+
folder("N8N", [
|
|
263
|
+
req("n8n healthz", "GET", "https://n8n.komplian.com/healthz", "none"),
|
|
264
|
+
{
|
|
265
|
+
name: "Lead pipeline webhook (info)",
|
|
266
|
+
request: {
|
|
267
|
+
method: "GET",
|
|
268
|
+
header: [],
|
|
269
|
+
url: "https://n8n.komplian.com/webhook/komplian-lead-pipeline",
|
|
270
|
+
description:
|
|
271
|
+
"POST real desde automatización; GET solo como referencia de URL en Postman.",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
]),
|
|
275
|
+
],
|
|
276
|
+
variable: [
|
|
277
|
+
{ key: "baseUrl", value: "https://api.komplian.com", type: "string" },
|
|
278
|
+
],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function buildEnvironments() {
|
|
283
|
+
const secret = (key) => ({
|
|
284
|
+
key,
|
|
285
|
+
value: "",
|
|
286
|
+
type: "secret",
|
|
287
|
+
enabled: true,
|
|
288
|
+
});
|
|
289
|
+
const plain = (key, value) => ({
|
|
290
|
+
key,
|
|
291
|
+
value,
|
|
292
|
+
type: "default",
|
|
293
|
+
enabled: true,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const common = [
|
|
297
|
+
plain("baseUrl", ""),
|
|
298
|
+
secret("apiKey"),
|
|
299
|
+
secret("adminApiKey"),
|
|
300
|
+
plain("workspaceId", ""),
|
|
301
|
+
plain("widgetId", ""),
|
|
302
|
+
plain("sessionId", ""),
|
|
303
|
+
plain("leadId", ""),
|
|
304
|
+
plain("flowId", ""),
|
|
305
|
+
plain("sourceId", ""),
|
|
306
|
+
plain("documentId", ""),
|
|
307
|
+
plain("queryId", ""),
|
|
308
|
+
plain("userId", ""),
|
|
309
|
+
plain("entryId", ""),
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
return [
|
|
313
|
+
{
|
|
314
|
+
name: "Komplian — Local Dev",
|
|
315
|
+
values: common.map((v) =>
|
|
316
|
+
v.key === "baseUrl"
|
|
317
|
+
? { ...v, value: "http://localhost:4000", type: "default" }
|
|
318
|
+
: { ...v }
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: "Komplian — Production",
|
|
323
|
+
values: common.map((v) =>
|
|
324
|
+
v.key === "baseUrl"
|
|
325
|
+
? { ...v, value: "https://api.komplian.com", type: "default" }
|
|
326
|
+
: { ...v }
|
|
327
|
+
),
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function pickWorkspaceId(apiKey, explicit) {
|
|
333
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
334
|
+
const { ok, body } = await pmFetch(apiKey, "/workspaces");
|
|
335
|
+
if (!ok || !body.workspaces?.length) {
|
|
336
|
+
log(`${c.red}✗${c.reset} No se pudieron listar workspaces. ¿API key válida?`);
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
const w =
|
|
340
|
+
body.workspaces.find((x) => x.type === "personal") || body.workspaces[0];
|
|
341
|
+
return w.id;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function verifyKomplianEmail(apiKey, domain) {
|
|
345
|
+
const { ok, status, body } = await pmFetch(apiKey, "/me");
|
|
346
|
+
if (!ok) {
|
|
347
|
+
log(
|
|
348
|
+
`${c.red}✗${c.reset} API key de Postman inválida o sin acceso (${status}).`
|
|
349
|
+
);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
const email = extractEmail(body);
|
|
353
|
+
if (!email) {
|
|
354
|
+
log(
|
|
355
|
+
`${c.red}✗${c.reset} La API de Postman no devolvió email en /me. Usa una cuenta Postman con email visible.`
|
|
356
|
+
);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
if (!emailAllowed(email, domain)) {
|
|
360
|
+
log(
|
|
361
|
+
`${c.red}✗${c.reset} Este comando solo permite cuentas **@${domain}**. Sesión Postman: ${c.bold}${email}${c.reset}`
|
|
362
|
+
);
|
|
363
|
+
log(
|
|
364
|
+
`${c.dim} Crea la API key desde una cuenta @${domain} o añade ese email a tu usuario en Postman.${c.reset}`
|
|
365
|
+
);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
log(
|
|
369
|
+
`${c.green}✓${c.reset} Postman: ${c.bold}${email}${c.reset} (${c.dim}dominio permitido${c.reset})`
|
|
370
|
+
);
|
|
371
|
+
return email;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function createCollection(apiKey, workspaceId, collection) {
|
|
375
|
+
const q = new URLSearchParams({ workspace: workspaceId });
|
|
376
|
+
return pmFetch(apiKey, `/collections?${q}`, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
body: JSON.stringify({ collection }),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function createEnvironment(apiKey, workspaceId, env) {
|
|
383
|
+
const q = new URLSearchParams({ workspace: workspaceId });
|
|
384
|
+
return pmFetch(apiKey, `/environments?${q}`, {
|
|
385
|
+
method: "POST",
|
|
386
|
+
body: JSON.stringify({ environment: env }),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function parseArgs(argv) {
|
|
391
|
+
const out = { yes: false, exportOnly: false, outDir: "", help: false };
|
|
392
|
+
for (let i = 0; i < argv.length; i++) {
|
|
393
|
+
const a = argv[i];
|
|
394
|
+
if (a === "--yes" || a === "-y") out.yes = true;
|
|
395
|
+
else if (a === "--export-only") out.exportOnly = true;
|
|
396
|
+
else if (a === "--out") out.outDir = argv[++i] || "";
|
|
397
|
+
else if (a === "-h" || a === "--help") out.help = true;
|
|
398
|
+
else if (a.startsWith("-")) {
|
|
399
|
+
log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return out;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function parseLoginArgs(argv) {
|
|
407
|
+
const out = { help: false };
|
|
408
|
+
for (const a of argv) {
|
|
409
|
+
if (a === "-h" || a === "--help") out.help = true;
|
|
410
|
+
else if (a.startsWith("-")) {
|
|
411
|
+
log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function usage() {
|
|
419
|
+
log(`Uso: komplian postman [opciones] | komplian postman login`);
|
|
420
|
+
log(` Clave: ${c.dim}POSTMAN_API_KEY${c.reset} o archivo ${c.dim}~/.komplian/postman-api-key${c.reset} (véase ${c.cyan}postman login${c.reset})`);
|
|
421
|
+
log(` Dominio email: solo @komplian.com (GET /me)`);
|
|
422
|
+
log(``);
|
|
423
|
+
log(` -y, --yes Sin prompts extra`);
|
|
424
|
+
log(` --export-only Solo escribe JSON en disco (no llama a la API de Postman)`);
|
|
425
|
+
log(` --out <dir> Carpeta para export (por defecto: ./komplian-postman)`);
|
|
426
|
+
log(` -h, --help`);
|
|
427
|
+
log(``);
|
|
428
|
+
log(` Opcional: POSTMAN_WORKSPACE_ID, KOMPLIAN_EMAIL_DOMAIN, KOMPLIAN_POSTMAN_KEY_FILE`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function usageLogin() {
|
|
432
|
+
log(`Uso: komplian postman login`);
|
|
433
|
+
log(` Guarda la API key de Postman en ${c.dim}~/.komplian/postman-api-key${c.reset} (permiso 600).`);
|
|
434
|
+
log(` Tras esto: ${c.cyan}npx komplian postman --yes${c.reset} sin exportar variables.`);
|
|
435
|
+
log(``);
|
|
436
|
+
log(` Postman → Settings (avatar) → API keys → Generate`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async function promptApiKey() {
|
|
440
|
+
if (input.isTTY) {
|
|
441
|
+
const rl = createInterface({ input, output });
|
|
442
|
+
const line = await rl.question(
|
|
443
|
+
`${c.cyan}Pega tu Postman API key${c.reset} (Settings → API keys): `
|
|
444
|
+
);
|
|
445
|
+
rl.close();
|
|
446
|
+
return line.trim();
|
|
447
|
+
}
|
|
448
|
+
return new Promise((resolve, reject) => {
|
|
449
|
+
const chunks = [];
|
|
450
|
+
input.on("data", (d) => chunks.push(d));
|
|
451
|
+
input.on("end", () => {
|
|
452
|
+
resolve(Buffer.concat(chunks).toString("utf8").trim());
|
|
453
|
+
});
|
|
454
|
+
input.on("error", reject);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function runPostmanLogin(argv) {
|
|
459
|
+
const la = parseLoginArgs(argv);
|
|
460
|
+
if (la.help) {
|
|
461
|
+
usageLogin();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
log(`${c.cyan}━━ Postman login (Komplian) ━━${c.reset}`);
|
|
466
|
+
log(
|
|
467
|
+
`${c.dim}Cuenta con email @komplian.com. Genera la clave en Postman → Settings → API keys.${c.reset}`
|
|
468
|
+
);
|
|
469
|
+
log("");
|
|
470
|
+
|
|
471
|
+
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
472
|
+
const keyPath = defaultKeyPath();
|
|
473
|
+
|
|
474
|
+
let apiKey = await promptApiKey();
|
|
475
|
+
if (!apiKey) {
|
|
476
|
+
log(`${c.red}✗${c.reset} Clave vacía.`);
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
await verifyKomplianEmail(apiKey, domain);
|
|
481
|
+
|
|
482
|
+
mkdirSync(join(homedir(), ".komplian"), { recursive: true });
|
|
483
|
+
writeFileSync(keyPath, `${apiKey}\n`, { encoding: "utf8", mode: 0o600 });
|
|
484
|
+
try {
|
|
485
|
+
chmodSync(keyPath, 0o600);
|
|
486
|
+
} catch {
|
|
487
|
+
/* Windows puede ignorar chmod */
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
log("");
|
|
491
|
+
log(
|
|
492
|
+
`${c.green}✓${c.reset} Guardada en ${c.bold}${keyPath}${c.reset} (solo tu usuario).`
|
|
493
|
+
);
|
|
494
|
+
log(
|
|
495
|
+
`${c.dim} Siguiente: ${c.reset}${c.cyan}npx komplian postman --yes${c.reset}`
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function normalizePostmanArgv(argv) {
|
|
500
|
+
const a = [...argv];
|
|
501
|
+
if (a[0] === "login") {
|
|
502
|
+
return { mode: "login", rest: a.slice(1) };
|
|
503
|
+
}
|
|
504
|
+
return { mode: "sync", rest: a };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export async function runPostman(argv) {
|
|
508
|
+
const { mode, rest } = normalizePostmanArgv(argv);
|
|
509
|
+
if (mode === "login") {
|
|
510
|
+
await runPostmanLogin(rest);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const args = parseArgs(rest);
|
|
515
|
+
if (args.help) {
|
|
516
|
+
usage();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const { key: apiKey, source } = resolveApiKey();
|
|
521
|
+
if (!apiKey) {
|
|
522
|
+
printMissingKeyHelp();
|
|
523
|
+
}
|
|
524
|
+
if (source && source !== "POSTMAN_API_KEY") {
|
|
525
|
+
log(`${c.dim}→ API key: ${source}${c.reset}`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
529
|
+
await verifyKomplianEmail(apiKey, domain);
|
|
530
|
+
|
|
531
|
+
const collection = buildCollection();
|
|
532
|
+
const envs = buildEnvironments();
|
|
533
|
+
const workspaceId = await pickWorkspaceId(
|
|
534
|
+
apiKey,
|
|
535
|
+
process.env.POSTMAN_WORKSPACE_ID
|
|
536
|
+
);
|
|
537
|
+
log(
|
|
538
|
+
`${c.green}✓${c.reset} Workspace Postman: ${c.bold}${workspaceId}${c.reset}`
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
const outBase = args.outDir
|
|
542
|
+
? resolve(args.outDir)
|
|
543
|
+
: resolve(process.cwd(), "komplian-postman");
|
|
544
|
+
mkdirSync(outBase, { recursive: true });
|
|
545
|
+
|
|
546
|
+
const collPath = join(outBase, "Komplian-API.postman_collection.json");
|
|
547
|
+
writeFileSync(collPath, JSON.stringify(collection, null, 2), "utf8");
|
|
548
|
+
log(`${c.green}✓${c.reset} Export: ${c.dim}${collPath}${c.reset}`);
|
|
549
|
+
|
|
550
|
+
for (const env of envs) {
|
|
551
|
+
const safe = env.name.replace(/[\\/]/g, "-") + ".postman_environment.json";
|
|
552
|
+
writeFileSync(join(outBase, safe), JSON.stringify({ ...env }, null, 2), "utf8");
|
|
553
|
+
log(`${c.green}✓${c.reset} Export: ${c.dim}${join(outBase, safe)}${c.reset}`);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (args.exportOnly) {
|
|
557
|
+
log("");
|
|
558
|
+
log(
|
|
559
|
+
`${c.cyan}━━ Listo (solo archivos) ━━${c.reset} Importa en Postman: ${c.bold}Import${c.reset} → arrastra la carpeta o los JSON.`
|
|
560
|
+
);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
log("");
|
|
565
|
+
log(`${c.cyan}━━ Subiendo a Postman (API) ━━${c.reset}`);
|
|
566
|
+
|
|
567
|
+
const cr = await createCollection(apiKey, workspaceId, collection);
|
|
568
|
+
if (!cr.ok) {
|
|
569
|
+
log(
|
|
570
|
+
`${c.yellow}○${c.reset} Colección API: ${cr.status} — ${JSON.stringify(cr.body).slice(0, 400)}`
|
|
571
|
+
);
|
|
572
|
+
log(
|
|
573
|
+
`${c.dim} Si el nombre ya existe, borra la colección antigua en Postman o importa el JSON exportado.${c.reset}`
|
|
574
|
+
);
|
|
575
|
+
} else {
|
|
576
|
+
const uid = cr.body?.collection?.uid || cr.body?.collection?.id || "?";
|
|
577
|
+
log(`${c.green}✓${c.reset} Colección creada en Postman (${uid})`);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
for (const env of envs) {
|
|
581
|
+
const er = await createEnvironment(apiKey, workspaceId, {
|
|
582
|
+
name: env.name,
|
|
583
|
+
values: env.values,
|
|
584
|
+
});
|
|
585
|
+
if (!er.ok) {
|
|
586
|
+
log(
|
|
587
|
+
`${c.yellow}○${c.reset} Entorno "${env.name}": ${er.status} — ${JSON.stringify(er.body).slice(0, 200)}`
|
|
588
|
+
);
|
|
589
|
+
} else {
|
|
590
|
+
log(`${c.green}✓${c.reset} Entorno: ${c.bold}${env.name}${c.reset}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
log("");
|
|
595
|
+
log(`${c.green}✓${c.reset} Abre Postman → workspace → revisa ${c.bold}Komplian API${c.reset} y los entornos.`);
|
|
596
|
+
log(
|
|
597
|
+
`${c.dim} Rellena apiKey / adminApiKey / IDs en el entorno (secret).${c.reset}`
|
|
598
|
+
);
|
|
599
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "komplian",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Komplian developer workspace
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Komplian developer workspace: GitHub clone (onboard) + Postman collection/environments (postman). Node 18+.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=18"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"komplian-onboard.mjs",
|
|
14
|
+
"komplian-postman.mjs",
|
|
14
15
|
"komplian-team-repos.json",
|
|
15
16
|
"README.md"
|
|
16
17
|
],
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
"access": "public"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
|
-
"prepublishOnly": "node --check komplian-onboard.mjs"
|
|
22
|
+
"prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [
|
|
24
25
|
"komplian",
|