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