repoburg 1.3.201 → 1.3.202
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/agent-pane/backend/data.db-shm +0 -0
- package/agent-pane/backend/data.db-wal +0 -0
- package/agent-pane/backend/dist/index.js +478 -250
- package/agent-pane/backend/src/db/index.ts +6 -0
- package/agent-pane/backend/src/providers/runtime-provider.ts +6 -0
- package/agent-pane/backend/src/repoburg/action-mapping.test.ts +122 -0
- package/agent-pane/backend/src/repoburg/action-mapping.ts +89 -11
- package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +23 -0
- package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +85 -0
- package/agent-pane/backend/src/repoburg/backend-resolver.ts +28 -13
- package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +21 -0
- package/agent-pane/backend/src/repoburg/repoburg-client.ts +13 -0
- package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +57 -0
- package/agent-pane/backend/src/repoburg/repoburg-provider.ts +49 -0
- package/agent-pane/backend/src/repoburg/turn-projector.test.ts +147 -1
- package/agent-pane/backend/src/repoburg/turn-projector.ts +101 -1
- package/agent-pane/backend/src/routes/sessions.test.ts +215 -1
- package/agent-pane/backend/src/routes/sessions.ts +70 -9
- package/agent-pane/frontend/dist/assets/index-CJxD4YmJ.js +370 -0
- package/agent-pane/frontend/dist/assets/{index-34WaZYsW.css → index-r-xwfOKQ.css} +1 -1
- package/agent-pane/frontend/dist/index.html +2 -2
- package/agent-pane/frontend/src/api/sessions.test.ts +1 -0
- package/agent-pane/frontend/src/api/websocket.test.ts +2 -2
- package/agent-pane/frontend/src/api/websocket.ts +3 -1
- package/agent-pane/frontend/src/components/AskUserItem.test.tsx +143 -0
- package/agent-pane/frontend/src/components/AskUserItem.tsx +207 -0
- package/agent-pane/frontend/src/components/TurnContent.test.tsx +79 -0
- package/agent-pane/frontend/src/components/TurnContent.tsx +49 -3
- package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +48 -0
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +7 -2
- package/agent-pane/frontend/src/store/store.test.ts +19 -0
- package/agent-pane/frontend/src/store/wsSlice.ts +10 -3
- package/agent-pane/frontend/src/styles/app.css +16 -0
- package/agent-pane/frontend/src/test/fixtures.ts +1 -0
- package/agent-pane/frontend/src/types/index.ts +2 -0
- package/agent-pane/frontend/src/utils/duration.test.ts +1 -0
- package/agent-pane/shared/types.ts +17 -0
- package/askuser-after-step5-postgres-selected.png +0 -0
- package/askuser-after-step7-answer-sent.png +0 -0
- package/askuser-card-visible.png +0 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.d.ts +16 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js +87 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js.map +1 -1
- package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +16 -1
- package/backend/dist/src/app.controller.d.ts +1 -0
- package/backend/dist/src/app.service.d.ts +1 -0
- package/backend/dist/src/app.service.js +4 -1
- package/backend/dist/src/app.service.js.map +1 -1
- package/backend/dist/src/interactive-chat/chat.service.d.ts +4 -0
- package/backend/dist/src/interactive-chat/chat.service.js +21 -1
- package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
- package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js +12 -1
- package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js.map +1 -1
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +1 -0
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js +1 -0
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +2 -0
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/daemon/dist/api/services.js +4 -0
- package/daemon/dist/serviceManager.js +30 -11
- package/daemon/dist/serviceManager.spec.js +26 -0
- package/daemon/dist/workspace-validation.js +63 -0
- package/daemon/dist/workspace-validation.spec.js +63 -0
- package/package.json +1 -1
- package/r2-step4-apple-banana.png +0 -0
- package/r2-step5-after-rewind-apple-last.png +0 -0
- package/r2-step7-apple-cherry.png +0 -0
- package/r2-step8-cherry-menu-no-restore.png +0 -0
- package/r3-step3-live-task-card-running.png +0 -0
- package/r3-step4-after-completion.png +0 -0
- package/r3-step5-persistence-after-reload.png +0 -0
- package/r3-step6-tasks-tab-card.png +0 -0
- package/reverify-answered-state-answer-sent.png +0 -0
- package/reverify-card-visible-after-completion.png +0 -0
- package/rewind-step3-apple-banana-turns.png +0 -0
- package/rewind-step5-ui-unchanged-after-confirmed-rewind.png +0 -0
- package/rewind-step6-apple-menu-no-restore.png +0 -0
- package/rewind-step7-final-turnlist-unchanged.png +0 -0
- package/rewind-step7-negative-error-toast.png +0 -0
- package/rewind-step7-negative-error-toast2.png +0 -0
- package/subagent-r2-step5-turn-after-reload-no-card.png +0 -0
- package/subagent-r2-step6-tasks-panel-card.png +0 -0
- package/subagent-step3-tasks-tab-done-card.png +0 -0
- package/subagent-step4-turn-stuck-no-cards.png +0 -0
- package/agent-pane/frontend/dist/assets/index-y7xlT86e.js +0 -362
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const express_1 = require("express");
|
|
7
7
|
const serviceManager_1 = require("../serviceManager");
|
|
8
8
|
const checkAuth_1 = require("../middleware/checkAuth");
|
|
9
|
+
const workspace_validation_1 = require("../workspace-validation");
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
11
|
const router = (0, express_1.Router)();
|
|
11
12
|
router.get('/', (req, res) => {
|
|
@@ -28,6 +29,9 @@ router.post('/start', checkAuth_1.checkAuth, async (req, res) => {
|
|
|
28
29
|
res.status(201).json(service);
|
|
29
30
|
}
|
|
30
31
|
catch (error) {
|
|
32
|
+
if (error instanceof workspace_validation_1.WorkspaceValidationError) {
|
|
33
|
+
return res.status(400).json({ error: error.message });
|
|
34
|
+
}
|
|
31
35
|
const message = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
32
36
|
res.status(500).json({ error: message });
|
|
33
37
|
}
|
|
@@ -37,10 +37,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.serviceManager = void 0;
|
|
40
|
+
exports.backendScriptPath = backendScriptPath;
|
|
41
|
+
exports.describeError = describeError;
|
|
40
42
|
const pm2_1 = __importDefault(require("pm2"));
|
|
41
43
|
const path_1 = __importDefault(require("path"));
|
|
42
44
|
const os_1 = __importDefault(require("os"));
|
|
43
45
|
const stateManager_1 = require("./stateManager");
|
|
46
|
+
const workspace_validation_1 = require("./workspace-validation");
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the backend entry script. Production resolves ABSOLUTELY from the
|
|
49
|
+
* package layout: pm2 resolves bare script names against ITS daemon cwd,
|
|
50
|
+
* which breaks `repoburg-backend` whenever the PM2 God daemon runs from an
|
|
51
|
+
* unrelated directory.
|
|
52
|
+
*/
|
|
53
|
+
function backendScriptPath(fromDir, isDevelopment) {
|
|
54
|
+
return isDevelopment
|
|
55
|
+
? path_1.default.resolve(fromDir, '../../backend/dist/main.js')
|
|
56
|
+
: path_1.default.resolve(fromDir, '../../backend/dist/src/main.js');
|
|
57
|
+
}
|
|
58
|
+
/** Human message for unknown errors (pm2 throws arrays of Errors). */
|
|
59
|
+
function describeError(error) {
|
|
60
|
+
if (error instanceof Error)
|
|
61
|
+
return error.message;
|
|
62
|
+
if (Array.isArray(error))
|
|
63
|
+
return error.map(describeError).join('; ');
|
|
64
|
+
return String(error);
|
|
65
|
+
}
|
|
44
66
|
const get_port_1 = __importStar(require("./utils/get-port"));
|
|
45
67
|
class ServiceManager {
|
|
46
68
|
constructor() {
|
|
@@ -137,27 +159,25 @@ class ServiceManager {
|
|
|
137
159
|
};
|
|
138
160
|
}
|
|
139
161
|
async start(projectPath, customName) {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
162
|
+
const name = customName || path_1.default.basename(path_1.default.resolve(projectPath));
|
|
163
|
+
// Validate BEFORE the same-name replacement below: the service being
|
|
164
|
+
// replaced must not collide with itself, but every other service counts.
|
|
165
|
+
const others = stateManager_1.stateManager.getServices().filter((s) => s.name !== name);
|
|
166
|
+
const absolutePath = (0, workspace_validation_1.validateWorkspacePath)(projectPath, others.map((s) => ({ name: s.name, path: s.path })));
|
|
167
|
+
console.log(`Starting service for workspace ${absolutePath}`);
|
|
143
168
|
if (stateManager_1.stateManager.getService(name)) {
|
|
144
169
|
console.log(`Service '${name}' already exists. Removing it before starting a new one.`);
|
|
145
170
|
await this.remove(name);
|
|
146
171
|
}
|
|
147
172
|
const port = await (0, get_port_1.default)({ port: (0, get_port_1.portNumbers)(3400, 3999) });
|
|
148
173
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
149
|
-
let script;
|
|
150
174
|
let interpreter = undefined;
|
|
175
|
+
const script = backendScriptPath(__dirname, isDevelopment);
|
|
151
176
|
if (isDevelopment) {
|
|
152
177
|
interpreter = 'node';
|
|
153
|
-
// In development, resolve relative to __dirname of this file
|
|
154
|
-
script = path_1.default.resolve(__dirname, '../../backend/dist/main.js');
|
|
155
178
|
console.log(`[ServiceManager] Development mode detected (NODE_ENV=${process.env.NODE_ENV}).`);
|
|
156
179
|
}
|
|
157
180
|
else {
|
|
158
|
-
// In production, we use the command exposed by the npm package's bin field.
|
|
159
|
-
// pm2 will resolve this from the system's PATH.
|
|
160
|
-
script = 'repoburg-backend';
|
|
161
181
|
console.log(`[ServiceManager] Production mode detected (NODE_ENV=${process.env.NODE_ENV}).`);
|
|
162
182
|
}
|
|
163
183
|
console.log(`[ServiceManager] Starting backend service using script: ${script}`);
|
|
@@ -190,9 +210,8 @@ class ServiceManager {
|
|
|
190
210
|
return newService;
|
|
191
211
|
}
|
|
192
212
|
catch (error) {
|
|
193
|
-
const message = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
194
213
|
console.error(`Failed to start service for ${projectPath}:`, error);
|
|
195
|
-
throw new Error(`Failed to start service: ${
|
|
214
|
+
throw new Error(`Failed to start service: ${describeError(error)}`);
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
async stop(name) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const serviceManager_1 = require("./serviceManager");
|
|
8
|
+
describe('backendScriptPath', () => {
|
|
9
|
+
it('resolves the repo build in development', () => {
|
|
10
|
+
expect((0, serviceManager_1.backendScriptPath)(path_1.default.join('/repo', 'daemon', 'src'), true)).toBe(path_1.default.join('/repo', 'backend', 'dist', 'main.js'));
|
|
11
|
+
});
|
|
12
|
+
it('resolves the package build absolutely in production', () => {
|
|
13
|
+
expect((0, serviceManager_1.backendScriptPath)(path_1.default.join('/pkg', 'repoburg', 'daemon', 'dist'), false)).toBe(path_1.default.join('/pkg', 'repoburg', 'backend', 'dist', 'src', 'main.js'));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('describeError', () => {
|
|
17
|
+
it('returns the message of an Error', () => {
|
|
18
|
+
expect((0, serviceManager_1.describeError)(new Error('boom'))).toBe('boom');
|
|
19
|
+
});
|
|
20
|
+
it('joins pm2 arrays of Errors', () => {
|
|
21
|
+
expect((0, serviceManager_1.describeError)([new Error('a'), new Error('b')])).toBe('a; b');
|
|
22
|
+
});
|
|
23
|
+
it('stringifies non-Error values', () => {
|
|
24
|
+
expect((0, serviceManager_1.describeError)('plain')).toBe('plain');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkspaceValidationError = void 0;
|
|
4
|
+
exports.validateWorkspacePath = validateWorkspacePath;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
/** Thrown when a workspace path fails start validation (HTTP 400 in the API). */
|
|
9
|
+
class WorkspaceValidationError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'WorkspaceValidationError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.WorkspaceValidationError = WorkspaceValidationError;
|
|
16
|
+
/** true when child is strictly under parent (not equal). */
|
|
17
|
+
function isUnder(child, parent) {
|
|
18
|
+
return child.startsWith(parent + path_1.sep);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validate a workspace path before a backend is started for it:
|
|
22
|
+
* - must be absolute,
|
|
23
|
+
* - must exist and be a directory,
|
|
24
|
+
* - must not be $HOME or a filesystem root,
|
|
25
|
+
* - must not overlap another service workspace (nested either way).
|
|
26
|
+
* Returns the resolved absolute path; throws WorkspaceValidationError
|
|
27
|
+
* with a clear message otherwise.
|
|
28
|
+
*/
|
|
29
|
+
function validateWorkspacePath(projectPath, existingServices) {
|
|
30
|
+
if (!projectPath || !(0, path_1.isAbsolute)(projectPath)) {
|
|
31
|
+
throw new WorkspaceValidationError(`Project path must be absolute (got "${projectPath}").`);
|
|
32
|
+
}
|
|
33
|
+
const resolved = (0, path_1.resolve)(projectPath);
|
|
34
|
+
let stat;
|
|
35
|
+
try {
|
|
36
|
+
stat = (0, fs_1.statSync)(resolved);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new WorkspaceValidationError(`Workspace directory does not exist: ${resolved}`);
|
|
40
|
+
}
|
|
41
|
+
if (!stat.isDirectory()) {
|
|
42
|
+
throw new WorkspaceValidationError(`Workspace path is not a directory: ${resolved}`);
|
|
43
|
+
}
|
|
44
|
+
if (resolved === (0, os_1.homedir)()) {
|
|
45
|
+
throw new WorkspaceValidationError(`Refusing to use the home directory as a workspace: ${resolved}`);
|
|
46
|
+
}
|
|
47
|
+
if (resolved === (0, path_1.parse)(resolved).root) {
|
|
48
|
+
throw new WorkspaceValidationError(`Refusing to use a filesystem root as a workspace: ${resolved}`);
|
|
49
|
+
}
|
|
50
|
+
for (const svc of existingServices) {
|
|
51
|
+
const svcPath = (0, path_1.resolve)(svc.path);
|
|
52
|
+
if (svcPath === resolved) {
|
|
53
|
+
throw new WorkspaceValidationError(`Workspace ${resolved} is already served by service "${svc.name}".`);
|
|
54
|
+
}
|
|
55
|
+
if (isUnder(resolved, svcPath)) {
|
|
56
|
+
throw new WorkspaceValidationError(`Workspace ${resolved} is nested inside existing service "${svc.name}" (${svcPath}).`);
|
|
57
|
+
}
|
|
58
|
+
if (isUnder(svcPath, resolved)) {
|
|
59
|
+
throw new WorkspaceValidationError(`Workspace ${resolved} contains existing service "${svc.name}" (${svcPath}).`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fs_1 = require("fs");
|
|
4
|
+
const os_1 = require("os");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const workspace_validation_1 = require("./workspace-validation");
|
|
7
|
+
describe('validateWorkspacePath', () => {
|
|
8
|
+
let dir;
|
|
9
|
+
let otherDir;
|
|
10
|
+
let filePath;
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'ws-valid-'));
|
|
13
|
+
otherDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'ws-other-'));
|
|
14
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(dir, 'child'));
|
|
15
|
+
filePath = (0, path_1.join)(dir, 'not-a-dir');
|
|
16
|
+
(0, fs_1.writeFileSync)(filePath, 'file');
|
|
17
|
+
});
|
|
18
|
+
afterAll(() => {
|
|
19
|
+
(0, fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
20
|
+
(0, fs_1.rmSync)(otherDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
it('accepts an existing absolute directory and returns the resolved path', () => {
|
|
23
|
+
const resolved = (0, path_1.resolve)(dir);
|
|
24
|
+
expect((0, workspace_validation_1.validateWorkspacePath)(dir, [])).toBe(resolved);
|
|
25
|
+
});
|
|
26
|
+
it('normalizes redundant path segments', () => {
|
|
27
|
+
const messy = (0, path_1.join)(dir, 'a', '..', 'b', '..');
|
|
28
|
+
expect((0, workspace_validation_1.validateWorkspacePath)(messy, [])).toBe((0, path_1.resolve)(dir));
|
|
29
|
+
});
|
|
30
|
+
it('rejects non-absolute paths', () => {
|
|
31
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)('relative/path', [])).toThrow(workspace_validation_1.WorkspaceValidationError);
|
|
32
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)('', [])).toThrow(workspace_validation_1.WorkspaceValidationError);
|
|
33
|
+
});
|
|
34
|
+
it('rejects paths that do not exist', () => {
|
|
35
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'missing'), [])).toThrow(/Workspace directory does not exist/);
|
|
36
|
+
});
|
|
37
|
+
it('rejects paths that are not directories', () => {
|
|
38
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)(filePath, [])).toThrow(/not a directory/);
|
|
39
|
+
});
|
|
40
|
+
it('rejects the home directory', () => {
|
|
41
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, os_1.homedir)(), [])).toThrow(/home directory/);
|
|
42
|
+
});
|
|
43
|
+
it('rejects filesystem roots', () => {
|
|
44
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.parse)(dir).root, [])).toThrow(/filesystem root/);
|
|
45
|
+
});
|
|
46
|
+
it('rejects a duplicate of an existing service workspace', () => {
|
|
47
|
+
const existing = [{ name: 'svc', path: otherDir }];
|
|
48
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)(otherDir, existing)).toThrow(/already served/);
|
|
49
|
+
});
|
|
50
|
+
it('rejects a workspace nested inside an existing service', () => {
|
|
51
|
+
const existing = [{ name: 'svc', path: dir }];
|
|
52
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'child'), [])).not.toThrow();
|
|
53
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'child'), existing)).toThrow(/nested inside/);
|
|
54
|
+
});
|
|
55
|
+
it('rejects a workspace that contains an existing service', () => {
|
|
56
|
+
const existing = [{ name: 'svc', path: (0, path_1.join)(dir, 'child') }];
|
|
57
|
+
expect(() => (0, workspace_validation_1.validateWorkspacePath)(dir, existing)).toThrow(/contains existing service/);
|
|
58
|
+
});
|
|
59
|
+
it('accepts a sibling of an existing service', () => {
|
|
60
|
+
const existing = [{ name: 'svc', path: otherDir }];
|
|
61
|
+
expect((0, workspace_validation_1.validateWorkspacePath)(dir, existing)).toBe((0, path_1.resolve)(dir));
|
|
62
|
+
});
|
|
63
|
+
});
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|