@keywaysh/cli 0.1.4 → 0.1.5
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/cli.js +205 -43
- package/package.json +17 -13
package/dist/cli.js
CHANGED
|
@@ -102,7 +102,7 @@ var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
|
102
102
|
// package.json
|
|
103
103
|
var package_default = {
|
|
104
104
|
name: "@keywaysh/cli",
|
|
105
|
-
version: "0.1.
|
|
105
|
+
version: "0.1.5",
|
|
106
106
|
description: "One link to all your secrets",
|
|
107
107
|
type: "module",
|
|
108
108
|
bin: {
|
|
@@ -146,6 +146,7 @@ var package_default = {
|
|
|
146
146
|
node: ">=18.0.0"
|
|
147
147
|
},
|
|
148
148
|
dependencies: {
|
|
149
|
+
"balanced-match": "^3.0.1",
|
|
149
150
|
commander: "^14.0.0",
|
|
150
151
|
conf: "^15.0.2",
|
|
151
152
|
open: "^11.0.0",
|
|
@@ -154,6 +155,7 @@ var package_default = {
|
|
|
154
155
|
prompts: "^2.4.2"
|
|
155
156
|
},
|
|
156
157
|
devDependencies: {
|
|
158
|
+
"@types/balanced-match": "^3.0.2",
|
|
157
159
|
"@types/node": "^24.2.0",
|
|
158
160
|
"@types/prompts": "^2.4.9",
|
|
159
161
|
tsup: "^8.5.0",
|
|
@@ -491,6 +493,19 @@ async function executeSync(accessToken, repoFullName, options) {
|
|
|
491
493
|
const wrapped = await handleResponse(response);
|
|
492
494
|
return wrapped.data;
|
|
493
495
|
}
|
|
496
|
+
async function connectWithToken(accessToken, provider, providerToken) {
|
|
497
|
+
const response = await fetchWithTimeout(`${API_BASE_URL}/v1/integrations/${provider}/connect`, {
|
|
498
|
+
method: "POST",
|
|
499
|
+
headers: {
|
|
500
|
+
"Content-Type": "application/json",
|
|
501
|
+
"User-Agent": USER_AGENT,
|
|
502
|
+
Authorization: `Bearer ${accessToken}`
|
|
503
|
+
},
|
|
504
|
+
body: JSON.stringify({ token: providerToken })
|
|
505
|
+
});
|
|
506
|
+
const wrapped = await handleResponse(response);
|
|
507
|
+
return wrapped.data;
|
|
508
|
+
}
|
|
494
509
|
async function checkVaultExists(accessToken, repoFullName) {
|
|
495
510
|
const [owner, repo] = repoFullName.split("/");
|
|
496
511
|
try {
|
|
@@ -673,23 +688,76 @@ import fs3 from "fs";
|
|
|
673
688
|
import path3 from "path";
|
|
674
689
|
import prompts from "prompts";
|
|
675
690
|
import pc2 from "picocolors";
|
|
691
|
+
import balanced from "balanced-match";
|
|
676
692
|
function generateBadge(repo) {
|
|
677
693
|
return `[](https://www.keyway.sh/vaults/${repo})`;
|
|
678
694
|
}
|
|
695
|
+
var BADGE_PREFIX = /\[!\[[^\]]*\]\([^)]*\)\]\(/g;
|
|
696
|
+
var H1_PATTERN = /^#\s+/;
|
|
697
|
+
var CODE_FENCE = /^```/;
|
|
698
|
+
function findLastBadgeEnd(line) {
|
|
699
|
+
let lastEnd = -1;
|
|
700
|
+
let match;
|
|
701
|
+
BADGE_PREFIX.lastIndex = 0;
|
|
702
|
+
while ((match = BADGE_PREFIX.exec(line)) !== null) {
|
|
703
|
+
const prefixEnd = match.index + match[0].length - 1;
|
|
704
|
+
const remainder = line.substring(prefixEnd);
|
|
705
|
+
const balancedMatch = balanced("(", ")", remainder);
|
|
706
|
+
if (balancedMatch) {
|
|
707
|
+
lastEnd = prefixEnd + balancedMatch.end + 1;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return lastEnd;
|
|
711
|
+
}
|
|
679
712
|
function insertBadgeIntoReadme(readmeContent, badge) {
|
|
680
713
|
if (readmeContent.includes("keyway.sh/badge.svg")) {
|
|
681
714
|
return readmeContent;
|
|
682
715
|
}
|
|
683
716
|
const lines = readmeContent.split(/\r?\n/);
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
717
|
+
let inCodeBlock = false;
|
|
718
|
+
let inHtmlComment = false;
|
|
719
|
+
let lastBadgeLine = -1;
|
|
720
|
+
let lastBadgeEndIndex = -1;
|
|
721
|
+
let firstH1Line = -1;
|
|
722
|
+
for (let i = 0; i < lines.length; i++) {
|
|
723
|
+
const line = lines[i];
|
|
724
|
+
const trimmed = line.trim();
|
|
725
|
+
if (CODE_FENCE.test(trimmed)) {
|
|
726
|
+
inCodeBlock = !inCodeBlock;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (inCodeBlock) continue;
|
|
730
|
+
if (trimmed.includes("<!--")) inHtmlComment = true;
|
|
731
|
+
if (trimmed.includes("-->")) {
|
|
732
|
+
inHtmlComment = false;
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (inHtmlComment) continue;
|
|
736
|
+
BADGE_PREFIX.lastIndex = 0;
|
|
737
|
+
if (BADGE_PREFIX.test(line)) {
|
|
738
|
+
lastBadgeLine = i;
|
|
739
|
+
lastBadgeEndIndex = findLastBadgeEnd(line);
|
|
740
|
+
}
|
|
741
|
+
if (firstH1Line === -1 && H1_PATTERN.test(line)) {
|
|
742
|
+
firstH1Line = i;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (lastBadgeLine >= 0 && lastBadgeEndIndex > 0) {
|
|
746
|
+
const line = lines[lastBadgeLine];
|
|
747
|
+
lines[lastBadgeLine] = line.slice(0, lastBadgeEndIndex) + " " + badge + line.slice(lastBadgeEndIndex);
|
|
748
|
+
return lines.join("\n");
|
|
749
|
+
}
|
|
750
|
+
if (firstH1Line >= 0) {
|
|
751
|
+
const before = lines.slice(0, firstH1Line + 1);
|
|
752
|
+
const after = lines.slice(firstH1Line + 1);
|
|
688
753
|
while (after.length > 0 && after[0].trim() === "") {
|
|
689
754
|
after.shift();
|
|
690
755
|
}
|
|
691
|
-
|
|
692
|
-
|
|
756
|
+
if (after.length > 0) {
|
|
757
|
+
return [...before, "", badge, "", ...after].join("\n");
|
|
758
|
+
} else {
|
|
759
|
+
return [...before, "", badge, ""].join("\n");
|
|
760
|
+
}
|
|
693
761
|
}
|
|
694
762
|
return `${badge}
|
|
695
763
|
|
|
@@ -1812,6 +1880,86 @@ Summary: ${formatSummary(results)}`);
|
|
|
1812
1880
|
import pc8 from "picocolors";
|
|
1813
1881
|
import open3 from "open";
|
|
1814
1882
|
import prompts6 from "prompts";
|
|
1883
|
+
var TOKEN_AUTH_PROVIDERS = ["railway"];
|
|
1884
|
+
function getTokenCreationUrl(provider) {
|
|
1885
|
+
switch (provider) {
|
|
1886
|
+
case "railway":
|
|
1887
|
+
return "https://railway.com/account/tokens";
|
|
1888
|
+
default:
|
|
1889
|
+
return "";
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
async function connectWithTokenFlow(accessToken, provider, displayName) {
|
|
1893
|
+
const tokenUrl = getTokenCreationUrl(provider);
|
|
1894
|
+
console.log(pc8.gray(`Create a ${displayName} API Token at:`));
|
|
1895
|
+
console.log(pc8.cyan(`\u2192 ${tokenUrl}
|
|
1896
|
+
`));
|
|
1897
|
+
if (provider === "railway") {
|
|
1898
|
+
console.log(pc8.yellow("Tip: Select the workspace containing your projects."));
|
|
1899
|
+
console.log(pc8.yellow(` Do NOT use "No workspace" - it won't have access to your projects.
|
|
1900
|
+
`));
|
|
1901
|
+
}
|
|
1902
|
+
const { token } = await prompts6({
|
|
1903
|
+
type: "password",
|
|
1904
|
+
name: "token",
|
|
1905
|
+
message: `${displayName} API Token:`
|
|
1906
|
+
});
|
|
1907
|
+
if (!token) {
|
|
1908
|
+
console.log(pc8.gray("Cancelled."));
|
|
1909
|
+
return false;
|
|
1910
|
+
}
|
|
1911
|
+
console.log(pc8.gray("\nValidating token..."));
|
|
1912
|
+
try {
|
|
1913
|
+
const result = await connectWithToken(accessToken, provider, token);
|
|
1914
|
+
if (result.success) {
|
|
1915
|
+
console.log(pc8.green(`
|
|
1916
|
+
\u2713 Connected to ${displayName}!`));
|
|
1917
|
+
console.log(pc8.gray(` Account: ${result.user.username}`));
|
|
1918
|
+
if (result.user.teamName) {
|
|
1919
|
+
console.log(pc8.gray(` Team: ${result.user.teamName}`));
|
|
1920
|
+
}
|
|
1921
|
+
return true;
|
|
1922
|
+
} else {
|
|
1923
|
+
console.log(pc8.red("\n\u2717 Connection failed."));
|
|
1924
|
+
return false;
|
|
1925
|
+
}
|
|
1926
|
+
} catch (error) {
|
|
1927
|
+
const message = error instanceof Error ? error.message : "Token validation failed";
|
|
1928
|
+
console.log(pc8.red(`
|
|
1929
|
+
\u2717 ${message}`));
|
|
1930
|
+
return false;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
async function connectWithOAuthFlow(accessToken, provider, displayName) {
|
|
1934
|
+
const authUrl = getProviderAuthUrl(provider);
|
|
1935
|
+
const startTime = /* @__PURE__ */ new Date();
|
|
1936
|
+
console.log(pc8.gray("Opening browser for authorization..."));
|
|
1937
|
+
console.log(pc8.gray(`If the browser doesn't open, visit: ${authUrl}`));
|
|
1938
|
+
await open3(authUrl).catch(() => {
|
|
1939
|
+
});
|
|
1940
|
+
console.log(pc8.gray("Waiting for authorization..."));
|
|
1941
|
+
const maxAttempts = 60;
|
|
1942
|
+
let attempts = 0;
|
|
1943
|
+
while (attempts < maxAttempts) {
|
|
1944
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
1945
|
+
attempts++;
|
|
1946
|
+
try {
|
|
1947
|
+
const { connections } = await getConnections(accessToken);
|
|
1948
|
+
const newConn = connections.find(
|
|
1949
|
+
(c) => c.provider === provider && new Date(c.createdAt) > startTime
|
|
1950
|
+
);
|
|
1951
|
+
if (newConn) {
|
|
1952
|
+
console.log(pc8.green(`
|
|
1953
|
+
\u2713 Connected to ${displayName}!`));
|
|
1954
|
+
return true;
|
|
1955
|
+
}
|
|
1956
|
+
} catch {
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
console.log(pc8.red("\n\u2717 Authorization timeout."));
|
|
1960
|
+
console.log(pc8.gray("Run `keyway connections` to check if the connection was established."));
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1815
1963
|
async function connectCommand(provider, options = {}) {
|
|
1816
1964
|
try {
|
|
1817
1965
|
const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
|
|
@@ -1845,36 +1993,11 @@ async function connectCommand(provider, options = {}) {
|
|
|
1845
1993
|
console.log(pc8.blue(`
|
|
1846
1994
|
Connecting to ${providerInfo.displayName}...
|
|
1847
1995
|
`));
|
|
1848
|
-
const authUrl = getProviderAuthUrl(provider.toLowerCase());
|
|
1849
|
-
const startTime = /* @__PURE__ */ new Date();
|
|
1850
|
-
console.log(pc8.gray("Opening browser for authorization..."));
|
|
1851
|
-
console.log(pc8.gray(`If the browser doesn't open, visit: ${authUrl}`));
|
|
1852
|
-
await open3(authUrl).catch(() => {
|
|
1853
|
-
});
|
|
1854
|
-
console.log(pc8.gray("Waiting for authorization..."));
|
|
1855
|
-
const maxAttempts = 60;
|
|
1856
|
-
let attempts = 0;
|
|
1857
1996
|
let connected = false;
|
|
1858
|
-
|
|
1859
|
-
await
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
const { connections: connections2 } = await getConnections(accessToken);
|
|
1863
|
-
const newConn = connections2.find(
|
|
1864
|
-
(c) => c.provider === provider.toLowerCase() && new Date(c.createdAt) > startTime
|
|
1865
|
-
);
|
|
1866
|
-
if (newConn) {
|
|
1867
|
-
connected = true;
|
|
1868
|
-
console.log(pc8.green(`
|
|
1869
|
-
\u2713 Connected to ${providerInfo.displayName}!`));
|
|
1870
|
-
break;
|
|
1871
|
-
}
|
|
1872
|
-
} catch {
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
if (!connected) {
|
|
1876
|
-
console.log(pc8.red("\n\u2717 Authorization timeout."));
|
|
1877
|
-
console.log(pc8.gray("Run `keyway connections` to check if the connection was established."));
|
|
1997
|
+
if (TOKEN_AUTH_PROVIDERS.includes(provider.toLowerCase())) {
|
|
1998
|
+
connected = await connectWithTokenFlow(accessToken, provider.toLowerCase(), providerInfo.displayName);
|
|
1999
|
+
} else {
|
|
2000
|
+
connected = await connectWithOAuthFlow(accessToken, provider.toLowerCase(), providerInfo.displayName);
|
|
1878
2001
|
}
|
|
1879
2002
|
trackEvent(AnalyticsEvents.CLI_CONNECT, {
|
|
1880
2003
|
provider: provider.toLowerCase(),
|
|
@@ -1898,7 +2021,7 @@ async function connectionsCommand(options = {}) {
|
|
|
1898
2021
|
if (connections.length === 0) {
|
|
1899
2022
|
console.log(pc8.gray("No provider connections found."));
|
|
1900
2023
|
console.log(pc8.gray("\nConnect to a provider with: keyway connect <provider>"));
|
|
1901
|
-
console.log(pc8.gray("Available providers: vercel"));
|
|
2024
|
+
console.log(pc8.gray("Available providers: vercel, railway"));
|
|
1902
2025
|
return;
|
|
1903
2026
|
}
|
|
1904
2027
|
console.log(pc8.blue("\n\u{1F4E1} Provider Connections\n"));
|
|
@@ -1968,6 +2091,25 @@ function mapToVercelEnvironment(keywayEnv) {
|
|
|
1968
2091
|
};
|
|
1969
2092
|
return mapping[keywayEnv.toLowerCase()] || "production";
|
|
1970
2093
|
}
|
|
2094
|
+
function mapToRailwayEnvironment(keywayEnv) {
|
|
2095
|
+
const mapping = {
|
|
2096
|
+
production: "production",
|
|
2097
|
+
staging: "staging",
|
|
2098
|
+
dev: "development",
|
|
2099
|
+
development: "development"
|
|
2100
|
+
};
|
|
2101
|
+
return mapping[keywayEnv.toLowerCase()] || "production";
|
|
2102
|
+
}
|
|
2103
|
+
function mapToProviderEnvironment(provider, keywayEnv) {
|
|
2104
|
+
switch (provider.toLowerCase()) {
|
|
2105
|
+
case "vercel":
|
|
2106
|
+
return mapToVercelEnvironment(keywayEnv);
|
|
2107
|
+
case "railway":
|
|
2108
|
+
return mapToRailwayEnvironment(keywayEnv);
|
|
2109
|
+
default:
|
|
2110
|
+
return keywayEnv;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
1971
2113
|
function findMatchingProject(projects, repoFullName) {
|
|
1972
2114
|
const repoFullNameLower = repoFullName.toLowerCase();
|
|
1973
2115
|
const repoName = repoFullName.split("/")[1]?.toLowerCase();
|
|
@@ -2045,12 +2187,32 @@ async function syncCommand(provider, options = {}) {
|
|
|
2045
2187
|
process.exit(1);
|
|
2046
2188
|
}
|
|
2047
2189
|
console.log(pc9.gray(`Repository: ${repoFullName}`));
|
|
2048
|
-
|
|
2049
|
-
|
|
2190
|
+
let { connections } = await getConnections(accessToken);
|
|
2191
|
+
let connection = connections.find((c) => c.provider === provider.toLowerCase());
|
|
2050
2192
|
if (!connection) {
|
|
2051
|
-
|
|
2052
|
-
console.log(pc9.
|
|
2053
|
-
|
|
2193
|
+
const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
2194
|
+
console.log(pc9.yellow(`
|
|
2195
|
+
Not connected to ${providerDisplayName}.`));
|
|
2196
|
+
const { shouldConnect } = await prompts7({
|
|
2197
|
+
type: "confirm",
|
|
2198
|
+
name: "shouldConnect",
|
|
2199
|
+
message: `Connect to ${providerDisplayName} now?`,
|
|
2200
|
+
initial: true
|
|
2201
|
+
});
|
|
2202
|
+
if (!shouldConnect) {
|
|
2203
|
+
console.log(pc9.gray("Cancelled."));
|
|
2204
|
+
process.exit(0);
|
|
2205
|
+
}
|
|
2206
|
+
await connectCommand(provider, { loginPrompt: false });
|
|
2207
|
+
const refreshed = await getConnections(accessToken);
|
|
2208
|
+
connections = refreshed.connections;
|
|
2209
|
+
connection = connections.find((c) => c.provider === provider.toLowerCase());
|
|
2210
|
+
if (!connection) {
|
|
2211
|
+
console.error(pc9.red(`
|
|
2212
|
+
Connection to ${providerDisplayName} failed.`));
|
|
2213
|
+
process.exit(1);
|
|
2214
|
+
}
|
|
2215
|
+
console.log("");
|
|
2054
2216
|
}
|
|
2055
2217
|
const { projects } = await getConnectionProjects(accessToken, connection.id);
|
|
2056
2218
|
if (projects.length === 0) {
|
|
@@ -2178,7 +2340,7 @@ async function syncCommand(provider, options = {}) {
|
|
|
2178
2340
|
}
|
|
2179
2341
|
keywayEnv = selectedEnv;
|
|
2180
2342
|
if (!options.providerEnv) {
|
|
2181
|
-
providerEnv =
|
|
2343
|
+
providerEnv = mapToProviderEnvironment(provider, keywayEnv);
|
|
2182
2344
|
}
|
|
2183
2345
|
}
|
|
2184
2346
|
if (needsDirectionPrompt) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keywaysh/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "One link to all your secrets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "pnpm exec tsx src/cli.ts",
|
|
15
|
+
"dev:local": "NODE_TLS_REJECT_UNAUTHORIZED=0 KEYWAY_API_URL=https://localhost/api pnpm exec tsx src/cli.ts",
|
|
16
|
+
"build": "pnpm exec tsup",
|
|
17
|
+
"build:watch": "pnpm exec tsup --watch",
|
|
18
|
+
"prepublishOnly": "pnpm run build",
|
|
19
|
+
"test": "pnpm exec vitest run",
|
|
20
|
+
"test:watch": "pnpm exec vitest",
|
|
21
|
+
"release": "npm version patch && git push && git push --tags",
|
|
22
|
+
"release:minor": "npm version minor && git push && git push --tags",
|
|
23
|
+
"release:major": "npm version major && git push && git push --tags"
|
|
24
|
+
},
|
|
13
25
|
"keywords": [
|
|
14
26
|
"secrets",
|
|
15
27
|
"env",
|
|
@@ -27,10 +39,12 @@
|
|
|
27
39
|
"bugs": {
|
|
28
40
|
"url": "https://github.com/keywaysh/cli/issues"
|
|
29
41
|
},
|
|
42
|
+
"packageManager": "pnpm@10.6.1",
|
|
30
43
|
"engines": {
|
|
31
44
|
"node": ">=18.0.0"
|
|
32
45
|
},
|
|
33
46
|
"dependencies": {
|
|
47
|
+
"balanced-match": "^3.0.1",
|
|
34
48
|
"commander": "^14.0.0",
|
|
35
49
|
"conf": "^15.0.2",
|
|
36
50
|
"open": "^11.0.0",
|
|
@@ -39,22 +53,12 @@
|
|
|
39
53
|
"prompts": "^2.4.2"
|
|
40
54
|
},
|
|
41
55
|
"devDependencies": {
|
|
56
|
+
"@types/balanced-match": "^3.0.2",
|
|
42
57
|
"@types/node": "^24.2.0",
|
|
43
58
|
"@types/prompts": "^2.4.9",
|
|
44
59
|
"tsup": "^8.5.0",
|
|
45
60
|
"tsx": "^4.20.3",
|
|
46
61
|
"typescript": "^5.9.2",
|
|
47
62
|
"vitest": "^3.2.4"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"dev": "pnpm exec tsx src/cli.ts",
|
|
51
|
-
"dev:local": "NODE_TLS_REJECT_UNAUTHORIZED=0 KEYWAY_API_URL=https://localhost/api pnpm exec tsx src/cli.ts",
|
|
52
|
-
"build": "pnpm exec tsup",
|
|
53
|
-
"build:watch": "pnpm exec tsup --watch",
|
|
54
|
-
"test": "pnpm exec vitest run",
|
|
55
|
-
"test:watch": "pnpm exec vitest",
|
|
56
|
-
"release": "npm version patch && git push && git push --tags",
|
|
57
|
-
"release:minor": "npm version minor && git push && git push --tags",
|
|
58
|
-
"release:major": "npm version major && git push && git push --tags"
|
|
59
63
|
}
|
|
60
|
-
}
|
|
64
|
+
}
|