@exaudeus/workrail 3.43.0 → 3.44.0
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/console-ui/assets/{index-Sb57DW4B.js → index-Bi38ITiQ.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/daemon/workflow-runner.d.ts +4 -0
- package/dist/daemon/workflow-runner.js +15 -0
- package/dist/manifest.json +33 -17
- package/dist/trigger/adapters/github-queue-poller.d.ts +34 -0
- package/dist/trigger/adapters/github-queue-poller.js +200 -0
- package/dist/trigger/github-queue-config.d.ts +18 -0
- package/dist/trigger/github-queue-config.js +155 -0
- package/dist/trigger/polling-scheduler.d.ts +1 -0
- package/dist/trigger/polling-scheduler.js +185 -6
- package/dist/trigger/trigger-store.js +35 -2
- package/dist/trigger/types.d.ts +16 -0
- package/package.json +1 -1
package/dist/manifest.json
CHANGED
|
@@ -449,16 +449,16 @@
|
|
|
449
449
|
"sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
|
|
450
450
|
"bytes": 8011
|
|
451
451
|
},
|
|
452
|
+
"console-ui/assets/index-Bi38ITiQ.js": {
|
|
453
|
+
"sha256": "e3229116ac20f315184c69ef9eaa33267457e5e7aac1e22015ed830cb098e39a",
|
|
454
|
+
"bytes": 760528
|
|
455
|
+
},
|
|
452
456
|
"console-ui/assets/index-DGj8EsFR.css": {
|
|
453
457
|
"sha256": "3bdb55ec0957928e0ebbb86a7d6b36d28f7ba7d5c0f3e236fd8f2e2aacee2fa4",
|
|
454
458
|
"bytes": 60631
|
|
455
459
|
},
|
|
456
|
-
"console-ui/assets/index-Sb57DW4B.js": {
|
|
457
|
-
"sha256": "54d09def45773f707ebf2bc17d109411a36ae1098d97d1f81a7423c69686520a",
|
|
458
|
-
"bytes": 760528
|
|
459
|
-
},
|
|
460
460
|
"console-ui/index.html": {
|
|
461
|
-
"sha256": "
|
|
461
|
+
"sha256": "fb9efa806376b8f8f18d3c5d7853d04b9dfc9abc305b0ed22782379bc13a2c86",
|
|
462
462
|
"bytes": 417
|
|
463
463
|
},
|
|
464
464
|
"console/standalone-console.d.ts": {
|
|
@@ -550,12 +550,12 @@
|
|
|
550
550
|
"bytes": 1512
|
|
551
551
|
},
|
|
552
552
|
"daemon/workflow-runner.d.ts": {
|
|
553
|
-
"sha256": "
|
|
554
|
-
"bytes":
|
|
553
|
+
"sha256": "0406654be8c6eb147706e81e0ba666ce372db140f4720246258e0f001653181e",
|
|
554
|
+
"bytes": 6628
|
|
555
555
|
},
|
|
556
556
|
"daemon/workflow-runner.js": {
|
|
557
|
-
"sha256": "
|
|
558
|
-
"bytes":
|
|
557
|
+
"sha256": "f40f265284aa1e32168d8a0cf28c08007186eafac6d55182b7ec6ddb53bed5a8",
|
|
558
|
+
"bytes": 89592
|
|
559
559
|
},
|
|
560
560
|
"di/container.d.ts": {
|
|
561
561
|
"sha256": "003bb7fb7478d627524b9b1e76bd0a963a243794a687ff233b96dc0e33a06d9f",
|
|
@@ -1549,6 +1549,14 @@
|
|
|
1549
1549
|
"sha256": "dd1a1bc20f1bf6da550960887c75fab4a794c19a1b79f3c89f4277145d009c2e",
|
|
1550
1550
|
"bytes": 6723
|
|
1551
1551
|
},
|
|
1552
|
+
"trigger/adapters/github-queue-poller.d.ts": {
|
|
1553
|
+
"sha256": "f36b01b118e6986e8f89701fc5d88fa7a1873fb38acaa67b1612895056bd5ef8",
|
|
1554
|
+
"bytes": 1363
|
|
1555
|
+
},
|
|
1556
|
+
"trigger/adapters/github-queue-poller.js": {
|
|
1557
|
+
"sha256": "73270636634fb8673c63b0c0347e6e5d7f676606d36b87a4c1aadc11f1939eb2",
|
|
1558
|
+
"bytes": 7340
|
|
1559
|
+
},
|
|
1552
1560
|
"trigger/adapters/gitlab-poller.d.ts": {
|
|
1553
1561
|
"sha256": "f685490fafad77194fdd0f0bbaf80dbc56730aeb344853da365199a120fbe399",
|
|
1554
1562
|
"bytes": 911
|
|
@@ -1581,6 +1589,14 @@
|
|
|
1581
1589
|
"sha256": "da358ced4e99c327493b6d3ca975a623aca21f72e68787a092b2760601801c99",
|
|
1582
1590
|
"bytes": 1269
|
|
1583
1591
|
},
|
|
1592
|
+
"trigger/github-queue-config.d.ts": {
|
|
1593
|
+
"sha256": "bff922c1b435da55e02b4b11d3fde9f06e999f13b8ac0494788cac4a4fc4c432",
|
|
1594
|
+
"bytes": 766
|
|
1595
|
+
},
|
|
1596
|
+
"trigger/github-queue-config.js": {
|
|
1597
|
+
"sha256": "a0ec09a7725ac1ee1adb9d41fdcb4c9545128f0b9515c6de4df9535d4bec1acc",
|
|
1598
|
+
"bytes": 6857
|
|
1599
|
+
},
|
|
1584
1600
|
"trigger/index.d.ts": {
|
|
1585
1601
|
"sha256": "a9cfd053714173e2a8cc5a282fd5b09a5c3f3001304d507facd0e12de9cc0733",
|
|
1586
1602
|
"bytes": 735
|
|
@@ -1606,12 +1622,12 @@
|
|
|
1606
1622
|
"bytes": 6968
|
|
1607
1623
|
},
|
|
1608
1624
|
"trigger/polling-scheduler.d.ts": {
|
|
1609
|
-
"sha256": "
|
|
1610
|
-
"bytes":
|
|
1625
|
+
"sha256": "009b3340f5d46a3fc22b9cd087abcf484abe34c526a46dd4f958768fb8f61c9c",
|
|
1626
|
+
"bytes": 792
|
|
1611
1627
|
},
|
|
1612
1628
|
"trigger/polling-scheduler.js": {
|
|
1613
|
-
"sha256": "
|
|
1614
|
-
"bytes":
|
|
1629
|
+
"sha256": "ec3c73739c9dfbbe3b2d7a55237fd35a290d6aab7bbc30ec5408bde69b2c95e0",
|
|
1630
|
+
"bytes": 19769
|
|
1615
1631
|
},
|
|
1616
1632
|
"trigger/trigger-listener.d.ts": {
|
|
1617
1633
|
"sha256": "92e971ab8f47c3c867860cffc01f54c4aae54fcc4ae199b9469210f4ce639423",
|
|
@@ -1634,12 +1650,12 @@
|
|
|
1634
1650
|
"bytes": 1588
|
|
1635
1651
|
},
|
|
1636
1652
|
"trigger/trigger-store.js": {
|
|
1637
|
-
"sha256": "
|
|
1638
|
-
"bytes":
|
|
1653
|
+
"sha256": "8015b54da7ab5b1cec78e1ecea099d5b457eaa5de267bb1ed52cbb75eca207c4",
|
|
1654
|
+
"bytes": 38148
|
|
1639
1655
|
},
|
|
1640
1656
|
"trigger/types.d.ts": {
|
|
1641
|
-
"sha256": "
|
|
1642
|
-
"bytes":
|
|
1657
|
+
"sha256": "4ccedde5b927f17edbb96203083e8ffd2d578e2cc007ff2427511112ae262e30",
|
|
1658
|
+
"bytes": 3475
|
|
1643
1659
|
},
|
|
1644
1660
|
"trigger/types.js": {
|
|
1645
1661
|
"sha256": "45b4e4f23a6d1a2b07350196871b0c53840e5d8142b47f7acedd2f40ae7a6b73",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { GitHubQueuePollingSource } from '../types.js';
|
|
2
|
+
import type { GitHubQueueConfig } from '../github-queue-config.js';
|
|
3
|
+
import type { Result } from '../../runtime/result.js';
|
|
4
|
+
export interface GitHubQueueLabel {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
}
|
|
7
|
+
export interface GitHubQueueIssue {
|
|
8
|
+
readonly id: number;
|
|
9
|
+
readonly number: number;
|
|
10
|
+
readonly title: string;
|
|
11
|
+
readonly body: string;
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly labels: readonly GitHubQueueLabel[];
|
|
14
|
+
readonly createdAt: string;
|
|
15
|
+
}
|
|
16
|
+
export type GitHubQueuePollError = {
|
|
17
|
+
readonly kind: 'http_error';
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'network_error';
|
|
22
|
+
readonly message: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly kind: 'parse_error';
|
|
25
|
+
readonly message: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'not_implemented';
|
|
28
|
+
readonly message: string;
|
|
29
|
+
};
|
|
30
|
+
export type FetchFn = (url: string, init: RequestInit) => Promise<Response>;
|
|
31
|
+
export declare const DEFAULT_SESSIONS_DIR: string;
|
|
32
|
+
export declare function pollGitHubQueueIssues(source: GitHubQueuePollingSource, config: GitHubQueueConfig, fetchFn?: FetchFn): Promise<Result<GitHubQueueIssue[], GitHubQueuePollError>>;
|
|
33
|
+
export declare function inferMaturity(body: string): 'idea' | 'specced' | 'ready';
|
|
34
|
+
export declare function checkIdempotency(issueNumber: number, sessionsDir?: string): Promise<'clear' | 'active'>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_SESSIONS_DIR = void 0;
|
|
37
|
+
exports.pollGitHubQueueIssues = pollGitHubQueueIssues;
|
|
38
|
+
exports.inferMaturity = inferMaturity;
|
|
39
|
+
exports.checkIdempotency = checkIdempotency;
|
|
40
|
+
const result_js_1 = require("../../runtime/result.js");
|
|
41
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const os = __importStar(require("node:os"));
|
|
44
|
+
exports.DEFAULT_SESSIONS_DIR = path.join(os.homedir(), '.workrail', 'daemon-sessions');
|
|
45
|
+
function checkRateLimit(response) {
|
|
46
|
+
const remainingHeader = response.headers.get('X-RateLimit-Remaining');
|
|
47
|
+
const resetHeader = response.headers.get('X-RateLimit-Reset');
|
|
48
|
+
if (remainingHeader === null)
|
|
49
|
+
return true;
|
|
50
|
+
const remaining = parseInt(remainingHeader, 10);
|
|
51
|
+
if (isNaN(remaining) || remaining >= 100)
|
|
52
|
+
return true;
|
|
53
|
+
const resetTs = parseInt(resetHeader ?? '0', 10);
|
|
54
|
+
const resetAt = resetTs > 0 ? new Date(resetTs * 1000).toISOString() : 'unknown';
|
|
55
|
+
console.warn(`[GitHubQueuePoller] Rate limit low: remaining=${remaining}, resets at ${resetAt}. ` +
|
|
56
|
+
`Skipping poll cycle to avoid exhaustion.`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
async function pollGitHubQueueIssues(source, config, fetchFn = globalThis.fetch) {
|
|
60
|
+
if (config.type !== 'assignee') {
|
|
61
|
+
return (0, result_js_1.err)({
|
|
62
|
+
kind: 'not_implemented',
|
|
63
|
+
message: `Queue type '${config.type}' is not implemented. Only 'assignee' is supported.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const [owner, repo] = source.repo.split('/');
|
|
67
|
+
const url = new URL(`https://api.github.com/repos/${owner}/${repo}/issues`);
|
|
68
|
+
url.searchParams.set('state', 'open');
|
|
69
|
+
url.searchParams.set('per_page', '100');
|
|
70
|
+
if (config.user) {
|
|
71
|
+
url.searchParams.set('assignee', config.user);
|
|
72
|
+
}
|
|
73
|
+
let response;
|
|
74
|
+
try {
|
|
75
|
+
response = await fetchFn(url.toString(), {
|
|
76
|
+
headers: {
|
|
77
|
+
'Authorization': `Bearer ${source.token}`,
|
|
78
|
+
'Accept': 'application/vnd.github+json',
|
|
79
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
return (0, result_js_1.err)({
|
|
85
|
+
kind: 'network_error',
|
|
86
|
+
message: e instanceof Error ? e.message : String(e),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
return (0, result_js_1.err)({
|
|
91
|
+
kind: 'http_error',
|
|
92
|
+
status: response.status,
|
|
93
|
+
message: `GitHub API returned HTTP ${response.status}: ${response.statusText}`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!checkRateLimit(response)) {
|
|
97
|
+
return (0, result_js_1.ok)([]);
|
|
98
|
+
}
|
|
99
|
+
let raw;
|
|
100
|
+
try {
|
|
101
|
+
raw = await response.json();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
return (0, result_js_1.err)({
|
|
105
|
+
kind: 'parse_error',
|
|
106
|
+
message: `Failed to parse GitHub Issues API response: ${e instanceof Error ? e.message : String(e)}`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(raw)) {
|
|
110
|
+
return (0, result_js_1.err)({
|
|
111
|
+
kind: 'parse_error',
|
|
112
|
+
message: `Expected array from GitHub Issues API, got: ${typeof raw}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const issues = [];
|
|
116
|
+
for (const item of raw) {
|
|
117
|
+
const shaped = toGitHubQueueIssue(item);
|
|
118
|
+
if (shaped !== null) {
|
|
119
|
+
issues.push(shaped);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return (0, result_js_1.ok)(issues);
|
|
123
|
+
}
|
|
124
|
+
function inferMaturity(body) {
|
|
125
|
+
const specLineMatch = /upstream_spec:\s*(https?:\/\/\S+)/i.exec(body);
|
|
126
|
+
if (specLineMatch)
|
|
127
|
+
return 'ready';
|
|
128
|
+
const firstPara = body.split(/\n\s*\n/)[0] ?? '';
|
|
129
|
+
if (/https?:\/\/\S*\/(?:pitch|prd|spec|brd|rfc|design)\b/i.test(firstPara))
|
|
130
|
+
return 'ready';
|
|
131
|
+
if (/- \[ \]/.test(body))
|
|
132
|
+
return 'specced';
|
|
133
|
+
if (/^#{1,6}\s*(Acceptance Criteria|Implementation Plan)\s*$/im.test(body))
|
|
134
|
+
return 'specced';
|
|
135
|
+
return 'idea';
|
|
136
|
+
}
|
|
137
|
+
async function checkIdempotency(issueNumber, sessionsDir = exports.DEFAULT_SESSIONS_DIR) {
|
|
138
|
+
let files;
|
|
139
|
+
try {
|
|
140
|
+
files = await fs.readdir(sessionsDir);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return 'clear';
|
|
144
|
+
}
|
|
145
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
146
|
+
for (const filename of jsonFiles) {
|
|
147
|
+
try {
|
|
148
|
+
const content = await fs.readFile(path.join(sessionsDir, filename), 'utf8');
|
|
149
|
+
const parsed = JSON.parse(content);
|
|
150
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
151
|
+
return 'active';
|
|
152
|
+
}
|
|
153
|
+
const session = parsed;
|
|
154
|
+
const context = session['context'];
|
|
155
|
+
if (typeof context !== 'object' || context === null) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const ctx = context;
|
|
159
|
+
const taskCandidate = ctx['taskCandidate'];
|
|
160
|
+
if (typeof taskCandidate !== 'object' || taskCandidate === null) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const tc = taskCandidate;
|
|
164
|
+
if (tc['issueNumber'] === issueNumber) {
|
|
165
|
+
return 'active';
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return 'active';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return 'clear';
|
|
173
|
+
}
|
|
174
|
+
function toGitHubQueueIssue(item) {
|
|
175
|
+
if (typeof item !== 'object' || item === null)
|
|
176
|
+
return null;
|
|
177
|
+
const obj = item;
|
|
178
|
+
if (typeof obj['id'] !== 'number' ||
|
|
179
|
+
typeof obj['number'] !== 'number' ||
|
|
180
|
+
typeof obj['title'] !== 'string' ||
|
|
181
|
+
typeof obj['html_url'] !== 'string') {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const body = typeof obj['body'] === 'string' ? obj['body'] : '';
|
|
185
|
+
const createdAt = typeof obj['created_at'] === 'string' ? obj['created_at'] : '';
|
|
186
|
+
const rawLabels = Array.isArray(obj['labels']) ? obj['labels'] : [];
|
|
187
|
+
const labels = rawLabels
|
|
188
|
+
.filter((l) => typeof l === 'object' && l !== null)
|
|
189
|
+
.filter((l) => typeof l['name'] === 'string')
|
|
190
|
+
.map((l) => ({ name: l['name'] }));
|
|
191
|
+
return {
|
|
192
|
+
id: obj['id'],
|
|
193
|
+
number: obj['number'],
|
|
194
|
+
title: obj['title'],
|
|
195
|
+
body,
|
|
196
|
+
url: obj['html_url'],
|
|
197
|
+
labels,
|
|
198
|
+
createdAt,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Result } from '../runtime/result.js';
|
|
2
|
+
export interface GitHubQueueConfig {
|
|
3
|
+
readonly type: 'assignee' | 'label' | 'mention' | 'query';
|
|
4
|
+
readonly user?: string;
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
readonly handle?: string;
|
|
7
|
+
readonly search?: string;
|
|
8
|
+
readonly workOnAll?: boolean;
|
|
9
|
+
readonly pollIntervalSeconds: number;
|
|
10
|
+
readonly maxTotalConcurrentSessions: number;
|
|
11
|
+
readonly excludeLabels: readonly string[];
|
|
12
|
+
readonly repo: string;
|
|
13
|
+
readonly token: string;
|
|
14
|
+
readonly botName?: string;
|
|
15
|
+
readonly botEmail?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const WORKRAIL_CONFIG_PATH: string;
|
|
18
|
+
export declare function loadQueueConfig(configPath?: string, env?: Record<string, string | undefined>): Promise<Result<GitHubQueueConfig | null, string>>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WORKRAIL_CONFIG_PATH = void 0;
|
|
37
|
+
exports.loadQueueConfig = loadQueueConfig;
|
|
38
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const os = __importStar(require("node:os"));
|
|
41
|
+
const result_js_1 = require("../runtime/result.js");
|
|
42
|
+
exports.WORKRAIL_CONFIG_PATH = path.join(os.homedir(), '.workrail', 'config.json');
|
|
43
|
+
async function loadQueueConfig(configPath = exports.WORKRAIL_CONFIG_PATH, env = process.env) {
|
|
44
|
+
let raw;
|
|
45
|
+
try {
|
|
46
|
+
raw = await fs.readFile(configPath, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
const error = e;
|
|
50
|
+
if (error.code === 'ENOENT') {
|
|
51
|
+
return (0, result_js_1.ok)(null);
|
|
52
|
+
}
|
|
53
|
+
return (0, result_js_1.err)(`Failed to read config file at ${configPath}: ${error.message ?? String(e)}`);
|
|
54
|
+
}
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return (0, result_js_1.err)(`Failed to parse config JSON at ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
63
|
+
return (0, result_js_1.err)(`Config at ${configPath} is not a JSON object`);
|
|
64
|
+
}
|
|
65
|
+
const config = parsed;
|
|
66
|
+
if (!('queue' in config)) {
|
|
67
|
+
return (0, result_js_1.ok)(null);
|
|
68
|
+
}
|
|
69
|
+
const queue = config['queue'];
|
|
70
|
+
if (typeof queue !== 'object' || queue === null) {
|
|
71
|
+
return (0, result_js_1.err)('config.queue is not an object');
|
|
72
|
+
}
|
|
73
|
+
const q = queue;
|
|
74
|
+
const rawType = q['type'];
|
|
75
|
+
if (typeof rawType !== 'string') {
|
|
76
|
+
return (0, result_js_1.err)('config.queue.type is required and must be a string');
|
|
77
|
+
}
|
|
78
|
+
const VALID_TYPES = new Set(['assignee', 'label', 'mention', 'query']);
|
|
79
|
+
if (!VALID_TYPES.has(rawType)) {
|
|
80
|
+
return (0, result_js_1.err)(`config.queue.type must be one of: assignee, label, mention, query. Got: "${rawType}"`);
|
|
81
|
+
}
|
|
82
|
+
const rawRepo = q['repo'];
|
|
83
|
+
if (typeof rawRepo !== 'string' || !rawRepo.trim()) {
|
|
84
|
+
return (0, result_js_1.err)('config.queue.repo is required and must be a non-empty string');
|
|
85
|
+
}
|
|
86
|
+
const rawToken = q['token'];
|
|
87
|
+
if (typeof rawToken !== 'string' || !rawToken.trim()) {
|
|
88
|
+
return (0, result_js_1.err)('config.queue.token is required and must be a non-empty string');
|
|
89
|
+
}
|
|
90
|
+
let resolvedToken;
|
|
91
|
+
if (rawToken.startsWith('$')) {
|
|
92
|
+
const envVarName = rawToken.slice(1);
|
|
93
|
+
const envValue = env[envVarName];
|
|
94
|
+
if (!envValue) {
|
|
95
|
+
return (0, result_js_1.err)(`config.queue.token references env var $${envVarName} which is unset or empty`);
|
|
96
|
+
}
|
|
97
|
+
resolvedToken = envValue;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
resolvedToken = rawToken.trim();
|
|
101
|
+
}
|
|
102
|
+
const rawPollInterval = q['pollIntervalSeconds'];
|
|
103
|
+
let pollIntervalSeconds = 300;
|
|
104
|
+
if (rawPollInterval !== undefined) {
|
|
105
|
+
if (typeof rawPollInterval !== 'number' || !Number.isInteger(rawPollInterval) || rawPollInterval <= 0) {
|
|
106
|
+
return (0, result_js_1.err)('config.queue.pollIntervalSeconds must be a positive integer');
|
|
107
|
+
}
|
|
108
|
+
pollIntervalSeconds = rawPollInterval;
|
|
109
|
+
}
|
|
110
|
+
const rawMaxConcurrent = q['maxTotalConcurrentSessions'] ?? q['maxConcurrentSelf'];
|
|
111
|
+
let maxTotalConcurrentSessions = 1;
|
|
112
|
+
if (rawMaxConcurrent !== undefined) {
|
|
113
|
+
if (typeof rawMaxConcurrent !== 'number' || !Number.isInteger(rawMaxConcurrent) || rawMaxConcurrent <= 0) {
|
|
114
|
+
return (0, result_js_1.err)('config.queue.maxTotalConcurrentSessions must be a positive integer');
|
|
115
|
+
}
|
|
116
|
+
maxTotalConcurrentSessions = rawMaxConcurrent;
|
|
117
|
+
}
|
|
118
|
+
const rawExcludeLabels = q['excludeLabels'];
|
|
119
|
+
let excludeLabels = [];
|
|
120
|
+
if (rawExcludeLabels !== undefined) {
|
|
121
|
+
if (!Array.isArray(rawExcludeLabels) || !rawExcludeLabels.every(l => typeof l === 'string')) {
|
|
122
|
+
return (0, result_js_1.err)('config.queue.excludeLabels must be an array of strings');
|
|
123
|
+
}
|
|
124
|
+
excludeLabels = rawExcludeLabels;
|
|
125
|
+
}
|
|
126
|
+
const rawUser = q['user'];
|
|
127
|
+
const user = typeof rawUser === 'string' && rawUser.trim() ? rawUser.trim() : undefined;
|
|
128
|
+
const rawName = q['name'];
|
|
129
|
+
const labelName = typeof rawName === 'string' && rawName.trim() ? rawName.trim() : undefined;
|
|
130
|
+
const rawHandle = q['handle'];
|
|
131
|
+
const handle = typeof rawHandle === 'string' && rawHandle.trim() ? rawHandle.trim() : undefined;
|
|
132
|
+
const rawSearch = q['search'];
|
|
133
|
+
const search = typeof rawSearch === 'string' && rawSearch.trim() ? rawSearch.trim() : undefined;
|
|
134
|
+
const rawWorkOnAll = q['workOnAll'];
|
|
135
|
+
const workOnAll = rawWorkOnAll === true;
|
|
136
|
+
const rawBotName = q['botName'];
|
|
137
|
+
const botName = typeof rawBotName === 'string' && rawBotName.trim() ? rawBotName.trim() : undefined;
|
|
138
|
+
const rawBotEmail = q['botEmail'];
|
|
139
|
+
const botEmail = typeof rawBotEmail === 'string' && rawBotEmail.trim() ? rawBotEmail.trim() : undefined;
|
|
140
|
+
return (0, result_js_1.ok)({
|
|
141
|
+
type: rawType,
|
|
142
|
+
...(user !== undefined ? { user } : {}),
|
|
143
|
+
...(labelName !== undefined ? { name: labelName } : {}),
|
|
144
|
+
...(handle !== undefined ? { handle } : {}),
|
|
145
|
+
...(search !== undefined ? { search } : {}),
|
|
146
|
+
...(workOnAll ? { workOnAll } : {}),
|
|
147
|
+
pollIntervalSeconds,
|
|
148
|
+
maxTotalConcurrentSessions,
|
|
149
|
+
excludeLabels,
|
|
150
|
+
repo: rawRepo.trim(),
|
|
151
|
+
token: resolvedToken,
|
|
152
|
+
...(botName !== undefined ? { botName } : {}),
|
|
153
|
+
...(botEmail !== undefined ? { botEmail } : {}),
|
|
154
|
+
});
|
|
155
|
+
}
|