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.
Files changed (86) hide show
  1. package/agent-pane/backend/data.db-shm +0 -0
  2. package/agent-pane/backend/data.db-wal +0 -0
  3. package/agent-pane/backend/dist/index.js +478 -250
  4. package/agent-pane/backend/src/db/index.ts +6 -0
  5. package/agent-pane/backend/src/providers/runtime-provider.ts +6 -0
  6. package/agent-pane/backend/src/repoburg/action-mapping.test.ts +122 -0
  7. package/agent-pane/backend/src/repoburg/action-mapping.ts +89 -11
  8. package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +23 -0
  9. package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +85 -0
  10. package/agent-pane/backend/src/repoburg/backend-resolver.ts +28 -13
  11. package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +21 -0
  12. package/agent-pane/backend/src/repoburg/repoburg-client.ts +13 -0
  13. package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +57 -0
  14. package/agent-pane/backend/src/repoburg/repoburg-provider.ts +49 -0
  15. package/agent-pane/backend/src/repoburg/turn-projector.test.ts +147 -1
  16. package/agent-pane/backend/src/repoburg/turn-projector.ts +101 -1
  17. package/agent-pane/backend/src/routes/sessions.test.ts +215 -1
  18. package/agent-pane/backend/src/routes/sessions.ts +70 -9
  19. package/agent-pane/frontend/dist/assets/index-CJxD4YmJ.js +370 -0
  20. package/agent-pane/frontend/dist/assets/{index-34WaZYsW.css → index-r-xwfOKQ.css} +1 -1
  21. package/agent-pane/frontend/dist/index.html +2 -2
  22. package/agent-pane/frontend/src/api/sessions.test.ts +1 -0
  23. package/agent-pane/frontend/src/api/websocket.test.ts +2 -2
  24. package/agent-pane/frontend/src/api/websocket.ts +3 -1
  25. package/agent-pane/frontend/src/components/AskUserItem.test.tsx +143 -0
  26. package/agent-pane/frontend/src/components/AskUserItem.tsx +207 -0
  27. package/agent-pane/frontend/src/components/TurnContent.test.tsx +79 -0
  28. package/agent-pane/frontend/src/components/TurnContent.tsx +49 -3
  29. package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +48 -0
  30. package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +7 -2
  31. package/agent-pane/frontend/src/store/store.test.ts +19 -0
  32. package/agent-pane/frontend/src/store/wsSlice.ts +10 -3
  33. package/agent-pane/frontend/src/styles/app.css +16 -0
  34. package/agent-pane/frontend/src/test/fixtures.ts +1 -0
  35. package/agent-pane/frontend/src/types/index.ts +2 -0
  36. package/agent-pane/frontend/src/utils/duration.test.ts +1 -0
  37. package/agent-pane/shared/types.ts +17 -0
  38. package/askuser-after-step5-postgres-selected.png +0 -0
  39. package/askuser-after-step7-answer-sent.png +0 -0
  40. package/askuser-card-visible.png +0 -0
  41. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.d.ts +16 -0
  42. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js +87 -0
  43. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js.map +1 -1
  44. package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +16 -1
  45. package/backend/dist/src/app.controller.d.ts +1 -0
  46. package/backend/dist/src/app.service.d.ts +1 -0
  47. package/backend/dist/src/app.service.js +4 -1
  48. package/backend/dist/src/app.service.js.map +1 -1
  49. package/backend/dist/src/interactive-chat/chat.service.d.ts +4 -0
  50. package/backend/dist/src/interactive-chat/chat.service.js +21 -1
  51. package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
  52. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js +12 -1
  53. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js.map +1 -1
  54. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +1 -0
  55. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js +1 -0
  56. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
  57. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +2 -0
  58. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
  59. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  60. package/daemon/dist/api/services.js +4 -0
  61. package/daemon/dist/serviceManager.js +30 -11
  62. package/daemon/dist/serviceManager.spec.js +26 -0
  63. package/daemon/dist/workspace-validation.js +63 -0
  64. package/daemon/dist/workspace-validation.spec.js +63 -0
  65. package/package.json +1 -1
  66. package/r2-step4-apple-banana.png +0 -0
  67. package/r2-step5-after-rewind-apple-last.png +0 -0
  68. package/r2-step7-apple-cherry.png +0 -0
  69. package/r2-step8-cherry-menu-no-restore.png +0 -0
  70. package/r3-step3-live-task-card-running.png +0 -0
  71. package/r3-step4-after-completion.png +0 -0
  72. package/r3-step5-persistence-after-reload.png +0 -0
  73. package/r3-step6-tasks-tab-card.png +0 -0
  74. package/reverify-answered-state-answer-sent.png +0 -0
  75. package/reverify-card-visible-after-completion.png +0 -0
  76. package/rewind-step3-apple-banana-turns.png +0 -0
  77. package/rewind-step5-ui-unchanged-after-confirmed-rewind.png +0 -0
  78. package/rewind-step6-apple-menu-no-restore.png +0 -0
  79. package/rewind-step7-final-turnlist-unchanged.png +0 -0
  80. package/rewind-step7-negative-error-toast.png +0 -0
  81. package/rewind-step7-negative-error-toast2.png +0 -0
  82. package/subagent-r2-step5-turn-after-reload-no-card.png +0 -0
  83. package/subagent-r2-step6-tasks-panel-card.png +0 -0
  84. package/subagent-step3-tasks-tab-done-card.png +0 -0
  85. package/subagent-step4-turn-stuck-no-cards.png +0 -0
  86. 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 absolutePath = path_1.default.resolve(projectPath);
141
- console.log(`Starting service absolutePath ${projectPath}`);
142
- const name = customName || path_1.default.basename(absolutePath);
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: ${message}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repoburg",
3
- "version": "1.3.201",
3
+ "version": "1.3.202",
4
4
  "description": "A local AI-powered software developer assistant that runs on your own machine.",
5
5
  "author": "Celal Ertug",
6
6
  "license": "SEE LICENSE IN LICENSE",
Binary file
Binary file
Binary file
Binary file