jules-orchestrator-kit 0.75.0 → 0.75.2
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/CHANGELOG.md +28 -1
- package/JULES_RULES_TEMPLATE.md +3 -0
- package/README.md +4 -2
- package/bin/agentctl.mjs +52 -24
- package/package.json +2 -2
- package/scripts/ci-scope-guard.mjs +197 -17
- package/scripts/run-tests.mjs +5 -1
- package/src/ci-templates.mjs +185 -21
- package/src/engine.mjs +167 -4
- package/src/envelope.mjs +17 -1
- package/src/evidence.mjs +22 -5
- package/src/ops/command-registry.mjs +4 -1
- package/src/ops/tui-menu.mjs +4 -2
- package/src/prompt-guard.mjs +9 -0
- package/src/provider.mjs +14 -0
- package/src/secret-scanner.mjs +13 -1
- package/src/security.mjs +4 -0
- package/src/state.mjs +23 -9
- package/src/wizard-task.mjs +9 -20
package/src/secret-scanner.mjs
CHANGED
|
@@ -254,6 +254,11 @@ export function secretScanVariants(addedLines) {
|
|
|
254
254
|
// Collapse method concatenations like .concat("...") or .join("")
|
|
255
255
|
dejoined = dejoined.replace(/\.concat\(\s*["'`]/g, "").replace(/\.join\(\s*["'`]{2}\s*\)/g, "");
|
|
256
256
|
|
|
257
|
+
// Collapse array elements like ["A", "B"]
|
|
258
|
+
dejoined = dejoined.replace(/["'`]\s*,\s*["'`]/g, "");
|
|
259
|
+
// Collapse split variables across newlines (e.g. const a = "AKIA"; const b = "...")
|
|
260
|
+
dejoined = dejoined.replace(/["'`]\s*[,;+]*\s*[\r\n]+\s*(?:(?:export\s+)?(?:const|let|var)\s+[A-Za-z0-9_]+\s*=\s*)?["'`]/g, "");
|
|
261
|
+
|
|
257
262
|
// Collapse whitespace/newlines between adjacent base64 characters (including line-wrapped PEM/base64, template literals, and quoted string chunks)
|
|
258
263
|
const base64Dejoined = stripped
|
|
259
264
|
.replace(/([A-Za-z0-9+/=_-])\s*[\r\n]+\s*(?=[A-Za-z0-9+/=_-])/g, "$1")
|
|
@@ -266,9 +271,16 @@ export function secretScanVariants(addedLines) {
|
|
|
266
271
|
// source-level join AND spelled with homoglyphs still surfaces.
|
|
267
272
|
const confusable = normalizeSecretText(stripped);
|
|
268
273
|
const confusableDejoined = normalizeSecretText(dejoined);
|
|
274
|
+
const uniDecoded = dejoined
|
|
275
|
+
.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
|
|
276
|
+
.replace(/\\x([0-9a-fA-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
277
|
+
|
|
278
|
+
const stringLiteralsCollapsed = (stripped.match(/(["'`])(?:(?!\1)[^\n])*\1/g) || [])
|
|
279
|
+
.map(s => s.slice(1, -1))
|
|
280
|
+
.join('');
|
|
269
281
|
|
|
270
282
|
return {
|
|
271
|
-
all: [...new Set([addedLines, stripped, dejoined, base64Dejoined, hexDecoded, pctDecoded, confusable, confusableDejoined])],
|
|
283
|
+
all: [...new Set([addedLines, stripped, dejoined, base64Dejoined, hexDecoded, pctDecoded, confusable, confusableDejoined, uniDecoded, stringLiteralsCollapsed])],
|
|
272
284
|
normalized: dejoined,
|
|
273
285
|
base64Normalized: base64Dejoined,
|
|
274
286
|
};
|
package/src/security.mjs
CHANGED
|
@@ -167,6 +167,10 @@ function splitDiffByFile(diffText) {
|
|
|
167
167
|
current.lines.push({ text: line.slice(1), no: lineNo });
|
|
168
168
|
if (lineNo !== null) lineNo++;
|
|
169
169
|
} else if (lineNo !== null && !line.startsWith("-") && !line.startsWith("\\")) {
|
|
170
|
+
if (line.startsWith(" ")) {
|
|
171
|
+
if (!current) select(null);
|
|
172
|
+
current.lines.push({ text: line.slice(1), no: lineNo });
|
|
173
|
+
}
|
|
170
174
|
lineNo++; // A context line advances the post-image just as an added one does.
|
|
171
175
|
}
|
|
172
176
|
}
|
package/src/state.mjs
CHANGED
|
@@ -233,24 +233,36 @@ export function withVfsMutex(mutexDir, fn, opts = {}) {
|
|
|
233
233
|
const maxRetries = opts.maxRetries || 200;
|
|
234
234
|
const retryDelayMs = opts.retryDelayMs || 10;
|
|
235
235
|
|
|
236
|
+
const staleMs = opts.staleMs || 2000;
|
|
237
|
+
|
|
236
238
|
for (let i = 0; i < maxRetries; i++) {
|
|
239
|
+
let acquired = false;
|
|
237
240
|
try {
|
|
238
241
|
mkdirSync(mutexDir);
|
|
239
|
-
|
|
240
|
-
return fn();
|
|
241
|
-
} finally {
|
|
242
|
-
try {
|
|
243
|
-
rmdirSync(mutexDir);
|
|
244
|
-
} catch (_) {}
|
|
245
|
-
}
|
|
242
|
+
acquired = true;
|
|
246
243
|
} catch (err) {
|
|
247
244
|
if (err.code === "EEXIST") {
|
|
245
|
+
try {
|
|
246
|
+
if (Date.now() - statSync(mutexDir).mtimeMs > staleMs) {
|
|
247
|
+
rmdirSync(mutexDir);
|
|
248
|
+
}
|
|
249
|
+
} catch (_) {}
|
|
248
250
|
const deadline = Date.now() + retryDelayMs;
|
|
249
251
|
while (Date.now() < deadline) {}
|
|
250
252
|
continue;
|
|
251
253
|
}
|
|
252
254
|
throw err;
|
|
253
255
|
}
|
|
256
|
+
|
|
257
|
+
if (acquired) {
|
|
258
|
+
try {
|
|
259
|
+
return fn();
|
|
260
|
+
} finally {
|
|
261
|
+
try {
|
|
262
|
+
rmdirSync(mutexDir);
|
|
263
|
+
} catch (_) {}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
254
266
|
}
|
|
255
267
|
throw new MutexTimeoutError(`Failed to acquire VFS mutex lock at ${mutexDir} after ${maxRetries} retries`);
|
|
256
268
|
}
|
|
@@ -634,9 +646,10 @@ export function acquireLock(agentName, taskId, files = [], rootOrOpts = resolveR
|
|
|
634
646
|
const root = typeof rootOrOpts === "string" ? rootOrOpts : (rootOrOpts?.root || resolveRoot());
|
|
635
647
|
const branch = (typeof rootOrOpts === "object" && rootOrOpts?.branch) || opts?.branch || process.env.JULES_BRANCH || process.env.BRANCH_NAME || "";
|
|
636
648
|
const lockDir = getLockDir(root);
|
|
637
|
-
|
|
649
|
+
return withVfsMutex(join(lockDir, ".acquire.mutex"), () => {
|
|
650
|
+
const lockFile = join(lockDir, `${taskId}.json`);
|
|
638
651
|
|
|
639
|
-
|
|
652
|
+
if (existsSync(lockFile)) {
|
|
640
653
|
try {
|
|
641
654
|
const existing = JSON.parse(readFileSync(lockFile, "utf-8"));
|
|
642
655
|
if (isLockLive(existing)) {
|
|
@@ -740,6 +753,7 @@ export function acquireLock(agentName, taskId, files = [], rootOrOpts = resolveR
|
|
|
740
753
|
try { closeSync(fd); } catch (_) {}
|
|
741
754
|
}
|
|
742
755
|
}
|
|
756
|
+
});
|
|
743
757
|
}
|
|
744
758
|
|
|
745
759
|
export function releaseLock(taskId, rootOrOpts = resolveRoot()) {
|
package/src/wizard-task.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
2
|
-
import { resolve
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
3
|
import { untrackedOnboardingArtifacts } from "./git.mjs";
|
|
4
4
|
import { loadConfig } from "./config.mjs";
|
|
5
5
|
import { gate } from "./engine.mjs";
|
|
@@ -220,33 +220,22 @@ export function planTaskCreate(root = process.cwd(), inputObj = {}) {
|
|
|
220
220
|
const rawId = inputObj.id || `TASK-${Date.now().toString(36).toUpperCase()}`;
|
|
221
221
|
const taskId = String(rawId).replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
222
222
|
|
|
223
|
-
const
|
|
224
|
-
const hasWrangler =
|
|
225
|
-
existsSync(join(root, "wrangler.jsonc")) ||
|
|
226
|
-
existsSync(join(root, "wrangler.json")) ||
|
|
227
|
-
existsSync(join(root, "wrangler.toml"));
|
|
228
|
-
|
|
223
|
+
const configInvariants = Array.isArray(config.invariants) ? config.invariants : [];
|
|
229
224
|
const autoInvariants = [
|
|
230
225
|
"Preserve existing signatures, surrounding logic, and coding style.",
|
|
231
226
|
"Do not modify files outside scope.allow.",
|
|
227
|
+
...configInvariants,
|
|
232
228
|
];
|
|
233
|
-
if (hasAstro) {
|
|
234
|
-
autoInvariants.push("Zero-JS runtime-princip: skicka noll onödig klient-JS i src/ och använd plana <img> framför Astro <Image>.");
|
|
235
|
-
}
|
|
236
|
-
if (hasWrangler) {
|
|
237
|
-
autoInvariants.push("Edge runtime constraints: inga otillåtna Node-imports (t.ex. node:fs, node:path, sharp) i workerd runtime-kod.");
|
|
238
|
-
}
|
|
239
229
|
const invariants = Array.isArray(inputObj.invariants) && inputObj.invariants.length > 0
|
|
240
230
|
? inputObj.invariants
|
|
241
231
|
: autoInvariants;
|
|
242
232
|
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const mcpDirectives = inputObj.mcpDirectives || inputObj.mcp_directives || (autoMcpDirectives.length > 0 ? autoMcpDirectives : undefined);
|
|
233
|
+
const configDirectives = Array.isArray(config.mcp_directives)
|
|
234
|
+
? config.mcp_directives
|
|
235
|
+
: Array.isArray(config.mcpDirectives)
|
|
236
|
+
? config.mcpDirectives
|
|
237
|
+
: [];
|
|
238
|
+
const mcpDirectives = inputObj.mcpDirectives || inputObj.mcp_directives || (configDirectives.length > 0 ? configDirectives : undefined);
|
|
250
239
|
const mcpDirectiveLine = mcpDirectives && mcpDirectives.length > 0
|
|
251
240
|
? `MCP DIRECTIVE: Mandating pre-execution documentation lookup via [${mcpDirectives.join(" | ")}] before modifying code.\n\n`
|
|
252
241
|
: "";
|