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,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend implementation endpoints.
|
|
5
|
+
* These functions provide a typed interface to trigger issue implementations
|
|
6
|
+
* and poll task status.
|
|
7
|
+
*/
|
|
8
|
+
import { createApiClient } from "./index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Triggers implementation for a specific issue/task.
|
|
11
|
+
*
|
|
12
|
+
* This function calls the backend endpoint to start implementing an issue
|
|
13
|
+
* that was created as part of a plan. The backend identifies issues using
|
|
14
|
+
* the combination of draft ID and issue number.
|
|
15
|
+
*
|
|
16
|
+
* @param draftId - The unique identifier of the plan draft containing the issue.
|
|
17
|
+
* @param issueNumber - The GitHub issue number to implement.
|
|
18
|
+
* @param options - Optional configuration for the implementation.
|
|
19
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
20
|
+
* @returns A promise resolving to the implementation response containing task ID for polling.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // Implement a single issue with default settings
|
|
25
|
+
* const result = await implementIssue("draft-123", 1);
|
|
26
|
+
* console.log(result.message);
|
|
27
|
+
* if (result.taskId) {
|
|
28
|
+
* // Poll for status using the task ID
|
|
29
|
+
* const status = await getTaskStatus(result.taskId);
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Implement with a specific model
|
|
33
|
+
* const result = await implementIssue("draft-123", 1, {
|
|
34
|
+
* agent_alias: "claude-code",
|
|
35
|
+
* model_name: "claude-sonnet-4-20250514"
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Implement with Epic PR and auto-merge
|
|
39
|
+
* const result = await implementIssue("draft-123", 1, {
|
|
40
|
+
* useEpic: true,
|
|
41
|
+
* autoMerge: true
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export async function implementIssue(draftId, issueNumber, options = {}, client) {
|
|
46
|
+
const apiClient = client ?? (await createApiClient());
|
|
47
|
+
const body = {};
|
|
48
|
+
if (options.agent_alias !== undefined) {
|
|
49
|
+
body.agent_alias = options.agent_alias;
|
|
50
|
+
}
|
|
51
|
+
if (options.model_name !== undefined) {
|
|
52
|
+
body.model_name = options.model_name;
|
|
53
|
+
}
|
|
54
|
+
if (options.models !== undefined) {
|
|
55
|
+
body.models = options.models;
|
|
56
|
+
}
|
|
57
|
+
if (options.useEpic !== undefined) {
|
|
58
|
+
body.useEpic = options.useEpic;
|
|
59
|
+
}
|
|
60
|
+
if (options.autoMerge !== undefined) {
|
|
61
|
+
body.autoMerge = options.autoMerge;
|
|
62
|
+
}
|
|
63
|
+
const endpoint = `/api/planner/drafts/${encodeURIComponent(draftId)}/issues/${encodeURIComponent(String(issueNumber))}/implement`;
|
|
64
|
+
const response = await apiClient.post(endpoint, {
|
|
65
|
+
body,
|
|
66
|
+
});
|
|
67
|
+
return response.data;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Fetches the status of an executing background task.
|
|
71
|
+
*
|
|
72
|
+
* This function retrieves the current state and history of a task,
|
|
73
|
+
* allowing the CLI to poll for progress and completion.
|
|
74
|
+
*
|
|
75
|
+
* @param taskId - The unique identifier of the task to check.
|
|
76
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
77
|
+
* @returns A promise resolving to the parsed task status with convenience fields.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Get the status of a task
|
|
82
|
+
* const status = await getTaskStatus("task-abc123");
|
|
83
|
+
* console.log(`Current state: ${status.currentState}`);
|
|
84
|
+
*
|
|
85
|
+
* if (status.isCompleted) {
|
|
86
|
+
* console.log("Task completed successfully!");
|
|
87
|
+
* if (status.prNumber) {
|
|
88
|
+
* console.log(`PR created: #${status.prNumber}`);
|
|
89
|
+
* }
|
|
90
|
+
* } else if (status.isFailed) {
|
|
91
|
+
* console.log(`Task failed: ${status.failureReason}`);
|
|
92
|
+
* } else if (status.isInProgress) {
|
|
93
|
+
* console.log("Task is still running...");
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export async function getTaskStatus(taskId, client) {
|
|
98
|
+
const apiClient = client ?? (await createApiClient());
|
|
99
|
+
const endpoint = `/api/task/${encodeURIComponent(taskId)}/history`;
|
|
100
|
+
const response = await apiClient.get(endpoint);
|
|
101
|
+
return parseTaskStatus(response.data);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parses the raw task status response into a more convenient format.
|
|
105
|
+
*
|
|
106
|
+
* @param response - The raw task status response from the API.
|
|
107
|
+
* @returns A parsed TaskStatus with convenience fields.
|
|
108
|
+
*/
|
|
109
|
+
function parseTaskStatus(response) {
|
|
110
|
+
const { taskId, history, taskInfo } = response;
|
|
111
|
+
// Get the latest state from history
|
|
112
|
+
const latestEntry = history.length > 0 ? history[history.length - 1] : null;
|
|
113
|
+
const currentState = latestEntry?.state?.toLowerCase() || "pending";
|
|
114
|
+
// Determine status flags
|
|
115
|
+
const completedStates = ["completed", "failed", "cancelled"];
|
|
116
|
+
const inProgressStates = ["pending", "queued", "processing", "claude_execution", "post_processing"];
|
|
117
|
+
const isCompleted = currentState === "completed";
|
|
118
|
+
const isFailed = currentState === "failed" || currentState === "cancelled";
|
|
119
|
+
const isInProgress = !completedStates.includes(currentState) && inProgressStates.includes(currentState);
|
|
120
|
+
// Extract failure reason
|
|
121
|
+
let failureReason;
|
|
122
|
+
if (isFailed) {
|
|
123
|
+
failureReason = latestEntry?.reason || latestEntry?.message || latestEntry?.metadata?.error;
|
|
124
|
+
}
|
|
125
|
+
// Extract PR information from history
|
|
126
|
+
let prNumber;
|
|
127
|
+
let prUrl;
|
|
128
|
+
for (const entry of history) {
|
|
129
|
+
if (entry.metadata?.pullRequest) {
|
|
130
|
+
prNumber = entry.metadata.pullRequest.number;
|
|
131
|
+
prUrl = entry.metadata.pullRequest.url;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
taskId,
|
|
137
|
+
currentState: currentState,
|
|
138
|
+
isInProgress,
|
|
139
|
+
isCompleted,
|
|
140
|
+
isFailed,
|
|
141
|
+
failureReason,
|
|
142
|
+
history,
|
|
143
|
+
taskInfo,
|
|
144
|
+
prNumber,
|
|
145
|
+
prUrl,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI API Module
|
|
3
|
+
*
|
|
4
|
+
* Exports the API client and related types for communicating
|
|
5
|
+
* with the ProPR backend REST API.
|
|
6
|
+
*/
|
|
7
|
+
export { ApiClient, createApiClient, createApiClientWithConfig, } from "./client.js";
|
|
8
|
+
export { ApiError, UnauthorizedError, ForbiddenError, NotFoundError, BadRequestError, InternalServerError, NetworkError, TimeoutError, createApiError, } from "./errors.js";
|
|
9
|
+
// Plan Management API
|
|
10
|
+
export { listPlans, createPlan, getPlan, deletePlan, abortPlan, listPlanIssues, generatePlan, finalizePlan, } from "./plans.js";
|
|
11
|
+
// Implementation API
|
|
12
|
+
export { implementIssue, getTaskStatus, } from "./implement.js";
|
|
13
|
+
// Tasks API
|
|
14
|
+
export { listTasks, stopTask, deleteTask, revertTask, } from "./tasks.js";
|
|
15
|
+
// Repository Configuration API
|
|
16
|
+
export { getRepos, addRepo, updateRepo, removeRepo, triggerIndexing, getIndexingStatus, reposApi, } from "./repos.js";
|
|
17
|
+
// Agents Configuration API
|
|
18
|
+
export { listAgents, addAgent, deleteAgent, } from "./agents.js";
|
|
19
|
+
// System Settings API
|
|
20
|
+
export { getSettings, updateSettings, updateSetting, isValidSettingKey, parseSettingValue, settingsApi, VALID_SETTING_KEYS, } from "./settings.js";
|
|
21
|
+
// LLM Logs API
|
|
22
|
+
export { listLlmLogs, } from "./logs.js";
|
|
23
|
+
// Repository To-Dos API
|
|
24
|
+
export { listTodos, getTodo, createTodo, updateTodo, deleteTodo, listCategories, createCategory, updateCategory, deleteCategory, reorderTodos, reorderCategories, } from "./todos.js";
|
|
25
|
+
// System Status API
|
|
26
|
+
export { getSystemStatus, getQueueStats, } from "./system.js";
|
package/dist/api/logs.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Logs API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend LLM logs endpoints.
|
|
5
|
+
* These functions provide a typed interface to list and filter LLM execution logs.
|
|
6
|
+
*/
|
|
7
|
+
import { createApiClient } from "./index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Lists LLM logs 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 logs with pagination info.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // List all logs
|
|
18
|
+
* const result = await listLlmLogs();
|
|
19
|
+
* console.log(`Found ${result.pagination.total} logs`);
|
|
20
|
+
*
|
|
21
|
+
* // List logs for a specific model
|
|
22
|
+
* const result = await listLlmLogs({ model: "claude-3-opus" });
|
|
23
|
+
*
|
|
24
|
+
* // List logs with pagination
|
|
25
|
+
* const result = await listLlmLogs({ page: 2, limit: 20 });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export async function listLlmLogs(options = {}, client) {
|
|
29
|
+
const apiClient = client ?? (await createApiClient());
|
|
30
|
+
const params = {};
|
|
31
|
+
if (options.page !== undefined) {
|
|
32
|
+
params.page = String(options.page);
|
|
33
|
+
}
|
|
34
|
+
if (options.limit !== undefined) {
|
|
35
|
+
params.limit = String(options.limit);
|
|
36
|
+
}
|
|
37
|
+
if (options.executionType !== undefined) {
|
|
38
|
+
params.execution_type = options.executionType;
|
|
39
|
+
}
|
|
40
|
+
if (options.model !== undefined) {
|
|
41
|
+
params.model = options.model;
|
|
42
|
+
}
|
|
43
|
+
if (options.success !== undefined) {
|
|
44
|
+
params.success = options.success ? "true" : "false";
|
|
45
|
+
}
|
|
46
|
+
if (options.draftId !== undefined) {
|
|
47
|
+
params.draft_id = options.draftId;
|
|
48
|
+
}
|
|
49
|
+
if (options.agentAlias !== undefined) {
|
|
50
|
+
params.agent_alias = options.agentAlias;
|
|
51
|
+
}
|
|
52
|
+
if (options.workType !== undefined) {
|
|
53
|
+
params.work_type = options.workType;
|
|
54
|
+
}
|
|
55
|
+
const response = await apiClient.get("/api/llm-logs", {
|
|
56
|
+
params,
|
|
57
|
+
});
|
|
58
|
+
return response.data;
|
|
59
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Management API
|
|
3
|
+
*
|
|
4
|
+
* Functions for interacting with the ProPR backend plan endpoints.
|
|
5
|
+
* These functions provide a typed interface to list, create, and fetch plans.
|
|
6
|
+
*/
|
|
7
|
+
import { createApiClient } from "./index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Fetches plans for a specific repository.
|
|
10
|
+
*
|
|
11
|
+
* @param project - The repository identifier (e.g., "owner/repo").
|
|
12
|
+
* @param options - Optional filtering and pagination options.
|
|
13
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
14
|
+
* @returns A promise resolving to the list of plans with pagination info.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const result = await listPlans("owner/repo");
|
|
19
|
+
* console.log(`Found ${result.total} plans`);
|
|
20
|
+
* for (const plan of result.drafts) {
|
|
21
|
+
* console.log(`- ${plan.name} (${plan.status})`);
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export async function listPlans(project, options = {}, client) {
|
|
26
|
+
const apiClient = client ?? (await createApiClient());
|
|
27
|
+
const params = {
|
|
28
|
+
repository: project,
|
|
29
|
+
page: options.page,
|
|
30
|
+
limit: options.limit,
|
|
31
|
+
search: options.search,
|
|
32
|
+
status: options.status,
|
|
33
|
+
excludeStatuses: options.excludeStatuses,
|
|
34
|
+
};
|
|
35
|
+
const response = await apiClient.get("/api/planner/drafts", {
|
|
36
|
+
params,
|
|
37
|
+
});
|
|
38
|
+
return response.data;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Submits a new plan generation request.
|
|
42
|
+
*
|
|
43
|
+
* @param project - The repository identifier (e.g., "owner/repo").
|
|
44
|
+
* @param prompt - The initial prompt describing the plan.
|
|
45
|
+
* @param options - Optional additional options for plan creation.
|
|
46
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
47
|
+
* @returns A promise resolving to the created plan.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const plan = await createPlan("owner/repo", "Add user authentication feature");
|
|
52
|
+
* console.log(`Created plan: ${plan.draft_id}`);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export async function createPlan(project, prompt, options = {}, client) {
|
|
56
|
+
const apiClient = client ?? (await createApiClient());
|
|
57
|
+
const body = {
|
|
58
|
+
repository: project,
|
|
59
|
+
prompt,
|
|
60
|
+
...options,
|
|
61
|
+
};
|
|
62
|
+
const response = await apiClient.post("/api/planner/drafts", {
|
|
63
|
+
body,
|
|
64
|
+
});
|
|
65
|
+
return response.data;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetches details of a specific plan.
|
|
69
|
+
*
|
|
70
|
+
* @param planId - The unique identifier of the plan (draft_id).
|
|
71
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
72
|
+
* @returns A promise resolving to the plan details.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const plan = await getPlan("abc123-uuid");
|
|
77
|
+
* console.log(`Plan status: ${plan.status}`);
|
|
78
|
+
* console.log(`Plan items: ${plan.plan_json.length}`);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export async function getPlan(planId, client) {
|
|
82
|
+
const apiClient = client ?? (await createApiClient());
|
|
83
|
+
const response = await apiClient.get(`/api/planner/drafts/${encodeURIComponent(planId)}`);
|
|
84
|
+
return response.data;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Deletes a specific plan.
|
|
88
|
+
*
|
|
89
|
+
* @param planId - The unique identifier of the plan (draft_id).
|
|
90
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
91
|
+
* @returns A promise that resolves when the plan is deleted.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* await deletePlan("abc123-uuid");
|
|
96
|
+
* console.log("Plan deleted successfully");
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export async function deletePlan(planId, client) {
|
|
100
|
+
const apiClient = client ?? (await createApiClient());
|
|
101
|
+
await apiClient.delete(`/api/planner/drafts/${encodeURIComponent(planId)}`);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Aborts an ongoing plan generation.
|
|
105
|
+
*
|
|
106
|
+
* @param planId - The unique identifier of the plan (draft_id).
|
|
107
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
108
|
+
* @returns A promise resolving to the abort response.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const result = await abortPlan("abc123-uuid");
|
|
113
|
+
* console.log(result.message); // "Generation aborted"
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export async function abortPlan(planId, client) {
|
|
117
|
+
const apiClient = client ?? (await createApiClient());
|
|
118
|
+
const response = await apiClient.post("/api/planner/abort", {
|
|
119
|
+
body: { draftId: planId },
|
|
120
|
+
});
|
|
121
|
+
return response.data;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Finalizes a plan by creating GitHub issues from its plan items.
|
|
125
|
+
*
|
|
126
|
+
* @param draftId - The unique identifier of the plan draft.
|
|
127
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
128
|
+
* @returns A promise resolving to the finalization response.
|
|
129
|
+
*/
|
|
130
|
+
export async function finalizePlan(draftId, client) {
|
|
131
|
+
const apiClient = client ?? (await createApiClient());
|
|
132
|
+
const response = await apiClient.post("/api/planner/finalize", { body: { draftId } });
|
|
133
|
+
return response.data;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Triggers plan generation for an existing draft.
|
|
137
|
+
*
|
|
138
|
+
* @param draftId - The unique identifier of the plan draft.
|
|
139
|
+
* @param options - Optional generation configuration.
|
|
140
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
141
|
+
* @returns A promise resolving to the generation response.
|
|
142
|
+
*/
|
|
143
|
+
export async function generatePlan(draftId, options = {}, client) {
|
|
144
|
+
const apiClient = client ?? (await createApiClient());
|
|
145
|
+
const body = { draftId, ...options };
|
|
146
|
+
const response = await apiClient.post("/api/planner/generate", { body });
|
|
147
|
+
return response.data;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Lists issues associated with a plan.
|
|
151
|
+
*
|
|
152
|
+
* @param planId - The unique identifier of the plan (draft_id).
|
|
153
|
+
* @param client - Optional ApiClient instance. If not provided, one will be created.
|
|
154
|
+
* @returns A promise resolving to the list of plan issues.
|
|
155
|
+
*/
|
|
156
|
+
export async function listPlanIssues(planId, client) {
|
|
157
|
+
const apiClient = client ?? (await createApiClient());
|
|
158
|
+
const response = await apiClient.get(`/api/planner/drafts/${encodeURIComponent(planId)}/issues`);
|
|
159
|
+
return response.data;
|
|
160
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub token relay API (auth path 2).
|
|
3
|
+
*
|
|
4
|
+
* These call the vendor-run relay's enrollment endpoints (propr-routing's
|
|
5
|
+
* /v1/relay-tokens routes), authenticated by the user's GitHub token (the same
|
|
6
|
+
* token `propr login` stores). They are distinct from the ProPR backend API —
|
|
7
|
+
* the relay is a separate service, and the daemon later uses the issued relay
|
|
8
|
+
* token against /v1/installation-token.
|
|
9
|
+
*/
|
|
10
|
+
const FETCH_TIMEOUT_MS = 15_000;
|
|
11
|
+
function baseUrl(options) {
|
|
12
|
+
return options.baseUrl.replace(/\/+$/, "");
|
|
13
|
+
}
|
|
14
|
+
async function relayRequest(options, path, method, body, init) {
|
|
15
|
+
let response;
|
|
16
|
+
try {
|
|
17
|
+
const headers = {
|
|
18
|
+
authorization: `Bearer ${options.githubToken}`,
|
|
19
|
+
accept: "application/json",
|
|
20
|
+
};
|
|
21
|
+
if (body !== undefined) {
|
|
22
|
+
headers["content-type"] = "application/json";
|
|
23
|
+
}
|
|
24
|
+
response = await fetch(`${baseUrl(options)}${path}`, {
|
|
25
|
+
method,
|
|
26
|
+
headers,
|
|
27
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
28
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new Error(`Cannot reach the relay at ${options.baseUrl}: ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
let code = "";
|
|
36
|
+
try {
|
|
37
|
+
const parsed = (await response.json());
|
|
38
|
+
code = parsed?.error?.code ?? "";
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
/* non-JSON error body */
|
|
42
|
+
}
|
|
43
|
+
if (response.status === 401) {
|
|
44
|
+
throw new Error("The relay rejected your GitHub token. Run `propr login` to refresh it.");
|
|
45
|
+
}
|
|
46
|
+
if (response.status === 403) {
|
|
47
|
+
throw new Error("You are not authorized for this installation. Confirm the shared GitHub App is installed and you have access to it.");
|
|
48
|
+
}
|
|
49
|
+
if (response.status === 404 && init?.notFoundMessage) {
|
|
50
|
+
throw new Error(init.notFoundMessage);
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Relay request failed (HTTP ${response.status}${code ? ` ${code}` : ""}).`);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return (await response.json());
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error("The relay returned a malformed JSON response.");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function enrollRelayToken(options, params) {
|
|
62
|
+
return relayRequest(options, "/relay-tokens", "POST", {
|
|
63
|
+
installation_id: params.installationId,
|
|
64
|
+
label: params.label ?? null,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function listRelayTokens(options, installationId) {
|
|
68
|
+
const query = encodeURIComponent(String(installationId));
|
|
69
|
+
return relayRequest(options, `/relay-tokens?installation_id=${query}`, "GET");
|
|
70
|
+
}
|
|
71
|
+
export function revokeRelayToken(options, params) {
|
|
72
|
+
return relayRequest(options, "/relay-tokens/revoke", "POST", { installation_id: params.installationId, token_id: params.tokenId }, { notFoundMessage: "Relay token not found (already revoked or wrong token id)." });
|
|
73
|
+
}
|