jules-orchestrator-kit 0.27.0 → 0.28.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/README.md +226 -108
- package/bin/agentctl.mjs +77 -12
- package/bin/init.js +10 -8
- package/index.mjs +5 -2
- package/package.json +1 -1
- package/scripts/jules-dispatch.mjs +6 -4
- package/scripts/jules-self-audit.mjs +14 -3
- package/src/config.mjs +14 -1
- package/src/dashboard.mjs +7 -14
- package/src/engine.mjs +16 -10
- package/src/git.mjs +65 -48
- package/src/mcp.mjs +1 -1
- package/src/prompt-guard.mjs +4 -1
- package/src/provider.mjs +42 -10
- package/src/process-group.mjs +0 -80
package/src/provider.mjs
CHANGED
|
@@ -3,15 +3,20 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
export const JULES_PRESET = {
|
|
4
4
|
name: "jules",
|
|
5
5
|
type: "http",
|
|
6
|
-
url: "https://jules.googleapis.com/
|
|
6
|
+
url: "https://jules.googleapis.com/v1alpha/sessions",
|
|
7
7
|
headers: {
|
|
8
|
-
"
|
|
8
|
+
"X-Goog-Api-Key": "{token}",
|
|
9
9
|
"Content-Type": "application/json",
|
|
10
10
|
},
|
|
11
11
|
bodyTemplate: {
|
|
12
|
-
prompt: "{prompt}",
|
|
13
12
|
title: "{title}",
|
|
14
|
-
|
|
13
|
+
prompt: "{prompt}",
|
|
14
|
+
sourceContext: {
|
|
15
|
+
source: "{source}",
|
|
16
|
+
githubRepoContext: {
|
|
17
|
+
startingBranch: "{branch}",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
15
20
|
},
|
|
16
21
|
};
|
|
17
22
|
|
|
@@ -31,6 +36,14 @@ export const CODEX_PRESET = {
|
|
|
31
36
|
promptViaStdin: false,
|
|
32
37
|
};
|
|
33
38
|
|
|
39
|
+
export class MissingApiKeyError extends Error {
|
|
40
|
+
constructor(message = "JULES_API_KEY environment variable is required for Google Jules API dispatch.") {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "MissingApiKeyError";
|
|
43
|
+
this.status = 401;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
export class ProviderRateLimitError extends Error {
|
|
35
48
|
constructor(message, opts = {}) {
|
|
36
49
|
super(message);
|
|
@@ -92,6 +105,9 @@ function interpolateDeep(node, data) {
|
|
|
92
105
|
* Creates an HTTP or Exec provider adapter based on spec/config.
|
|
93
106
|
*/
|
|
94
107
|
export function createProvider(spec = "jules", config = {}) {
|
|
108
|
+
if (spec && typeof spec === "object" && typeof spec.dispatch === "function") {
|
|
109
|
+
return spec;
|
|
110
|
+
}
|
|
95
111
|
const providerSpec = typeof spec === "string"
|
|
96
112
|
? (spec === "claude-code" ? CLAUDE_PRESET : spec === "codex" ? CODEX_PRESET : JULES_PRESET)
|
|
97
113
|
: spec;
|
|
@@ -100,11 +116,21 @@ export function createProvider(spec = "jules", config = {}) {
|
|
|
100
116
|
name: providerSpec.name || "custom-provider",
|
|
101
117
|
|
|
102
118
|
async dispatch(task, ctx = {}) {
|
|
103
|
-
const rawToken = process.env.JULES_API_KEY || process.env.GEMINI_API_KEY || "";
|
|
119
|
+
const rawToken = process.env.JULES_API_KEY || (ctx.allowLegacyKey ? process.env.GEMINI_API_KEY : "") || "";
|
|
120
|
+
if (!rawToken && !ctx.dryRun && providerSpec.name === "jules") {
|
|
121
|
+
throw new MissingApiKeyError();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const rawRepo = task.source || ctx.source || process.env.JULES_REPO || "";
|
|
125
|
+
const sourceName = rawRepo
|
|
126
|
+
? (rawRepo.startsWith("sources/") ? rawRepo : `sources/github/${rawRepo}`)
|
|
127
|
+
: "sources/github/default/repo";
|
|
128
|
+
|
|
104
129
|
const data = {
|
|
105
130
|
prompt: task.prompt || "",
|
|
106
131
|
title: task.title || "Agent Task",
|
|
107
132
|
branch: task.branch || (config.branchPrefix ? config.branchPrefix + "task" : "agent/task"),
|
|
133
|
+
source: sourceName,
|
|
108
134
|
project: process.env.JULES_PROJECT_ID || "default",
|
|
109
135
|
...ctx,
|
|
110
136
|
};
|
|
@@ -135,19 +161,25 @@ export function createProvider(spec = "jules", config = {}) {
|
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
let body;
|
|
164
|
+
let bodyObj;
|
|
138
165
|
if (typeof providerSpec.bodyTemplate === "string") {
|
|
139
166
|
try {
|
|
140
167
|
const parsedObj = JSON.parse(providerSpec.bodyTemplate);
|
|
141
|
-
|
|
142
|
-
body = JSON.stringify(interpolatedObj);
|
|
168
|
+
bodyObj = interpolateDeep(parsedObj, data);
|
|
143
169
|
} catch (_) {
|
|
144
170
|
body = interpolateString(providerSpec.bodyTemplate, data);
|
|
145
171
|
}
|
|
146
172
|
} else if (providerSpec.bodyTemplate) {
|
|
147
|
-
|
|
148
|
-
body = JSON.stringify(interpolatedObj);
|
|
173
|
+
bodyObj = interpolateDeep(providerSpec.bodyTemplate, data);
|
|
149
174
|
} else {
|
|
150
|
-
|
|
175
|
+
bodyObj = { ...data };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (bodyObj && typeof bodyObj === "object") {
|
|
179
|
+
if (data.repoless || ctx.repoless || task.repoless) {
|
|
180
|
+
delete bodyObj.sourceContext;
|
|
181
|
+
}
|
|
182
|
+
body = JSON.stringify(bodyObj);
|
|
151
183
|
}
|
|
152
184
|
|
|
153
185
|
const timeoutMs = ctx.timeoutMs || config.timeoutMs || providerSpec.timeoutMs || 120_000;
|
package/src/process-group.mjs
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ProcessGroupManager manages child processes in isolated process groups
|
|
5
|
-
* and ensures 100% leak-free cleanup on process exit or termination signals.
|
|
6
|
-
*/
|
|
7
|
-
export class ProcessGroupManager {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.activePgids = new Set();
|
|
10
|
-
this.activePids = new Set();
|
|
11
|
-
this.installed = false;
|
|
12
|
-
this.installSignalHandlers();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
installSignalHandlers() {
|
|
16
|
-
if (this.installed) return;
|
|
17
|
-
this.installed = true;
|
|
18
|
-
|
|
19
|
-
const cleanup = (signal) => {
|
|
20
|
-
this.killAll(signal || "SIGTERM");
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
process.once("SIGINT", () => {
|
|
24
|
-
cleanup("SIGINT");
|
|
25
|
-
process.exit(130);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
process.once("SIGTERM", () => {
|
|
29
|
-
cleanup("SIGTERM");
|
|
30
|
-
process.exit(143);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
process.once("exit", () => {
|
|
34
|
-
this.killAll("SIGKILL");
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
spawnProcess(command, args = [], options = {}) {
|
|
39
|
-
const opts = {
|
|
40
|
-
...options,
|
|
41
|
-
detached: true,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const child = spawn(command, args, opts);
|
|
45
|
-
|
|
46
|
-
if (child.pid) {
|
|
47
|
-
const pgid = child.pid;
|
|
48
|
-
this.activePgids.add(pgid);
|
|
49
|
-
this.activePids.add(child.pid);
|
|
50
|
-
|
|
51
|
-
child.on("exit", () => {
|
|
52
|
-
this.activePgids.delete(pgid);
|
|
53
|
-
this.activePids.delete(child.pid);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
child.on("error", () => {
|
|
57
|
-
this.activePgids.delete(pgid);
|
|
58
|
-
this.activePids.delete(child.pid);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return child;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
killAll(signal = "SIGTERM") {
|
|
66
|
-
for (const pgid of this.activePgids) {
|
|
67
|
-
try {
|
|
68
|
-
process.kill(-pgid, signal);
|
|
69
|
-
} catch (_) {
|
|
70
|
-
try {
|
|
71
|
-
process.kill(pgid, signal);
|
|
72
|
-
} catch (_) {}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
this.activePgids.clear();
|
|
76
|
-
this.activePids.clear();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const processGroupManager = new ProcessGroupManager();
|