@nkmc/cli 0.3.3 → 0.3.4
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/dist-NYTRZS3R.js +625 -0
- package/dist/index.js +5 -3
- package/package.json +4 -1
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../node_modules/.pnpm/@shipkey+core@0.1.0/node_modules/@shipkey/core/dist/index.js
|
|
4
|
+
import { execFile } from "child_process";
|
|
5
|
+
function exec(cmd, args, env) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
execFile(
|
|
8
|
+
cmd,
|
|
9
|
+
args,
|
|
10
|
+
{ maxBuffer: 10 * 1024 * 1024, env: env ?? process.env },
|
|
11
|
+
(err, stdout, stderr) => {
|
|
12
|
+
if (err)
|
|
13
|
+
reject(new Error(`${cmd} failed: ${stderr?.trim() || err.message}`));
|
|
14
|
+
else resolve(stdout.trim());
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async function exec2(args) {
|
|
20
|
+
return exec("op", args, {
|
|
21
|
+
...process.env,
|
|
22
|
+
OP_BIOMETRIC_UNLOCK_ENABLED: "true"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
var OnePasswordBackend = class {
|
|
26
|
+
name = "1Password";
|
|
27
|
+
sectionName(project, env) {
|
|
28
|
+
return `${project}-${env}`;
|
|
29
|
+
}
|
|
30
|
+
buildRef(ref) {
|
|
31
|
+
const section = this.sectionName(ref.project, ref.env);
|
|
32
|
+
return `op://${ref.vault}/${ref.provider}/${section}/${ref.field}`;
|
|
33
|
+
}
|
|
34
|
+
buildInlineRef(ref) {
|
|
35
|
+
return this.buildRef(ref);
|
|
36
|
+
}
|
|
37
|
+
buildWriteArgs(entry) {
|
|
38
|
+
const { ref, value } = entry;
|
|
39
|
+
const section = this.sectionName(ref.project, ref.env);
|
|
40
|
+
const fieldKey = `${section}.${ref.field}`;
|
|
41
|
+
return [
|
|
42
|
+
"item",
|
|
43
|
+
"edit",
|
|
44
|
+
ref.provider,
|
|
45
|
+
"--vault",
|
|
46
|
+
ref.vault,
|
|
47
|
+
`${fieldKey}[password]=${value}`
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
async isAvailable() {
|
|
51
|
+
const status = await this.checkStatus();
|
|
52
|
+
return status === "ready";
|
|
53
|
+
}
|
|
54
|
+
async checkStatus() {
|
|
55
|
+
try {
|
|
56
|
+
await exec2(["--version"]);
|
|
57
|
+
} catch {
|
|
58
|
+
return "not_installed";
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const output = await exec2(["account", "list", "--format=json"]);
|
|
62
|
+
const accounts = JSON.parse(output);
|
|
63
|
+
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
64
|
+
return "not_logged_in";
|
|
65
|
+
}
|
|
66
|
+
return "ready";
|
|
67
|
+
} catch {
|
|
68
|
+
return "not_logged_in";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async read(ref) {
|
|
72
|
+
return exec2(["read", this.buildRef(ref)]);
|
|
73
|
+
}
|
|
74
|
+
async readRaw(opUri) {
|
|
75
|
+
return exec2(["read", opUri]);
|
|
76
|
+
}
|
|
77
|
+
async listVaultItems(vault) {
|
|
78
|
+
try {
|
|
79
|
+
const raw = await exec2([
|
|
80
|
+
"item",
|
|
81
|
+
"list",
|
|
82
|
+
"--vault",
|
|
83
|
+
vault,
|
|
84
|
+
"--format",
|
|
85
|
+
"json"
|
|
86
|
+
]);
|
|
87
|
+
return JSON.parse(raw);
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async getItemFields(provider, vault) {
|
|
93
|
+
try {
|
|
94
|
+
const raw = await exec2([
|
|
95
|
+
"item",
|
|
96
|
+
"get",
|
|
97
|
+
provider,
|
|
98
|
+
"--vault",
|
|
99
|
+
vault,
|
|
100
|
+
"--format",
|
|
101
|
+
"json"
|
|
102
|
+
]);
|
|
103
|
+
const item = JSON.parse(raw);
|
|
104
|
+
if (!item.fields) return [];
|
|
105
|
+
return item.fields.filter((f) => f.section?.label && f.label).map((f) => ({
|
|
106
|
+
section: f.section.label,
|
|
107
|
+
label: f.label
|
|
108
|
+
}));
|
|
109
|
+
} catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async ensureVault(vault) {
|
|
114
|
+
try {
|
|
115
|
+
await exec2(["vault", "get", vault]);
|
|
116
|
+
} catch {
|
|
117
|
+
await exec2(["vault", "create", vault, "--icon", "vault-door"]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async write(entry) {
|
|
121
|
+
const { ref, value } = entry;
|
|
122
|
+
const section = this.sectionName(ref.project, ref.env);
|
|
123
|
+
const fieldKey = `${section}.${ref.field}`;
|
|
124
|
+
await this.ensureVault(ref.vault);
|
|
125
|
+
try {
|
|
126
|
+
await exec2([
|
|
127
|
+
"item",
|
|
128
|
+
"edit",
|
|
129
|
+
ref.provider,
|
|
130
|
+
"--vault",
|
|
131
|
+
ref.vault,
|
|
132
|
+
`${fieldKey}[password]=${value}`
|
|
133
|
+
]);
|
|
134
|
+
} catch {
|
|
135
|
+
await exec2([
|
|
136
|
+
"item",
|
|
137
|
+
"create",
|
|
138
|
+
"--vault",
|
|
139
|
+
ref.vault,
|
|
140
|
+
"--category",
|
|
141
|
+
"API Credential",
|
|
142
|
+
"--title",
|
|
143
|
+
ref.provider,
|
|
144
|
+
`${fieldKey}[password]=${value}`
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async list(project, env, vault = "shipkey") {
|
|
149
|
+
const raw = await exec2([
|
|
150
|
+
"item",
|
|
151
|
+
"list",
|
|
152
|
+
"--vault",
|
|
153
|
+
vault,
|
|
154
|
+
"--format",
|
|
155
|
+
"json"
|
|
156
|
+
]);
|
|
157
|
+
const items = JSON.parse(raw);
|
|
158
|
+
const refs = [];
|
|
159
|
+
for (const item of items) {
|
|
160
|
+
const detail = await exec2([
|
|
161
|
+
"item",
|
|
162
|
+
"get",
|
|
163
|
+
item.id,
|
|
164
|
+
"--format",
|
|
165
|
+
"json"
|
|
166
|
+
]);
|
|
167
|
+
const parsed = JSON.parse(detail);
|
|
168
|
+
if (!parsed.fields) continue;
|
|
169
|
+
for (const field of parsed.fields) {
|
|
170
|
+
if (!field.section?.label) continue;
|
|
171
|
+
const sectionLabel = field.section.label;
|
|
172
|
+
const dashIndex = sectionLabel.lastIndexOf("-");
|
|
173
|
+
if (dashIndex === -1) continue;
|
|
174
|
+
const proj = sectionLabel.slice(0, dashIndex);
|
|
175
|
+
const e = sectionLabel.slice(dashIndex + 1);
|
|
176
|
+
if (project && proj !== project) continue;
|
|
177
|
+
if (env && e !== env) continue;
|
|
178
|
+
refs.push({
|
|
179
|
+
vault,
|
|
180
|
+
provider: item.title,
|
|
181
|
+
project: proj,
|
|
182
|
+
env: e,
|
|
183
|
+
field: field.label
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return refs;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
async function exec3(args) {
|
|
191
|
+
return exec("bw", args);
|
|
192
|
+
}
|
|
193
|
+
var FIELD_TYPE_HIDDEN = 1;
|
|
194
|
+
var BitwardenBackend = class _BitwardenBackend {
|
|
195
|
+
name = "Bitwarden";
|
|
196
|
+
sectionName(project, env) {
|
|
197
|
+
return `${project}-${env}`;
|
|
198
|
+
}
|
|
199
|
+
/** Encode a field name for storage as a custom field: "project-env.FIELD_NAME" */
|
|
200
|
+
buildFieldName(ref) {
|
|
201
|
+
const section = this.sectionName(ref.project, ref.env);
|
|
202
|
+
return `${section}.${ref.field}`;
|
|
203
|
+
}
|
|
204
|
+
/** Parse a custom field name back into project, env, field */
|
|
205
|
+
static parseFieldName(fieldName) {
|
|
206
|
+
const dotIndex = fieldName.indexOf(".");
|
|
207
|
+
if (dotIndex === -1) return null;
|
|
208
|
+
const section = fieldName.slice(0, dotIndex);
|
|
209
|
+
const field = fieldName.slice(dotIndex + 1);
|
|
210
|
+
const dashIndex = section.lastIndexOf("-");
|
|
211
|
+
if (dashIndex === -1) return null;
|
|
212
|
+
return {
|
|
213
|
+
project: section.slice(0, dashIndex),
|
|
214
|
+
env: section.slice(dashIndex + 1),
|
|
215
|
+
field
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
buildInlineRef() {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
async isAvailable() {
|
|
222
|
+
const status = await this.checkStatus();
|
|
223
|
+
return status === "ready";
|
|
224
|
+
}
|
|
225
|
+
async checkStatus() {
|
|
226
|
+
try {
|
|
227
|
+
await exec3(["--version"]);
|
|
228
|
+
} catch {
|
|
229
|
+
return "not_installed";
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const output = await exec3(["status"]);
|
|
233
|
+
const status = JSON.parse(output);
|
|
234
|
+
if (status.status === "unauthenticated") {
|
|
235
|
+
return "not_logged_in";
|
|
236
|
+
}
|
|
237
|
+
if (status.status === "locked") {
|
|
238
|
+
return "not_logged_in";
|
|
239
|
+
}
|
|
240
|
+
return "ready";
|
|
241
|
+
} catch {
|
|
242
|
+
return "not_logged_in";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async ensureUnlocked() {
|
|
246
|
+
const output = await exec3(["status"]);
|
|
247
|
+
const status = JSON.parse(output);
|
|
248
|
+
if (status.status === "locked") {
|
|
249
|
+
throw new Error("Bitwarden vault is locked. Run: bw unlock");
|
|
250
|
+
}
|
|
251
|
+
if (status.status === "unauthenticated") {
|
|
252
|
+
throw new Error("Bitwarden CLI not logged in. Run: bw login");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async findOrCreateFolder(name) {
|
|
256
|
+
const foldersRaw = await exec3(["list", "folders"]);
|
|
257
|
+
const folders = JSON.parse(foldersRaw);
|
|
258
|
+
const existing = folders.find((f) => f.name === name);
|
|
259
|
+
if (existing) return existing.id;
|
|
260
|
+
const templateRaw = await exec3(["get", "template", "folder"]);
|
|
261
|
+
const template = JSON.parse(templateRaw);
|
|
262
|
+
template.name = name;
|
|
263
|
+
const encodedFolder = Buffer.from(JSON.stringify(template)).toString(
|
|
264
|
+
"base64"
|
|
265
|
+
);
|
|
266
|
+
const created = await exec3(["create", "folder", encodedFolder]);
|
|
267
|
+
const folder = JSON.parse(created);
|
|
268
|
+
return folder.id;
|
|
269
|
+
}
|
|
270
|
+
async findItem(provider, folderId) {
|
|
271
|
+
try {
|
|
272
|
+
const raw = await exec3([
|
|
273
|
+
"list",
|
|
274
|
+
"items",
|
|
275
|
+
"--folderid",
|
|
276
|
+
folderId,
|
|
277
|
+
"--search",
|
|
278
|
+
provider
|
|
279
|
+
]);
|
|
280
|
+
const items = JSON.parse(raw);
|
|
281
|
+
return items.find((i) => i.name === provider) || null;
|
|
282
|
+
} catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async read(ref) {
|
|
287
|
+
await this.ensureUnlocked();
|
|
288
|
+
const folderId = await this.findOrCreateFolder(ref.vault);
|
|
289
|
+
const item = await this.findItem(ref.provider, folderId);
|
|
290
|
+
if (!item) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Item "${ref.provider}" not found in folder "${ref.vault}"`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const fieldName = this.buildFieldName(ref);
|
|
296
|
+
const field = item.fields?.find((f) => f.name === fieldName);
|
|
297
|
+
if (!field) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Field "${fieldName}" not found in item "${ref.provider}"`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return field.value;
|
|
303
|
+
}
|
|
304
|
+
async write(entry) {
|
|
305
|
+
await this.ensureUnlocked();
|
|
306
|
+
const { ref, value } = entry;
|
|
307
|
+
const folderId = await this.findOrCreateFolder(ref.vault);
|
|
308
|
+
const fieldName = this.buildFieldName(ref);
|
|
309
|
+
const existingItem = await this.findItem(ref.provider, folderId);
|
|
310
|
+
if (existingItem) {
|
|
311
|
+
const fields = existingItem.fields || [];
|
|
312
|
+
const existingFieldIndex = fields.findIndex(
|
|
313
|
+
(f) => f.name === fieldName
|
|
314
|
+
);
|
|
315
|
+
if (existingFieldIndex !== -1) {
|
|
316
|
+
fields[existingFieldIndex].value = value;
|
|
317
|
+
} else {
|
|
318
|
+
fields.push({
|
|
319
|
+
name: fieldName,
|
|
320
|
+
value,
|
|
321
|
+
type: FIELD_TYPE_HIDDEN
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
existingItem.fields = fields;
|
|
325
|
+
const encoded = Buffer.from(JSON.stringify(existingItem)).toString(
|
|
326
|
+
"base64"
|
|
327
|
+
);
|
|
328
|
+
await exec3(["edit", "item", existingItem.id, encoded]);
|
|
329
|
+
} else {
|
|
330
|
+
const templateRaw = await exec3(["get", "template", "item"]);
|
|
331
|
+
const template = JSON.parse(templateRaw);
|
|
332
|
+
template.type = 2;
|
|
333
|
+
template.secureNote = { type: 0 };
|
|
334
|
+
template.name = ref.provider;
|
|
335
|
+
template.folderId = folderId;
|
|
336
|
+
template.fields = [
|
|
337
|
+
{
|
|
338
|
+
name: fieldName,
|
|
339
|
+
value,
|
|
340
|
+
type: FIELD_TYPE_HIDDEN
|
|
341
|
+
}
|
|
342
|
+
];
|
|
343
|
+
const encoded = Buffer.from(JSON.stringify(template)).toString("base64");
|
|
344
|
+
await exec3(["create", "item", encoded]);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async list(project, env, vault = "shipkey") {
|
|
348
|
+
await this.ensureUnlocked();
|
|
349
|
+
const foldersRaw = await exec3(["list", "folders"]);
|
|
350
|
+
const folders = JSON.parse(foldersRaw);
|
|
351
|
+
const folder = folders.find((f) => f.name === vault);
|
|
352
|
+
if (!folder) return [];
|
|
353
|
+
const itemsRaw = await exec3([
|
|
354
|
+
"list",
|
|
355
|
+
"items",
|
|
356
|
+
"--folderid",
|
|
357
|
+
folder.id
|
|
358
|
+
]);
|
|
359
|
+
const items = JSON.parse(itemsRaw);
|
|
360
|
+
const refs = [];
|
|
361
|
+
for (const item of items) {
|
|
362
|
+
if (!item.fields) continue;
|
|
363
|
+
for (const field of item.fields) {
|
|
364
|
+
const parsed = _BitwardenBackend.parseFieldName(field.name);
|
|
365
|
+
if (!parsed) continue;
|
|
366
|
+
if (project && parsed.project !== project) continue;
|
|
367
|
+
if (env && parsed.env !== env) continue;
|
|
368
|
+
refs.push({
|
|
369
|
+
vault,
|
|
370
|
+
provider: item.name,
|
|
371
|
+
project: parsed.project,
|
|
372
|
+
env: parsed.env,
|
|
373
|
+
field: parsed.field
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return refs;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
var BACKENDS = {
|
|
381
|
+
"1password": () => new OnePasswordBackend(),
|
|
382
|
+
bitwarden: () => new BitwardenBackend()
|
|
383
|
+
};
|
|
384
|
+
function getBackend(name = "1password") {
|
|
385
|
+
const factory = BACKENDS[name];
|
|
386
|
+
if (!factory) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
`Unknown backend: ${name}. Available: ${Object.keys(BACKENDS).join(", ")}`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return factory();
|
|
392
|
+
}
|
|
393
|
+
function listBackends() {
|
|
394
|
+
return Object.keys(BACKENDS);
|
|
395
|
+
}
|
|
396
|
+
var PROVIDERS = [
|
|
397
|
+
// --- AI ---
|
|
398
|
+
{
|
|
399
|
+
name: "OpenRouter",
|
|
400
|
+
patterns: [/OPENROUTER/i],
|
|
401
|
+
guide_url: "https://openrouter.ai/keys",
|
|
402
|
+
guide: "OpenRouter \u2192 Keys \u2192 Create Key"
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: "OpenAI",
|
|
406
|
+
patterns: [/OPENAI/i],
|
|
407
|
+
guide_url: "https://platform.openai.com/api-keys",
|
|
408
|
+
guide: "OpenAI Platform \u2192 API Keys \u2192 Create new secret key"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "Anthropic",
|
|
412
|
+
patterns: [/ANTHROPIC/i, /CLAUDE_API/i],
|
|
413
|
+
guide_url: "https://console.anthropic.com/settings/keys",
|
|
414
|
+
guide: "Anthropic Console \u2192 Settings \u2192 API Keys \u2192 Create Key"
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "Google AI",
|
|
418
|
+
patterns: [/GEMINI/i, /GOOGLE_AI/i, /GOOGLE_GENERATIVE/i],
|
|
419
|
+
guide_url: "https://aistudio.google.com/apikey",
|
|
420
|
+
guide: "Google AI Studio \u2192 Get API key \u2192 Create API key"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: "Replicate",
|
|
424
|
+
patterns: [/REPLICATE/i],
|
|
425
|
+
guide_url: "https://replicate.com/account/api-tokens",
|
|
426
|
+
guide: "Replicate \u2192 Account \u2192 API Tokens"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "Hugging Face",
|
|
430
|
+
patterns: [/HUGGING_?FACE/i, /^HF_/i],
|
|
431
|
+
guide_url: "https://huggingface.co/settings/tokens",
|
|
432
|
+
guide: "Hugging Face \u2192 Settings \u2192 Access Tokens \u2192 New token"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "fal.ai",
|
|
436
|
+
patterns: [/FAL/i],
|
|
437
|
+
guide_url: "https://fal.ai/dashboard/keys",
|
|
438
|
+
guide: "fal.ai \u2192 Dashboard \u2192 Keys"
|
|
439
|
+
},
|
|
440
|
+
// --- Payments ---
|
|
441
|
+
{
|
|
442
|
+
name: "Stripe",
|
|
443
|
+
patterns: [/STRIPE/i],
|
|
444
|
+
guide_url: "https://dashboard.stripe.com/apikeys",
|
|
445
|
+
guide: "Stripe Dashboard \u2192 Developers \u2192 API Keys"
|
|
446
|
+
},
|
|
447
|
+
// --- Social / OAuth ---
|
|
448
|
+
{
|
|
449
|
+
name: "GitHub OAuth",
|
|
450
|
+
patterns: [/GITHUB/i],
|
|
451
|
+
guide_url: "https://github.com/settings/developers",
|
|
452
|
+
guide: "GitHub \u2192 Settings \u2192 Developer settings \u2192 OAuth Apps"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: "Reddit",
|
|
456
|
+
patterns: [/REDDIT/i],
|
|
457
|
+
guide_url: "https://www.reddit.com/prefs/apps",
|
|
458
|
+
guide: "Reddit \u2192 Preferences \u2192 Apps \u2192 Create app"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: "Product Hunt",
|
|
462
|
+
patterns: [/PRODUCTHUNT/i, /PRODUCT_HUNT/i, /^PH_/i],
|
|
463
|
+
guide_url: "https://www.producthunt.com/v2/oauth/applications",
|
|
464
|
+
guide: "Product Hunt \u2192 API Dashboard \u2192 Add an Application"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "Discord",
|
|
468
|
+
patterns: [/DISCORD/i],
|
|
469
|
+
guide_url: "https://discord.com/developers/applications",
|
|
470
|
+
guide: "Discord Developer Portal \u2192 Applications \u2192 Bot \u2192 Token"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "Slack",
|
|
474
|
+
patterns: [/SLACK/i],
|
|
475
|
+
guide_url: "https://api.slack.com/apps",
|
|
476
|
+
guide: "Slack API \u2192 Your Apps \u2192 Create New App \u2192 OAuth Tokens"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "Google",
|
|
480
|
+
patterns: [/GOOGLE/i, /^GCP_/i, /^GCLOUD_/i],
|
|
481
|
+
guide_url: "https://console.cloud.google.com/apis/credentials",
|
|
482
|
+
guide: "Google Cloud Console \u2192 APIs & Services \u2192 Credentials"
|
|
483
|
+
},
|
|
484
|
+
// --- Auth ---
|
|
485
|
+
{
|
|
486
|
+
name: "Clerk",
|
|
487
|
+
patterns: [/CLERK/i],
|
|
488
|
+
guide_url: "https://dashboard.clerk.com",
|
|
489
|
+
guide: "Clerk Dashboard \u2192 API Keys"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "Auth0",
|
|
493
|
+
patterns: [/AUTH0/i],
|
|
494
|
+
guide_url: "https://manage.auth0.com/dashboard",
|
|
495
|
+
guide: "Auth0 Dashboard \u2192 Applications \u2192 Settings"
|
|
496
|
+
},
|
|
497
|
+
// --- Communication ---
|
|
498
|
+
{
|
|
499
|
+
name: "Twilio",
|
|
500
|
+
patterns: [/TWILIO/i],
|
|
501
|
+
guide_url: "https://console.twilio.com",
|
|
502
|
+
guide: "Twilio Console \u2192 Account \u2192 API keys & tokens"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: "SendGrid",
|
|
506
|
+
patterns: [/SENDGRID/i],
|
|
507
|
+
guide_url: "https://app.sendgrid.com/settings/api_keys",
|
|
508
|
+
guide: "SendGrid \u2192 Settings \u2192 API Keys \u2192 Create API Key"
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "Resend",
|
|
512
|
+
patterns: [/RESEND/i],
|
|
513
|
+
guide_url: "https://resend.com/api-keys",
|
|
514
|
+
guide: "Resend \u2192 API Keys \u2192 Create API Key"
|
|
515
|
+
},
|
|
516
|
+
// --- Databases ---
|
|
517
|
+
{
|
|
518
|
+
name: "Supabase",
|
|
519
|
+
patterns: [/SUPABASE/i],
|
|
520
|
+
guide_url: "https://supabase.com/dashboard/project/_/settings/api",
|
|
521
|
+
guide: "Supabase \u2192 Project Settings \u2192 API"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "Turso",
|
|
525
|
+
patterns: [/TURSO/i],
|
|
526
|
+
guide_url: "https://turso.tech/app",
|
|
527
|
+
guide: "Turso \u2192 Dashboard \u2192 Database \u2192 Create Token"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: "Upstash",
|
|
531
|
+
patterns: [/UPSTASH/i],
|
|
532
|
+
guide_url: "https://console.upstash.com",
|
|
533
|
+
guide: "Upstash Console \u2192 Database \u2192 REST API credentials"
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
name: "Neon",
|
|
537
|
+
patterns: [/NEON/i],
|
|
538
|
+
guide_url: "https://console.neon.tech",
|
|
539
|
+
guide: "Neon Console \u2192 Project \u2192 Connection Details"
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: "Database",
|
|
543
|
+
patterns: [/DATABASE/i, /^DB_/i]
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "Redis",
|
|
547
|
+
patterns: [/REDIS/i]
|
|
548
|
+
},
|
|
549
|
+
// --- Infrastructure ---
|
|
550
|
+
{
|
|
551
|
+
name: "Cloudflare",
|
|
552
|
+
patterns: [/CLOUDFLARE/i],
|
|
553
|
+
guide_url: "https://dash.cloudflare.com/profile/api-tokens",
|
|
554
|
+
guide: "Cloudflare Dashboard \u2192 Profile \u2192 API Tokens \u2192 Create Token"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "npm",
|
|
558
|
+
patterns: [/^NPM/i],
|
|
559
|
+
guide_url: "https://www.npmjs.com/settings/~/tokens",
|
|
560
|
+
guide: "npmjs.com \u2192 Access Tokens \u2192 Generate New Token (Classic) \u2192 Publish"
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: "AWS",
|
|
564
|
+
patterns: [/^AWS/i],
|
|
565
|
+
guide_url: "https://console.aws.amazon.com/iam/",
|
|
566
|
+
guide: "AWS Console \u2192 IAM \u2192 Users \u2192 Security credentials"
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "Vercel",
|
|
570
|
+
patterns: [/VERCEL/i],
|
|
571
|
+
guide_url: "https://vercel.com/account/tokens",
|
|
572
|
+
guide: "Vercel \u2192 Account Settings \u2192 Tokens"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "Fly",
|
|
576
|
+
patterns: [/FLY/i],
|
|
577
|
+
guide_url: "https://fly.io/user/personal_access_tokens",
|
|
578
|
+
guide: "Fly.io \u2192 Account \u2192 Access Tokens"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
name: "Sentry",
|
|
582
|
+
patterns: [/SENTRY/i],
|
|
583
|
+
guide_url: "https://sentry.io/settings/account/api/auth-tokens/",
|
|
584
|
+
guide: "Sentry \u2192 Settings \u2192 Auth Tokens \u2192 Create New Token"
|
|
585
|
+
},
|
|
586
|
+
// --- Misc ---
|
|
587
|
+
{
|
|
588
|
+
name: "Session",
|
|
589
|
+
patterns: [/SESSION/i]
|
|
590
|
+
}
|
|
591
|
+
];
|
|
592
|
+
function guessProvider(key) {
|
|
593
|
+
for (const provider of PROVIDERS) {
|
|
594
|
+
if (provider.patterns.some((p) => p.test(key))) {
|
|
595
|
+
return provider.name;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return "General";
|
|
599
|
+
}
|
|
600
|
+
function groupByProvider(envKeys) {
|
|
601
|
+
const result = {};
|
|
602
|
+
for (const key of envKeys) {
|
|
603
|
+
const providerName = guessProvider(key);
|
|
604
|
+
if (!result[providerName]) {
|
|
605
|
+
const def = PROVIDERS.find((p) => p.name === providerName);
|
|
606
|
+
result[providerName] = {
|
|
607
|
+
fields: [],
|
|
608
|
+
...def?.guide_url && { guide_url: def.guide_url },
|
|
609
|
+
...def?.guide && { guide: def.guide }
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
result[providerName].fields.push(key);
|
|
613
|
+
}
|
|
614
|
+
return result;
|
|
615
|
+
}
|
|
616
|
+
export {
|
|
617
|
+
BitwardenBackend,
|
|
618
|
+
OnePasswordBackend,
|
|
619
|
+
PROVIDERS,
|
|
620
|
+
exec,
|
|
621
|
+
getBackend,
|
|
622
|
+
groupByProvider,
|
|
623
|
+
guessProvider,
|
|
624
|
+
listBackends
|
|
625
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
} from "./chunk-ZFNDBUPR.js";
|
|
15
15
|
|
|
16
16
|
// src/index.ts
|
|
17
|
-
import {
|
|
17
|
+
import { readFileSync } from "fs";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import { dirname as dirname2, resolve } from "path";
|
|
18
20
|
import { Command } from "commander";
|
|
19
21
|
|
|
20
22
|
// src/scanner/detect.ts
|
|
@@ -983,8 +985,8 @@ async function syncToGateway(domain, auth) {
|
|
|
983
985
|
}
|
|
984
986
|
|
|
985
987
|
// src/index.ts
|
|
986
|
-
var
|
|
987
|
-
var { version } =
|
|
988
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
989
|
+
var { version } = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
|
|
988
990
|
var program = new Command();
|
|
989
991
|
program.name("nkmc").description("nkmc SDK CLI").version(version);
|
|
990
992
|
program.command("init").description("Initialize nkmc in the current project").argument("[dir]", "Project directory", ".").action(async (dir) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nkmc/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "CLI for scanning and registering APIs with the nkmc gateway",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"commander": "^13.0.0",
|
|
18
18
|
"ts-morph": "^27.0.2"
|
|
19
19
|
},
|
|
20
|
+
"optionalDependencies": {
|
|
21
|
+
"@shipkey/core": "^0.1.0"
|
|
22
|
+
},
|
|
20
23
|
"repository": {
|
|
21
24
|
"type": "git",
|
|
22
25
|
"url": "https://github.com/chekusu/nkmc-sdk.git",
|