@nkmc/cli 0.2.5 → 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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getToken,
4
4
  saveToken
5
- } from "./chunk-MPXYSTOK.js";
5
+ } from "./chunk-TDMTBOMD.js";
6
6
 
7
7
  // src/commands/register.ts
8
8
  import { readFile } from "fs/promises";
@@ -72,11 +72,43 @@ async function getToken(domain) {
72
72
  }
73
73
  return entry.publishToken;
74
74
  }
75
+ async function saveCredentials(creds) {
76
+ const dir = nkmcDir();
77
+ await mkdir(dir, { recursive: true });
78
+ const filePath = credentialsPath();
79
+ await writeFile(filePath, JSON.stringify(creds, null, 2) + "\n");
80
+ await chmod(filePath, 384);
81
+ }
82
+ async function saveKey(domain, auth) {
83
+ const creds = await loadCredentials();
84
+ if (!creds.keys) creds.keys = {};
85
+ creds.keys[domain] = { auth, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
86
+ await saveCredentials(creds);
87
+ }
88
+ async function getKey(domain) {
89
+ const creds = await loadCredentials();
90
+ return creds.keys?.[domain] ?? null;
91
+ }
92
+ async function listKeys() {
93
+ const creds = await loadCredentials();
94
+ return creds.keys ?? {};
95
+ }
96
+ async function deleteKey(domain) {
97
+ const creds = await loadCredentials();
98
+ if (!creds.keys?.[domain]) return false;
99
+ delete creds.keys[domain];
100
+ await saveCredentials(creds);
101
+ return true;
102
+ }
75
103
 
76
104
  export {
77
105
  loadCredentials,
78
106
  saveToken,
79
107
  saveAgentToken,
80
108
  getAgentToken,
81
- getToken
109
+ getToken,
110
+ saveKey,
111
+ getKey,
112
+ listKeys,
113
+ deleteKey
82
114
  };
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/gateway/client.ts
4
+ var GatewayClient = class {
5
+ constructor(gatewayUrl, token) {
6
+ this.gatewayUrl = gatewayUrl;
7
+ this.token = token;
8
+ }
9
+ baseUrl() {
10
+ return this.gatewayUrl.replace(/\/$/, "");
11
+ }
12
+ async execute(command) {
13
+ const url = `${this.baseUrl()}/execute`;
14
+ const res = await fetch(url, {
15
+ method: "POST",
16
+ headers: {
17
+ Authorization: `Bearer ${this.token}`,
18
+ "Content-Type": "application/json"
19
+ },
20
+ body: JSON.stringify({ command })
21
+ });
22
+ if (!res.ok) {
23
+ const body = await res.text();
24
+ throw new Error(`Gateway error ${res.status}: ${body}`);
25
+ }
26
+ return res.json();
27
+ }
28
+ // --- BYOK methods ---
29
+ async uploadByok(domain, auth) {
30
+ const url = `${this.baseUrl()}/byok/${domain}`;
31
+ const res = await fetch(url, {
32
+ method: "PUT",
33
+ headers: {
34
+ Authorization: `Bearer ${this.token}`,
35
+ "Content-Type": "application/json"
36
+ },
37
+ body: JSON.stringify({ auth })
38
+ });
39
+ if (!res.ok) {
40
+ const body = await res.text();
41
+ throw new Error(`BYOK upload failed ${res.status}: ${body}`);
42
+ }
43
+ }
44
+ async listByok() {
45
+ const url = `${this.baseUrl()}/byok`;
46
+ const res = await fetch(url, {
47
+ headers: { Authorization: `Bearer ${this.token}` }
48
+ });
49
+ if (!res.ok) {
50
+ const body = await res.text();
51
+ throw new Error(`BYOK list failed ${res.status}: ${body}`);
52
+ }
53
+ return res.json();
54
+ }
55
+ async deleteByok(domain) {
56
+ const url = `${this.baseUrl()}/byok/${domain}`;
57
+ const res = await fetch(url, {
58
+ method: "DELETE",
59
+ headers: { Authorization: `Bearer ${this.token}` }
60
+ });
61
+ if (!res.ok) {
62
+ const body = await res.text();
63
+ throw new Error(`BYOK delete failed ${res.status}: ${body}`);
64
+ }
65
+ }
66
+ };
67
+ async function createClient() {
68
+ const { getAgentToken } = await import("./credentials-O3WAXKAV.js");
69
+ const stored = await getAgentToken();
70
+ const gatewayUrl = process.env.NKMC_GATEWAY_URL ?? stored?.gatewayUrl ?? "https://api.nkmc.ai";
71
+ const token = process.env.NKMC_TOKEN ?? stored?.token ?? null;
72
+ if (!token) {
73
+ throw new Error(
74
+ "No token found. Run 'nkmc auth' first, or set NKMC_TOKEN."
75
+ );
76
+ }
77
+ return new GatewayClient(gatewayUrl, token);
78
+ }
79
+
80
+ export {
81
+ GatewayClient,
82
+ createClient
83
+ };
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ GatewayClient,
4
+ createClient
5
+ } from "./chunk-ZFNDBUPR.js";
6
+ export {
7
+ GatewayClient,
8
+ createClient
9
+ };
@@ -1,15 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ deleteKey,
3
4
  getAgentToken,
