@glwhappen/web-code 1.32.0 → 1.32.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/dist/assets/{index-u6XmIqLb.js → index-CfT-2Nkf.js} +2 -2
- package/dist/index.html +1 -1
- package/dist-server/server/cursor-cli.js +33 -15
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +48 -29
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/index.js +20 -17
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/database/repositories/push-subscriptions.js +11 -5
- package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -1
- package/dist-server/server/modules/projects/projects.routes.js +14 -0
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-authorization.service.js +54 -0
- package/dist-server/server/modules/projects/services/project-authorization.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +13 -4
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-management.service.js +4 -2
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
- package/dist-server/server/modules/projects/tests/project-authorization.service.test.js +51 -0
- package/dist-server/server/modules/projects/tests/project-authorization.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +10 -1
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
- package/dist-server/server/modules/projects/tests/project-management.service.test.js +31 -10
- package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +77 -13
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/routes/agent.js +35 -7
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/settings.js +1 -1
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +1 -1
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/shared/utils.js +60 -9
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +1 -1
- package/server/cursor-cli.js +33 -15
- package/server/gemini-cli.js +48 -28
- package/server/index.js +21 -17
- package/server/modules/database/repositories/push-subscriptions.ts +14 -5
- package/server/modules/projects/projects.routes.ts +16 -0
- package/server/modules/projects/services/project-authorization.service.ts +70 -0
- package/server/modules/projects/services/project-clone.service.ts +18 -4
- package/server/modules/projects/services/project-management.service.ts +12 -3
- package/server/modules/projects/tests/project-authorization.service.test.ts +68 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +11 -1
- package/server/modules/projects/tests/project-management.service.test.ts +38 -10
- package/server/modules/websocket/services/chat-websocket.service.ts +87 -19
- package/server/routes/agent.js +34 -7
- package/server/routes/settings.js +1 -1
- package/server/services/notification-orchestrator.js +1 -1
- package/server/shared/utils.ts +70 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-authorization.service.test.js","sourceRoot":"","sources":["../../../../../server/modules/projects/tests/project-authorization.service.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,8DAA8D,CAAC;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,UAAU,GAAG,8BAA8B,CAAC;AAElD,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO;QACL,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,IAAI,CAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,EAAE,GAAG,UAAU,GAAG,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IAClG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iFAAiF,EAAE,GAAG,EAAE;IAC3F,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CAAC,yBAAyB,CAAC,SAAS,EAAE,UAAU,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,EAC/E,CAAC,KAAc,EAAE,EAAE;QACjB,MAAM,CAAC,EAAE,CAAC,KAAK,YAAY,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2EAA2E,EAAE,GAAG,EAAE;IACrF,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CAAC,yBAAyB,CAAC,YAAY,EAAE,KAAK,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,EAC7E,CAAC,KAAc,EAAE,EAAE;QACjB,MAAM,CAAC,EAAE,CAAC,KAAK,YAAY,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4GAA4G,EAAE,GAAG,EAAE;IACtH,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CAAC,yBAAyB,CAAC,YAAY,EAAE,UAAU,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,EACnF,CAAC,KAAc,EAAE,EAAE;QACjB,MAAM,CAAC,EAAE,CAAC,KAAK,YAAY,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;IACjE,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,MAAM,YAAY,GAAG;QACnB,cAAc,EAAE,CAAC,MAAc,EAAE,EAAE;YACjC,cAAc,GAAG,MAAM,CAAC;YACxB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC9B,CAAC;KACF,CAAC;IAEF,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC"}
|
|
@@ -9,6 +9,7 @@ function buildDependencies(overrides = {}) {
|
|
|
9
9
|
return {
|
|
10
10
|
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/root' }),
|
|
11
11
|
ensureDirectory: async () => undefined,
|
|
12
|
+
resolveUserWorkspaceRoot: async () => '/workspace/root',
|
|
12
13
|
pathExists: async () => false,
|
|
13
14
|
removePath: async () => undefined,
|
|
14
15
|
getGithubTokenById: async () => ({ github_token: 'token-value' }),
|
|
@@ -34,6 +35,7 @@ test('startCloneProject rejects when workspace path is missing', async () => {
|
|
|
34
35
|
workspacePath: '',
|
|
35
36
|
githubUrl: 'https://github.com/example/repo',
|
|
36
37
|
userId: 1,
|
|
38
|
+
username: 'tester',
|
|
37
39
|
}, {
|
|
38
40
|
onProgress: () => undefined,
|
|
39
41
|
onComplete: () => undefined,
|
|
@@ -48,6 +50,7 @@ test('startCloneProject rejects when github URL is missing', async () => {
|
|
|
48
50
|
workspacePath: '/workspace/root',
|
|
49
51
|
githubUrl: '',
|
|
50
52
|
userId: 1,
|
|
53
|
+
username: 'tester',
|
|
51
54
|
}, {
|
|
52
55
|
onProgress: () => undefined,
|
|
53
56
|
onComplete: () => undefined,
|
|
@@ -62,6 +65,7 @@ test('startCloneProject rejects github URL values that begin with option prefixe
|
|
|
62
65
|
workspacePath: '/workspace/root',
|
|
63
66
|
githubUrl: '--upload-pack=malicious',
|
|
64
67
|
userId: 1,
|
|
68
|
+
username: 'tester',
|
|
65
69
|
}, {
|
|
66
70
|
onProgress: () => undefined,
|
|
67
71
|
onComplete: () => undefined,
|
|
@@ -77,6 +81,7 @@ test('startCloneProject rejects when selected github token does not exist', asyn
|
|
|
77
81
|
githubUrl: 'https://github.com/example/repo',
|
|
78
82
|
githubTokenId: 12,
|
|
79
83
|
userId: 1,
|
|
84
|
+
username: 'tester',
|
|
80
85
|
}, {
|
|
81
86
|
onProgress: () => undefined,
|
|
82
87
|
onComplete: () => undefined,
|
|
@@ -95,10 +100,12 @@ test('startCloneProject completes and emits complete payload when git exits succ
|
|
|
95
100
|
let capturedUserId = 0;
|
|
96
101
|
let capturedProjectPath = '';
|
|
97
102
|
let capturedCustomName = '';
|
|
103
|
+
let capturedUsername = '';
|
|
98
104
|
const operation = await startCloneProject({
|
|
99
105
|
workspacePath: '/workspace/root',
|
|
100
106
|
githubUrl: 'https://github.com/example/repo.git',
|
|
101
107
|
userId: 1,
|
|
108
|
+
username: 'tester',
|
|
102
109
|
}, {
|
|
103
110
|
onProgress: (message) => {
|
|
104
111
|
progressMessages.push(message);
|
|
@@ -108,8 +115,9 @@ test('startCloneProject completes and emits complete payload when git exits succ
|
|
|
108
115
|
},
|
|
109
116
|
}, buildDependencies({
|
|
110
117
|
spawnGitClone: () => gitProcess,
|
|
111
|
-
registerProject: async (userId, projectPath, customName) => {
|
|
118
|
+
registerProject: async (userId, username, projectPath, customName) => {
|
|
112
119
|
capturedUserId = userId;
|
|
120
|
+
capturedUsername = username;
|
|
113
121
|
capturedProjectPath = projectPath;
|
|
114
122
|
capturedCustomName = customName;
|
|
115
123
|
return { project: { projectId: 'project-1', path: projectPath } };
|
|
@@ -119,6 +127,7 @@ test('startCloneProject completes and emits complete payload when git exits succ
|
|
|
119
127
|
await operation.waitForCompletion;
|
|
120
128
|
assert.ok(progressMessages.some((message) => message.includes("Cloning into 'repo'")));
|
|
121
129
|
assert.equal(capturedUserId, 1);
|
|
130
|
+
assert.equal(capturedUsername, 'tester');
|
|
122
131
|
assert.equal(capturedCustomName, 'repo');
|
|
123
132
|
assert.equal(path.basename(capturedProjectPath), 'repo');
|
|
124
133
|
assert.notEqual(completePayload, null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-clone.service.test.js","sourceRoot":"","sources":["../../../../../server/modules/projects/tests/project-clone.service.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,sDAAsD,CAAC;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI7C,SAAS,iBAAiB,CAAC,YAAoD,EAAE;IAC/E,OAAO;QACL,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;QAC5E,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;QACtC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK;QAC7B,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;QACjC,kBAAkB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;QACjE,aAAa,EAAE,GAAG,EAAE;YAClB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QACD,eAAe,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,CAAC;QACtE,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;QACzB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB;IAC3B,MAAM,OAAO,GAAG,IAAI,YAAY,EAI/B,CAAC;IAEF,OAAO,CAAC,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IACnC,OAAO,CAAC,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IACnC,OAAO,CAAC,IAAI,GAAG,GAAG,EAAE;QAClB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;IAC1E,MAAM,MAAM,CAAC,OAAO,CAClB,KAAK,IAAI,EAAE,CACT,iBAAiB,CACf;QACE,aAAa,EAAE,EAAE;QACjB,SAAS,EAAE,iCAAiC;QAC5C,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"project-clone.service.test.js","sourceRoot":"","sources":["../../../../../server/modules/projects/tests/project-clone.service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,15 +3,18 @@ import test from 'node:test';
|
|
|
3
3
|
import { createProject } from '../../../modules/projects/services/project-management.service.js';
|
|
4
4
|
import { AppError } from '../../../shared/utils.js';
|
|
5
5
|
const TEST_USER_ID = 1;
|
|
6
|
+
const TEST_USERNAME = 'tester';
|
|
7
|
+
const TEST_USER_WORKSPACE_ROOT = '/workspace/tester';
|
|
6
8
|
const projectRow = {
|
|
7
9
|
project_id: 'project-1',
|
|
8
|
-
project_path: '/workspace/my-project',
|
|
10
|
+
project_path: '/workspace/tester/my-project',
|
|
9
11
|
custom_project_name: 'my-project',
|
|
10
12
|
isStarred: 0,
|
|
11
13
|
isArchived: 0,
|
|
12
14
|
};
|
|
15
|
+
const resolveUserWorkspaceRoot = async () => TEST_USER_WORKSPACE_ROOT;
|
|
13
16
|
test('createProject throws when project path is missing', async () => {
|
|
14
|
-
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, projectPath: '' }), (error) => {
|
|
17
|
+
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '' }), (error) => {
|
|
15
18
|
assert.ok(error instanceof AppError);
|
|
16
19
|
assert.equal(error.code, 'PROJECT_PATH_REQUIRED');
|
|
17
20
|
assert.equal(error.statusCode, 400);
|
|
@@ -19,9 +22,10 @@ test('createProject throws when project path is missing', async () => {
|
|
|
19
22
|
});
|
|
20
23
|
});
|
|
21
24
|
test('createProject throws when path validation fails', async () => {
|
|
22
|
-
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, projectPath: '/invalid/path' }, {
|
|
25
|
+
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '/invalid/path' }, {
|
|
23
26
|
validatePath: async () => ({ valid: false, error: 'blocked path' }),
|
|
24
27
|
ensureWorkspaceDirectory: async () => undefined,
|
|
28
|
+
resolveUserWorkspaceRoot,
|
|
25
29
|
persistProjectPath: () => ({ outcome: 'created', project: projectRow }),
|
|
26
30
|
getProjectByPath: () => projectRow,
|
|
27
31
|
}), (error) => {
|
|
@@ -33,25 +37,41 @@ test('createProject throws when path validation fails', async () => {
|
|
|
33
37
|
});
|
|
34
38
|
});
|
|
35
39
|
test('createProject throws conflict when active project path already exists', async () => {
|
|
36
|
-
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, projectPath: '/workspace/my-project' }, {
|
|
37
|
-
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/my-project' }),
|
|
40
|
+
await assert.rejects(async () => createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '/workspace/tester/my-project' }, {
|
|
41
|
+
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/tester/my-project' }),
|
|
38
42
|
ensureWorkspaceDirectory: async () => undefined,
|
|
43
|
+
resolveUserWorkspaceRoot,
|
|
39
44
|
persistProjectPath: () => ({ outcome: 'active_conflict', project: projectRow }),
|
|
40
45
|
getProjectByPath: () => projectRow,
|
|
41
46
|
}), (error) => {
|
|
42
47
|
assert.ok(error instanceof AppError);
|
|
43
48
|
assert.equal(error.code, 'PROJECT_ALREADY_EXISTS');
|
|
44
49
|
assert.equal(error.statusCode, 409);
|
|
45
|
-
assert.equal(error.details, 'Project path already exists: /workspace/my-project');
|
|
50
|
+
assert.equal(error.details, 'Project path already exists: /workspace/tester/my-project');
|
|
46
51
|
return true;
|
|
47
52
|
});
|
|
48
53
|
});
|
|
54
|
+
test('createProject passes the per-user workspace root to validatePath', async () => {
|
|
55
|
+
let capturedWorkspaceRoot = '';
|
|
56
|
+
await createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '/workspace/tester/my-project' }, {
|
|
57
|
+
validatePath: async (_projectPath, workspaceRoot) => {
|
|
58
|
+
capturedWorkspaceRoot = workspaceRoot;
|
|
59
|
+
return { valid: true, resolvedPath: '/workspace/tester/my-project' };
|
|
60
|
+
},
|
|
61
|
+
ensureWorkspaceDirectory: async () => undefined,
|
|
62
|
+
resolveUserWorkspaceRoot,
|
|
63
|
+
persistProjectPath: () => ({ outcome: 'created', project: projectRow }),
|
|
64
|
+
getProjectByPath: () => projectRow,
|
|
65
|
+
});
|
|
66
|
+
assert.equal(capturedWorkspaceRoot, TEST_USER_WORKSPACE_ROOT);
|
|
67
|
+
});
|
|
49
68
|
test('createProject falls back to directory name when custom name is not provided', async () => {
|
|
50
69
|
let capturedUserId = 0;
|
|
51
70
|
let capturedCustomName = null;
|
|
52
|
-
const result = await createProject({ userId: TEST_USER_ID, projectPath: '/workspace/my-project', customName: '' }, {
|
|
53
|
-
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/my-project' }),
|
|
71
|
+
const result = await createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '/workspace/tester/my-project', customName: '' }, {
|
|
72
|
+
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/tester/my-project' }),
|
|
54
73
|
ensureWorkspaceDirectory: async () => undefined,
|
|
74
|
+
resolveUserWorkspaceRoot,
|
|
55
75
|
persistProjectPath: (userId, _projectPath, customName) => {
|
|
56
76
|
capturedUserId = userId;
|
|
57
77
|
capturedCustomName = customName;
|
|
@@ -71,9 +91,10 @@ test('createProject falls back to directory name when custom name is not provide
|
|
|
71
91
|
assert.equal(result.project.displayName, 'my-project');
|
|
72
92
|
});
|
|
73
93
|
test('createProject returns archived reuse outcome when archived row is reused', async () => {
|
|
74
|
-
const result = await createProject({ userId: TEST_USER_ID, projectPath: '/workspace/my-project' }, {
|
|
75
|
-
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/my-project' }),
|
|
94
|
+
const result = await createProject({ userId: TEST_USER_ID, username: TEST_USERNAME, projectPath: '/workspace/tester/my-project' }, {
|
|
95
|
+
validatePath: async () => ({ valid: true, resolvedPath: '/workspace/tester/my-project' }),
|
|
76
96
|
ensureWorkspaceDirectory: async () => undefined,
|
|
97
|
+
resolveUserWorkspaceRoot,
|
|
77
98
|
persistProjectPath: () => ({
|
|
78
99
|
outcome: 'reactivated_archived',
|
|
79
100
|
project: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-management.service.test.js","sourceRoot":"","sources":["../../../../../server/modules/projects/tests/project-management.service.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,2DAA2D,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,YAAY,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"project-management.service.test.js","sourceRoot":"","sources":["../../../../../server/modules/projects/tests/project-management.service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { assertUserOwnsProjectPath } from '../../../modules/projects/services/project-authorization.service.js';
|
|
1
2
|
import { connectedClients } from '../../../modules/websocket/services/websocket-state.service.js';
|
|
2
3
|
import { WebSocketWriter } from '../../../modules/websocket/services/websocket-writer.service.js';
|
|
3
|
-
import { createNormalizedMessage, parseIncomingJsonObject } from '../../../shared/utils.js';
|
|
4
|
+
import { AppError, createNormalizedMessage, parseIncomingJsonObject } from '../../../shared/utils.js';
|
|
4
5
|
const DEFAULT_PROVIDER = 'claude';
|
|
5
6
|
/**
|
|
6
7
|
* Normalizes potentially invalid provider names coming from websocket payloads.
|
|
@@ -28,6 +29,32 @@ function readRequestUserId(request) {
|
|
|
28
29
|
}
|
|
29
30
|
return null;
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Pulls `cwd` out of the websocket message options and confirms the caller
|
|
34
|
+
* owns it. Returning the normalized path lets the downstream spawn use the
|
|
35
|
+
* canonical form instead of whatever the client happened to send.
|
|
36
|
+
*
|
|
37
|
+
* Without this gate, any authenticated user could spawn a CLI in another
|
|
38
|
+
* user's project (or any path on disk) by setting `options.cwd` directly.
|
|
39
|
+
*/
|
|
40
|
+
function authorizeOptionsCwd(userId, options) {
|
|
41
|
+
try {
|
|
42
|
+
const cwd = assertUserOwnsProjectPath(userId, options?.cwd);
|
|
43
|
+
return { authorized: true, cwd };
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof AppError) {
|
|
47
|
+
return { authorized: false, error };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
authorized: false,
|
|
51
|
+
error: new AppError('Failed to authorize working directory', {
|
|
52
|
+
code: 'PROJECT_AUTHORIZATION_FAILED',
|
|
53
|
+
statusCode: 500,
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
31
58
|
/**
|
|
32
59
|
* Handles authenticated chat websocket messages used by the main chat panel.
|
|
33
60
|
*/
|
|
@@ -35,6 +62,14 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
35
62
|
console.log('[INFO] Chat WebSocket connected');
|
|
36
63
|
connectedClients.add(ws);
|
|
37
64
|
const writer = new WebSocketWriter(ws, readRequestUserId(request));
|
|
65
|
+
const sendAuthorizationError = (error, provider) => {
|
|
66
|
+
writer.send({
|
|
67
|
+
type: 'error',
|
|
68
|
+
error: error.message,
|
|
69
|
+
code: error.code,
|
|
70
|
+
provider,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
38
73
|
ws.on('message', async (rawMessage) => {
|
|
39
74
|
try {
|
|
40
75
|
const parsed = parseIncomingJsonObject(rawMessage);
|
|
@@ -47,26 +82,55 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
47
82
|
throw new Error('Message type is required');
|
|
48
83
|
}
|
|
49
84
|
if (messageType === 'claude-command') {
|
|
50
|
-
|
|
85
|
+
const authorization = authorizeOptionsCwd(writer.userId, data.options);
|
|
86
|
+
if (!authorization.authorized) {
|
|
87
|
+
sendAuthorizationError(authorization.error, 'claude');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const sanitizedOptions = { ...(data.options ?? {}), cwd: authorization.cwd };
|
|
91
|
+
await dependencies.queryClaudeSDK(data.command ?? '', sanitizedOptions, writer);
|
|
51
92
|
return;
|
|
52
93
|
}
|
|
53
94
|
if (messageType === 'cursor-command') {
|
|
54
|
-
|
|
95
|
+
const authorization = authorizeOptionsCwd(writer.userId, data.options);
|
|
96
|
+
if (!authorization.authorized) {
|
|
97
|
+
sendAuthorizationError(authorization.error, 'cursor');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const sanitizedOptions = { ...(data.options ?? {}), cwd: authorization.cwd };
|
|
101
|
+
await dependencies.spawnCursor(data.command ?? '', sanitizedOptions, writer);
|
|
55
102
|
return;
|
|
56
103
|
}
|
|
57
104
|
if (messageType === 'codex-command') {
|
|
58
|
-
|
|
105
|
+
const authorization = authorizeOptionsCwd(writer.userId, data.options);
|
|
106
|
+
if (!authorization.authorized) {
|
|
107
|
+
sendAuthorizationError(authorization.error, 'codex');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const sanitizedOptions = { ...(data.options ?? {}), cwd: authorization.cwd };
|
|
111
|
+
await dependencies.queryCodex(data.command ?? '', sanitizedOptions, writer);
|
|
59
112
|
return;
|
|
60
113
|
}
|
|
61
114
|
if (messageType === 'gemini-command') {
|
|
62
|
-
|
|
115
|
+
const authorization = authorizeOptionsCwd(writer.userId, data.options);
|
|
116
|
+
if (!authorization.authorized) {
|
|
117
|
+
sendAuthorizationError(authorization.error, 'gemini');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const sanitizedOptions = { ...(data.options ?? {}), cwd: authorization.cwd };
|
|
121
|
+
await dependencies.spawnGemini(data.command ?? '', sanitizedOptions, writer);
|
|
63
122
|
return;
|
|
64
123
|
}
|
|
65
124
|
if (messageType === 'cursor-resume') {
|
|
125
|
+
const authorization = authorizeOptionsCwd(writer.userId, data.options);
|
|
126
|
+
if (!authorization.authorized) {
|
|
127
|
+
sendAuthorizationError(authorization.error, 'cursor');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
66
130
|
await dependencies.spawnCursor('', {
|
|
67
131
|
sessionId: data.sessionId,
|
|
68
132
|
resume: true,
|
|
69
|
-
cwd:
|
|
133
|
+
cwd: authorization.cwd,
|
|
70
134
|
}, writer);
|
|
71
135
|
return;
|
|
72
136
|
}
|
|
@@ -76,13 +140,13 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
76
140
|
const userId = writer.userId;
|
|
77
141
|
let success = false;
|
|
78
142
|
if (provider === 'cursor') {
|
|
79
|
-
success = dependencies.abortCursorSession(sessionId);
|
|
143
|
+
success = dependencies.abortCursorSession(sessionId, userId);
|
|
80
144
|
}
|
|
81
145
|
else if (provider === 'codex') {
|
|
82
146
|
success = dependencies.abortCodexSession(sessionId, userId);
|
|
83
147
|
}
|
|
84
148
|
else if (provider === 'gemini') {
|
|
85
|
-
success = dependencies.abortGeminiSession(sessionId);
|
|
149
|
+
success = dependencies.abortGeminiSession(sessionId, userId);
|
|
86
150
|
}
|
|
87
151
|
else {
|
|
88
152
|
success = await dependencies.abortClaudeSDKSession(sessionId, userId);
|
|
@@ -110,7 +174,7 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
110
174
|
}
|
|
111
175
|
if (messageType === 'cursor-abort') {
|
|
112
176
|
const sessionId = typeof data.sessionId === 'string' ? data.sessionId : '';
|
|
113
|
-
const success = dependencies.abortCursorSession(sessionId);
|
|
177
|
+
const success = dependencies.abortCursorSession(sessionId, writer.userId);
|
|
114
178
|
writer.send(createNormalizedMessage({
|
|
115
179
|
kind: 'complete',
|
|
116
180
|
exitCode: success ? 0 : 1,
|
|
@@ -127,13 +191,13 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
127
191
|
const userId = writer.userId;
|
|
128
192
|
let isActive = false;
|
|
129
193
|
if (provider === 'cursor') {
|
|
130
|
-
isActive = dependencies.isCursorSessionActive(sessionId);
|
|
194
|
+
isActive = dependencies.isCursorSessionActive(sessionId, userId);
|
|
131
195
|
}
|
|
132
196
|
else if (provider === 'codex') {
|
|
133
197
|
isActive = dependencies.isCodexSessionActive(sessionId, userId);
|
|
134
198
|
}
|
|
135
199
|
else if (provider === 'gemini') {
|
|
136
|
-
isActive = dependencies.isGeminiSessionActive(sessionId);
|
|
200
|
+
isActive = dependencies.isGeminiSessionActive(sessionId, userId);
|
|
137
201
|
}
|
|
138
202
|
else {
|
|
139
203
|
isActive = dependencies.isClaudeSDKSessionActive(sessionId, userId);
|
|
@@ -168,9 +232,9 @@ export function handleChatConnection(ws, request, dependencies) {
|
|
|
168
232
|
type: 'active-sessions',
|
|
169
233
|
sessions: {
|
|
170
234
|
claude: dependencies.getActiveClaudeSDKSessions(userId),
|
|
171
|
-
cursor: dependencies.getActiveCursorSessions(),
|
|
235
|
+
cursor: dependencies.getActiveCursorSessions(userId),
|
|
172
236
|
codex: dependencies.getActiveCodexSessions(userId),
|
|
173
|
-
gemini: dependencies.getActiveGeminiSessions(),
|
|
237
|
+
gemini: dependencies.getActiveGeminiSessions(userId),
|
|
174
238
|
},
|
|
175
239
|
});
|
|
176
240
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-websocket.service.js","sourceRoot":"","sources":["../../../../../server/modules/websocket/services/chat-websocket.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yDAAyD,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAM3F,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-websocket.service.js","sourceRoot":"","sources":["../../../../../server/modules/websocket/services/chat-websocket.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,8 @@ import { spawnGemini } from '../gemini-cli.js';
|
|
|
12
12
|
import { Octokit } from '@octokit/rest';
|
|
13
13
|
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants.js';
|
|
14
14
|
import { IS_PLATFORM } from '../constants/config.js';
|
|
15
|
-
import {
|
|
15
|
+
import { assertUserOwnsProjectPath } from '../modules/projects/services/project-authorization.service.js';
|
|
16
|
+
import { AppError, ensureUserWorkspaceRoot, normalizeProjectPath, validateWorkspacePath } from '../shared/utils.js';
|
|
16
17
|
const router = express.Router();
|
|
17
18
|
/**
|
|
18
19
|
* Middleware to authenticate agent API requests.
|
|
@@ -805,7 +806,17 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
805
806
|
const tokenToUse = githubToken || githubTokensDb.getActiveGithubToken(req.user.id);
|
|
806
807
|
let targetPath;
|
|
807
808
|
if (projectPath) {
|
|
808
|
-
|
|
809
|
+
// Confine clone destinations to the caller's per-user workspace root so
|
|
810
|
+
// an API key cannot drop a fresh checkout into another user's directory.
|
|
811
|
+
const userWorkspaceRoot = await ensureUserWorkspaceRoot(req.user.username);
|
|
812
|
+
const validation = await validateWorkspacePath(projectPath, userWorkspaceRoot);
|
|
813
|
+
if (!validation.valid) {
|
|
814
|
+
return res.status(403).json({
|
|
815
|
+
success: false,
|
|
816
|
+
error: validation.error || 'Invalid project path for clone destination',
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
targetPath = validation.resolvedPath || projectPath;
|
|
809
820
|
}
|
|
810
821
|
else {
|
|
811
822
|
// Generate a unique path for cloning
|
|
@@ -815,9 +826,21 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
815
826
|
finalProjectPath = await cloneGitHubRepo(githubUrl.trim(), tokenToUse, targetPath);
|
|
816
827
|
}
|
|
817
828
|
else {
|
|
818
|
-
//
|
|
819
|
-
|
|
820
|
-
|
|
829
|
+
// Existing project path: only the owning user may operate on it.
|
|
830
|
+
try {
|
|
831
|
+
finalProjectPath = assertUserOwnsProjectPath(req.user.id, projectPath);
|
|
832
|
+
}
|
|
833
|
+
catch (authorizationError) {
|
|
834
|
+
if (authorizationError instanceof AppError) {
|
|
835
|
+
return res.status(authorizationError.statusCode).json({
|
|
836
|
+
success: false,
|
|
837
|
+
error: authorizationError.message,
|
|
838
|
+
code: authorizationError.code,
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
throw authorizationError;
|
|
842
|
+
}
|
|
843
|
+
// Verify the path exists on disk; ownership check above already confirmed registration.
|
|
821
844
|
try {
|
|
822
845
|
await fs.access(finalProjectPath);
|
|
823
846
|
}
|
|
@@ -826,10 +849,15 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
826
849
|
}
|
|
827
850
|
}
|
|
828
851
|
finalProjectPath = normalizeProjectPath(finalProjectPath);
|
|
829
|
-
//
|
|
852
|
+
// Reject cross-user shared paths outright; previous warn-only behavior let
|
|
853
|
+
// user A operate inside a path also registered to user B.
|
|
830
854
|
const sharedPathCheck = projectsDb.isProjectPathUsedByOthers(req.user.id, finalProjectPath);
|
|
831
855
|
if (sharedPathCheck.used) {
|
|
832
|
-
|
|
856
|
+
return res.status(403).json({
|
|
857
|
+
success: false,
|
|
858
|
+
error: 'Project path is already registered to another user',
|
|
859
|
+
code: 'PROJECT_PATH_CROSS_USER',
|
|
860
|
+
});
|
|
833
861
|
}
|
|
834
862
|
// Register project path in DB (or reuse existing active registration)
|
|
835
863
|
const registrationResult = projectsDb.createProjectPath(req.user.id, finalProjectPath, null);
|