codeant-cli 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/getApiKey.js +30 -0
- package/src/commands/setApiKey.js +37 -0
- package/src/index.js +16 -0
- package/src/utils/installPushProtectionHook.js +23 -7
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Text, Box, useApp } from 'ink';
|
|
3
|
+
import { getConfigValue } from '../utils/config.js';
|
|
4
|
+
|
|
5
|
+
export default function GetApiKey() {
|
|
6
|
+
const { exit } = useApp();
|
|
7
|
+
|
|
8
|
+
const apiKey = getConfigValue('apiKeyV2');
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
exit();
|
|
12
|
+
}, []);
|
|
13
|
+
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
return React.createElement(
|
|
16
|
+
Box,
|
|
17
|
+
{ flexDirection: 'column', padding: 1 },
|
|
18
|
+
React.createElement(Text, { color: 'yellow' }, 'No CodeAnt API key configured.'),
|
|
19
|
+
React.createElement(Text, { color: 'gray' }, 'Set one with: codeant set-codeant-api-key <key>')
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const masked = apiKey.slice(0, 4) + '…' + apiKey.slice(-4);
|
|
24
|
+
|
|
25
|
+
return React.createElement(
|
|
26
|
+
Box,
|
|
27
|
+
{ flexDirection: 'column', padding: 1 },
|
|
28
|
+
React.createElement(Text, { bold: true }, 'CodeAnt API Key: ', masked)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Text, Box, useApp } from 'ink';
|
|
3
|
+
import { setConfigValue, CONFIG_FILE } from '../utils/config.js';
|
|
4
|
+
|
|
5
|
+
export default function SetApiKey({ apiKey }) {
|
|
6
|
+
const { exit } = useApp();
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (!apiKey) {
|
|
10
|
+
exit(new Error('API key is required'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
setConfigValue('apiKeyV2', apiKey);
|
|
16
|
+
exit();
|
|
17
|
+
} catch (err) {
|
|
18
|
+
exit(err);
|
|
19
|
+
}
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
if (!apiKey) {
|
|
23
|
+
return React.createElement(
|
|
24
|
+
Box,
|
|
25
|
+
{ flexDirection: 'column', padding: 1 },
|
|
26
|
+
React.createElement(Text, { color: 'red' }, '✗ Error: API key is required'),
|
|
27
|
+
React.createElement(Text, { color: 'gray' }, 'Usage: codeant set-codeant-api-key <key>')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return React.createElement(
|
|
32
|
+
Box,
|
|
33
|
+
{ flexDirection: 'column', padding: 1 },
|
|
34
|
+
React.createElement(Text, { color: 'green' }, '✓ CodeAnt API key saved.'),
|
|
35
|
+
React.createElement(Text, { color: 'gray' }, 'Saved to: ', CONFIG_FILE)
|
|
36
|
+
);
|
|
37
|
+
}
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import { createRequire } from 'module';
|
|
|
7
7
|
import Secrets from './commands/secrets.js';
|
|
8
8
|
import SetBaseUrl from './commands/setBaseUrl.js';
|
|
9
9
|
import GetBaseUrl from './commands/getBaseUrl.js';
|
|
10
|
+
import SetApiKey from './commands/setApiKey.js';
|
|
11
|
+
import GetApiKey from './commands/getApiKey.js';
|
|
10
12
|
import Login from './commands/login.js';
|
|
11
13
|
import Logout from './commands/logout.js';
|
|
12
14
|
import Review from './commands/review.js';
|
|
@@ -183,6 +185,20 @@ program
|
|
|
183
185
|
render(React.createElement(GetBaseUrl));
|
|
184
186
|
});
|
|
185
187
|
|
|
188
|
+
program
|
|
189
|
+
.command('set-codeant-api-key <key>')
|
|
190
|
+
.description('Set the CodeAnt API key')
|
|
191
|
+
.action((key) => {
|
|
192
|
+
render(React.createElement(SetApiKey, { apiKey: key }));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
program
|
|
196
|
+
.command('get-codeant-api-key')
|
|
197
|
+
.description('Show the current CodeAnt API key')
|
|
198
|
+
.action(() => {
|
|
199
|
+
render(React.createElement(GetApiKey));
|
|
200
|
+
});
|
|
201
|
+
|
|
186
202
|
program
|
|
187
203
|
.command('login')
|
|
188
204
|
.description('Login to CodeAnt')
|
|
@@ -8,16 +8,31 @@ const HOOK_MARKER_END = '# end-codeant-push-protection';
|
|
|
8
8
|
/**
|
|
9
9
|
* Build the full pre-push hook script (with shebang).
|
|
10
10
|
*/
|
|
11
|
-
function buildHookScript() {
|
|
11
|
+
function buildHookScript(cliPath) {
|
|
12
12
|
return `#!/bin/sh
|
|
13
|
-
${buildHookBlock()}
|
|
13
|
+
${buildHookBlock(cliPath)}
|
|
14
14
|
`;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Build just the CodeAnt block (no shebang), used when appending to an existing hook.
|
|
19
|
+
* If cliPath is provided, uses the absolute path to the extension-bundled CLI via node.
|
|
20
|
+
* Otherwise falls back to the global `codeant` command.
|
|
19
21
|
*/
|
|
20
|
-
function buildHookBlock() {
|
|
22
|
+
function buildHookBlock(cliPath) {
|
|
23
|
+
if (cliPath) {
|
|
24
|
+
return `${HOOK_MARKER}
|
|
25
|
+
# Auto-installed by CodeAnt AI — blocks pushes containing secrets.
|
|
26
|
+
# To disable: delete this hook or run "codeant push-protection disable"
|
|
27
|
+
# Uses the CLI bundled with the VS Code extension.
|
|
28
|
+
if [ -f "${cliPath}" ]; then
|
|
29
|
+
node "${cliPath}" secrets --committed --hook
|
|
30
|
+
else
|
|
31
|
+
command -v codeant >/dev/null 2>&1 || exit 0
|
|
32
|
+
codeant secrets --committed --hook
|
|
33
|
+
fi
|
|
34
|
+
${HOOK_MARKER_END}`;
|
|
35
|
+
}
|
|
21
36
|
return `${HOOK_MARKER}
|
|
22
37
|
# Auto-installed by CodeAnt AI — blocks pushes containing secrets.
|
|
23
38
|
# To disable: delete this hook or run "codeant push-protection disable"
|
|
@@ -81,9 +96,10 @@ function getHooksDir(gitRoot) {
|
|
|
81
96
|
* Install a pre-push hook that runs secret scanning before push.
|
|
82
97
|
*
|
|
83
98
|
* @param {string} workspacePath - Path to the git repository
|
|
99
|
+
* @param {string} [cliPath] - Absolute path to the codeant CLI entry point (from extension node_modules)
|
|
84
100
|
* @returns {{ installed: boolean, hookPath: string|null, message: string }}
|
|
85
101
|
*/
|
|
86
|
-
export function installPushProtectionHook(workspacePath) {
|
|
102
|
+
export function installPushProtectionHook(workspacePath, cliPath) {
|
|
87
103
|
const gitRoot = findGitRoot(workspacePath);
|
|
88
104
|
if (!gitRoot) {
|
|
89
105
|
return { installed: false, hookPath: null, message: 'Not a git repository' };
|
|
@@ -100,7 +116,7 @@ export function installPushProtectionHook(workspacePath) {
|
|
|
100
116
|
const existing = readFileSync(hookPath, 'utf-8');
|
|
101
117
|
if (existing.includes(HOOK_MARKER)) {
|
|
102
118
|
// Replace only our block, preserve everything else
|
|
103
|
-
const updated = replaceCodeAntBlock(existing, buildHookBlock());
|
|
119
|
+
const updated = replaceCodeAntBlock(existing, buildHookBlock(cliPath));
|
|
104
120
|
writeFileSync(hookPath, updated, 'utf-8');
|
|
105
121
|
chmodSync(hookPath, 0o755);
|
|
106
122
|
return { installed: true, hookPath, message: 'Hook updated' };
|
|
@@ -111,13 +127,13 @@ export function installPushProtectionHook(workspacePath) {
|
|
|
111
127
|
if (!isShellHook) {
|
|
112
128
|
return { installed: false, hookPath, message: 'Existing pre-push hook is non-shell; cannot append CodeAnt block safely' };
|
|
113
129
|
}
|
|
114
|
-
const appended = existing.trimEnd() + '\n\n' + buildHookBlock() + '\n';
|
|
130
|
+
const appended = existing.trimEnd() + '\n\n' + buildHookBlock(cliPath) + '\n';
|
|
115
131
|
writeFileSync(hookPath, appended, 'utf-8');
|
|
116
132
|
chmodSync(hookPath, 0o755);
|
|
117
133
|
return { installed: true, hookPath, message: 'Hook appended to existing pre-push' };
|
|
118
134
|
}
|
|
119
135
|
|
|
120
|
-
writeFileSync(hookPath, buildHookScript(), 'utf-8');
|
|
136
|
+
writeFileSync(hookPath, buildHookScript(cliPath), 'utf-8');
|
|
121
137
|
chmodSync(hookPath, 0o755);
|
|
122
138
|
return { installed: true, hookPath, message: 'Hook installed' };
|
|
123
139
|
}
|