5
+ getKey,
4
6
  getToken,
7
+ listKeys,
5
8
  loadCredentials,
6
9
  saveAgentToken,
10
+ saveKey,
7
11
  saveToken
8
- } from "./chunk-MPXYSTOK.js";
12
+ } from "./chunk-TDMTBOMD.js";
9
13
  export {
14
+ deleteKey,
10
15
  getAgentToken,
16
+ getKey,
11
17
  getToken,
18
+ listKeys,
12
19
  loadCredentials,
13
20
  saveAgentToken,
21
+ saveKey,
14
22
  saveToken
15
23
  };
@@ -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
@@ -1,13 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runRegister
4
- } from "./chunk-5ZYHHSZI.js";
4
+ } from "./chunk-HXHGF76V.js";
5
5
  import {
6
+ deleteKey,
7
+ listKeys,
6
8
  saveAgentToken,
9
+ saveKey,
7
10
  saveToken
8
- } from "./chunk-MPXYSTOK.js";
11
+ } from "./chunk-TDMTBOMD.js";
12
+ import {
13
+ createClient
14
+ } from "./chunk-ZFNDBUPR.js";
9
15
 
10
16
  // src/index.ts
17
+ import { readFileSync } from "fs";
18
+ import { fileURLToPath } from "url";
19
+ import { dirname as dirname2, resolve } from "path";
11
20
  import { Command } from "commander";
12
21
 
13
22
  // src/scanner/detect.ts
