propr-cli 0.8.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/README.md +549 -0
- package/dist/api/agentTank.js +27 -0
- package/dist/api/agents.js +201 -0
- package/dist/api/client.js +284 -0
- package/dist/api/errors.js +145 -0
- package/dist/api/implement.js +147 -0
- package/dist/api/index.js +26 -0
- package/dist/api/logs.js +59 -0
- package/dist/api/plans.js +160 -0
- package/dist/api/relay.js +73 -0
- package/dist/api/repos.js +243 -0
- package/dist/api/settings.js +219 -0
- package/dist/api/system.js +53 -0
- package/dist/api/tasks.js +140 -0
- package/dist/api/todos.js +77 -0
- package/dist/api/types.js +6 -0
- package/dist/assets/.env.example +183 -0
- package/dist/assets/env.example.txt +198 -0
- package/dist/commands/agentCommands.js +405 -0
- package/dist/commands/checkCommands.js +384 -0
- package/dist/commands/implementCommands.js +178 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/initCommands.js +167 -0
- package/dist/commands/initStack.js +193 -0
- package/dist/commands/logCommands.js +170 -0
- package/dist/commands/planCommands.js +552 -0
- package/dist/commands/relayCommands.js +149 -0
- package/dist/commands/repoCommands.js +526 -0
- package/dist/commands/settingCommands.js +237 -0
- package/dist/commands/stackCommands.js +86 -0
- package/dist/commands/startCommand.js +36 -0
- package/dist/commands/systemCommands.js +221 -0
- package/dist/commands/tankCommands.js +55 -0
- package/dist/commands/taskCommands.js +554 -0
- package/dist/commands/todoCommands.js +620 -0
- package/dist/commands/uiDocsCommands.js +69 -0
- package/dist/config/ConfigManager.js +360 -0
- package/dist/config/index.js +8 -0
- package/dist/config/types.js +16 -0
- package/dist/index.js +276 -0
- package/dist/orchestrator/format.js +31 -0
- package/dist/orchestrator/index.js +102 -0
- package/dist/orchestrator/manifest.json +16 -0
- package/dist/orchestrator/orchestrator.mjs +798 -0
- package/dist/orchestrator/types.js +10 -0
- package/dist/tui/StartApp.js +175 -0
- package/dist/tui/app.js +9 -0
- package/dist/tui/render.js +87 -0
- package/dist/utils/envFile.js +65 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/io.js +186 -0
- package/dist/utils/parseState.js +14 -0
- package/dist/utils/resolveProject.js +50 -0
- package/dist/vendor/shared/demoMode.js +6 -0
- package/dist/vendor/shared/events.js +30 -0
- package/dist/vendor/shared/githubAuthMode.js +35 -0
- package/dist/vendor/shared/index.js +15 -0
- package/dist/vendor/shared/labelUtils.js +32 -0
- package/dist/vendor/shared/modelDefinitions.js +146 -0
- package/dist/vendor/shared/reviewPrompt.js +18 -0
- package/dist/vendor/shared/usageTypes.js +13 -0
- package/dist/vendor/shared/userWhitelist.js +30 -0
- package/dist/vendor/shared/validateRelayUrl.js +21 -0
- package/package.json +31 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Configuration API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend repository configuration endpoints.
|
|
5
|
+
* These functions provide a typed interface to list, add, update, and remove monitored repositories.
|
|
6
|
+
*/
|
|
7
|
+
import { createApiClient } from "./index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Fetches the list of monitored repositories.
|
|
10
|
+
*
|
|
11
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
12
|
+
* @returns A promise resolving to the list of monitored repositories.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const result = await getRepos();
|
|
17
|
+
* console.log(`Monitoring ${result.repos_to_monitor.length} repositories`);
|
|
18
|
+
* for (const repo of result.repos_to_monitor) {
|
|
19
|
+
* console.log(`- ${repo.name} (${repo.enabled ? 'enabled' : 'disabled'})`);
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export async function getRepos(client) {
|
|
24
|
+
const apiClient = client ?? (await createApiClient());
|
|
25
|
+
const response = await apiClient.get("/api/config/repos");
|
|
26
|
+
return response.data;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Adds a new repository to the monitored list.
|
|
30
|
+
*
|
|
31
|
+
* @param fullName - The full repository name in "owner/repo" format.
|
|
32
|
+
* @param options - Optional configuration for the repository.
|
|
33
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
34
|
+
* @returns A promise resolving to the updated repository configuration.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Add a repository with default settings
|
|
39
|
+
* const result = await addRepo("owner/repo");
|
|
40
|
+
*
|
|
41
|
+
* // Add a repository with custom settings
|
|
42
|
+
* const result = await addRepo("owner/repo", {
|
|
43
|
+
* alias: "my-project",
|
|
44
|
+
* baseBranch: "develop"
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export async function addRepo(fullName, options = {}, client) {
|
|
49
|
+
const apiClient = client ?? (await createApiClient());
|
|
50
|
+
// First, fetch the current list of repos
|
|
51
|
+
const currentRepos = await getRepos(apiClient);
|
|
52
|
+
// Check if repo already exists
|
|
53
|
+
const existingRepo = currentRepos.repos_to_monitor.find((r) => r.name.toLowerCase() === fullName.toLowerCase());
|
|
54
|
+
if (existingRepo) {
|
|
55
|
+
throw new Error(`Repository "${fullName}" is already being monitored`);
|
|
56
|
+
}
|
|
57
|
+
// Create new repo entry
|
|
58
|
+
const newRepo = {
|
|
59
|
+
id: crypto.randomUUID(),
|
|
60
|
+
name: fullName,
|
|
61
|
+
enabled: options.enabled ?? true,
|
|
62
|
+
alias: options.alias?.trim() || undefined,
|
|
63
|
+
baseBranch: options.baseBranch?.trim() || undefined,
|
|
64
|
+
};
|
|
65
|
+
// Add to list and save
|
|
66
|
+
const updatedRepos = [...currentRepos.repos_to_monitor, newRepo];
|
|
67
|
+
const response = await apiClient.post("/api/config/repos", {
|
|
68
|
+
body: { repos_to_monitor: updatedRepos },
|
|
69
|
+
});
|
|
70
|
+
return response.data;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Updates an existing monitored repository.
|
|
74
|
+
*
|
|
75
|
+
* @param fullName - The full repository name in "owner/repo" format.
|
|
76
|
+
* @param updates - The fields to update.
|
|
77
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
78
|
+
* @returns A promise resolving to the updated repository configuration.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* // Enable a repository
|
|
83
|
+
* const result = await updateRepo("owner/repo", { enabled: true });
|
|
84
|
+
*
|
|
85
|
+
* // Update multiple fields
|
|
86
|
+
* const result = await updateRepo("owner/repo", {
|
|
87
|
+
* alias: "new-alias",
|
|
88
|
+
* baseBranch: "main"
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export async function updateRepo(fullName, updates, client) {
|
|
93
|
+
const apiClient = client ?? (await createApiClient());
|
|
94
|
+
// Fetch current repos
|
|
95
|
+
const currentRepos = await getRepos(apiClient);
|
|
96
|
+
// Find the repo to update
|
|
97
|
+
const repoIndex = currentRepos.repos_to_monitor.findIndex((r) => r.name.toLowerCase() === fullName.toLowerCase());
|
|
98
|
+
if (repoIndex === -1) {
|
|
99
|
+
throw new Error(`Repository "${fullName}" is not being monitored`);
|
|
100
|
+
}
|
|
101
|
+
// Apply updates
|
|
102
|
+
const existingRepo = currentRepos.repos_to_monitor[repoIndex];
|
|
103
|
+
const updatedRepo = {
|
|
104
|
+
...existingRepo,
|
|
105
|
+
...(updates.enabled !== undefined && { enabled: updates.enabled }),
|
|
106
|
+
...(updates.alias !== undefined && { alias: updates.alias?.trim() || undefined }),
|
|
107
|
+
...(updates.baseBranch !== undefined && { baseBranch: updates.baseBranch?.trim() || undefined }),
|
|
108
|
+
};
|
|
109
|
+
// Replace in list
|
|
110
|
+
const updatedRepos = [...currentRepos.repos_to_monitor];
|
|
111
|
+
updatedRepos[repoIndex] = updatedRepo;
|
|
112
|
+
const response = await apiClient.post("/api/config/repos", {
|
|
113
|
+
body: { repos_to_monitor: updatedRepos },
|
|
114
|
+
});
|
|
115
|
+
return response.data;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Removes a repository from the monitored list.
|
|
119
|
+
*
|
|
120
|
+
* @param fullName - The full repository name in "owner/repo" format.
|
|
121
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
122
|
+
* @returns A promise resolving to the updated repository configuration.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const result = await removeRepo("owner/repo");
|
|
127
|
+
* console.log(`Now monitoring ${result.repos_to_monitor.length} repositories`);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export async function removeRepo(fullName, client) {
|
|
131
|
+
const apiClient = client ?? (await createApiClient());
|
|
132
|
+
// Fetch current repos
|
|
133
|
+
const currentRepos = await getRepos(apiClient);
|
|
134
|
+
// Find the repo to remove
|
|
135
|
+
const repoIndex = currentRepos.repos_to_monitor.findIndex((r) => r.name.toLowerCase() === fullName.toLowerCase());
|
|
136
|
+
if (repoIndex === -1) {
|
|
137
|
+
throw new Error(`Repository "${fullName}" is not being monitored`);
|
|
138
|
+
}
|
|
139
|
+
// Remove from list
|
|
140
|
+
const updatedRepos = currentRepos.repos_to_monitor.filter((_, index) => index !== repoIndex);
|
|
141
|
+
const response = await apiClient.post("/api/config/repos", {
|
|
142
|
+
body: { repos_to_monitor: updatedRepos },
|
|
143
|
+
});
|
|
144
|
+
return response.data;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Triggers indexing for a repository.
|
|
148
|
+
*
|
|
149
|
+
* @param fullName - The full repository name in "owner/repo" format.
|
|
150
|
+
* @param options - Optional indexing options.
|
|
151
|
+
* @param options.fullReindex - Whether to perform a full reindex. Defaults to true.
|
|
152
|
+
* @param options.baseBranch - The base branch to index.
|
|
153
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
154
|
+
* @returns A promise resolving to the trigger indexing response.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* // Trigger full reindexing for a repository
|
|
159
|
+
* const result = await triggerIndexing("owner/repo");
|
|
160
|
+
* console.log(`Indexing started with job ID: ${result.jobId}`);
|
|
161
|
+
*
|
|
162
|
+
* // Trigger indexing for a specific branch
|
|
163
|
+
* const result = await triggerIndexing("owner/repo", { baseBranch: "develop" });
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export async function triggerIndexing(fullName, options = {}, client) {
|
|
167
|
+
const apiClient = client ?? (await createApiClient());
|
|
168
|
+
const response = await apiClient.post("/api/config/repos/trigger-indexing", {
|
|
169
|
+
body: {
|
|
170
|
+
repository: fullName,
|
|
171
|
+
fullReindex: options.fullReindex ?? true,
|
|
172
|
+
baseBranch: options.baseBranch,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
return response.data;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Gets the indexing status for all repositories or filters for a specific repository.
|
|
179
|
+
*
|
|
180
|
+
* @param fullName - Optional. The full repository name in "owner/repo" format to filter by.
|
|
181
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
182
|
+
* @returns A promise resolving to the indexing status response.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* // Get indexing status for all repositories
|
|
187
|
+
* const result = await getIndexingStatus();
|
|
188
|
+
* for (const repo of result.repositories) {
|
|
189
|
+
* console.log(`${repo.full_name}: ${repo.indexing_status}`);
|
|
190
|
+
* }
|
|
191
|
+
*
|
|
192
|
+
* // Get indexing status for a specific repository
|
|
193
|
+
* const result = await getIndexingStatus("owner/repo");
|
|
194
|
+
* const status = result.repositories[0];
|
|
195
|
+
* if (status?.progress) {
|
|
196
|
+
* console.log(`Progress: ${status.progress.percentComplete}%`);
|
|
197
|
+
* }
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export async function getIndexingStatus(fullName, client) {
|
|
201
|
+
const apiClient = client ?? (await createApiClient());
|
|
202
|
+
const response = await apiClient.get("/api/config/repos/indexing-status");
|
|
203
|
+
// Filter by fullName if provided
|
|
204
|
+
if (fullName) {
|
|
205
|
+
const filtered = response.data.repositories.filter((repo) => repo.full_name.toLowerCase() === fullName.toLowerCase());
|
|
206
|
+
return { repositories: filtered };
|
|
207
|
+
}
|
|
208
|
+
return response.data;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Repository API namespace providing all repository configuration operations.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* import { reposApi } from "@propr/cli/api";
|
|
216
|
+
*
|
|
217
|
+
* // List all monitored repos
|
|
218
|
+
* const { repos_to_monitor } = await reposApi.getRepos();
|
|
219
|
+
*
|
|
220
|
+
* // Add a new repo
|
|
221
|
+
* await reposApi.addRepo("owner/repo", { alias: "my-project" });
|
|
222
|
+
*
|
|
223
|
+
* // Update a repo
|
|
224
|
+
* await reposApi.updateRepo("owner/repo", { enabled: false });
|
|
225
|
+
*
|
|
226
|
+
* // Remove a repo
|
|
227
|
+
* await reposApi.removeRepo("owner/repo");
|
|
228
|
+
*
|
|
229
|
+
* // Trigger indexing
|
|
230
|
+
* await reposApi.triggerIndexing("owner/repo");
|
|
231
|
+
*
|
|
232
|
+
* // Get indexing status
|
|
233
|
+
* const status = await reposApi.getIndexingStatus("owner/repo");
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
export const reposApi = {
|
|
237
|
+
getRepos,
|
|
238
|
+
addRepo,
|
|
239
|
+
updateRepo,
|
|
240
|
+
removeRepo,
|
|
241
|
+
triggerIndexing,
|
|
242
|
+
getIndexingStatus,
|
|
243
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Settings API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend system settings endpoints.
|
|
5
|
+
* These functions provide a typed interface to view and update global system configuration
|
|
6
|
+
* like worker concurrency, auto-followup thresholds, and model settings.
|
|
7
|
+
*/
|
|
8
|
+
import { createApiClient } from "./index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Maximum allowed length for the free-form `pr_review_prompt` setting.
|
|
11
|
+
* Mirrors the server-side limit enforced in the config routes.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_PR_REVIEW_PROMPT_LENGTH = 20000;
|
|
14
|
+
/**
|
|
15
|
+
* List of valid setting keys for validation.
|
|
16
|
+
*/
|
|
17
|
+
export const VALID_SETTING_KEYS = [
|
|
18
|
+
"default_agent_alias",
|
|
19
|
+
"worker_concurrency",
|
|
20
|
+
"github_user_whitelist",
|
|
21
|
+
"analysis_model_fast",
|
|
22
|
+
"planner_context_model",
|
|
23
|
+
"planner_generation_model",
|
|
24
|
+
"auto_followup_score_threshold",
|
|
25
|
+
"auto_resolve_merge_conflicts",
|
|
26
|
+
"pr_review_model",
|
|
27
|
+
"pr_review_prompt",
|
|
28
|
+
"ultrafix_rating_goal",
|
|
29
|
+
"ultrafix_max_cycles",
|
|
30
|
+
"ultrafix_pause_seconds",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Validates if a string is a valid setting key.
|
|
34
|
+
*
|
|
35
|
+
* @param key - The key to validate.
|
|
36
|
+
* @returns True if the key is valid, false otherwise.
|
|
37
|
+
*/
|
|
38
|
+
export function isValidSettingKey(key) {
|
|
39
|
+
return VALID_SETTING_KEYS.includes(key);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parses a value string to the appropriate type for the given setting key.
|
|
43
|
+
*
|
|
44
|
+
* @param key - The setting key.
|
|
45
|
+
* @param value - The value string to parse.
|
|
46
|
+
* @returns The parsed value.
|
|
47
|
+
* @throws Error if the value cannot be parsed for the given key.
|
|
48
|
+
*/
|
|
49
|
+
export function parseSettingValue(key, value) {
|
|
50
|
+
switch (key) {
|
|
51
|
+
case "worker_concurrency":
|
|
52
|
+
case "auto_followup_score_threshold": {
|
|
53
|
+
if (!/^-?\d+$/.test(value)) {
|
|
54
|
+
throw new Error(`Invalid value for ${key}: must be an integer`);
|
|
55
|
+
}
|
|
56
|
+
const parsed = Number(value);
|
|
57
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
58
|
+
throw new Error(`Invalid value for ${key}: must be an integer up to ${Number.MAX_SAFE_INTEGER}`);
|
|
59
|
+
}
|
|
60
|
+
if (key === "auto_followup_score_threshold" && (parsed < 0 || parsed > 9)) {
|
|
61
|
+
throw new Error(`Invalid value for ${key}: must be between 0 and 9`);
|
|
62
|
+
}
|
|
63
|
+
if (key === "worker_concurrency" && parsed < 1) {
|
|
64
|
+
throw new Error(`Invalid value for ${key}: must be at least 1`);
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
case "ultrafix_rating_goal": {
|
|
69
|
+
if (!/^\d+$/.test(value)) {
|
|
70
|
+
throw new Error(`Invalid value for ${key}: must be a positive integer between 1 and 10`);
|
|
71
|
+
}
|
|
72
|
+
const parsed = Number(value);
|
|
73
|
+
if (parsed < 1 || parsed > 10) {
|
|
74
|
+
throw new Error(`Invalid value for ${key}: must be a number between 1 and 10`);
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
case "ultrafix_max_cycles": {
|
|
79
|
+
if (!/^\d+$/.test(value)) {
|
|
80
|
+
throw new Error(`Invalid value for ${key}: must be a positive integer`);
|
|
81
|
+
}
|
|
82
|
+
const parsed = Number(value);
|
|
83
|
+
if (parsed < 1 || !Number.isSafeInteger(parsed)) {
|
|
84
|
+
throw new Error(`Invalid value for ${key}: must be a positive integer up to ${Number.MAX_SAFE_INTEGER}`);
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
case "ultrafix_pause_seconds": {
|
|
89
|
+
if (!/^\d+$/.test(value)) {
|
|
90
|
+
throw new Error(`Invalid value for ${key}: must be a non-negative integer`);
|
|
91
|
+
}
|
|
92
|
+
const parsed = Number(value);
|
|
93
|
+
if (parsed < 0 || !Number.isSafeInteger(parsed)) {
|
|
94
|
+
throw new Error(`Invalid value for ${key}: must be a non-negative integer up to ${Number.MAX_SAFE_INTEGER}`);
|
|
95
|
+
}
|
|
96
|
+
return parsed;
|
|
97
|
+
}
|
|
98
|
+
case "auto_resolve_merge_conflicts": {
|
|
99
|
+
const lower = value.toLowerCase();
|
|
100
|
+
if (lower !== "true" && lower !== "false") {
|
|
101
|
+
throw new Error(`Invalid value for ${key}: must be "true" or "false"`);
|
|
102
|
+
}
|
|
103
|
+
return lower === "true";
|
|
104
|
+
}
|
|
105
|
+
case "github_user_whitelist":
|
|
106
|
+
// Parse comma-separated list
|
|
107
|
+
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
108
|
+
case "analysis_model_fast":
|
|
109
|
+
case "default_agent_alias":
|
|
110
|
+
case "planner_context_model":
|
|
111
|
+
case "planner_generation_model":
|
|
112
|
+
return value;
|
|
113
|
+
case "pr_review_model": {
|
|
114
|
+
const trimmed = value.trim();
|
|
115
|
+
if (trimmed === '' && value.length > 0) {
|
|
116
|
+
throw new Error(`Invalid value for ${key}: must not be whitespace-only; use an empty string to clear`);
|
|
117
|
+
}
|
|
118
|
+
return trimmed;
|
|
119
|
+
}
|
|
120
|
+
case "pr_review_prompt": {
|
|
121
|
+
if (value.length > MAX_PR_REVIEW_PROMPT_LENGTH) {
|
|
122
|
+
throw new Error(`Invalid value for ${key}: must be at most ${MAX_PR_REVIEW_PROMPT_LENGTH} characters`);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Fetches the current system settings.
|
|
132
|
+
*
|
|
133
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
134
|
+
* @returns A promise resolving to the current system settings.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const settings = await getSettings();
|
|
139
|
+
* console.log(`Worker concurrency: ${settings.worker_concurrency}`);
|
|
140
|
+
* console.log(`Auto-followup threshold: ${settings.auto_followup_score_threshold}`);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export async function getSettings(client) {
|
|
144
|
+
const apiClient = client ?? (await createApiClient());
|
|
145
|
+
const response = await apiClient.get("/api/config/settings");
|
|
146
|
+
return response.data;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Updates one or more system settings.
|
|
150
|
+
*
|
|
151
|
+
* @param settings - Object containing settings to update.
|
|
152
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
153
|
+
* @returns A promise resolving to the update response.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* // Update a single setting
|
|
158
|
+
* await updateSettings({ worker_concurrency: 10 });
|
|
159
|
+
*
|
|
160
|
+
* // Update multiple settings
|
|
161
|
+
* await updateSettings({
|
|
162
|
+
* worker_concurrency: 10,
|
|
163
|
+
* auto_followup_score_threshold: 7
|
|
164
|
+
* });
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export async function updateSettings(settings, client) {
|
|
168
|
+
const apiClient = client ?? (await createApiClient());
|
|
169
|
+
const response = await apiClient.post("/api/config/settings", {
|
|
170
|
+
body: { settings },
|
|
171
|
+
});
|
|
172
|
+
return response.data;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Updates a single system setting by key.
|
|
176
|
+
*
|
|
177
|
+
* @param key - The setting key to update.
|
|
178
|
+
* @param value - The new value for the setting.
|
|
179
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
180
|
+
* @returns A promise resolving to the update response.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* // Update worker concurrency
|
|
185
|
+
* await updateSetting("worker_concurrency", 10);
|
|
186
|
+
*
|
|
187
|
+
* // Update auto-followup threshold
|
|
188
|
+
* await updateSetting("auto_followup_score_threshold", 7);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export async function updateSetting(key, value, client) {
|
|
192
|
+
const settings = { [key]: value };
|
|
193
|
+
return updateSettings(settings, client);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Settings API namespace providing all system settings operations.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* import { settingsApi } from "@propr/cli/api";
|
|
201
|
+
*
|
|
202
|
+
* // Get current settings
|
|
203
|
+
* const settings = await settingsApi.getSettings();
|
|
204
|
+
*
|
|
205
|
+
* // Update settings
|
|
206
|
+
* await settingsApi.updateSettings({ worker_concurrency: 10 });
|
|
207
|
+
*
|
|
208
|
+
* // Update a single setting
|
|
209
|
+
* await settingsApi.updateSetting("auto_followup_score_threshold", 7);
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export const settingsApi = {
|
|
213
|
+
getSettings,
|
|
214
|
+
updateSettings,
|
|
215
|
+
updateSetting,
|
|
216
|
+
isValidSettingKey,
|
|
217
|
+
parseSettingValue,
|
|
218
|
+
VALID_SETTING_KEYS,
|
|
219
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Status API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend system status and queue endpoints.
|
|
5
|
+
* These functions provide a typed interface to check system health and queue statistics.
|
|
6
|
+
*/
|
|
7
|
+
import { createApiClient } from "./index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Fetches the current system status from the backend.
|
|
10
|
+
*
|
|
11
|
+
* This function retrieves health information about various backend components
|
|
12
|
+
* including the API, Redis, daemon, workers, and authentication status.
|
|
13
|
+
*
|
|
14
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
15
|
+
* @returns A promise resolving to the system status.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Get system status
|
|
20
|
+
* const status = await getSystemStatus();
|
|
21
|
+
* console.log(`API: ${status.api}`);
|
|
22
|
+
* console.log(`Redis: ${status.redis}`);
|
|
23
|
+
* console.log(`Workers: ${status.workerCount}`);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export async function getSystemStatus(client) {
|
|
27
|
+
const apiClient = client ?? (await createApiClient());
|
|
28
|
+
const response = await apiClient.get("/api/status");
|
|
29
|
+
return response.data;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetches queue statistics from the backend.
|
|
33
|
+
*
|
|
34
|
+
* This function retrieves information about the current state of the task queue
|
|
35
|
+
* including counts of waiting, active, completed, failed, and delayed jobs.
|
|
36
|
+
*
|
|
37
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
38
|
+
* @returns A promise resolving to the queue statistics.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Get queue stats
|
|
43
|
+
* const stats = await getQueueStats();
|
|
44
|
+
* console.log(`Active: ${stats.active}`);
|
|
45
|
+
* console.log(`Completed: ${stats.completed}`);
|
|
46
|
+
* console.log(`Failed: ${stats.failed}`);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export async function getQueueStats(client) {
|
|
50
|
+
const apiClient = client ?? (await createApiClient());
|
|
51
|
+
const response = await apiClient.get("/api/queue/stats");
|
|
52
|
+
return response.data;
|
|
53
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tasks API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend task management endpoints.
|
|
5
|
+
* These functions provide a typed interface to list, get, stop, and delete tasks.
|
|
6
|
+
*/
|
|
7
|
+
import { createApiClient } from "./index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Lists tasks from the backend with optional filtering.
|
|
10
|
+
*
|
|
11
|
+
* @param options - Optional filtering and pagination options.
|
|
12
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
13
|
+
* @returns A promise resolving to the list of tasks with pagination info.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // List all tasks
|
|
18
|
+
* const result = await listTasks();
|
|
19
|
+
* console.log(`Found ${result.total} tasks`);
|
|
20
|
+
*
|
|
21
|
+
* // List tasks for a specific project
|
|
22
|
+
* const result = await listTasks({ repository: "owner/repo" });
|
|
23
|
+
*
|
|
24
|
+
* // List tasks with a specific status
|
|
25
|
+
* const result = await listTasks({ status: "completed" });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export async function listTasks(options = {}, client) {
|
|
29
|
+
const apiClient = client ?? (await createApiClient());
|
|
30
|
+
const params = {};
|
|
31
|
+
if (options.status !== undefined) {
|
|
32
|
+
params.status = options.status;
|
|
33
|
+
}
|
|
34
|
+
if (options.repository !== undefined) {
|
|
35
|
+
params.repository = options.repository;
|
|
36
|
+
}
|
|
37
|
+
if (options.limit !== undefined) {
|
|
38
|
+
params.limit = String(options.limit);
|
|
39
|
+
}
|
|
40
|
+
if (options.offset !== undefined) {
|
|
41
|
+
params.offset = String(options.offset);
|
|
42
|
+
}
|
|
43
|
+
if (options.search !== undefined) {
|
|
44
|
+
params.search = options.search;
|
|
45
|
+
}
|
|
46
|
+
const response = await apiClient.get("/api/tasks", {
|
|
47
|
+
params,
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Stops a running task by cancelling it.
|
|
53
|
+
*
|
|
54
|
+
* This function sends a cancellation request to the backend to stop
|
|
55
|
+
* a task that is currently in progress.
|
|
56
|
+
*
|
|
57
|
+
* @param taskId - The unique identifier of the task to stop.
|
|
58
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
59
|
+
* @returns A promise resolving to the stop response.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Stop a running task
|
|
64
|
+
* const result = await stopTask("task-abc123");
|
|
65
|
+
* if (result.success) {
|
|
66
|
+
* console.log("Task stopped successfully");
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export async function stopTask(taskId, client) {
|
|
71
|
+
const apiClient = client ?? (await createApiClient());
|
|
72
|
+
const endpoint = `/api/task/${encodeURIComponent(taskId)}/cancel`;
|
|
73
|
+
const response = await apiClient.post(endpoint);
|
|
74
|
+
return response.data;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Deletes a task from the system.
|
|
78
|
+
*
|
|
79
|
+
* This function removes a task and its associated data from the backend.
|
|
80
|
+
* By default, it will not delete tasks that are in an active state.
|
|
81
|
+
* Use the force option to override this check.
|
|
82
|
+
*
|
|
83
|
+
* @param taskId - The unique identifier of the task to delete.
|
|
84
|
+
* @param force - If true, force deletion even for active tasks.
|
|
85
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
86
|
+
* @returns A promise that resolves when the task is deleted.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Delete a completed task
|
|
91
|
+
* await deleteTask("task-abc123");
|
|
92
|
+
*
|
|
93
|
+
* // Force delete an active task
|
|
94
|
+
* await deleteTask("task-abc123", true);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export async function deleteTask(taskId, force = false, client) {
|
|
98
|
+
const apiClient = client ?? (await createApiClient());
|
|
99
|
+
const endpoint = `/api/task/${encodeURIComponent(taskId)}`;
|
|
100
|
+
const params = {};
|
|
101
|
+
if (force) {
|
|
102
|
+
params.force = "true";
|
|
103
|
+
}
|
|
104
|
+
await apiClient.delete(endpoint, { params });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Queues a revert task to revert changes from a specific commit in a PR.
|
|
108
|
+
*
|
|
109
|
+
* This function sends a revert request to the backend to queue a background
|
|
110
|
+
* job that will revert the specified commit from the PR.
|
|
111
|
+
*
|
|
112
|
+
* @param owner - The repository owner.
|
|
113
|
+
* @param repo - The repository name.
|
|
114
|
+
* @param prNumber - The PR number.
|
|
115
|
+
* @param commitHash - The commit hash to revert.
|
|
116
|
+
* @param commentId - The comment ID that triggered the revert.
|
|
117
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
118
|
+
* @returns A promise resolving to the revert task response.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* // Revert a commit from a PR
|
|
123
|
+
* const result = await revertTask("owner", "repo", 42, "abc1234", 12345);
|
|
124
|
+
* if (result.success) {
|
|
125
|
+
* console.log(`Revert task queued: ${result.jobId}`);
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export async function revertTask(owner, repo, prNumber, commitHash, commentId, client) {
|
|
130
|
+
const apiClient = client ?? (await createApiClient());
|
|
131
|
+
const body = {
|
|
132
|
+
owner,
|
|
133
|
+
repo,
|
|
134
|
+
pr: String(prNumber),
|
|
135
|
+
commit: commitHash,
|
|
136
|
+
commentId: String(commentId),
|
|
137
|
+
};
|
|
138
|
+
const response = await apiClient.post("/api/tasks/revert", { body });
|
|
139
|
+
return response.data;
|
|
140
|
+
}
|