codeant-cli 0.4.11 → 0.4.12
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/settings/analysis-feature-flags.js +32 -0
- package/src/commands/settings/audit-logs.js +9 -0
- package/src/commands/settings/branches.js +33 -0
- package/src/commands/settings/cve-reporting.js +40 -0
- package/src/commands/settings/feature-flags.js +34 -0
- package/src/commands/settings/index.js +413 -0
- package/src/commands/settings/pr-instructions.js +44 -0
- package/src/commands/settings/recurring-scans.js +40 -0
- package/src/commands/settings/repos.js +2 -0
- package/src/commands/settings/sprint-reports.js +40 -0
- package/src/commands/settings/team-management.js +55 -0
- package/src/index.js +4 -0
- package/src/scm/gitlab/client.js +2 -2
- package/src/scm/index.js +1 -1
- package/src/utils/fetchApi.js +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runAnalysisFeatureFlagsGet({ repo } = {}) {
|
|
4
|
+
if (!repo) {
|
|
5
|
+
const err = new Error('--repo is required');
|
|
6
|
+
err.exitCode = 1;
|
|
7
|
+
throw err;
|
|
8
|
+
}
|
|
9
|
+
return fetchApi('/extension/analysis/config/feature-flags/get', 'POST', { repo });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function runAnalysisFeatureFlagsUpdate({ repo, flags } = {}) {
|
|
13
|
+
if (!repo) {
|
|
14
|
+
const err = new Error('--repo is required');
|
|
15
|
+
err.exitCode = 1;
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
if (!flags) {
|
|
19
|
+
const err = new Error('--flags is required');
|
|
20
|
+
err.exitCode = 1;
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
let feature_flags;
|
|
24
|
+
try {
|
|
25
|
+
feature_flags = JSON.parse(flags);
|
|
26
|
+
} catch {
|
|
27
|
+
const err = new Error('--flags must be valid JSON (e.g. \'{"sast_analysis":"enabled"}\')');
|
|
28
|
+
err.exitCode = 1;
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
return fetchApi('/extension/analysis/config/feature-flags/update', 'POST', { repo, feature_flags });
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runAuditLogsSettings({ days, page, limit } = {}) {
|
|
4
|
+
return fetchApi('/extension/account/audit/settings', 'POST', {
|
|
5
|
+
days: days !== undefined ? Number(days) : 30,
|
|
6
|
+
page: page !== undefined ? Number(page) : 1,
|
|
7
|
+
limit: limit !== undefined ? Number(limit) : 50,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runBranchesAll({ repo } = {}) {
|
|
4
|
+
if (!repo) {
|
|
5
|
+
const err = new Error('--repo is required');
|
|
6
|
+
err.exitCode = 1;
|
|
7
|
+
throw err;
|
|
8
|
+
}
|
|
9
|
+
return fetchApi('/extension/branches/all', 'POST', { repo });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function runBranchesDefault({ repo } = {}) {
|
|
13
|
+
if (!repo) {
|
|
14
|
+
const err = new Error('--repo is required');
|
|
15
|
+
err.exitCode = 1;
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
return fetchApi('/extension/branches/default', 'POST', { repo });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function runBranchesUpdateDefault({ repo, branch } = {}) {
|
|
22
|
+
if (!repo) {
|
|
23
|
+
const err = new Error('--repo is required');
|
|
24
|
+
err.exitCode = 1;
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
if (!branch) {
|
|
28
|
+
const err = new Error('--branch is required');
|
|
29
|
+
err.exitCode = 1;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
return fetchApi('/extension/branches/update-default', 'POST', { repo, branch });
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runCveReportingList({ repo, status, reportId } = {}) {
|
|
4
|
+
return fetchApi('/extension/analysis/cve-reporting/list', 'POST', { repo, status, report_id: reportId });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function runCveReportingCreate({ name, repo, scheduleConfig, reportConfig, description, notificationConfig, createdBy } = {}) {
|
|
8
|
+
if (!name) {
|
|
9
|
+
const err = new Error('--name is required');
|
|
10
|
+
err.exitCode = 1;
|
|
11
|
+
throw err;
|
|
12
|
+
}
|
|
13
|
+
return fetchApi('/extension/analysis/cve-reporting/create', 'POST', {
|
|
14
|
+
name,
|
|
15
|
+
repo,
|
|
16
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
17
|
+
report_config: reportConfig ? JSON.parse(reportConfig) : undefined,
|
|
18
|
+
description,
|
|
19
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
20
|
+
created_by: createdBy,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runCveReportingUpdate({ reportId, repo, status, name, description, scheduleConfig, reportConfig, notificationConfig } = {}) {
|
|
25
|
+
if (!reportId) {
|
|
26
|
+
const err = new Error('--report-id is required');
|
|
27
|
+
err.exitCode = 1;
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
return fetchApi('/extension/analysis/cve-reporting/update', 'POST', {
|
|
31
|
+
report_id: reportId,
|
|
32
|
+
repo,
|
|
33
|
+
status,
|
|
34
|
+
name,
|
|
35
|
+
description,
|
|
36
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
37
|
+
report_config: reportConfig ? JSON.parse(reportConfig) : undefined,
|
|
38
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runFeatureFlagsGet({ repo, v2 = false } = {}) {
|
|
4
|
+
if (!repo) {
|
|
5
|
+
const err = new Error('--repo is required');
|
|
6
|
+
err.exitCode = 1;
|
|
7
|
+
throw err;
|
|
8
|
+
}
|
|
9
|
+
const version = v2 ? 'v2' : 'v1';
|
|
10
|
+
return fetchApi(`/extension/config/feature-flags/${version}/get`, 'POST', { repo });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function runFeatureFlagsUpdate({ repo, flags, v2 = false } = {}) {
|
|
14
|
+
if (!repo) {
|
|
15
|
+
const err = new Error('--repo is required');
|
|
16
|
+
err.exitCode = 1;
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
if (!flags) {
|
|
20
|
+
const err = new Error('--flags is required');
|
|
21
|
+
err.exitCode = 1;
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
let feature_flags;
|
|
25
|
+
try {
|
|
26
|
+
feature_flags = JSON.parse(flags);
|
|
27
|
+
} catch {
|
|
28
|
+
const err = new Error('--flags must be valid JSON (e.g. \'{"pr_review":"enable"}\')');
|
|
29
|
+
err.exitCode = 1;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
const version = v2 ? 'v2' : 'v1';
|
|
33
|
+
return fetchApi(`/extension/config/feature-flags/${version}/update`, 'POST', { repo, feature_flags });
|
|
34
|
+
}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { runAuditLogsSettings } from './audit-logs.js';
|
|
2
|
+
import { runRepos } from './repos.js';
|
|
3
|
+
import { runBranchesAll, runBranchesDefault, runBranchesUpdateDefault } from './branches.js';
|
|
4
|
+
import { runFeatureFlagsGet, runFeatureFlagsUpdate } from './feature-flags.js';
|
|
5
|
+
import { runAnalysisFeatureFlagsGet, runAnalysisFeatureFlagsUpdate } from './analysis-feature-flags.js';
|
|
6
|
+
import { runPrInstructionsGet, runPrInstructionsSave, runPrInstructionsEdit, runPrInstructionsDelete } from './pr-instructions.js';
|
|
7
|
+
import { runRecurringScanslist, runRecurringScansCreate, runRecurringScansUpdate } from './recurring-scans.js';
|
|
8
|
+
import { runSprintReportsList, runSprintReportsCreate, runSprintReportsUpdate } from './sprint-reports.js';
|
|
9
|
+
import { runCveReportingList, runCveReportingCreate, runCveReportingUpdate } from './cve-reporting.js';
|
|
10
|
+
import {
|
|
11
|
+
runTeamSubscriptionsList,
|
|
12
|
+
runTeamUsersList,
|
|
13
|
+
runTeamUserGet,
|
|
14
|
+
runTeamUserCreate,
|
|
15
|
+
runTeamUserDelete,
|
|
16
|
+
runTeamUserUpdate,
|
|
17
|
+
runTeamRbacGet,
|
|
18
|
+
runTeamRbacSave,
|
|
19
|
+
runTeamPlanEnd,
|
|
20
|
+
} from './team-management.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register all `codeant settings <verb>` subcommands.
|
|
24
|
+
*
|
|
25
|
+
* Each subcommand implementation lives in its own file alongside this index
|
|
26
|
+
* and exports a single async function named run<Command>(opts).
|
|
27
|
+
*
|
|
28
|
+
* To add a new subcommand:
|
|
29
|
+
* 1. Create src/commands/settings/<name>.js exporting run<Name>(opts).
|
|
30
|
+
* 2. Import it here.
|
|
31
|
+
* 3. Add a settings.command(...).action(...) block below following the
|
|
32
|
+
* existing pattern.
|
|
33
|
+
*
|
|
34
|
+
* @param {import('commander').Command} program
|
|
35
|
+
* @param {{ runCmd: Function }} helpers
|
|
36
|
+
*/
|
|
37
|
+
export default function registerSettingsCommands(program, { runCmd }) {
|
|
38
|
+
const settings = program
|
|
39
|
+
.command('settings')
|
|
40
|
+
.description('Manage CodeAnt AI settings');
|
|
41
|
+
|
|
42
|
+
// ── repos ──────────────────────────────────────────────────────────────────
|
|
43
|
+
settings
|
|
44
|
+
.command('repos')
|
|
45
|
+
.description('List repositories')
|
|
46
|
+
.option('--org <org>', 'Organization name (auto-picked when only one is authenticated)')
|
|
47
|
+
.action((opts) => runCmd(() => runRepos({ org: opts.org })));
|
|
48
|
+
|
|
49
|
+
// ── audit-logs settings ────────────────────────────────────────────────────
|
|
50
|
+
settings
|
|
51
|
+
.command('audit-logs')
|
|
52
|
+
.description('List settings change audit logs for the org')
|
|
53
|
+
.option('--days <n>', 'Number of days to look back', '30')
|
|
54
|
+
.option('--page <n>', 'Page number', '1')
|
|
55
|
+
.option('--limit <n>', 'Results per page (max 200)', '50')
|
|
56
|
+
.action((opts) => runCmd(() => runAuditLogsSettings({ days: opts.days, page: opts.page, limit: opts.limit })));
|
|
57
|
+
|
|
58
|
+
// ── branches all ───────────────────────────────────────────────────────────
|
|
59
|
+
settings
|
|
60
|
+
.command('branches-all')
|
|
61
|
+
.description('List all branches for a repository')
|
|
62
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
63
|
+
.action((opts) => runCmd(() => runBranchesAll({ repo: opts.repo })));
|
|
64
|
+
|
|
65
|
+
// ── branches default ───────────────────────────────────────────────────────
|
|
66
|
+
settings
|
|
67
|
+
.command('branches-default')
|
|
68
|
+
.description('Get the default branch for a repository')
|
|
69
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
70
|
+
.action((opts) => runCmd(() => runBranchesDefault({ repo: opts.repo })));
|
|
71
|
+
|
|
72
|
+
// ── branches update-default ────────────────────────────────────────────────
|
|
73
|
+
settings
|
|
74
|
+
.command('branches-update-default')
|
|
75
|
+
.description('Set the default branch for a repository')
|
|
76
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
77
|
+
.requiredOption('--branch <name>', 'Branch name to set as default')
|
|
78
|
+
.action((opts) => runCmd(() => runBranchesUpdateDefault({ repo: opts.repo, branch: opts.branch })));
|
|
79
|
+
|
|
80
|
+
// ── feature-flags get ──────────────────────────────────────────────────────
|
|
81
|
+
settings
|
|
82
|
+
.command('feature-flags-get')
|
|
83
|
+
.description('Get feature flags for a repository')
|
|
84
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
85
|
+
.option('--v2', 'Use v2 feature flags', false)
|
|
86
|
+
.action((opts) => runCmd(() => runFeatureFlagsGet({ repo: opts.repo, v2: opts.v2 })));
|
|
87
|
+
|
|
88
|
+
// ── feature-flags update ───────────────────────────────────────────────────
|
|
89
|
+
settings
|
|
90
|
+
.command('feature-flags-update')
|
|
91
|
+
.description('Update feature flags for a repository')
|
|
92
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
93
|
+
.requiredOption('--flags <json>', 'Feature flags as a JSON string (e.g. \'{"pr_review":"enable"}\')')
|
|
94
|
+
.option('--v2', 'Use v2 feature flags', false)
|
|
95
|
+
.action((opts) => runCmd(() => runFeatureFlagsUpdate({ repo: opts.repo, flags: opts.flags, v2: opts.v2 })));
|
|
96
|
+
|
|
97
|
+
// ── analysis feature-flags get ─────────────────────────────────────────────
|
|
98
|
+
settings
|
|
99
|
+
.command('analysis-feature-flags-get')
|
|
100
|
+
.description('Get analysis feature flags for a repository')
|
|
101
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
102
|
+
.action((opts) => runCmd(() => runAnalysisFeatureFlagsGet({ repo: opts.repo })));
|
|
103
|
+
|
|
104
|
+
// ── analysis feature-flags update ─────────────────────────────────────────
|
|
105
|
+
settings
|
|
106
|
+
.command('analysis-feature-flags-update')
|
|
107
|
+
.description('Update analysis feature flags for a repository')
|
|
108
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
109
|
+
.requiredOption('--flags <json>', 'Feature flags as a JSON string (e.g. \'{"sast_analysis":"enabled"}\')')
|
|
110
|
+
.action((opts) => runCmd(() => runAnalysisFeatureFlagsUpdate({ repo: opts.repo, flags: opts.flags })));
|
|
111
|
+
|
|
112
|
+
// ── pr-instructions get ────────────────────────────────────────────────────
|
|
113
|
+
settings
|
|
114
|
+
.command('pr-instructions-get')
|
|
115
|
+
.description('Get PR review instructions')
|
|
116
|
+
.requiredOption('--type <type>', 'Instruction type (e.g. custom)')
|
|
117
|
+
.action((opts) => runCmd(() => runPrInstructionsGet({ instructionsType: opts.type })));
|
|
118
|
+
|
|
119
|
+
// ── pr-instructions save ───────────────────────────────────────────────────
|
|
120
|
+
settings
|
|
121
|
+
.command('pr-instructions-save')
|
|
122
|
+
.description('Save a new PR review instruction')
|
|
123
|
+
.option('--type <type>', 'Instruction type (e.g. custom)')
|
|
124
|
+
.option('--file-pattern <pattern>', 'File pattern (e.g. *.py)')
|
|
125
|
+
.option('--description <text>', 'Instruction description')
|
|
126
|
+
.option('--description-file <path>', 'Path to a file containing the description')
|
|
127
|
+
.option('--instruction-id <id>', 'Instruction ID (for upsert)')
|
|
128
|
+
.action((opts) => runCmd(() => runPrInstructionsSave({
|
|
129
|
+
instructionType: opts.type,
|
|
130
|
+
filePattern: opts.filePattern,
|
|
131
|
+
description: opts.description,
|
|
132
|
+
descriptionFile: opts.descriptionFile,
|
|
133
|
+
instructionId: opts.instructionId,
|
|
134
|
+
})));
|
|
135
|
+
|
|
136
|
+
// ── pr-instructions edit ───────────────────────────────────────────────────
|
|
137
|
+
settings
|
|
138
|
+
.command('pr-instructions-edit')
|
|
139
|
+
.description('Edit an existing PR review instruction')
|
|
140
|
+
.requiredOption('--instruction-id <id>', 'Instruction ID to edit')
|
|
141
|
+
.option('--type <type>', 'Instruction type (e.g. custom)')
|
|
142
|
+
.option('--description <text>', 'Updated description')
|
|
143
|
+
.option('--description-file <path>', 'Path to a file containing the description')
|
|
144
|
+
.option('--file-pattern <pattern>', 'Updated file pattern')
|
|
145
|
+
.action((opts) => runCmd(() => runPrInstructionsEdit({
|
|
146
|
+
instructionType: opts.type,
|
|
147
|
+
instructionId: opts.instructionId,
|
|
148
|
+
description: opts.description,
|
|
149
|
+
descriptionFile: opts.descriptionFile,
|
|
150
|
+
filePattern: opts.filePattern,
|
|
151
|
+
})));
|
|
152
|
+
|
|
153
|
+
// ── pr-instructions delete ─────────────────────────────────────────────────
|
|
154
|
+
settings
|
|
155
|
+
.command('pr-instructions-delete')
|
|
156
|
+
.description('Delete a PR review instruction')
|
|
157
|
+
.requiredOption('--instruction-id <id>', 'Instruction ID to delete')
|
|
158
|
+
.option('--type <type>', 'Instruction type (e.g. custom)')
|
|
159
|
+
.action((opts) => runCmd(() => runPrInstructionsDelete({
|
|
160
|
+
instructionType: opts.type,
|
|
161
|
+
instructionId: opts.instructionId,
|
|
162
|
+
})));
|
|
163
|
+
|
|
164
|
+
// ── recurring-scans list ───────────────────────────────────────────────────
|
|
165
|
+
settings
|
|
166
|
+
.command('recurring-scans-list')
|
|
167
|
+
.description('List recurring scan schedules')
|
|
168
|
+
.option('--repo <repo>', 'Filter by repository (owner/repo)')
|
|
169
|
+
.option('--status <status>', 'Filter by status (e.g. ACTIVE)')
|
|
170
|
+
.option('--schedule-id <id>', 'Filter by schedule ID')
|
|
171
|
+
.action((opts) => runCmd(() => runRecurringScanslist({ repo: opts.repo, status: opts.status, scheduleId: opts.scheduleId })));
|
|
172
|
+
|
|
173
|
+
// ── recurring-scans create ─────────────────────────────────────────────────
|
|
174
|
+
settings
|
|
175
|
+
.command('recurring-scans-create')
|
|
176
|
+
.description('Create a recurring scan schedule')
|
|
177
|
+
.requiredOption('--name <name>', 'Schedule name')
|
|
178
|
+
.option('--repo <repo>', 'Repository (owner/repo)')
|
|
179
|
+
.option('--schedule-config <json>', 'Schedule config as JSON (e.g. \'{"frequency":"weekly"}\')')
|
|
180
|
+
.option('--scan-config <json>', 'Scan config as JSON')
|
|
181
|
+
.option('--description <text>', 'Description')
|
|
182
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
183
|
+
.option('--created-by <user>', 'Creator identifier')
|
|
184
|
+
.action((opts) => runCmd(() => runRecurringScansCreate({
|
|
185
|
+
name: opts.name,
|
|
186
|
+
repo: opts.repo,
|
|
187
|
+
scheduleConfig: opts.scheduleConfig,
|
|
188
|
+
scanConfig: opts.scanConfig,
|
|
189
|
+
description: opts.description,
|
|
190
|
+
notificationConfig: opts.notificationConfig,
|
|
191
|
+
createdBy: opts.createdBy,
|
|
192
|
+
})));
|
|
193
|
+
|
|
194
|
+
// ── recurring-scans update ─────────────────────────────────────────────────
|
|
195
|
+
settings
|
|
196
|
+
.command('recurring-scans-update')
|
|
197
|
+
.description('Update a recurring scan schedule')
|
|
198
|
+
.requiredOption('--schedule-id <id>', 'Schedule ID to update')
|
|
199
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
200
|
+
.option('--status <status>', 'New status (e.g. ACTIVE, INACTIVE)')
|
|
201
|
+
.option('--name <name>', 'New name')
|
|
202
|
+
.option('--description <text>', 'New description')
|
|
203
|
+
.option('--schedule-config <json>', 'Schedule config as JSON')
|
|
204
|
+
.option('--scan-config <json>', 'Scan config as JSON')
|
|
205
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
206
|
+
.action((opts) => runCmd(() => runRecurringScansUpdate({
|
|
207
|
+
scheduleId: opts.scheduleId,
|
|
208
|
+
repo: opts.repo,
|
|
209
|
+
status: opts.status,
|
|
210
|
+
name: opts.name,
|
|
211
|
+
description: opts.description,
|
|
212
|
+
scheduleConfig: opts.scheduleConfig,
|
|
213
|
+
scanConfig: opts.scanConfig,
|
|
214
|
+
notificationConfig: opts.notificationConfig,
|
|
215
|
+
})));
|
|
216
|
+
|
|
217
|
+
// ── sprint-reports list ────────────────────────────────────────────────────
|
|
218
|
+
settings
|
|
219
|
+
.command('sprint-reports-list')
|
|
220
|
+
.description('List sprint report configurations')
|
|
221
|
+
.option('--repo <repo>', 'Filter by repository (owner/repo)')
|
|
222
|
+
.option('--status <status>', 'Filter by status (e.g. ACTIVE)')
|
|
223
|
+
.option('--config-id <id>', 'Filter by config ID')
|
|
224
|
+
.action((opts) => runCmd(() => runSprintReportsList({ repo: opts.repo, status: opts.status, configId: opts.configId })));
|
|
225
|
+
|
|
226
|
+
// ── sprint-reports create ──────────────────────────────────────────────────
|
|
227
|
+
settings
|
|
228
|
+
.command('sprint-reports-create')
|
|
229
|
+
.description('Create a sprint report configuration')
|
|
230
|
+
.requiredOption('--name <name>', 'Report name')
|
|
231
|
+
.option('--repo <repo>', 'Repository (owner/repo)')
|
|
232
|
+
.option('--schedule-config <json>', 'Schedule config as JSON (e.g. \'{"frequency":"weekly"}\')')
|
|
233
|
+
.option('--report-config <json>', 'Report config as JSON')
|
|
234
|
+
.option('--description <text>', 'Description')
|
|
235
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
236
|
+
.option('--created-by <user>', 'Creator identifier')
|
|
237
|
+
.action((opts) => runCmd(() => runSprintReportsCreate({
|
|
238
|
+
name: opts.name,
|
|
239
|
+
repo: opts.repo,
|
|
240
|
+
scheduleConfig: opts.scheduleConfig,
|
|
241
|
+
reportConfig: opts.reportConfig,
|
|
242
|
+
description: opts.description,
|
|
243
|
+
notificationConfig: opts.notificationConfig,
|
|
244
|
+
createdBy: opts.createdBy,
|
|
245
|
+
})));
|
|
246
|
+
|
|
247
|
+
// ── sprint-reports update ──────────────────────────────────────────────────
|
|
248
|
+
settings
|
|
249
|
+
.command('sprint-reports-update')
|
|
250
|
+
.description('Update a sprint report configuration')
|
|
251
|
+
.requiredOption('--config-id <id>', 'Config ID to update')
|
|
252
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
253
|
+
.option('--status <status>', 'New status (e.g. ACTIVE, INACTIVE)')
|
|
254
|
+
.option('--name <name>', 'New name')
|
|
255
|
+
.option('--description <text>', 'New description')
|
|
256
|
+
.option('--schedule-config <json>', 'Schedule config as JSON')
|
|
257
|
+
.option('--report-config <json>', 'Report config as JSON')
|
|
258
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
259
|
+
.action((opts) => runCmd(() => runSprintReportsUpdate({
|
|
260
|
+
configId: opts.configId,
|
|
261
|
+
repo: opts.repo,
|
|
262
|
+
status: opts.status,
|
|
263
|
+
name: opts.name,
|
|
264
|
+
description: opts.description,
|
|
265
|
+
scheduleConfig: opts.scheduleConfig,
|
|
266
|
+
reportConfig: opts.reportConfig,
|
|
267
|
+
notificationConfig: opts.notificationConfig,
|
|
268
|
+
})));
|
|
269
|
+
|
|
270
|
+
// ── cve-reporting list ─────────────────────────────────────────────────────
|
|
271
|
+
settings
|
|
272
|
+
.command('cve-reporting-list')
|
|
273
|
+
.description('List CVE report configurations')
|
|
274
|
+
.option('--repo <repo>', 'Filter by repository (owner/repo)')
|
|
275
|
+
.option('--status <status>', 'Filter by status (e.g. ACTIVE)')
|
|
276
|
+
.option('--report-id <id>', 'Filter by report ID')
|
|
277
|
+
.action((opts) => runCmd(() => runCveReportingList({ repo: opts.repo, status: opts.status, reportId: opts.reportId })));
|
|
278
|
+
|
|
279
|
+
// ── cve-reporting create ───────────────────────────────────────────────────
|
|
280
|
+
settings
|
|
281
|
+
.command('cve-reporting-create')
|
|
282
|
+
.description('Create a CVE report configuration')
|
|
283
|
+
.requiredOption('--name <name>', 'Report name')
|
|
284
|
+
.option('--repo <repo>', 'Repository (owner/repo)')
|
|
285
|
+
.option('--schedule-config <json>', 'Schedule config as JSON (e.g. \'{"frequency":"weekly"}\')')
|
|
286
|
+
.option('--report-config <json>', 'Report config as JSON')
|
|
287
|
+
.option('--description <text>', 'Description')
|
|
288
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
289
|
+
.option('--created-by <user>', 'Creator identifier')
|
|
290
|
+
.action((opts) => runCmd(() => runCveReportingCreate({
|
|
291
|
+
name: opts.name,
|
|
292
|
+
repo: opts.repo,
|
|
293
|
+
scheduleConfig: opts.scheduleConfig,
|
|
294
|
+
reportConfig: opts.reportConfig,
|
|
295
|
+
description: opts.description,
|
|
296
|
+
notificationConfig: opts.notificationConfig,
|
|
297
|
+
createdBy: opts.createdBy,
|
|
298
|
+
})));
|
|
299
|
+
|
|
300
|
+
// ── cve-reporting update ───────────────────────────────────────────────────
|
|
301
|
+
settings
|
|
302
|
+
.command('cve-reporting-update')
|
|
303
|
+
.description('Update a CVE report configuration')
|
|
304
|
+
.requiredOption('--report-id <id>', 'Report ID to update')
|
|
305
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
306
|
+
.option('--status <status>', 'New status (e.g. ACTIVE, INACTIVE)')
|
|
307
|
+
.option('--name <name>', 'New name')
|
|
308
|
+
.option('--description <text>', 'New description')
|
|
309
|
+
.option('--schedule-config <json>', 'Schedule config as JSON')
|
|
310
|
+
.option('--report-config <json>', 'Report config as JSON')
|
|
311
|
+
.option('--notification-config <json>', 'Notification config as JSON')
|
|
312
|
+
.action((opts) => runCmd(() => runCveReportingUpdate({
|
|
313
|
+
reportId: opts.reportId,
|
|
314
|
+
repo: opts.repo,
|
|
315
|
+
status: opts.status,
|
|
316
|
+
name: opts.name,
|
|
317
|
+
description: opts.description,
|
|
318
|
+
scheduleConfig: opts.scheduleConfig,
|
|
319
|
+
reportConfig: opts.reportConfig,
|
|
320
|
+
notificationConfig: opts.notificationConfig,
|
|
321
|
+
})));
|
|
322
|
+
|
|
323
|
+
// ── team-subscriptions-list ────────────────────────────────────────────────
|
|
324
|
+
settings
|
|
325
|
+
.command('team-subscriptions-list')
|
|
326
|
+
.description('List all subscriptions for the account')
|
|
327
|
+
.action(() => runCmd(() => runTeamSubscriptionsList()));
|
|
328
|
+
|
|
329
|
+
// ── team-users-list ────────────────────────────────────────────────────────
|
|
330
|
+
settings
|
|
331
|
+
.command('team-users-list')
|
|
332
|
+
.description('List users for a subscription')
|
|
333
|
+
.requiredOption('--subscription-id <id>', 'Subscription ID')
|
|
334
|
+
.action((opts) => runCmd(() => runTeamUsersList({ subscriptionId: opts.subscriptionId })));
|
|
335
|
+
|
|
336
|
+
// ── team-user-get ──────────────────────────────────────────────────────────
|
|
337
|
+
settings
|
|
338
|
+
.command('team-user-get')
|
|
339
|
+
.description('Get a user by ID')
|
|
340
|
+
.requiredOption('--user-id <id>', 'User ID')
|
|
341
|
+
.action((opts) => runCmd(() => runTeamUserGet({ userId: opts.userId })));
|
|
342
|
+
|
|
343
|
+
// ── team-user-create ───────────────────────────────────────────────────────
|
|
344
|
+
settings
|
|
345
|
+
.command('team-user-create')
|
|
346
|
+
.description('Create a new team user')
|
|
347
|
+
.requiredOption('--user-id <id>', 'User ID')
|
|
348
|
+
.requiredOption('--email <email>', 'User email')
|
|
349
|
+
.requiredOption('--name <name>', 'User display name')
|
|
350
|
+
.requiredOption('--role <role>', 'User role (e.g. developer, admin)')
|
|
351
|
+
.option('--plan-ids <json>', 'Plan IDs as JSON array (e.g. \'["plan_1"]\')', '[]')
|
|
352
|
+
.option('--subscription-ids <json>', 'Subscription IDs as JSON array', '[]')
|
|
353
|
+
.option('--invited', 'Send invitation email', false)
|
|
354
|
+
.action((opts) => runCmd(() => runTeamUserCreate({
|
|
355
|
+
userId: opts.userId,
|
|
356
|
+
email: opts.email,
|
|
357
|
+
name: opts.name,
|
|
358
|
+
role: opts.role,
|
|
359
|
+
planIds: opts.planIds,
|
|
360
|
+
subscriptionIds: opts.subscriptionIds,
|
|
361
|
+
invited: opts.invited,
|
|
362
|
+
})));
|
|
363
|
+
|
|
364
|
+
// ── team-user-delete ───────────────────────────────────────────────────────
|
|
365
|
+
settings
|
|
366
|
+
.command('team-user-delete')
|
|
367
|
+
.description('Delete a team user')
|
|
368
|
+
.requiredOption('--user-id <id>', 'User ID to delete')
|
|
369
|
+
.action((opts) => runCmd(() => runTeamUserDelete({ userId: opts.userId })));
|
|
370
|
+
|
|
371
|
+
// ── team-user-update ───────────────────────────────────────────────────────
|
|
372
|
+
settings
|
|
373
|
+
.command('team-user-update')
|
|
374
|
+
.description('Update a team user')
|
|
375
|
+
.requiredOption('--user-id <id>', 'User ID to update')
|
|
376
|
+
.requiredOption('--email <email>', 'User email')
|
|
377
|
+
.requiredOption('--name <name>', 'User display name')
|
|
378
|
+
.requiredOption('--role <role>', 'User role (e.g. developer, admin)')
|
|
379
|
+
.requiredOption('--status <status>', 'User status (e.g. active, inactive)')
|
|
380
|
+
.option('--plan-ids <json>', 'Plan IDs as JSON array', '[]')
|
|
381
|
+
.option('--subscription-ids <json>', 'Subscription IDs as JSON array', '[]')
|
|
382
|
+
.action((opts) => runCmd(() => runTeamUserUpdate({
|
|
383
|
+
userId: opts.userId,
|
|
384
|
+
email: opts.email,
|
|
385
|
+
name: opts.name,
|
|
386
|
+
role: opts.role,
|
|
387
|
+
status: opts.status,
|
|
388
|
+
planIds: opts.planIds,
|
|
389
|
+
subscriptionIds: opts.subscriptionIds,
|
|
390
|
+
})));
|
|
391
|
+
|
|
392
|
+
// ── team-rbac-get ──────────────────────────────────────────────────────────
|
|
393
|
+
settings
|
|
394
|
+
.command('team-rbac-get')
|
|
395
|
+
.description('Get RBAC settings for a repository')
|
|
396
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
397
|
+
.action((opts) => runCmd(() => runTeamRbacGet({ repo: opts.repo })));
|
|
398
|
+
|
|
399
|
+
// ── team-rbac-save ─────────────────────────────────────────────────────────
|
|
400
|
+
settings
|
|
401
|
+
.command('team-rbac-save')
|
|
402
|
+
.description('Save RBAC settings for a repository')
|
|
403
|
+
.requiredOption('--repo <repo>', 'Repository (owner/repo)')
|
|
404
|
+
.requiredOption('--data <json>', 'RBAC settings as JSON (e.g. \'{"enabled":true}\')')
|
|
405
|
+
.action((opts) => runCmd(() => runTeamRbacSave({ repo: opts.repo, data: opts.data })));
|
|
406
|
+
|
|
407
|
+
// ── team-plan-end ──────────────────────────────────────────────────────────
|
|
408
|
+
settings
|
|
409
|
+
.command('team-plan-end')
|
|
410
|
+
.description('End/expire the active subscription plan')
|
|
411
|
+
.option('--include-users', 'Also remove all users from the plan', false)
|
|
412
|
+
.action((opts) => runCmd(() => runTeamPlanEnd({ includeUsers: opts.includeUsers })));
|
|
413
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runPrInstructionsGet({ instructionsType } = {}) {
|
|
4
|
+
return fetchApi('/extension/config/pullrequest/instructions/get', 'POST', {
|
|
5
|
+
instructions_type: instructionsType,
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function runPrInstructionsSave({ instructionType, filePattern, description, descriptionFile, instructionId } = {}) {
|
|
10
|
+
return fetchApi('/extension/config/pullrequest/instructions/save', 'POST', {
|
|
11
|
+
instruction_type: instructionType,
|
|
12
|
+
file_pattern: filePattern,
|
|
13
|
+
description,
|
|
14
|
+
description_file: descriptionFile,
|
|
15
|
+
instruction_id: instructionId,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function runPrInstructionsEdit({ instructionType, instructionId, description, descriptionFile, filePattern } = {}) {
|
|
20
|
+
if (!instructionId) {
|
|
21
|
+
const err = new Error('--instruction-id is required');
|
|
22
|
+
err.exitCode = 1;
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
return fetchApi('/extension/config/pullrequest/instructions/edit', 'POST', {
|
|
26
|
+
instruction_type: instructionType,
|
|
27
|
+
instruction_id: instructionId,
|
|
28
|
+
description,
|
|
29
|
+
description_file: descriptionFile,
|
|
30
|
+
file_pattern: filePattern,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function runPrInstructionsDelete({ instructionType, instructionId } = {}) {
|
|
35
|
+
if (!instructionId) {
|
|
36
|
+
const err = new Error('--instruction-id is required');
|
|
37
|
+
err.exitCode = 1;
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
return fetchApi('/extension/config/pullrequest/instructions/delete', 'POST', {
|
|
41
|
+
instruction_type: instructionType,
|
|
42
|
+
instruction_id: instructionId,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runRecurringScanslist({ repo, status, scheduleId } = {}) {
|
|
4
|
+
return fetchApi('/extension/analysis/recurring-scans/list', 'POST', { repo, status, schedule_id: scheduleId });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function runRecurringScansCreate({ name, repo, scheduleConfig, scanConfig, description, notificationConfig, createdBy } = {}) {
|
|
8
|
+
if (!name) {
|
|
9
|
+
const err = new Error('--name is required');
|
|
10
|
+
err.exitCode = 1;
|
|
11
|
+
throw err;
|
|
12
|
+
}
|
|
13
|
+
return fetchApi('/extension/analysis/recurring-scans/create', 'POST', {
|
|
14
|
+
name,
|
|
15
|
+
repo,
|
|
16
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
17
|
+
scan_config: scanConfig ? JSON.parse(scanConfig) : undefined,
|
|
18
|
+
description,
|
|
19
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
20
|
+
created_by: createdBy,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runRecurringScansUpdate({ scheduleId, repo, status, name, description, scheduleConfig, scanConfig, notificationConfig } = {}) {
|
|
25
|
+
if (!scheduleId) {
|
|
26
|
+
const err = new Error('--schedule-id is required');
|
|
27
|
+
err.exitCode = 1;
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
return fetchApi('/extension/analysis/recurring-scans/update', 'POST', {
|
|
31
|
+
schedule_id: scheduleId,
|
|
32
|
+
repo,
|
|
33
|
+
status,
|
|
34
|
+
name,
|
|
35
|
+
description,
|
|
36
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
37
|
+
scan_config: scanConfig ? JSON.parse(scanConfig) : undefined,
|
|
38
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runSprintReportsList({ repo, status, configId } = {}) {
|
|
4
|
+
return fetchApi('/extension/metrics/sprint-reports/list', 'POST', { repo, status, config_id: configId });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function runSprintReportsCreate({ name, repo, scheduleConfig, reportConfig, description, notificationConfig, createdBy } = {}) {
|
|
8
|
+
if (!name) {
|
|
9
|
+
const err = new Error('--name is required');
|
|
10
|
+
err.exitCode = 1;
|
|
11
|
+
throw err;
|
|
12
|
+
}
|
|
13
|
+
return fetchApi('/extension/metrics/sprint-reports/create', 'POST', {
|
|
14
|
+
name,
|
|
15
|
+
repo,
|
|
16
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
17
|
+
report_config: reportConfig ? JSON.parse(reportConfig) : undefined,
|
|
18
|
+
description,
|
|
19
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
20
|
+
created_by: createdBy,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runSprintReportsUpdate({ configId, repo, status, name, description, scheduleConfig, reportConfig, notificationConfig } = {}) {
|
|
25
|
+
if (!configId) {
|
|
26
|
+
const err = new Error('--config-id is required');
|
|
27
|
+
err.exitCode = 1;
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
return fetchApi('/extension/metrics/sprint-reports/update', 'POST', {
|
|
31
|
+
config_id: configId,
|
|
32
|
+
repo,
|
|
33
|
+
status,
|
|
34
|
+
name,
|
|
35
|
+
description,
|
|
36
|
+
schedule_config: scheduleConfig ? JSON.parse(scheduleConfig) : undefined,
|
|
37
|
+
report_config: reportConfig ? JSON.parse(reportConfig) : undefined,
|
|
38
|
+
notification_config: notificationConfig ? JSON.parse(notificationConfig) : undefined,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { fetchApi } from '../../utils/fetchApi.js';
|
|
2
|
+
|
|
3
|
+
export async function runTeamSubscriptionsList() {
|
|
4
|
+
return fetchApi('/extension/account/subscriptions/list', 'POST', {});
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function runTeamUsersList({ subscriptionId } = {}) {
|
|
8
|
+
return fetchApi('/extension/account/subscription/users/list', 'POST', { subscription_id: subscriptionId });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function runTeamUserGet({ userId } = {}) {
|
|
12
|
+
return fetchApi('/extension/account/users/user/get', 'POST', { user_id: userId });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function runTeamUserCreate({ userId, email, name, role, planIds, subscriptionIds, invited } = {}) {
|
|
16
|
+
return fetchApi('/extension/account/users/user/create', 'POST', {
|
|
17
|
+
users: [{
|
|
18
|
+
user_id: userId,
|
|
19
|
+
email,
|
|
20
|
+
name,
|
|
21
|
+
role,
|
|
22
|
+
plan_ids: planIds ? JSON.parse(planIds) : [],
|
|
23
|
+
subscription_ids: subscriptionIds ? JSON.parse(subscriptionIds) : [],
|
|
24
|
+
}],
|
|
25
|
+
invited: invited ?? false,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function runTeamUserDelete({ userId } = {}) {
|
|
30
|
+
return fetchApi('/extension/account/users/user/delete', 'POST', { user_id: userId });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runTeamUserUpdate({ userId, email, name, role, status, planIds, subscriptionIds } = {}) {
|
|
34
|
+
return fetchApi('/extension/account/users/user/update', 'POST', {
|
|
35
|
+
user_id: userId,
|
|
36
|
+
email,
|
|
37
|
+
name,
|
|
38
|
+
role,
|
|
39
|
+
status,
|
|
40
|
+
plan_ids: planIds ? JSON.parse(planIds) : [],
|
|
41
|
+
subscription_ids: subscriptionIds ? JSON.parse(subscriptionIds) : [],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runTeamRbacGet({ repo } = {}) {
|
|
46
|
+
return fetchApi('/extension/account/rbac/settings/get', 'POST', { repo });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runTeamRbacSave({ repo, data } = {}) {
|
|
50
|
+
return fetchApi('/extension/account/rbac/settings/save', 'POST', { repo, data: JSON.parse(data) });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function runTeamPlanEnd({ includeUsers } = {}) {
|
|
54
|
+
return fetchApi('/extension/account/plan/subscriptions/end', 'POST', { include_users: includeUsers ?? false });
|
|
55
|
+
}
|
package/src/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import * as scm from './scm/index.js';
|
|
|
19
19
|
import { setConfigValue } from './utils/config.js';
|
|
20
20
|
import { track, shutdown as analyticsShutdown, isTelemetryDisabled } from './utils/analytics.js';
|
|
21
21
|
import registerScansCommands from './commands/scans/index.js';
|
|
22
|
+
import registerSettingsCommands from './commands/settings/index.js';
|
|
22
23
|
|
|
23
24
|
// Read version from package.json
|
|
24
25
|
const require = createRequire(import.meta.url);
|
|
@@ -393,6 +394,9 @@ program
|
|
|
393
394
|
// ─── Scans commands ───
|
|
394
395
|
registerScansCommands(program, { runCmd });
|
|
395
396
|
|
|
397
|
+
// ─── Settings commands ───
|
|
398
|
+
registerSettingsCommands(program, { runCmd });
|
|
399
|
+
|
|
396
400
|
// ─── Telemetry control ───
|
|
397
401
|
program
|
|
398
402
|
.command('set-telemetry <enabled>')
|
package/src/scm/gitlab/client.js
CHANGED
|
@@ -10,7 +10,7 @@ export function getClient() {
|
|
|
10
10
|
return new Gitlab(opts);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// GitLab uses project ID or
|
|
13
|
+
// GitLab uses project ID or the full path — gitbeaker's endpoint tag encodes it
|
|
14
14
|
export function projectId(name) {
|
|
15
|
-
return
|
|
15
|
+
return name;
|
|
16
16
|
}
|
package/src/scm/index.js
CHANGED
|
@@ -67,7 +67,7 @@ export function detectRepoName() {
|
|
|
67
67
|
if (!origin) return null;
|
|
68
68
|
// Handle SSH: git@github.com:owner/repo.git
|
|
69
69
|
// Handle HTTPS: https://github.com/owner/repo.git
|
|
70
|
-
const match = origin.match(/[/:]([\w.-]
|
|
70
|
+
const match = origin.match(/[/:]([\w.-]+(?:\/[\w.-]+)+?)(?:\.git)?$/);
|
|
71
71
|
return match ? match[1] : null;
|
|
72
72
|
}
|
|
73
73
|
|
package/src/utils/fetchApi.js
CHANGED
|
@@ -23,6 +23,7 @@ const fetchApi = async (endpoint, method = 'GET', body = null) => {
|
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
25
|
const response = await fetch(url, options);
|
|
26
|
+
console.error('API Response Status:', response.status);
|
|
26
27
|
|
|
27
28
|
if (response.status === 403) {
|
|
28
29
|
throw new Error('Access denied (403). Please run `codeant logout` and then `codeant login` to re-authenticate.');
|