@@ -55,14 +64,14 @@ async function detectFramework(projectDir) {
55
64
  import { readFile as readFile2, writeFile } from "fs/promises";
56
65
  import { join as join2 } from "path";
57
66
  function generateConfig(options) {
58
- const { name, version, roles, detected } = options;
67
+ const { name, version: version2, roles, detected } = options;
59
68
  const rolesStr = roles.map((r) => `"${r}"`).join(", ");
60
69
  const lines = [
61
70
  `import { defineConfig } from "@nkmc/core";`,
62
71
  ``,
63
72
  `export default defineConfig({`,
64
73
  ` name: "${name}",`,
65
- ` version: "${version}",`,
74
+ ` version: "${version2}",`,
66
75
  ` roles: [${rolesStr}],`,
67
76
  ` framework: "${detected.framework}",`
68
77
  ];
@@ -444,7 +453,7 @@ async function runGenerate(projectDir, options) {
444
453
  await writeFile2(outputPath, md);
445
454
  console.log(`Generated ${outputPath}`);
446
455
  if (options?.register) {
447
- const { registerService, resolveToken } = await import("./register-7SUETZ7U.js");
456
+ const { registerService, resolveToken } = await import("./register-UBRUY256.js");
448
457
  const gatewayUrl = options.gatewayUrl ?? process.env.NKMC_GATEWAY_URL;
449
458
  const domain = options.domain ?? process.env.NKMC_DOMAIN;
450
459
  if (!gatewayUrl || !domain) {
@@ -560,40 +569,160 @@ async function runAuth(opts) {
560
569
  console.log(` Gateway: ${gatewayUrl}`);
561
570
  }
562
571
 
563
- // src/gateway/client.ts
564
- var GatewayClient = class {
565
- constructor(gatewayUrl, token) {
566
- this.gatewayUrl = gatewayUrl;
567
- this.token = token;
568
- }
569
- async execute(command) {
570
- const url = `${this.gatewayUrl.replace(/\/$/, "")}/execute`;
571
- const res = await fetch(url, {
572
- method: "POST",
573
- headers: {
574
- Authorization: `Bearer ${this.token}`,
575
- "Content-Type": "application/json"
576
- },
577
- body: JSON.stringify({ command })
578
- });
579
- if (!res.ok) {
580
- const body = await res.text();
581
- throw new Error(`Gateway error ${res.status}: ${body}`);
582
- }
583
- return res.json();
572
+ // src/keys/provider-map.ts
573
+ var DOMAIN_AUTH_HINTS = {
574
+ // --- AI ---
575
+ "api.openai.com": {
576
+ envVar: "OPENAI_API_KEY",
577
+ authType: "bearer",
578
+ guideUrl: "https://platform.openai.com/api-keys"
579
+ },
580
+ "api.anthropic.com": {
581
+ envVar: "ANTHROPIC_API_KEY",
582
+ authType: "api-key",
583
+ headerName: "x-api-key",
584
+ guideUrl: "https://console.anthropic.com/settings/keys"
585
+ },
586
+ "generativelanguage.googleapis.com": {
587
+ envVar: "GEMINI_API_KEY",
588
+ authType: "api-key",
589
+ headerName: "x-goog-api-key",
590
+ guideUrl: "https://aistudio.google.com/apikey"
591
+ },
592
+ "openrouter.ai": {
593
+ envVar: "OPENROUTER_API_KEY",
594
+ authType: "bearer",
595
+ guideUrl: "https://openrouter.ai/keys"
596
+ },
597
+ "api.replicate.com": {
598
+ envVar: "REPLICATE_API_TOKEN",
599
+ authType: "bearer",
600
+ guideUrl: "https://replicate.com/account/api-tokens"
601
+ },
602
+ // --- DevOps ---
603
+ "api.github.com": {
604
+ envVar: "GITHUB_TOKEN",
605
+ authType: "bearer",
606
+ guideUrl: "https://github.com/settings/tokens"
607
+ },
608
+ "gitlab.com": {
609
+ envVar: "GITLAB_TOKEN",
610
+ authType: "bearer",
611
+ guideUrl: "https://gitlab.com/-/user_settings/personal_access_tokens"
612
+ },
613
+ "api.cloudflare.com": {
614
+ envVar: "CLOUDFLARE_API_TOKEN",
615
+ authType: "bearer",
616
+ guideUrl: "https://dash.cloudflare.com/profile/api-tokens"
617
+ },
618
+ "api.vercel.com": {
619
+ envVar: "VERCEL_TOKEN",
620
+ authType: "bearer",
621
+ guideUrl: "https://vercel.com/account/tokens"
622
+ },
623
+ "fly.io": {
624
+ envVar: "FLY_API_TOKEN",
625
+ authType: "bearer",
626
+ guideUrl: "https://fly.io/user/personal_access_tokens"
627
+ },
628
+ "api.render.com": {
629
+ envVar: "RENDER_API_KEY",
630
+ authType: "bearer",
631
+ guideUrl: "https://render.com/docs/api#authentication"
632
+ },
633
+ // --- Collaboration ---
634
+ "api.notion.com": {
635
+ envVar: "NOTION_API_KEY",
636
+ authType: "bearer",
637
+ guideUrl: "https://www.notion.so/my-integrations"
638
+ },
639
+ "app.asana.com": {
640
+ envVar: "ASANA_TOKEN",
641
+ authType: "bearer",
642
+ guideUrl: "https://app.asana.com/0/developer-console"
643
+ },
644
+ "jira.atlassian.com": {
645
+ envVar: "JIRA_API_TOKEN",
646
+ authType: "bearer",
647
+ guideUrl: "https://id.atlassian.com/manage-profile/security/api-tokens"
648
+ },
649
+ "slack.com": {
650
+ envVar: "SLACK_TOKEN",
651
+ authType: "bearer",
652
+ guideUrl: "https://api.slack.com/apps"
653
+ },
654
+ "discord.com": {
655
+ envVar: "DISCORD_TOKEN",
656
+ authType: "bearer",
657
+ guideUrl: "https://discord.com/developers/applications"
658
+ },
659
+ // --- Databases ---
660
+ "api.supabase.com": {
661
+ envVar: "SUPABASE_SERVICE_ROLE_KEY",
662
+ authType: "bearer",
663
+ guideUrl: "https://supabase.com/dashboard/project/_/settings/api"
664
+ },
665
+ "api.turso.tech": {
666
+ envVar: "TURSO_AUTH_TOKEN",
667
+ authType: "bearer",
668
+ guideUrl: "https://turso.tech/app"
669
+ },
670
+ "console.neon.tech": {
671
+ envVar: "NEON_API_KEY",
672
+ authType: "bearer",
673
+ guideUrl: "https://console.neon.tech"
674
+ },
675
+ // --- Communication ---
676
+ "api.twilio.com": {
677
+ envVar: "TWILIO_AUTH_TOKEN",
678
+ authType: "bearer",
679
+ guideUrl: "https://console.twilio.com"
680
+ },
681
+ "api.resend.com": {
682
+ envVar: "RESEND_API_KEY",
683
+ authType: "bearer",
684
+ guideUrl: "https://resend.com/api-keys"
685
+ },
686
+ // --- Payments ---
687
+ "api.stripe.com": {
688
+ envVar: "STRIPE_SECRET_KEY",
689
+ authType: "bearer",
690
+ guideUrl: "https://dashboard.stripe.com/apikeys"
691
+ },
692
+ // --- Monitoring ---
693
+ "sentry.io": {
694
+ envVar: "SENTRY_AUTH_TOKEN",
695
+ authType: "bearer",
696
+ guideUrl: "https://sentry.io/settings/account/api/auth-tokens/"
697
+ },
698
+ "api.datadoghq.com": {
699
+ envVar: "DD_API_KEY",
700
+ authType: "api-key",
701
+ headerName: "DD-API-KEY",
702
+ guideUrl: "https://app.datadoghq.com/account/settings#api"
703
+ },
704
+ // --- Music ---
705
+ "api.spotify.com": {
706
+ envVar: "SPOTIFY_TOKEN",
707
+ authType: "bearer",
708
+ guideUrl: "https://developer.spotify.com/dashboard"
709
+ },
710
+ // --- CI/CD ---
711
+ "circleci.com": {
712
+ envVar: "CIRCLECI_TOKEN",
713
+ authType: "bearer",
714
+ guideUrl: "https://app.circleci.com/settings/user/tokens"
715
+ },
716
+ // --- API Tools ---
717
+ "api.getpostman.com": {
718
+ envVar: "POSTMAN_API_KEY",
719
+ authType: "api-key",
720
+ headerName: "X-Api-Key",
721
+ guideUrl: "https://web.postman.co/settings/me/api-keys"
584
722
  }
585
723
  };
586
- async function createClient() {
587
- const { getAgentToken } = await import("./credentials-42DL3WPT.js");
588
- const stored = await getAgentToken();
589
- const gatewayUrl = process.env.NKMC_GATEWAY_URL ?? stored?.gatewayUrl ?? "https://api.nkmc.ai";
590
- const token = process.env.NKMC_TOKEN ?? stored?.token ?? null;
591
- if (!token) {
592
- throw new Error(
593
- "No token found. Run 'nkmc auth' first, or set NKMC_TOKEN."
594
- );
595
- }
596
- return new GatewayClient(gatewayUrl, token);
724
+ function getAuthHint(domain) {
725
+ return DOMAIN_AUTH_HINTS[domain] ?? null;
597
726
  }
598
727
 
599
728
  // src/commands/fs.ts
@@ -628,8 +757,33 @@ ${endpoints}`;
628
757
  }
629
758
  return JSON.stringify(data);
630
759
  }
631
- function handleError(err) {
760
+ function extractDomain(path) {
761
+ const segments = path.replace(/^\/+/, "").split("/");
762
+ const first = segments[0];
763
+ if (!first) return null;
764
+ const domain = first.includes("@") ? first.slice(0, first.indexOf("@")) : first;
765
+ return domain.includes(".") ? domain : null;
766
+ }
767
+ function isAuthError(message) {
768
+ if (/Gateway error (401|403):/.test(message)) return true;
769
+ if (/Gateway error 500:/.test(message) && /\b(Unauthorized|Unauthenticated|authenticate|API key|api[_-]?key)\b/i.test(message)) return true;
770
+ return false;
771
+ }
772
+ function handleError(err, cmdPath) {
632
773
  const message = err instanceof Error ? err.message : String(err);
774
+ if (isAuthError(message)) {
775
+ const domain = cmdPath && extractDomain(cmdPath) || message.match(/([a-z0-9-]+(?:\.[a-z0-9-]+){1,})/i)?.[1] || null;
776
+ if (domain) {
777
+ const hint = getAuthHint(domain);
778
+ console.error(`Error: Authentication required for ${domain}`);
779
+ if (hint?.guideUrl) {
780
+ console.error(` Get your key: ${hint.guideUrl}`);
781
+ }
782
+ console.error(` Set your key: nkmc keys set ${domain} --token <YOUR_KEY> --sync`);
783
+ console.error(` Then retry your command.`);
784
+ process.exit(1);
785
+ }
786
+ }
633
787
  console.error(JSON.stringify({ error: message }));
634
788
  process.exit(1);
635
789
  }
@@ -640,7 +794,7 @@ function registerFsCommands(program2) {
640
794
  const result = await client.execute(`ls ${path}`);
641
795
  output(result);
642
796
  } catch (err) {
643
- handleError(err);
797
+ handleError(err, path);
644
798
  }
645
799
  });
646
800
  program2.command("cat").description("Read file contents").argument("<path>", "File path").action(async (path) => {
@@ -649,7 +803,7 @@ function registerFsCommands(program2) {
649
803
  const result = await client.execute(`cat ${path}`);
650
804
  output(result);
651
805
  } catch (err) {
652
- handleError(err);
806
+ handleError(err, path);
653
807
  }
654
808
  });
655
809
  program2.command("write").description("Write data to a file").argument("<path>", "File path").argument("<data>", "Data to write").action(async (path, data) => {
@@ -658,7 +812,7 @@ function registerFsCommands(program2) {
658
812
  const result = await client.execute(`write ${path} ${data}`);
659
813
  output(result);
660
814
  } catch (err) {
661
- handleError(err);
815
+ handleError(err, path);
662
816
  }
663
817
  });
664
818
  program2.command("rm").description("Remove a file").argument("<path>", "File path").action(async (path) => {
@@ -667,7 +821,7 @@ function registerFsCommands(program2) {
667
821
  const result = await client.execute(`rm ${path}`);
668
822
  output(result);
669
823
  } catch (err) {
670
- handleError(err);
824
+ handleError(err, path);
671
825
  }
672
826
  });
673
827
  program2.command("grep").description("Search file contents").argument("<pattern>", "Search pattern").argument("<path>", "File or directory path").action(async (pattern, path) => {
@@ -676,7 +830,7 @@ function registerFsCommands(program2) {
676
830
  const result = await client.execute(`grep ${pattern} ${path}`);
677
831
  console.log(formatGrepResults(result));
678
832
  } catch (err) {
679
- handleError(err);
833
+ handleError(err, path);
680
834
  }
681
835
  });
682
836
  program2.command("pipe").description("Pipe commands: cat <path> | write <path>").argument("<expression...>", "Pipe expression").action(async (expression) => {
@@ -686,8 +840,8 @@ function registerFsCommands(program2) {
686
840
  if (parts.length !== 2) {
687
841
  throw new Error("Pipe expression must have exactly two stages separated by '|'");
688
842
  }
689
- const [source, target] = parts;
690
- if (!source.startsWith("cat ")) {
843
+ const [source2, target] = parts;
844
+ if (!source2.startsWith("cat ")) {
691
845
  throw new Error("Pipe step 1 must be a 'cat' command");
692
846
  }
693
847
  if (!target.startsWith("write ")) {
@@ -696,7 +850,7 @@ function registerFsCommands(program2) {
696
850
  const client = await createClient();
697
851
  let data;
698
852
  try {
699
- data = await client.execute(source);
853
+ data = await client.execute(source2);
700
854
  } catch (err) {
701
855
  const msg = err instanceof Error ? err.message : String(err);
702
856
  throw new Error(`Pipe step 1 failed: ${msg}`);
@@ -711,14 +865,130 @@ function registerFsCommands(program2) {
711
865
  }
712
866
  output(result);
713
867
  } catch (err) {
714
- handleError(err);
868
+ handleError(err, source.slice("cat ".length).trim());
869
+ }
870
+ });
871
+ }
872
+
873
+ // src/commands/keys.ts
874
+ function registerKeysCommand(program2) {
875
+ const keys = program2.command("keys").description("Manage API keys for authenticated services (BYOK)");
876
+ keys.command("set <domain>").description("Set an API key for a domain").option("--token <value>", "API key / token value").option("--sync", "Also upload to gateway (BYOK)").action(async (domain, opts) => {
877
+ try {
878
+ const hint = getAuthHint(domain);
879
+ let tokenValue = opts.token;
880
+ if (!tokenValue) {
881
+ const envHint = hint ? `(${hint.envVar})` : "";
882
+ console.error(
883
+ `Usage: nkmc keys set ${domain} --token <value> ${envHint}`
884
+ );
885
+ if (hint?.guideUrl) {
886
+ console.error(` Get your key: ${hint.guideUrl}`);
887
+ }
888
+ process.exit(1);
889
+ }
890
+ const auth = buildAuth(domain, tokenValue, hint);
891
+ await saveKey(domain, auth);
892
+ console.log(`Key saved for ${domain}`);
893
+ if (opts.sync) {
894
+ await syncToGateway(domain, auth);
895
+ }
896
+ } catch (err) {
897
+ const msg = err instanceof Error ? err.message : String(err);
898
+ console.error(`Error: ${msg}`);
899
+ process.exit(1);
900
+ }
901
+ });
902
+ keys.command("list").description("List all saved API keys").option("--remote", "Also list keys stored on gateway").action(async (opts) => {
903
+ try {
904
+ const localKeys = await listKeys();
905
+ const domains = Object.keys(localKeys);
906
+ if (domains.length === 0 && !opts.remote) {
907
+ console.log("No keys stored. Use 'nkmc keys set <domain>' to add one.");
908
+ return;
909
+ }
910
+ if (domains.length > 0) {
911
+ console.log("Local keys:");
912
+ for (const domain of domains) {
913
+ const entry = localKeys[domain];
914
+ const maskedAuth = maskAuth(entry.auth);
915
+ console.log(` ${domain} ${maskedAuth} (${entry.updatedAt})`);
916
+ }
917
+ }
918
+ if (opts.remote) {
919
+ try {
920
+ const { createClient: createClient2 } = await import("./client-GXGVRLEH.js");
921
+ const client = await createClient2();
922
+ const { domains: remoteDomains } = await client.listByok();
923
+ console.log("\nGateway BYOK keys:");
924
+ if (remoteDomains.length === 0) {
925
+ console.log(" (none)");
926
+ } else {
927
+ for (const d of remoteDomains) {
928
+ console.log(` ${d}`);
929
+ }
930
+ }
931
+ } catch (err) {
932
+ const msg = err instanceof Error ? err.message : String(err);
933
+ console.error(`
934
+ Could not fetch remote keys: ${msg}`);
935
+ }
936
+ }
937
+ } catch (err) {
938
+ const msg = err instanceof Error ? err.message : String(err);
939
+ console.error(`Error: ${msg}`);
940
+ process.exit(1);
941
+ }
942
+ });
943
+ keys.command("remove <domain>").description("Remove an API key for a domain").option("--remote", "Also remove from gateway").action(async (domain, opts) => {
944
+ try {
945
+ const removed = await deleteKey(domain);
946
+ if (removed) {
947
+ console.log(`Key removed for ${domain}`);
948
+ } else {
949
+ console.log(`No key found for ${domain}`);
950
+ }
951
+ if (opts.remote) {
952
+ try {
953
+ const { createClient: createClient2 } = await import("./client-GXGVRLEH.js");
954
+ const client = await createClient2();
955
+ await client.deleteByok(domain);
956
+ console.log(`Gateway BYOK key removed for ${domain}`);
957
+ } catch (err) {
958
+ const msg = err instanceof Error ? err.message : String(err);
959
+ console.error(`Could not remove remote key: ${msg}`);
960
+ }
961
+ }
962
+ } catch (err) {
963
+ const msg = err instanceof Error ? err.message : String(err);
964
+ console.error(`Error: ${msg}`);
965
+ process.exit(1);
715
966
  }
716
967
  });
717
968
  }
969
+ function buildAuth(domain, token, hint) {
970
+ if (hint?.authType === "api-key" && hint.headerName) {
971
+ return { type: "api-key", header: hint.headerName, key: token };
972
+ }
973
+ return { type: "bearer", token };
974
+ }
975
+ function maskAuth(auth) {
976
+ const value = auth.token ?? auth.key ?? "";
977
+ if (value.length <= 8) return `${auth.type}:****`;
978
+ return `${auth.type}:${value.slice(0, 4)}...${value.slice(-4)}`;
979
+ }
980
+ async function syncToGateway(domain, auth) {
981
+ const { createClient: createClient2 } = await import("./client-GXGVRLEH.js");
982
+ const client = await createClient2();
983
+ await client.uploadByok(domain, auth);
984
+ console.log(`Key synced to gateway for ${domain}`);
985
+ }
718
986
 
719
987
  // src/index.ts
988
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
989
+ var { version } = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
720
990
  var program = new Command();
721
- program.name("nkmc").description("nkmc SDK CLI").version("0.1.0");
991
+ program.name("nkmc").description("nkmc SDK CLI").version(version);
722
992
  program.command("init").description("Initialize nkmc in the current project").argument("[dir]", "Project directory", ".").action(async (dir) => {
723
993
  const projectDir = dir === "." ? process.cwd() : dir;
724
994
  await runInit(projectDir);
@@ -754,4 +1024,5 @@ program.command("auth").description("Authenticate with the nkmc gateway").option
754
1024
  await runAuth({ gatewayUrl: opts.gatewayUrl });
755
1025
  });
756
1026
  registerFsCommands(program);
1027
+ registerKeysCommand(program);
757
1028
  program.parse();
@@ -3,8 +3,8 @@ import {
3
3
  registerService,
4
4
  resolveToken,
5
5
  runRegister
6
- } from "./chunk-5ZYHHSZI.js";
7
- import "./chunk-MPXYSTOK.js";
6
+ } from "./chunk-HXHGF76V.js";
7
+ import "./chunk-TDMTBOMD.js";
8
8
  export {
9
9
  registerService,
10
10
  resolveToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nkmc/cli",
3
- "version": "0.2.5",
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",
@@ -13,10 +13,13 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@mrleebo/prisma-ast": "^0.13.1",
16
- "@nkmc/core": "^0.1.0",
16
+ "@nkmc/core": "^0.1.1",
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",