openthrottle 0.1.10 → 0.1.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/dist/config-cmd.js +125 -0
- package/dist/index.js +9 -0
- package/dist/init.js +4 -1
- package/package.json +3 -1
- package/templates/wake-sandbox.yml +3 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// openthrottle config — View and edit .openthrottle.yml settings.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// openthrottle config Show current config
|
|
6
|
+
// openthrottle config <key> <value> Set a value (dot-notation)
|
|
7
|
+
//
|
|
8
|
+
// Examples:
|
|
9
|
+
// openthrottle config phases.plan.model opus
|
|
10
|
+
// openthrottle config phases.build.timeout 3600
|
|
11
|
+
// openthrottle config limits.task_timeout 7200
|
|
12
|
+
// openthrottle config review.enabled false
|
|
13
|
+
// =============================================================================
|
|
14
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { parse, stringify } from 'yaml';
|
|
17
|
+
const CONFIG_FILE = '.openthrottle.yml';
|
|
18
|
+
function configPath() {
|
|
19
|
+
return join(process.cwd(), CONFIG_FILE);
|
|
20
|
+
}
|
|
21
|
+
function loadYaml() {
|
|
22
|
+
const path = configPath();
|
|
23
|
+
if (!existsSync(path)) {
|
|
24
|
+
console.error(`No ${CONFIG_FILE} found. Run "openthrottle init" first.`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
return parse(readFileSync(path, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
function saveYaml(doc) {
|
|
30
|
+
const header = [
|
|
31
|
+
'# openthrottle.yml — project config for Open Throttle (Daytona runtime)',
|
|
32
|
+
'# Edit directly or use: openthrottle config <key> <value>',
|
|
33
|
+
'',
|
|
34
|
+
].join('\n');
|
|
35
|
+
writeFileSync(configPath(), header + stringify(doc));
|
|
36
|
+
}
|
|
37
|
+
/** Get a nested value using dot notation: "phases.plan.model" */
|
|
38
|
+
function getDeep(obj, path) {
|
|
39
|
+
const parts = path.split('.');
|
|
40
|
+
let cur = obj;
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
if (cur == null || typeof cur !== 'object')
|
|
43
|
+
return undefined;
|
|
44
|
+
cur = cur[part];
|
|
45
|
+
}
|
|
46
|
+
return cur;
|
|
47
|
+
}
|
|
48
|
+
/** Set a nested value using dot notation, creating intermediate objects. */
|
|
49
|
+
function setDeep(obj, path, value) {
|
|
50
|
+
const parts = path.split('.');
|
|
51
|
+
let cur = obj;
|
|
52
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
53
|
+
if (cur[parts[i]] == null || typeof cur[parts[i]] !== 'object') {
|
|
54
|
+
cur[parts[i]] = {};
|
|
55
|
+
}
|
|
56
|
+
cur = cur[parts[i]];
|
|
57
|
+
}
|
|
58
|
+
cur[parts[parts.length - 1]] = value;
|
|
59
|
+
}
|
|
60
|
+
/** Coerce string values to appropriate types. */
|
|
61
|
+
function coerce(value) {
|
|
62
|
+
if (value === 'true')
|
|
63
|
+
return true;
|
|
64
|
+
if (value === 'false')
|
|
65
|
+
return false;
|
|
66
|
+
const num = Number(value);
|
|
67
|
+
if (!isNaN(num) && value.trim() !== '')
|
|
68
|
+
return num;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function printConfig(doc, prefix = '', indent = 0) {
|
|
72
|
+
const pad = ' '.repeat(indent);
|
|
73
|
+
for (const [key, val] of Object.entries(doc)) {
|
|
74
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
75
|
+
if (val != null && typeof val === 'object' && !Array.isArray(val)) {
|
|
76
|
+
console.log(`${pad}${key}:`);
|
|
77
|
+
printConfig(val, fullKey, indent + 1);
|
|
78
|
+
}
|
|
79
|
+
else if (Array.isArray(val)) {
|
|
80
|
+
console.log(`${pad}${key}:`);
|
|
81
|
+
for (const item of val) {
|
|
82
|
+
if (typeof item === 'object') {
|
|
83
|
+
console.log(`${pad} -`);
|
|
84
|
+
printConfig(item, fullKey, indent + 2);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.log(`${pad} - ${item}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(`${pad}${key}: ${val}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export default function configCmd(args) {
|
|
97
|
+
const doc = loadYaml();
|
|
98
|
+
// No args → show config
|
|
99
|
+
if (args.length === 0) {
|
|
100
|
+
printConfig(doc);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// One arg → show specific key
|
|
104
|
+
if (args.length === 1) {
|
|
105
|
+
const val = getDeep(doc, args[0]);
|
|
106
|
+
if (val === undefined) {
|
|
107
|
+
console.error(`Key not found: ${args[0]}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
if (typeof val === 'object') {
|
|
111
|
+
printConfig(val);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log(val);
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Two args → set key
|
|
119
|
+
const [key, ...rest] = args;
|
|
120
|
+
const rawValue = rest.join(' ');
|
|
121
|
+
const value = coerce(rawValue);
|
|
122
|
+
setDeep(doc, key, value);
|
|
123
|
+
saveYaml(doc);
|
|
124
|
+
console.log(` ${key}: ${value}`);
|
|
125
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -127,6 +127,9 @@ function cmdShip(args) {
|
|
|
127
127
|
'prd-queued', 'prd-running', 'prd-complete', 'prd-failed',
|
|
128
128
|
'needs-review', 'reviewing',
|
|
129
129
|
'bug-queued', 'bug-running', 'bug-complete', 'bug-failed',
|
|
130
|
+
'bug-confirmed', 'bug-unconfirmed',
|
|
131
|
+
'triage-queued', 'triage-running', 'triage-complete',
|
|
132
|
+
'skip-plan',
|
|
130
133
|
];
|
|
131
134
|
for (const label of labels) {
|
|
132
135
|
try {
|
|
@@ -278,6 +281,7 @@ const HELP = `Usage: openthrottle <command>
|
|
|
278
281
|
Commands:
|
|
279
282
|
init Set up Open Throttle in your project
|
|
280
283
|
ship <file.md> [--base <branch>] Create a GitHub issue to trigger a sandbox
|
|
284
|
+
config [key] [value] View or edit .openthrottle.yml settings
|
|
281
285
|
status Show running, queued, and completed tasks
|
|
282
286
|
logs Show recent GitHub Actions workflow runs
|
|
283
287
|
|
|
@@ -301,6 +305,11 @@ async function main() {
|
|
|
301
305
|
await init();
|
|
302
306
|
return;
|
|
303
307
|
}
|
|
308
|
+
if (command === 'config') {
|
|
309
|
+
const { default: configCmd } = await import('./config-cmd.js');
|
|
310
|
+
configCmd(args.slice(1));
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
304
313
|
preflight();
|
|
305
314
|
switch (command) {
|
|
306
315
|
case 'ship':
|
package/dist/init.js
CHANGED
|
@@ -155,6 +155,7 @@ async function promptConfig(detected) {
|
|
|
155
155
|
},
|
|
156
156
|
{ type: 'number', name: 'maxTurns', message: 'Max turns per agent run', initial: 200, min: 1 },
|
|
157
157
|
{ type: 'number', name: 'maxBudgetUsd', message: 'Max budget per run in USD (API only)', initial: 5, min: 0 },
|
|
158
|
+
{ type: 'number', name: 'taskTimeoutMin', message: 'Task timeout in minutes', initial: 120, min: 5 },
|
|
158
159
|
{ type: 'confirm', name: 'reviewEnabled', message: 'Enable automated PR review?', initial: true },
|
|
159
160
|
{
|
|
160
161
|
type: (prev) => prev ? 'number' : null,
|
|
@@ -162,7 +163,7 @@ async function promptConfig(detected) {
|
|
|
162
163
|
},
|
|
163
164
|
{ type: 'text', name: 'snapshotName', message: 'Daytona snapshot name', initial: 'openthrottle' },
|
|
164
165
|
], { onCancel: () => { console.log('\nCancelled.'); process.exit(0); } });
|
|
165
|
-
const { baseBranch, test, build, lint, format, dev, postBootstrap, agent, notifications, maxTurns, maxBudgetUsd, reviewEnabled, maxRounds, snapshotName } = response;
|
|
166
|
+
const { baseBranch, test, build, lint, format, dev, postBootstrap, agent, notifications, maxTurns, maxBudgetUsd, taskTimeoutMin, reviewEnabled, maxRounds, snapshotName } = response;
|
|
166
167
|
return {
|
|
167
168
|
...detected,
|
|
168
169
|
baseBranch: baseBranch || detected.baseBranch,
|
|
@@ -176,6 +177,7 @@ async function promptConfig(detected) {
|
|
|
176
177
|
notifications: notifications,
|
|
177
178
|
maxTurns: maxTurns,
|
|
178
179
|
maxBudgetUsd: maxBudgetUsd,
|
|
180
|
+
taskTimeoutMin: taskTimeoutMin,
|
|
179
181
|
reviewEnabled: reviewEnabled,
|
|
180
182
|
maxRounds: maxRounds,
|
|
181
183
|
snapshotName: snapshotName,
|
|
@@ -205,6 +207,7 @@ function generateConfig(config) {
|
|
|
205
207
|
limits: {
|
|
206
208
|
max_turns: config.maxTurns ?? 200,
|
|
207
209
|
max_budget_usd: config.maxBudgetUsd ?? 5,
|
|
210
|
+
task_timeout: (config.taskTimeoutMin ?? 120) * 60,
|
|
208
211
|
},
|
|
209
212
|
review: {
|
|
210
213
|
enabled: config.reviewEnabled,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openthrottle",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "CLI for Open Throttle — ship prompts to Daytona sandboxes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc",
|
|
15
|
+
"test": "echo 'No tests'",
|
|
16
|
+
"lint": "echo 'No linter configured'",
|
|
15
17
|
"prepublishOnly": "npm run build"
|
|
16
18
|
},
|
|
17
19
|
"dependencies": {
|
|
@@ -37,7 +37,7 @@ permissions:
|
|
|
37
37
|
|
|
38
38
|
jobs:
|
|
39
39
|
run-task:
|
|
40
|
-
if: ${{ contains(fromJSON('["prd-queued","bug-queued","needs-review","needs-investigation"]'), github.event.label.name) || (github.event.review.state == 'changes_requested') }}
|
|
40
|
+
if: ${{ contains(fromJSON('["prd-queued","bug-queued","needs-review","needs-investigation","triage-queued"]'), github.event.label.name) || (github.event.review.state == 'changes_requested') }}
|
|
41
41
|
runs-on: ubuntu-latest
|
|
42
42
|
steps:
|
|
43
43
|
- uses: actions/checkout@v6
|
|
@@ -70,6 +70,8 @@ jobs:
|
|
|
70
70
|
TASK_TYPE="review"
|
|
71
71
|
elif [[ "$EVENT_LABEL" == "needs-investigation" ]]; then
|
|
72
72
|
TASK_TYPE="investigation"
|
|
73
|
+
elif [[ "$EVENT_LABEL" == "triage-queued" ]]; then
|
|
74
|
+
TASK_TYPE="triage"
|
|
73
75
|
elif [[ "$EVENT_REVIEW_STATE" == "changes_requested" ]]; then
|
|
74
76
|
TASK_TYPE="review-fix"
|
|
75
77
|
fi
|