jules-orchestrator-kit 0.5.2 → 0.6.1

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/scripts/utils.mjs CHANGED
@@ -92,3 +92,184 @@ export function resolveMarkdownConflict(content) {
92
92
 
93
93
  return result.join("\n");
94
94
  }
95
+
96
+ export function calculateShannonEntropy(str) {
97
+ const len = str.length;
98
+ if (len === 0) return 0;
99
+ const frequencies = {};
100
+ for (let i = 0; i < len; i++) {
101
+ frequencies[str[i]] = (frequencies[str[i]] || 0) + 1;
102
+ }
103
+ return Object.values(frequencies).reduce((sum, count) => {
104
+ const p = count / len;
105
+ return sum - p * Math.log2(p);
106
+ }, 0);
107
+ }
108
+
109
+ export const HIGH_CONFIDENCE_PATTERNS = [
110
+ /\bgh[pousr]_[a-zA-Z0-9]{36,}\b/g,
111
+ /\bgithub_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}\b/g,
112
+ /\bAKIA[0-9A-Z]{16}\b/g,
113
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
114
+ /\bnpm_[a-zA-Z0-9]{36}\b/g,
115
+ /\bsk_live_[0-9a-zA-Z]{24,}\b/g,
116
+ ];
117
+
118
+ export const LOW_CONFIDENCE_PATTERNS = [
119
+ /\bAIza[0-9A-Za-z_-]{30,40}\b/g,
120
+ /\bya29\.[a-zA-Z0-9_\-]{20,}\b/g,
121
+ /\bBearer\s+[a-zA-Z0-9\-\._~+\/]+=*/g,
122
+ /\bsk-(?:ant-api03-|proj-|svcacct-)[a-zA-Z0-9\-\_]{32,}\b/g,
123
+ /\bxox[baprs]-[a-zA-Z0-9\-]{10,}\b/g,
124
+ /\beyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\b/g,
125
+ /\b(?:rk|pk)_(?:live|test)_[0-9a-zA-Z]{24,}\b/g,
126
+ /\bsk_test_[0-9a-zA-Z]{24,}\b/g,
127
+ ];
128
+
129
+ export function hasHighConfidenceSecret(text) {
130
+ if (!text) return false;
131
+ return HIGH_CONFIDENCE_PATTERNS.some((pat) => {
132
+ pat.lastIndex = 0;
133
+ return pat.test(text);
134
+ });
135
+ }
136
+
137
+ export function hasLowConfidenceSecret(text) {
138
+ if (!text) return false;
139
+ return LOW_CONFIDENCE_PATTERNS.some((pat) => {
140
+ pat.lastIndex = 0;
141
+ return pat.test(text);
142
+ });
143
+ }
144
+
145
+ export function redactSecrets(text) {
146
+ if (!text) return "";
147
+ let sanitized = text;
148
+
149
+ for (const [envKey, envVal] of Object.entries(process.env)) {
150
+ if (
151
+ envVal &&
152
+ (envVal.length >= 20 || calculateShannonEntropy(envVal) > 3.6) &&
153
+ /KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL|AUTH/i.test(envKey)
154
+ ) {
155
+ if (sanitized.includes(envVal)) {
156
+ sanitized = sanitized.split(envVal).join("[REDACTED_ENV_SECRET]");
157
+ }
158
+ }
159
+ }
160
+
161
+ const allPatterns = [...HIGH_CONFIDENCE_PATTERNS, ...LOW_CONFIDENCE_PATTERNS];
162
+ for (const pat of allPatterns) {
163
+ sanitized = sanitized.replace(pat, "[REDACTED_BY_SECURITY_GATE]");
164
+ }
165
+ return sanitized;
166
+ }
167
+
168
+ export function appendLedger(ledgerName, payload) {
169
+ const stateDir = path.resolve(process.env.JULES_PROJECT_ROOT || process.cwd(), ".agent/state");
170
+ ensureDir(stateDir);
171
+ const ledgerPath = path.join(stateDir, `${ledgerName}.jsonl`);
172
+ const entry = JSON.stringify({
173
+ timestamp: new Date().toISOString(),
174
+ ...payload
175
+ }) + "\n";
176
+ fs.appendFileSync(ledgerPath, entry, "utf-8");
177
+ return ledgerPath;
178
+ }
179
+
180
+ export function checkDailyBudget(maxSessions = 300) {
181
+ const dateStr = new Date().toISOString().split("T")[0];
182
+ const stateDir = path.resolve(process.env.JULES_PROJECT_ROOT || process.cwd(), ".agent/state");
183
+ const sessionsLedger = path.join(stateDir, "sessions.jsonl");
184
+
185
+ if (!fs.existsSync(sessionsLedger)) {
186
+ return { ok: true, used: 0, budget: maxSessions };
187
+ }
188
+
189
+ const content = fs.readFileSync(sessionsLedger, "utf-8");
190
+ const lines = content.split("\n").filter(Boolean);
191
+
192
+ let usedToday = 0;
193
+ for (const line of lines) {
194
+ try {
195
+ const entry = JSON.parse(line);
196
+ if (
197
+ entry.timestamp &&
198
+ entry.timestamp.startsWith(dateStr) &&
199
+ (entry.event === "budget_reserved" || !entry.event)
200
+ ) {
201
+ usedToday++;
202
+ }
203
+ } catch (e) {
204
+ // Ignore
205
+ }
206
+ }
207
+
208
+ return { ok: usedToday < maxSessions, used: usedToday, budget: maxSessions };
209
+ }
210
+
211
+ function sleepSync(ms) {
212
+ try {
213
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
214
+ } catch (_) {}
215
+ }
216
+
217
+ export function reserveDailyBudget(maxSessions = 300, taskKey = "") {
218
+ const stateDir = path.resolve(process.env.JULES_PROJECT_ROOT || process.cwd(), ".agent/state");
219
+ ensureDir(stateDir);
220
+
221
+ const lockFile = path.join(stateDir, "budget.lock");
222
+ let fd;
223
+ const maxLockAttempts = 10;
224
+
225
+ for (let attempt = 0; attempt < maxLockAttempts; attempt++) {
226
+ try {
227
+ try {
228
+ const stat = fs.statSync(lockFile);
229
+ if (Date.now() - stat.mtimeMs > 30000) {
230
+ fs.rmSync(lockFile, { force: true });
231
+ }
232
+ } catch (_) {}
233
+
234
+ fd = fs.openSync(lockFile, "wx");
235
+ break;
236
+ } catch (err) {
237
+ if (err.code !== "EEXIST") throw err;
238
+ if (attempt < maxLockAttempts - 1) {
239
+ sleepSync(Math.floor(50 + Math.random() * 50));
240
+ }
241
+ }
242
+ }
243
+
244
+ if (fd === undefined) {
245
+ return { ok: false, reason: "locked", used: null, budget: maxSessions };
246
+ }
247
+
248
+ try {
249
+ const check = checkDailyBudget(maxSessions);
250
+ if (!check.ok) {
251
+ return check;
252
+ }
253
+
254
+ appendLedger("sessions", {
255
+ event: "budget_reserved",
256
+ task_key: taskKey,
257
+ status: "reserved"
258
+ });
259
+
260
+ return {
261
+ ok: true,
262
+ used: check.used + 1,
263
+ budget: maxSessions
264
+ };
265
+ } finally {
266
+ if (fd !== undefined) {
267
+ try {
268
+ fs.closeSync(fd);
269
+ } catch {}
270
+ try {
271
+ fs.rmSync(lockFile, { force: true });
272
+ } catch {}
273
+ }
274
+ }
275
+ }