@waishnav/devspace 1.0.2 → 1.0.4
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 +43 -7
- package/dist/apply-patch.js +402 -0
- package/dist/cli.js +256 -4
- package/dist/config.js +17 -22
- package/dist/db/migrations.js +34 -0
- package/dist/db/schema.js +19 -0
- package/dist/local-agent-adapters.js +653 -0
- package/dist/local-agent-availability.js +119 -0
- package/dist/local-agent-path.js +24 -0
- package/dist/local-agent-profiles.js +135 -0
- package/dist/local-agent-runtime.js +48 -0
- package/dist/local-agent-store.js +145 -0
- package/dist/local-agent-targets.js +80 -0
- package/dist/process-platform.js +49 -0
- package/dist/process-sessions.js +337 -0
- package/dist/roots.js +5 -2
- package/dist/server.js +521 -243
- package/dist/skills.js +48 -4
- package/dist/ui/.vite/manifest.json +281 -281
- package/dist/ui/assets/{angular-html-Dj4LysSE.js → angular-html-CKeFzaPY.js} +1 -1
- package/dist/ui/assets/{angular-ts-CxuC_FFh.js → angular-ts-0wcM_Zxf.js} +1 -1
- package/dist/ui/assets/{apl-RwuvNFFo.js → apl-C7UdkNb2.js} +1 -1
- package/dist/ui/assets/{astro-ncoaUbsO.js → astro-DMqju4MJ.js} +1 -1
- package/dist/ui/assets/{blade-Dw8-Lzux.js → blade-DHanNgN5.js} +1 -1
- package/dist/ui/assets/{c-Dpbbo_f2.js → c-_iPKYPcx.js} +1 -1
- package/dist/ui/assets/{cobol-4ddX3THy.js → cobol-CagdLJsE.js} +1 -1
- package/dist/ui/assets/{coffee-Bn-a5KqK.js → coffee-CkNGriPi.js} +1 -1
- package/dist/ui/assets/{cpp-DoRrlM_T.js → cpp-CqmKOWIQ.js} +1 -1
- package/dist/ui/assets/{crystal-DiZfCxn-.js → crystal-DbAbDEO1.js} +1 -1
- package/dist/ui/assets/{css-DiQoSjDH.js → css-xwljL6Kw.js} +1 -1
- package/dist/ui/assets/{edge-wEhCUoEt.js → edge-CX3sYz15.js} +1 -1
- package/dist/ui/assets/{elixir-C3obhm0H.js → elixir-B3qL9hqd.js} +1 -1
- package/dist/ui/assets/{elm-DyTBfh1L.js → elm-CH4vrjBp.js} +1 -1
- package/dist/ui/assets/{erb-f4TnXRuY.js → erb-CupHqynN.js} +1 -1
- package/dist/ui/assets/{git-rebase-vhZbWNfp.js → git-rebase-CDxsU2LC.js} +1 -1
- package/dist/ui/assets/{glimmer-js-BG7puL6Y.js → glimmer-js-emkga3g1.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-CNCfLLL1.js → glimmer-ts-D8kQDnUw.js} +1 -1
- package/dist/ui/assets/{glsl-DhT76AL5.js → glsl-BtfTaPhi.js} +1 -1
- package/dist/ui/assets/{graphql-CJX08w8y.js → graphql-DEus8iSO.js} +1 -1
- package/dist/ui/assets/{hack-DY4NGCCL.js → hack-DI8qpBqE.js} +1 -1
- package/dist/ui/assets/{haml-DwSkmVcG.js → haml-Br1i1gmi.js} +1 -1
- package/dist/ui/assets/{handlebars-CQ_1dEmi.js → handlebars-Ce23mu9x.js} +1 -1
- package/dist/ui/assets/{heavy-payload-2rGBiitA.js → heavy-payload-BcZ_nGfF.js} +1 -1
- package/dist/ui/assets/{html-B_e3Njif.js → html-RXVmfixf.js} +1 -1
- package/dist/ui/assets/{html-derivative-_nHZ_Bk-.js → html-derivative-D1aqeobb.js} +1 -1
- package/dist/ui/assets/{http-CfcvPIru.js → http-BebG00CE.js} +1 -1
- package/dist/ui/assets/{hurl-9vWDQIMO.js → hurl-kHMuvenL.js} +1 -1
- package/dist/ui/assets/{java-C4S5r6L5.js → java-DYc2V5RJ.js} +1 -1
- package/dist/ui/assets/{javascript-BRwqbMMC.js → javascript-BcfzTzr3.js} +1 -1
- package/dist/ui/assets/{jinja-DHxzMMc5.js → jinja-D2RhihUZ.js} +1 -1
- package/dist/ui/assets/{jison-B1DqeWXb.js → jison-BuoFEePN.js} +1 -1
- package/dist/ui/assets/{json-DcFTkEdd.js → json-BIDkCuJA.js} +1 -1
- package/dist/ui/assets/{jsx-DgfmYa9f.js → jsx-DHCL2HA0.js} +1 -1
- package/dist/ui/assets/{julia-3M8SJ8iF.js → julia-CvUQXscF.js} +1 -1
- package/dist/ui/assets/{just-UyH7_DNz.js → just-etc9R2qw.js} +1 -1
- package/dist/ui/assets/{latex-wUbha6EX.js → latex-BB3zRnxC.js} +1 -1
- package/dist/ui/assets/{liquid-DqNTB9kN.js → liquid-CIi5Mvpv.js} +1 -1
- package/dist/ui/assets/{lua-DrEQ1QTW.js → lua-Cox7XDiW.js} +1 -1
- package/dist/ui/assets/{marko-B3GJw9Md.js → marko-ruBIRV0n.js} +1 -1
- package/dist/ui/assets/{mdc-BnThk82m.js → mdc-CcbvB9U5.js} +1 -1
- package/dist/ui/assets/{nginx-B5IeSsOX.js → nginx-DtWz6ZYE.js} +1 -1
- package/dist/ui/assets/{nim-BJraatnS.js → nim-BV5BR4P7.js} +1 -1
- package/dist/ui/assets/{perl-BAhrR4gt.js → perl-BvzoSaJ4.js} +1 -1
- package/dist/ui/assets/{php-BKRCk_wX.js → php-C-clrAOy.js} +1 -1
- package/dist/ui/assets/{pug-B8gmSV7z.js → pug-E07_GXAr.js} +1 -1
- package/dist/ui/assets/{qml-DKrxIjHR.js → qml-dqt33dti.js} +1 -1
- package/dist/ui/assets/{r-YASBZvTH.js → r-BcVQxsAw.js} +1 -1
- package/dist/ui/assets/{razor-P-XFZqh6.js → razor-BW5T_UZ-.js} +1 -1
- package/dist/ui/assets/{regexp-CCpiH6nO.js → regexp-Ed_DEsuU.js} +1 -1
- package/dist/ui/assets/{review-payload-BLYwg6t8.js → review-payload-Dsmx33pY.js} +1 -1
- package/dist/ui/assets/{rst-CFAzckbn.js → rst-BkcGqLs8.js} +1 -1
- package/dist/ui/assets/{ruby-Dyp_Rx-S.js → ruby-Sf_X_YID.js} +1 -1
- package/dist/ui/assets/{sas-YkWZIXxT.js → sas-COsombsA.js} +1 -1
- package/dist/ui/assets/{scss-Cf89idGl.js → scss-K6Ylfa26.js} +1 -1
- package/dist/ui/assets/{shellscript-BFMb52q8.js → shellscript-D2EdqkgV.js} +1 -1
- package/dist/ui/assets/{shellsession-DuthW-5v.js → shellsession-jRXgXrnI.js} +1 -1
- package/dist/ui/assets/{soy-BohVVlJU.js → soy-BGspKQWW.js} +1 -1
- package/dist/ui/assets/{sql-CnK9R2Gt.js → sql-C4lAmYnM.js} +1 -1
- package/dist/ui/assets/{stata-D-sV4Vbr.js → stata-DQWvRqxp.js} +1 -1
- package/dist/ui/assets/{surrealql-ChAy0gHv.js → surrealql-OmKt9XJ_.js} +1 -1
- package/dist/ui/assets/{svelte-3HBVqG-H.js → svelte-D0rUmwDI.js} +1 -1
- package/dist/ui/assets/{templ-BixlV4Cp.js → templ-B-U0RMc0.js} +1 -1
- package/dist/ui/assets/{tex-DWoJKHvD.js → tex-BIHD-btS.js} +1 -1
- package/dist/ui/assets/{ts-tags-DLA8eH13.js → ts-tags-CGuDWfPC.js} +1 -1
- package/dist/ui/assets/{tsx-BYWa5JUz.js → tsx-B0-h_HlD.js} +1 -1
- package/dist/ui/assets/{twig-CnY65vAo.js → twig-BN2qDJWN.js} +1 -1
- package/dist/ui/assets/{typescript-ByInUsro.js → typescript-BRnwPzY3.js} +1 -1
- package/dist/ui/assets/{useFileDiffInstance-Dg0pWcJV.js → useFileDiffInstance-5ivdkUYO.js} +3 -3
- package/dist/ui/assets/{vue-Qngt2rkz.js → vue-DekNU1qt.js} +1 -1
- package/dist/ui/assets/{vue-html-DDue9upr.js → vue-html-Bjlv-uvH.js} +1 -1
- package/dist/ui/assets/{vue-vine-DBcAKsA6.js → vue-vine-C2Doytjq.js} +1 -1
- package/dist/ui/assets/{workspace-app-DGD9R89D.css → workspace-app-DASlX26z.css} +1 -1
- package/dist/ui/assets/{workspace-app-DuzpU4Hf.js → workspace-app-DYi7VJam.js} +5 -5
- package/dist/ui/assets/{xml-CRLR5jiL.js → xml-pXbxxgI3.js} +1 -1
- package/dist/ui/assets/{xsl-CmcwyXbm.js → xsl-BEAlgDI2.js} +1 -1
- package/dist/ui/assets/{yaml-Dpe6Eg-k.js → yaml-C1O-To71.js} +1 -1
- package/dist/ui/workspace-app.html +2 -2
- package/dist/user-config.js +21 -1
- package/dist/workspaces.js +3 -0
- package/docs/agent-profile-schema.md +165 -0
- package/docs/chatgpt-coding-workflow.md +46 -12
- package/docs/configuration.md +47 -12
- package/docs/gotchas.md +24 -5
- package/docs/setup.md +1 -1
- package/examples/agents/claude-implementer.md +30 -0
- package/examples/agents/codex-explorer.md +27 -0
- package/examples/agents/codex-qa-tester.md +29 -0
- package/examples/agents/codex-worker.md +25 -0
- package/examples/agents/copilot-reviewer.md +25 -0
- package/examples/agents/cursor-agent-worker.md +26 -0
- package/examples/agents/opencode-explorer.md +27 -0
- package/examples/agents/pi-reviewer.md +27 -0
- package/package.json +18 -5
- package/scripts/fix-node-pty-permissions.mjs +22 -0
- package/skills/subagent-delegation/SKILL.md +132 -0
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
4
|
-
import {
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
5
10
|
import * as prompts from "@clack/prompts";
|
|
6
11
|
import { getShellConfig } from "@earendil-works/pi-coding-agent";
|
|
7
12
|
import { satisfies } from "semver";
|
|
8
13
|
import { loadConfig } from "./config.js";
|
|
9
|
-
import {
|
|
14
|
+
import { runLocalAgentProvider } from "./local-agent-adapters.js";
|
|
15
|
+
import { isLocalAgentProvider, loadLocalAgentProfiles, } from "./local-agent-profiles.js";
|
|
16
|
+
import { assertLocalAgentProviderAvailable, formatLocalAgentProviderAvailabilitySummary, } from "./local-agent-availability.js";
|
|
17
|
+
import { formatAvailableLocalAgentTargets, parseLocalAgentRunArgs, resolveLocalAgentTarget, } from "./local-agent-targets.js";
|
|
18
|
+
import { createLocalAgentStore } from "./local-agent-store.js";
|
|
19
|
+
import { ensureDevspaceDefaultSkills, generateOwnerToken, loadDevspaceFiles, resolveSubagentsFlag, writeDevspaceAuth, writeDevspaceConfig, } from "./user-config.js";
|
|
10
20
|
import { expandHomePath } from "./roots.js";
|
|
11
21
|
const require = createRequire(import.meta.url);
|
|
12
22
|
const SUPPORTED_NODE_RANGE = ">=20.12 <27";
|
|
@@ -28,18 +38,26 @@ async function main(argv) {
|
|
|
28
38
|
case "config":
|
|
29
39
|
runConfigCommand(args);
|
|
30
40
|
return;
|
|
41
|
+
case "agents":
|
|
42
|
+
await runAgentsCommand(args);
|
|
43
|
+
return;
|
|
31
44
|
case "help":
|
|
32
45
|
printHelp();
|
|
33
46
|
return;
|
|
47
|
+
case "version":
|
|
48
|
+
printVersion();
|
|
49
|
+
return;
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
function normalizeCommand(command) {
|
|
37
53
|
if (!command || command === "serve" || command === "start")
|
|
38
54
|
return "serve";
|
|
39
|
-
if (command === "init" || command === "doctor" || command === "config")
|
|
55
|
+
if (command === "init" || command === "doctor" || command === "config" || command === "agents")
|
|
40
56
|
return command;
|
|
41
57
|
if (command === "help" || command === "--help" || command === "-h")
|
|
42
58
|
return "help";
|
|
59
|
+
if (command === "version" || command === "--version" || command === "-v")
|
|
60
|
+
return "version";
|
|
43
61
|
throw new Error(`Unknown command: ${command}`);
|
|
44
62
|
}
|
|
45
63
|
async function ensureConfigured() {
|
|
@@ -108,15 +126,18 @@ async function runInit({ force }) {
|
|
|
108
126
|
port,
|
|
109
127
|
allowedRoots,
|
|
110
128
|
publicBaseUrl,
|
|
129
|
+
subagents: resolveSubagentsFlag(files.config),
|
|
111
130
|
};
|
|
112
131
|
const auth = {
|
|
113
132
|
ownerToken: files.auth.ownerToken ?? generateOwnerToken(),
|
|
114
133
|
};
|
|
115
134
|
const configPath = writeDevspaceConfig(config);
|
|
116
135
|
const authPath = writeDevspaceAuth(auth);
|
|
136
|
+
const seededSkillPaths = config.subagents ? ensureDevspaceDefaultSkills() : [];
|
|
117
137
|
const lines = [
|
|
118
138
|
`Config: ${configPath}`,
|
|
119
139
|
`Auth: ${authPath}`,
|
|
140
|
+
...seededSkillPaths.map((path) => `Default skill: ${path}`),
|
|
120
141
|
`Local MCP URL: http://${config.host}:${config.port}/mcp`,
|
|
121
142
|
...(publicBaseUrl ? [`Public MCP URL: ${publicBaseUrl}/mcp`] : []),
|
|
122
143
|
];
|
|
@@ -149,7 +170,7 @@ async function serve() {
|
|
|
149
170
|
}
|
|
150
171
|
const { createServer } = await import("./server.js");
|
|
151
172
|
const config = loadConfig();
|
|
152
|
-
const { app, close } = createServer(config);
|
|
173
|
+
const { app, close, localAgentProviders } = createServer(config);
|
|
153
174
|
const httpServer = app.listen(config.port, config.host, () => {
|
|
154
175
|
console.log(`devspace listening on http://${config.host}:${config.port}/mcp`);
|
|
155
176
|
console.log(`public base url: ${config.publicBaseUrl}`);
|
|
@@ -160,6 +181,9 @@ async function serve() {
|
|
|
160
181
|
}
|
|
161
182
|
console.log("auth: Owner password approval required");
|
|
162
183
|
console.log(`logging: ${config.logging.level} ${config.logging.format}`);
|
|
184
|
+
if (config.subagents) {
|
|
185
|
+
console.log(`subagent providers: ${formatLocalAgentProviderAvailabilitySummary(localAgentProviders)}`);
|
|
186
|
+
}
|
|
163
187
|
});
|
|
164
188
|
const shutdown = () => {
|
|
165
189
|
httpServer.close(() => {
|
|
@@ -226,11 +250,239 @@ function printHelp() {
|
|
|
226
250
|
" devspace doctor Show config, runtime, and native dependency status",
|
|
227
251
|
" devspace config get Print persisted config",
|
|
228
252
|
" devspace config set publicBaseUrl <url|null>",
|
|
253
|
+
" devspace agents ls List subagent sessions",
|
|
254
|
+
" devspace agents run <profile-or-provider-or-id> [--model <model>] <prompt>",
|
|
255
|
+
" devspace agents show <id>",
|
|
256
|
+
" devspace -v, --version Print the installed version",
|
|
229
257
|
"",
|
|
230
258
|
"For temporary tunnels:",
|
|
231
259
|
" DEVSPACE_PUBLIC_BASE_URL=https://example.trycloudflare.com devspace serve",
|
|
232
260
|
].join("\n"));
|
|
233
261
|
}
|
|
262
|
+
async function runAgentsCommand(args) {
|
|
263
|
+
const [subcommand, ...rest] = args;
|
|
264
|
+
switch (subcommand) {
|
|
265
|
+
case "ls":
|
|
266
|
+
case "list":
|
|
267
|
+
await runAgentsList();
|
|
268
|
+
return;
|
|
269
|
+
case "run":
|
|
270
|
+
await runAgentsRun(rest);
|
|
271
|
+
return;
|
|
272
|
+
case "show":
|
|
273
|
+
await runAgentsShow(rest);
|
|
274
|
+
return;
|
|
275
|
+
case "__worker":
|
|
276
|
+
await runAgentsWorker(rest);
|
|
277
|
+
return;
|
|
278
|
+
case undefined:
|
|
279
|
+
case "help":
|
|
280
|
+
case "--help":
|
|
281
|
+
case "-h":
|
|
282
|
+
printAgentsHelp();
|
|
283
|
+
return;
|
|
284
|
+
default:
|
|
285
|
+
throw new Error(`Unknown agents command: ${subcommand}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function runAgentsList() {
|
|
289
|
+
const config = loadConfig();
|
|
290
|
+
const store = createLocalAgentStore(config);
|
|
291
|
+
const agents = store.list(resolveCurrentWorkspaceScope());
|
|
292
|
+
if (agents.length === 0) {
|
|
293
|
+
console.log("No subagent sessions found for this workspace.");
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
for (const agent of agents) {
|
|
297
|
+
console.log(formatAgentLine(agent));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function runAgentsRun(args) {
|
|
301
|
+
const parsed = parseLocalAgentRunArgs(args);
|
|
302
|
+
const config = loadConfig();
|
|
303
|
+
const workspaceRoot = resolveCurrentWorkspaceRoot();
|
|
304
|
+
const store = createLocalAgentStore(config);
|
|
305
|
+
const existing = store.get(parsed.target);
|
|
306
|
+
if (existing) {
|
|
307
|
+
if (!isLocalAgentProvider(existing.provider)) {
|
|
308
|
+
throw new Error(`Unknown subagent provider for existing session: ${existing.provider}`);
|
|
309
|
+
}
|
|
310
|
+
assertLocalAgentProviderAvailable(existing.provider);
|
|
311
|
+
const promptFile = writeAgentPromptFile(parsed.prompt);
|
|
312
|
+
store.update(existing.id, {
|
|
313
|
+
status: "starting",
|
|
314
|
+
model: parsed.model ?? existing.model,
|
|
315
|
+
thinking: parsed.thinking ?? existing.thinking,
|
|
316
|
+
latestResponse: undefined,
|
|
317
|
+
error: undefined,
|
|
318
|
+
});
|
|
319
|
+
spawnAgentWorker(existing.id, promptFile);
|
|
320
|
+
console.log(formatAgentLine({
|
|
321
|
+
...existing,
|
|
322
|
+
status: "running",
|
|
323
|
+
model: parsed.model ?? existing.model,
|
|
324
|
+
thinking: parsed.thinking ?? existing.thinking,
|
|
325
|
+
}));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const profiles = await loadLocalAgentProfiles(config, workspaceRoot);
|
|
329
|
+
const target = resolveLocalAgentTarget(parsed.target, profiles, parsed.model, parsed.thinking);
|
|
330
|
+
if (!target) {
|
|
331
|
+
throw new Error(`Unknown subagent profile, provider, or id: ${parsed.target}. Available ${formatAvailableLocalAgentTargets(profiles)}`);
|
|
332
|
+
}
|
|
333
|
+
assertLocalAgentProviderAvailable(target.provider);
|
|
334
|
+
const promptFile = writeAgentPromptFile(parsed.prompt);
|
|
335
|
+
const record = store.create({
|
|
336
|
+
workspaceId: process.env.DEVSPACE_WORKSPACE_ID,
|
|
337
|
+
workspaceRoot,
|
|
338
|
+
profileName: target.name,
|
|
339
|
+
provider: target.provider,
|
|
340
|
+
model: target.model,
|
|
341
|
+
thinking: target.thinking,
|
|
342
|
+
});
|
|
343
|
+
spawnAgentWorker(record.id, promptFile);
|
|
344
|
+
console.log(formatAgentLine({ ...record, status: "running" }));
|
|
345
|
+
}
|
|
346
|
+
async function runAgentsShow(args) {
|
|
347
|
+
const [id] = args;
|
|
348
|
+
if (!id)
|
|
349
|
+
throw new Error("Usage: devspace agents show <id>");
|
|
350
|
+
const config = loadConfig();
|
|
351
|
+
const store = createLocalAgentStore(config);
|
|
352
|
+
let record = store.get(id);
|
|
353
|
+
if (!record)
|
|
354
|
+
throw new Error(`Unknown subagent id: ${id}`);
|
|
355
|
+
const deadline = Date.now() + 15_000;
|
|
356
|
+
while ((record.status === "starting" || record.status === "running") && Date.now() < deadline) {
|
|
357
|
+
await sleep(500);
|
|
358
|
+
record = store.get(id) ?? record;
|
|
359
|
+
}
|
|
360
|
+
console.log(formatAgentLine(record));
|
|
361
|
+
if (record.latestResponse) {
|
|
362
|
+
console.log(record.latestResponse);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (record.error) {
|
|
366
|
+
console.log(record.error);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (record.status === "starting" || record.status === "running") {
|
|
370
|
+
console.log(`No final response yet. Call \`devspace agents show ${record.id}\` again later.`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function runAgentsWorker(args) {
|
|
374
|
+
const [id, promptFileFlag, promptFile] = args;
|
|
375
|
+
if (!id || promptFileFlag !== "--prompt-file" || !promptFile) {
|
|
376
|
+
throw new Error("Usage: devspace agents __worker <id> --prompt-file <path>");
|
|
377
|
+
}
|
|
378
|
+
const config = loadConfig();
|
|
379
|
+
const store = createLocalAgentStore(config);
|
|
380
|
+
const record = store.get(id);
|
|
381
|
+
if (!record)
|
|
382
|
+
throw new Error(`Unknown subagent id: ${id}`);
|
|
383
|
+
store.update(record.id, { status: "running", error: undefined });
|
|
384
|
+
try {
|
|
385
|
+
const profiles = await loadLocalAgentProfiles(config, record.workspaceRoot);
|
|
386
|
+
const profile = profiles.find((candidate) => candidate.name === record.profileName);
|
|
387
|
+
const prompt = await readFile(promptFile, "utf8");
|
|
388
|
+
const result = profile
|
|
389
|
+
? await runLocalAgentProfile(profile, record, prompt)
|
|
390
|
+
: await runRawLocalAgentProvider(record, prompt);
|
|
391
|
+
store.update(record.id, {
|
|
392
|
+
providerSessionId: result.providerSessionId ?? undefined,
|
|
393
|
+
status: "idle",
|
|
394
|
+
latestResponse: result.finalResponse,
|
|
395
|
+
error: undefined,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
store.update(record.id, {
|
|
400
|
+
status: "error",
|
|
401
|
+
error: error instanceof Error ? error.message : String(error),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function runLocalAgentProfile(profile, record, prompt) {
|
|
406
|
+
const body = profile.body.trim();
|
|
407
|
+
const fullPrompt = body ? `${body}\n\nTask:\n${prompt}` : prompt;
|
|
408
|
+
return runLocalAgentProvider(profile.provider, {
|
|
409
|
+
prompt: fullPrompt,
|
|
410
|
+
workspace: record.workspaceRoot,
|
|
411
|
+
providerSessionId: record.providerSessionId,
|
|
412
|
+
writeMode: "allowed",
|
|
413
|
+
model: record.model ?? profile.model,
|
|
414
|
+
thinking: record.thinking ?? profile.thinking,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
async function runRawLocalAgentProvider(record, prompt) {
|
|
418
|
+
if (record.profileName !== record.provider || !isLocalAgentProvider(record.provider)) {
|
|
419
|
+
throw new Error(`Subagent profile not found: ${record.profileName}`);
|
|
420
|
+
}
|
|
421
|
+
return runLocalAgentProvider(record.provider, {
|
|
422
|
+
prompt,
|
|
423
|
+
workspace: record.workspaceRoot,
|
|
424
|
+
providerSessionId: record.providerSessionId,
|
|
425
|
+
writeMode: "allowed",
|
|
426
|
+
model: record.model,
|
|
427
|
+
thinking: record.thinking,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function spawnAgentWorker(agentId, promptFile) {
|
|
431
|
+
const child = spawn(process.execPath, [
|
|
432
|
+
...process.execArgv,
|
|
433
|
+
fileURLToPath(import.meta.url),
|
|
434
|
+
"agents",
|
|
435
|
+
"__worker",
|
|
436
|
+
agentId,
|
|
437
|
+
"--prompt-file",
|
|
438
|
+
promptFile,
|
|
439
|
+
], {
|
|
440
|
+
detached: true,
|
|
441
|
+
stdio: "ignore",
|
|
442
|
+
env: process.env,
|
|
443
|
+
});
|
|
444
|
+
child.unref();
|
|
445
|
+
}
|
|
446
|
+
function writeAgentPromptFile(prompt) {
|
|
447
|
+
const directory = mkdtempSync(join(tmpdir(), "devspace-agent-prompt-"));
|
|
448
|
+
const filePath = join(directory, "prompt.txt");
|
|
449
|
+
writeFileSync(filePath, prompt, { mode: 0o600 });
|
|
450
|
+
return filePath;
|
|
451
|
+
}
|
|
452
|
+
function resolveCurrentWorkspaceRoot() {
|
|
453
|
+
return resolve(process.env.DEVSPACE_WORKSPACE_ROOT || process.cwd());
|
|
454
|
+
}
|
|
455
|
+
function resolveCurrentWorkspaceScope() {
|
|
456
|
+
return {
|
|
457
|
+
workspaceId: process.env.DEVSPACE_WORKSPACE_ID,
|
|
458
|
+
workspaceRoot: resolveCurrentWorkspaceRoot(),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function formatAgentLine(agent) {
|
|
462
|
+
const model = agent.model ? ` ${agent.model}` : "";
|
|
463
|
+
const thinking = agent.thinking ? ` thinking=${agent.thinking}` : "";
|
|
464
|
+
return `${agent.id} ${agent.status} ${agent.profileName} ${agent.provider}${model}${thinking}`;
|
|
465
|
+
}
|
|
466
|
+
function sleep(ms) {
|
|
467
|
+
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
468
|
+
}
|
|
469
|
+
function printAgentsHelp() {
|
|
470
|
+
console.log([
|
|
471
|
+
"DevSpace agents",
|
|
472
|
+
"",
|
|
473
|
+
"Usage:",
|
|
474
|
+
" devspace agents ls",
|
|
475
|
+
" devspace agents run <profile-or-provider-or-id> [--model <model>] [--thinking <level>] <prompt>",
|
|
476
|
+
" devspace agents show <id>",
|
|
477
|
+
].join("\n"));
|
|
478
|
+
}
|
|
479
|
+
function printVersion() {
|
|
480
|
+
const packageJson = require("../package.json");
|
|
481
|
+
if (typeof packageJson.version !== "string") {
|
|
482
|
+
throw new Error("Unable to read DevSpace package version.");
|
|
483
|
+
}
|
|
484
|
+
console.log(packageJson.version);
|
|
485
|
+
}
|
|
234
486
|
function normalizeOptionalPublicBaseUrl(value) {
|
|
235
487
|
const trimmed = value.trim();
|
|
236
488
|
if (!trimmed || trimmed === "null" || trimmed === "none")
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { expandHomePath } from "./roots.js";
|
|
4
|
-
import { loadDevspaceFiles } from "./user-config.js";
|
|
4
|
+
import { devspaceAgentsDir, devspaceSkillsDir, loadDevspaceFiles } from "./user-config.js";
|
|
5
5
|
const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
|
|
6
6
|
const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
7
7
|
function parsePort(value) {
|
|
@@ -44,17 +44,16 @@ function normalizeAllowedHosts(rawHosts, derivedHosts) {
|
|
|
44
44
|
function parseBoolean(value) {
|
|
45
45
|
return ["1", "true", "yes", "on"].includes(value?.toLowerCase() ?? "");
|
|
46
46
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
function parseToolMode(env) {
|
|
48
|
+
const mode = env.DEVSPACE_TOOL_MODE;
|
|
49
|
+
if (mode === "minimal" || mode === "full" || mode === "codex")
|
|
50
|
+
return mode;
|
|
51
|
+
if (mode)
|
|
52
|
+
throw new Error(`Invalid DEVSPACE_TOOL_MODE: ${mode}`);
|
|
53
|
+
if (env.DEVSPACE_MINIMAL_TOOLS !== undefined) {
|
|
54
|
+
return parseBoolean(env.DEVSPACE_MINIMAL_TOOLS) ? "minimal" : "full";
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
return parseBoolean(env.DEVSPACE_MINIMAL_TOOLS);
|
|
57
|
-
return true;
|
|
56
|
+
return "minimal";
|
|
58
57
|
}
|
|
59
58
|
function parseLogLevel(value) {
|
|
60
59
|
if (!value || value === "info")
|
|
@@ -74,8 +73,7 @@ function parsePathList(value) {
|
|
|
74
73
|
return (value
|
|
75
74
|
?.split(",")
|
|
76
75
|
.map((entry) => entry.trim())
|
|
77
|
-
.filter(Boolean)
|
|
78
|
-
.map((entry) => resolve(expandHomePath(entry))) ?? []);
|
|
76
|
+
.filter(Boolean) ?? []);
|
|
79
77
|
}
|
|
80
78
|
function parseStringList(value, fallback) {
|
|
81
79
|
const entries = value
|
|
@@ -93,13 +91,6 @@ function parsePositiveInteger(value, fallback, name) {
|
|
|
93
91
|
}
|
|
94
92
|
return parsed;
|
|
95
93
|
}
|
|
96
|
-
function parseToolNaming(value) {
|
|
97
|
-
if (!value || value === "short")
|
|
98
|
-
return "short";
|
|
99
|
-
if (value === "legacy")
|
|
100
|
-
return "legacy";
|
|
101
|
-
throw new Error(`Invalid DEVSPACE_TOOL_NAMING: ${value}`);
|
|
102
|
-
}
|
|
103
94
|
function parseLoggingConfig(env) {
|
|
104
95
|
return {
|
|
105
96
|
level: parseLogLevel(env.DEVSPACE_LOG_LEVEL),
|
|
@@ -170,13 +161,17 @@ export function loadConfig(env = process.env) {
|
|
|
170
161
|
allowedRoots: parseAllowedRoots(env.DEVSPACE_ALLOWED_ROOTS ?? files.config.allowedRoots),
|
|
171
162
|
allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts),
|
|
172
163
|
publicBaseUrl,
|
|
173
|
-
|
|
174
|
-
toolNaming: parseToolNaming(env.DEVSPACE_TOOL_NAMING),
|
|
164
|
+
toolMode: parseToolMode(env),
|
|
175
165
|
widgets: parseWidgetMode(env.DEVSPACE_WIDGETS),
|
|
176
166
|
stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())),
|
|
177
167
|
worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())),
|
|
178
168
|
skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS),
|
|
179
169
|
skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS),
|
|
170
|
+
devspaceSkillsDir: devspaceSkillsDir(env),
|
|
171
|
+
devspaceAgentsDir: devspaceAgentsDir(env),
|
|
172
|
+
subagents: env.DEVSPACE_SUBAGENTS === undefined
|
|
173
|
+
? files.config.subagents === true
|
|
174
|
+
: parseBoolean(env.DEVSPACE_SUBAGENTS),
|
|
180
175
|
agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())),
|
|
181
176
|
logging: parseLoggingConfig(env),
|
|
182
177
|
};
|
package/dist/db/migrations.js
CHANGED
|
@@ -9,6 +9,11 @@ const migrations = [
|
|
|
9
9
|
name: "oauth-state",
|
|
10
10
|
up: migrateOAuthState,
|
|
11
11
|
},
|
|
12
|
+
{
|
|
13
|
+
version: 3,
|
|
14
|
+
name: "local-agent-sessions",
|
|
15
|
+
up: migrateLocalAgentSessions,
|
|
16
|
+
},
|
|
12
17
|
];
|
|
13
18
|
export function migrateDatabase(sqlite) {
|
|
14
19
|
const migrate = sqlite.transaction(() => {
|
|
@@ -115,6 +120,35 @@ function migrateOAuthState(sqlite) {
|
|
|
115
120
|
on oauth_refresh_tokens(expires_at);
|
|
116
121
|
`);
|
|
117
122
|
}
|
|
123
|
+
function migrateLocalAgentSessions(sqlite) {
|
|
124
|
+
sqlite.exec(`
|
|
125
|
+
create table if not exists local_agent_sessions (
|
|
126
|
+
id text primary key,
|
|
127
|
+
workspace_id text,
|
|
128
|
+
workspace_root text not null,
|
|
129
|
+
profile_name text not null,
|
|
130
|
+
provider text not null,
|
|
131
|
+
model text,
|
|
132
|
+
thinking text,
|
|
133
|
+
provider_session_id text,
|
|
134
|
+
status text not null,
|
|
135
|
+
latest_response text,
|
|
136
|
+
error text,
|
|
137
|
+
created_at text not null,
|
|
138
|
+
updated_at text not null
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
create index if not exists local_agent_sessions_workspace_id_idx
|
|
142
|
+
on local_agent_sessions(workspace_id, updated_at desc);
|
|
143
|
+
|
|
144
|
+
create index if not exists local_agent_sessions_workspace_root_idx
|
|
145
|
+
on local_agent_sessions(workspace_root, updated_at desc);
|
|
146
|
+
|
|
147
|
+
create index if not exists local_agent_sessions_provider_session_id_idx
|
|
148
|
+
on local_agent_sessions(provider_session_id);
|
|
149
|
+
`);
|
|
150
|
+
addColumnIfMissing(sqlite, "local_agent_sessions", "thinking", "text");
|
|
151
|
+
}
|
|
118
152
|
function addColumnIfMissing(sqlite, table, column, definition) {
|
|
119
153
|
const columns = sqlite.prepare(`pragma table_info(${table})`).all();
|
|
120
154
|
if (columns.some((existingColumn) => existingColumn.name === column))
|
package/dist/db/schema.js
CHANGED
|
@@ -50,3 +50,22 @@ export const oauthRefreshTokens = sqliteTable("oauth_refresh_tokens", {
|
|
|
50
50
|
expiresAt: integer("expires_at").notNull(),
|
|
51
51
|
resource: text("resource"),
|
|
52
52
|
});
|
|
53
|
+
export const localAgentSessions = sqliteTable("local_agent_sessions", {
|
|
54
|
+
id: text("id").primaryKey(),
|
|
55
|
+
workspaceId: text("workspace_id"),
|
|
56
|
+
workspaceRoot: text("workspace_root").notNull(),
|
|
57
|
+
profileName: text("profile_name").notNull(),
|
|
58
|
+
provider: text("provider").notNull(),
|
|
59
|
+
model: text("model"),
|
|
60
|
+
thinking: text("thinking"),
|
|
61
|
+
providerSessionId: text("provider_session_id"),
|
|
62
|
+
status: text("status").notNull(),
|
|
63
|
+
latestResponse: text("latest_response"),
|
|
64
|
+
error: text("error"),
|
|
65
|
+
createdAt: text("created_at").notNull(),
|
|
66
|
+
updatedAt: text("updated_at").notNull(),
|
|
67
|
+
}, (table) => [
|
|
68
|
+
index("local_agent_sessions_workspace_id_idx").on(table.workspaceId, table.updatedAt),
|
|
69
|
+
index("local_agent_sessions_workspace_root_idx").on(table.workspaceRoot, table.updatedAt),
|
|
70
|
+
index("local_agent_sessions_provider_session_id_idx").on(table.providerSessionId),
|
|
71
|
+
]);
|