@opencoop/opencode-plugin 1.0.0

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 (44) hide show
  1. package/LICENSE +178 -0
  2. package/README.fa.md +221 -0
  3. package/README.md +245 -0
  4. package/dist/auth/auth-manager.d.ts +45 -0
  5. package/dist/auth/auth-manager.js +157 -0
  6. package/dist/auth/auth-manager.js.map +1 -0
  7. package/dist/auth/session-manager.d.ts +14 -0
  8. package/dist/auth/session-manager.js +50 -0
  9. package/dist/auth/session-manager.js.map +1 -0
  10. package/dist/filesystem/change-tracker.d.ts +18 -0
  11. package/dist/filesystem/change-tracker.js +96 -0
  12. package/dist/filesystem/change-tracker.js.map +1 -0
  13. package/dist/filesystem/file-manager.d.ts +39 -0
  14. package/dist/filesystem/file-manager.js +189 -0
  15. package/dist/filesystem/file-manager.js.map +1 -0
  16. package/dist/filesystem/lock-manager.d.ts +15 -0
  17. package/dist/filesystem/lock-manager.js +120 -0
  18. package/dist/filesystem/lock-manager.js.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +36 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server/mcp-server.d.ts +20 -0
  23. package/dist/server/mcp-server.js +392 -0
  24. package/dist/server/mcp-server.js.map +1 -0
  25. package/dist/types/index.d.ts +80 -0
  26. package/dist/types/index.js +2 -0
  27. package/dist/types/index.js.map +1 -0
  28. package/dist/utils/config.d.ts +3 -0
  29. package/dist/utils/config.js +38 -0
  30. package/dist/utils/config.js.map +1 -0
  31. package/dist/utils/database.d.ts +12 -0
  32. package/dist/utils/database.js +110 -0
  33. package/dist/utils/database.js.map +1 -0
  34. package/dist/utils/logger.d.ts +2 -0
  35. package/dist/utils/logger.js +11 -0
  36. package/dist/utils/logger.js.map +1 -0
  37. package/dist/web/public/assets/app.js +446 -0
  38. package/dist/web/public/assets/style.css +639 -0
  39. package/dist/web/public/index.html +190 -0
  40. package/dist/web/public/invite.html +132 -0
  41. package/dist/web/server.d.ts +3 -0
  42. package/dist/web/server.js +150 -0
  43. package/dist/web/server.js.map +1 -0
  44. package/package.json +47 -0
@@ -0,0 +1,157 @@
1
+ import jwt from "jsonwebtoken";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import { nanoid } from "nanoid";
4
+ import { runQuery, getAllRows, getRow, } from "../utils/database.js";
5
+ export class AuthManager {
6
+ jwtSecret;
7
+ constructor(jwtSecret) {
8
+ this.jwtSecret = jwtSecret;
9
+ }
10
+ async initialize() {
11
+ runQuery(`
12
+ CREATE TABLE IF NOT EXISTS users (
13
+ id TEXT PRIMARY KEY,
14
+ email TEXT UNIQUE NOT NULL,
15
+ name TEXT,
16
+ role TEXT DEFAULT 'member',
17
+ created_at TEXT DEFAULT (datetime('now'))
18
+ )
19
+ `);
20
+ runQuery(`
21
+ CREATE TABLE IF NOT EXISTS invite_links (
22
+ id TEXT PRIMARY KEY,
23
+ workspace_id TEXT NOT NULL,
24
+ token TEXT UNIQUE NOT NULL,
25
+ email TEXT,
26
+ permissions TEXT DEFAULT 'read,write',
27
+ expires_at TEXT,
28
+ max_uses INTEGER,
29
+ use_count INTEGER DEFAULT 0,
30
+ created_by TEXT NOT NULL,
31
+ created_at TEXT DEFAULT (datetime('now')),
32
+ revoked_at TEXT
33
+ )
34
+ `);
35
+ runQuery(`
36
+ CREATE TABLE IF NOT EXISTS team_members (
37
+ id TEXT PRIMARY KEY,
38
+ workspace_id TEXT NOT NULL,
39
+ user_id TEXT NOT NULL,
40
+ role TEXT DEFAULT 'member',
41
+ permissions TEXT DEFAULT 'read,write',
42
+ invited_at TEXT DEFAULT (datetime('now')),
43
+ accepted_at TEXT
44
+ )
45
+ `);
46
+ }
47
+ async validateToken(token) {
48
+ try {
49
+ const decoded = jwt.verify(token, this.jwtSecret);
50
+ return {
51
+ valid: true,
52
+ userId: decoded.userId,
53
+ workspaceId: decoded.workspaceId,
54
+ permissions: decoded.permissions,
55
+ };
56
+ }
57
+ catch {
58
+ return { valid: false };
59
+ }
60
+ }
61
+ async generateInviteLink(params) {
62
+ const token = nanoid(32);
63
+ const expiresAt = new Date(Date.now() + params.expiresInDays * 24 * 60 * 60 * 1000);
64
+ runQuery(`INSERT INTO invite_links (id, workspace_id, token, email, permissions, expires_at, created_by)
65
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
66
+ uuidv4(),
67
+ params.workspaceId,
68
+ token,
69
+ params.email,
70
+ params.permissions.join(","),
71
+ expiresAt.toISOString(),
72
+ params.createdBy,
73
+ ]);
74
+ return {
75
+ link: `http://localhost:31313/invite/${token}`,
76
+ token,
77
+ expiresAt,
78
+ };
79
+ }
80
+ async validateInviteLink(token) {
81
+ const link = getRow("SELECT * FROM invite_links WHERE token = ?", [token]);
82
+ if (!link)
83
+ return { valid: false, reason: "Link not found" };
84
+ if (link.revoked_at)
85
+ return { valid: false, reason: "Link revoked" };
86
+ if (link.expires_at && new Date(link.expires_at) < new Date()) {
87
+ return { valid: false, reason: "Link expired" };
88
+ }
89
+ if (link.max_uses && link.use_count >= link.max_uses) {
90
+ return { valid: false, reason: "Max uses reached" };
91
+ }
92
+ return {
93
+ valid: true,
94
+ workspaceId: link.workspace_id,
95
+ permissions: link.permissions.split(","),
96
+ };
97
+ }
98
+ async acceptInvite(token, userId) {
99
+ const link = await this.validateInviteLink(token);
100
+ if (!link.valid || !link.workspaceId) {
101
+ throw new Error(link.reason || "Invalid invite link");
102
+ }
103
+ runQuery(`INSERT OR IGNORE INTO team_members (id, workspace_id, user_id, permissions, accepted_at)
104
+ VALUES (?, ?, ?, ?, datetime('now'))`, [uuidv4(), link.workspaceId, userId, link.permissions?.join(",")]);
105
+ runQuery("UPDATE invite_links SET use_count = use_count + 1 WHERE token = ?", [token]);
106
+ }
107
+ async getTeamMembers(workspaceId) {
108
+ return getAllRows(`SELECT tm.*, u.email, u.name
109
+ FROM team_members tm
110
+ LEFT JOIN users u ON tm.user_id = u.id
111
+ WHERE tm.workspace_id = ?`, [workspaceId]);
112
+ }
113
+ async revokeAccess(workspaceId, userId, ownerId) {
114
+ const workspace = getRow("SELECT id FROM workspaces WHERE id = ? AND owner_id = ?", [workspaceId, ownerId]);
115
+ if (!workspace) {
116
+ throw new Error("Only the workspace owner can revoke access");
117
+ }
118
+ runQuery("DELETE FROM team_members WHERE workspace_id = ? AND user_id = ?", [workspaceId, userId]);
119
+ }
120
+ async createWorkspace(params) {
121
+ const workspaceId = uuidv4();
122
+ runQuery(`INSERT INTO workspaces (id, name, path, owner_id, mode, host_url)
123
+ VALUES (?, ?, ?, ?, ?, ?)`, [
124
+ workspaceId,
125
+ params.name,
126
+ params.path,
127
+ params.ownerId,
128
+ params.mode,
129
+ params.hostUrl || null,
130
+ ]);
131
+ return workspaceId;
132
+ }
133
+ async getWorkspace(workspaceId) {
134
+ return getRow("SELECT * FROM workspaces WHERE id = ?", [workspaceId]);
135
+ }
136
+ async updateWorkspace(workspaceId, updates) {
137
+ const setClauses = [];
138
+ const values = [];
139
+ if (updates.mode) {
140
+ setClauses.push("mode = ?");
141
+ values.push(updates.mode);
142
+ }
143
+ if (updates.hostUrl !== undefined) {
144
+ setClauses.push("host_url = ?");
145
+ values.push(updates.hostUrl);
146
+ }
147
+ if (updates.workspacePath) {
148
+ setClauses.push("path = ?");
149
+ values.push(updates.workspacePath);
150
+ }
151
+ if (setClauses.length === 0)
152
+ return;
153
+ values.push(workspaceId);
154
+ runQuery(`UPDATE workspaces SET ${setClauses.join(", ")} WHERE id = ?`, values);
155
+ }
156
+ }
157
+ //# sourceMappingURL=auth-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../src/auth/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EACL,QAAQ,EACR,UAAU,EACV,MAAM,GAEP,MAAM,sBAAsB,CAAC;AAG9B,MAAM,OAAO,WAAW;IACd,SAAS,CAAS;IAE1B,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,QAAQ,CAAC;;;;;;;;KAQR,CAAC,CAAC;QAEH,QAAQ,CAAC;;;;;;;;;;;;;;KAcR,CAAC,CAAC;QAEH,QAAQ,CAAC;;;;;;;;;;KAUR,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,KAAa;QAOb,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAQ,CAAC;YACzD,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,WAAW,EAAE,OAAO,CAAC,WAAW;aACjC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAMxB;QAKC,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,MAAM,SAAS,GAAG,IAAI,IAAI,CACxB,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CACxD,CAAC;QAEF,QAAQ,CACN;oCAC8B,EAC9B;YACE,MAAM,EAAE;YACR,MAAM,CAAC,WAAW;YAClB,KAAK;YACL,MAAM,CAAC,KAAK;YACZ,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;YAC5B,SAAS,CAAC,WAAW,EAAE;YACvB,MAAM,CAAC,SAAS;SACjB,CACF,CAAC;QAEF,OAAO;YACL,IAAI,EAAE,iCAAiC,KAAK,EAAE;YAC9C,KAAK;YACL,SAAS;SACV,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,KAAa;QAOb,MAAM,IAAI,GAAG,MAAM,CAOhB,4CAA4C,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAE1D,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAC7D,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACrE,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;YAC9D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QAClD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;QACtD,CAAC;QAED,OAAO;YACL,KAAK,EAAE,IAAI;YACX,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC;SACzC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAa,EAAE,MAAc;QAC9C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,qBAAqB,CAAC,CAAC;QACxD,CAAC;QAED,QAAQ,CACN;4CACsC,EACtC,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAClE,CAAC;QAEF,QAAQ,CACN,mEAAmE,EACnE,CAAC,KAAK,CAAC,CACR,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAAmB;QACtC,OAAO,UAAU,CACf;;;iCAG2B,EAC3B,CAAC,WAAW,CAAC,CACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,WAAmB,EACnB,MAAc,EACd,OAAe;QAEf,MAAM,SAAS,GAAG,MAAM,CACtB,yDAAyD,EACzD,CAAC,WAAW,EAAE,OAAO,CAAC,CACvB,CAAC;QAEF,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,QAAQ,CACN,iEAAiE,EACjE,CAAC,WAAW,EAAE,MAAM,CAAC,CACtB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAMrB;QACC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC;QAE7B,QAAQ,CACN;iCAC2B,EAC3B;YACE,WAAW;YACX,MAAM,CAAC,IAAI;YACX,MAAM,CAAC,IAAI;YACX,MAAM,CAAC,OAAO;YACd,MAAM,CAAC,IAAI;YACX,MAAM,CAAC,OAAO,IAAI,IAAI;SACvB,CACF,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAmB;QACpC,OAAO,MAAM,CACX,uCAAuC,EACvC,CAAC,WAAW,CAAC,CACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,WAAmB,EACnB,OAAoE;QAEpE,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,MAAM,GAAU,EAAE,CAAC;QAEzB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAChC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEpC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzB,QAAQ,CACN,yBAAyB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAC7D,MAAM,CACP,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import { ActiveSession } from "../types/index.js";
2
+ export declare class SessionManager {
3
+ initialize(): Promise<void>;
4
+ registerSession(params: {
5
+ userId: string;
6
+ workspaceId: string;
7
+ mcpSessionId?: string;
8
+ userAgent?: string;
9
+ }): Promise<string>;
10
+ updateHeartbeat(sessionId: string): Promise<void>;
11
+ removeSession(sessionId: string): Promise<void>;
12
+ getOnlineUsers(workspaceId: string): Promise<ActiveSession[]>;
13
+ isUserOnline(workspaceId: string, userId: string): Promise<boolean>;
14
+ }
@@ -0,0 +1,50 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import { runQuery, getAllRows, } from "../utils/database.js";
3
+ export class SessionManager {
4
+ async initialize() {
5
+ runQuery(`
6
+ CREATE TABLE IF NOT EXISTS active_sessions (
7
+ id TEXT PRIMARY KEY,
8
+ user_id TEXT NOT NULL,
9
+ workspace_id TEXT NOT NULL,
10
+ mcp_session_id TEXT UNIQUE,
11
+ connected_at TEXT DEFAULT (datetime('now')),
12
+ last_heartbeat TEXT DEFAULT (datetime('now')),
13
+ user_agent TEXT
14
+ )
15
+ `);
16
+ runQuery(`
17
+ CREATE INDEX IF NOT EXISTS idx_sessions_workspace
18
+ ON active_sessions(workspace_id)
19
+ `);
20
+ }
21
+ async registerSession(params) {
22
+ const sessionId = uuidv4();
23
+ runQuery(`INSERT INTO active_sessions (id, user_id, workspace_id, mcp_session_id, user_agent)
24
+ VALUES (?, ?, ?, ?, ?)`, [
25
+ sessionId,
26
+ params.userId,
27
+ params.workspaceId,
28
+ params.mcpSessionId || null,
29
+ params.userAgent || null,
30
+ ]);
31
+ return sessionId;
32
+ }
33
+ async updateHeartbeat(sessionId) {
34
+ runQuery("UPDATE active_sessions SET last_heartbeat = datetime('now') WHERE id = ?", [sessionId]);
35
+ }
36
+ async removeSession(sessionId) {
37
+ runQuery("DELETE FROM active_sessions WHERE id = ?", [sessionId]);
38
+ }
39
+ async getOnlineUsers(workspaceId) {
40
+ runQuery(`DELETE FROM active_sessions
41
+ WHERE workspace_id = ? AND last_heartbeat < datetime('now', '-5 minutes')`, [workspaceId]);
42
+ return getAllRows("SELECT * FROM active_sessions WHERE workspace_id = ? ORDER BY connected_at DESC", [workspaceId]);
43
+ }
44
+ async isUserOnline(workspaceId, userId) {
45
+ const session = getAllRows(`SELECT id FROM active_sessions
46
+ WHERE workspace_id = ? AND user_id = ? AND last_heartbeat > datetime('now', '-5 minutes')`, [workspaceId, userId]);
47
+ return session.length > 0;
48
+ }
49
+ }
50
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/auth/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EACL,QAAQ,EACR,UAAU,GACX,MAAM,sBAAsB,CAAC;AAG9B,MAAM,OAAO,cAAc;IACzB,KAAK,CAAC,UAAU;QACd,QAAQ,CAAC;;;;;;;;;;KAUR,CAAC,CAAC;QAEH,QAAQ,CAAC;;;KAGR,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAKrB;QACC,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;QAE3B,QAAQ,CACN;8BACwB,EACxB;YACE,SAAS;YACT,MAAM,CAAC,MAAM;YACb,MAAM,CAAC,WAAW;YAClB,MAAM,CAAC,YAAY,IAAI,IAAI;YAC3B,MAAM,CAAC,SAAS,IAAI,IAAI;SACzB,CACF,CAAC;QAEF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,QAAQ,CACN,0EAA0E,EAC1E,CAAC,SAAS,CAAC,CACZ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,QAAQ,CAAC,0CAA0C,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAAmB;QACtC,QAAQ,CACN;iFAC2E,EAC3E,CAAC,WAAW,CAAC,CACd,CAAC;QAEF,OAAO,UAAU,CACf,iFAAiF,EACjF,CAAC,WAAW,CAAC,CACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,WAAmB,EACnB,MAAc;QAEd,MAAM,OAAO,GAAG,UAAU,CACxB;iGAC2F,EAC3F,CAAC,WAAW,EAAE,MAAM,CAAC,CACtB,CAAC;QAEF,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { ChangeLogEntry } from "../types/index.js";
2
+ export declare class ChangeTracker {
3
+ initialize(): Promise<void>;
4
+ logChange(entry: Omit<ChangeLogEntry, "id" | "timestamp">): Promise<void>;
5
+ getChanges(params: {
6
+ workspaceId: string;
7
+ filePath?: string;
8
+ userId?: string;
9
+ limit?: number;
10
+ offset?: number;
11
+ }): Promise<ChangeLogEntry[]>;
12
+ getStats(workspaceId: string): Promise<{
13
+ totalChanges: number;
14
+ changesByUser: Record<string, number>;
15
+ changesByAction: Record<string, number>;
16
+ recentActivity: ChangeLogEntry[];
17
+ }>;
18
+ }
@@ -0,0 +1,96 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import { runQuery, getAllRows, getScalar, } from "../utils/database.js";
3
+ export class ChangeTracker {
4
+ async initialize() {
5
+ runQuery(`
6
+ CREATE TABLE IF NOT EXISTS change_logs (
7
+ id TEXT PRIMARY KEY,
8
+ workspace_id TEXT NOT NULL,
9
+ file_path TEXT NOT NULL,
10
+ user_id TEXT NOT NULL,
11
+ action TEXT NOT NULL,
12
+ old_content_hash TEXT,
13
+ new_content_hash TEXT,
14
+ old_path TEXT,
15
+ new_path TEXT,
16
+ metadata TEXT,
17
+ timestamp TEXT DEFAULT (datetime('now'))
18
+ )
19
+ `);
20
+ runQuery(`
21
+ CREATE INDEX IF NOT EXISTS idx_changes_workspace
22
+ ON change_logs(workspace_id)
23
+ `);
24
+ runQuery(`
25
+ CREATE INDEX IF NOT EXISTS idx_changes_file
26
+ ON change_logs(workspace_id, file_path)
27
+ `);
28
+ runQuery(`
29
+ CREATE INDEX IF NOT EXISTS idx_changes_user
30
+ ON change_logs(workspace_id, user_id)
31
+ `);
32
+ runQuery(`
33
+ CREATE INDEX IF NOT EXISTS idx_changes_timestamp
34
+ ON change_logs(workspace_id, timestamp DESC)
35
+ `);
36
+ }
37
+ async logChange(entry) {
38
+ runQuery(`INSERT INTO change_logs (id, workspace_id, file_path, user_id, action,
39
+ old_content_hash, new_content_hash, old_path, new_path, metadata)
40
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
41
+ uuidv4(),
42
+ entry.workspaceId,
43
+ entry.filePath,
44
+ entry.userId,
45
+ entry.action,
46
+ entry.oldContentHash || null,
47
+ entry.newContentHash || null,
48
+ entry.oldPath || null,
49
+ entry.newPath || null,
50
+ entry.metadata || null,
51
+ ]);
52
+ }
53
+ async getChanges(params) {
54
+ let query = "SELECT * FROM change_logs WHERE workspace_id = ?";
55
+ const args = [params.workspaceId];
56
+ if (params.filePath) {
57
+ query += " AND file_path = ?";
58
+ args.push(params.filePath);
59
+ }
60
+ if (params.userId) {
61
+ query += " AND user_id = ?";
62
+ args.push(params.userId);
63
+ }
64
+ query += " ORDER BY timestamp DESC";
65
+ const limit = params.limit || 20;
66
+ const offset = params.offset || 0;
67
+ query += ` LIMIT ${limit} OFFSET ${offset}`;
68
+ const rows = getAllRows(query, args);
69
+ return rows.map((row) => ({
70
+ id: row.id,
71
+ workspaceId: row.workspace_id,
72
+ filePath: row.file_path,
73
+ userId: row.user_id,
74
+ action: row.action,
75
+ oldContentHash: row.old_content_hash || undefined,
76
+ newContentHash: row.new_content_hash || undefined,
77
+ oldPath: row.old_path || undefined,
78
+ newPath: row.new_path || undefined,
79
+ metadata: row.metadata || undefined,
80
+ timestamp: new Date(row.timestamp),
81
+ }));
82
+ }
83
+ async getStats(workspaceId) {
84
+ const total = getScalar("SELECT COUNT(*) as value FROM change_logs WHERE workspace_id = ?", [workspaceId]) || 0;
85
+ const byUserRows = getAllRows("SELECT user_id, COUNT(*) as count FROM change_logs WHERE workspace_id = ? GROUP BY user_id ORDER BY count DESC", [workspaceId]);
86
+ const byActionRows = getAllRows("SELECT action, COUNT(*) as count FROM change_logs WHERE workspace_id = ? GROUP BY action", [workspaceId]);
87
+ const recent = await this.getChanges({ workspaceId, limit: 10 });
88
+ return {
89
+ totalChanges: total,
90
+ changesByUser: Object.fromEntries(byUserRows.map((r) => [r.user_id, r.count])),
91
+ changesByAction: Object.fromEntries(byActionRows.map((r) => [r.action, r.count])),
92
+ recentActivity: recent,
93
+ };
94
+ }
95
+ }
96
+ //# sourceMappingURL=change-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-tracker.js","sourceRoot":"","sources":["../../src/filesystem/change-tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { FileMetadata } from "../types/index.js";
2
+ export declare class FileManager {
3
+ private workspacePath;
4
+ constructor(workspacePath: string);
5
+ private validatePath;
6
+ readFile(relativePath: string, options?: {
7
+ startLine?: number;
8
+ endLine?: number;
9
+ }): Promise<string>;
10
+ writeFile(relativePath: string, content: string, options?: {
11
+ createDirs?: boolean;
12
+ }): Promise<void>;
13
+ editFile(relativePath: string, search: string, replace: string, options?: {
14
+ replaceAll?: boolean;
15
+ }): Promise<{
16
+ changes: number;
17
+ }>;
18
+ listFiles(relativePath?: string, options?: {
19
+ recursive?: boolean;
20
+ }): Promise<FileMetadata[]>;
21
+ searchFiles(pattern: string, options?: {
22
+ maxResults?: number;
23
+ }): Promise<string[]>;
24
+ grep(pattern: string, options?: {
25
+ path?: string;
26
+ include?: string;
27
+ }): Promise<{
28
+ file: string;
29
+ line: number;
30
+ content: string;
31
+ }[]>;
32
+ getDirectoryTree(relativePath?: string, options?: {
33
+ maxDepth?: number;
34
+ excludePatterns?: string[];
35
+ }): Promise<any>;
36
+ hash(relativePath: string): Promise<string>;
37
+ private escapeRegExp;
38
+ private matchesGlob;
39
+ }
@@ -0,0 +1,189 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
3
+ import crypto from "crypto";
4
+ export class FileManager {
5
+ workspacePath;
6
+ constructor(workspacePath) {
7
+ this.workspacePath = path.resolve(workspacePath);
8
+ }
9
+ validatePath(relativePath) {
10
+ const resolved = path.resolve(this.workspacePath, relativePath);
11
+ if (!resolved.startsWith(this.workspacePath)) {
12
+ throw new Error(`Access denied: path "${relativePath}" is outside the workspace`);
13
+ }
14
+ if (resolved.includes("\0")) {
15
+ throw new Error("Access denied: path contains null bytes");
16
+ }
17
+ return resolved;
18
+ }
19
+ async readFile(relativePath, options) {
20
+ const fullPath = this.validatePath(relativePath);
21
+ let content = await fs.readFile(fullPath, "utf-8");
22
+ if (options?.startLine || options?.endLine) {
23
+ const lines = content.split("\n");
24
+ const start = (options.startLine || 1) - 1;
25
+ const end = options.endLine || lines.length;
26
+ content = lines.slice(start, end).join("\n");
27
+ }
28
+ return content;
29
+ }
30
+ async writeFile(relativePath, content, options) {
31
+ const fullPath = this.validatePath(relativePath);
32
+ if (options?.createDirs) {
33
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
34
+ }
35
+ const tmpPath = fullPath + ".tmp." + Date.now();
36
+ await fs.writeFile(tmpPath, content, "utf-8");
37
+ await fs.rename(tmpPath, fullPath);
38
+ }
39
+ async editFile(relativePath, search, replace, options) {
40
+ const fullPath = this.validatePath(relativePath);
41
+ let content = await fs.readFile(fullPath, "utf-8");
42
+ let changes = 0;
43
+ if (options?.replaceAll) {
44
+ const regex = new RegExp(this.escapeRegExp(search), "g");
45
+ const matches = content.match(regex);
46
+ changes = matches ? matches.length : 0;
47
+ content = content.replace(regex, replace);
48
+ }
49
+ else {
50
+ if (content.includes(search)) {
51
+ content = content.replace(search, replace);
52
+ changes = 1;
53
+ }
54
+ }
55
+ if (changes > 0) {
56
+ await this.writeFile(relativePath, content);
57
+ }
58
+ return { changes };
59
+ }
60
+ async listFiles(relativePath = ".", options) {
61
+ const fullPath = this.validatePath(relativePath);
62
+ const results = [];
63
+ const walk = async (dir, depth = 0) => {
64
+ if (options?.recursive && depth > 10)
65
+ return;
66
+ const entries = await fs.readdir(dir, { withFileTypes: true });
67
+ for (const entry of entries) {
68
+ const entryPath = path.join(dir, entry.name);
69
+ const stat = await fs.stat(entryPath);
70
+ results.push({
71
+ name: entry.name,
72
+ path: path.relative(this.workspacePath, entryPath),
73
+ type: entry.isDirectory() ? "directory" : "file",
74
+ size: stat.size,
75
+ modifiedAt: stat.mtime,
76
+ createdAt: stat.birthtime,
77
+ });
78
+ if (entry.isDirectory() && options?.recursive) {
79
+ await walk(entryPath, depth + 1);
80
+ }
81
+ }
82
+ };
83
+ await walk(fullPath);
84
+ return results;
85
+ }
86
+ async searchFiles(pattern, options) {
87
+ const maxResults = options?.maxResults || 50;
88
+ const results = [];
89
+ const walk = async (dir) => {
90
+ if (results.length >= maxResults)
91
+ return;
92
+ const entries = await fs.readdir(dir, { withFileTypes: true });
93
+ for (const entry of entries) {
94
+ if (results.length >= maxResults)
95
+ break;
96
+ const entryPath = path.join(dir, entry.name);
97
+ const relativePath = path.relative(this.workspacePath, entryPath);
98
+ if (this.matchesGlob(entry.name, pattern)) {
99
+ results.push(relativePath);
100
+ }
101
+ if (entry.isDirectory()) {
102
+ await walk(entryPath);
103
+ }
104
+ }
105
+ };
106
+ await walk(this.workspacePath);
107
+ return results;
108
+ }
109
+ async grep(pattern, options) {
110
+ const searchPath = options?.path
111
+ ? this.validatePath(options.path)
112
+ : this.workspacePath;
113
+ const results = [];
114
+ const regex = new RegExp(pattern, "g");
115
+ const walk = async (dir) => {
116
+ const entries = await fs.readdir(dir, { withFileTypes: true });
117
+ for (const entry of entries) {
118
+ const entryPath = path.join(dir, entry.name);
119
+ if (entry.isDirectory()) {
120
+ await walk(entryPath);
121
+ continue;
122
+ }
123
+ if (options?.include && !this.matchesGlob(entry.name, options.include)) {
124
+ continue;
125
+ }
126
+ try {
127
+ const content = await fs.readFile(entryPath, "utf-8");
128
+ const lines = content.split("\n");
129
+ for (let i = 0; i < lines.length; i++) {
130
+ if (regex.test(lines[i])) {
131
+ results.push({
132
+ file: path.relative(this.workspacePath, entryPath),
133
+ line: i + 1,
134
+ content: lines[i].trim(),
135
+ });
136
+ }
137
+ regex.lastIndex = 0;
138
+ }
139
+ }
140
+ catch {
141
+ // Skip binary files or files that can't be read
142
+ }
143
+ }
144
+ };
145
+ await walk(searchPath);
146
+ return results;
147
+ }
148
+ async getDirectoryTree(relativePath = ".", options) {
149
+ const fullPath = this.validatePath(relativePath);
150
+ const maxDepth = options?.maxDepth || 3;
151
+ const buildTree = async (dir, depth) => {
152
+ if (depth > maxDepth)
153
+ return { name: path.basename(dir), truncated: true };
154
+ const entries = await fs.readdir(dir, { withFileTypes: true });
155
+ const children = [];
156
+ for (const entry of entries) {
157
+ if (options?.excludePatterns?.some((p) => this.matchesGlob(entry.name, p))) {
158
+ continue;
159
+ }
160
+ const entryPath = path.join(dir, entry.name);
161
+ if (entry.isDirectory()) {
162
+ children.push({
163
+ name: entry.name,
164
+ type: "directory",
165
+ children: await buildTree(entryPath, depth + 1),
166
+ });
167
+ }
168
+ else {
169
+ children.push({ name: entry.name, type: "file" });
170
+ }
171
+ }
172
+ return { name: path.basename(dir), type: "directory", children };
173
+ };
174
+ return buildTree(fullPath, 0);
175
+ }
176
+ async hash(relativePath) {
177
+ const fullPath = this.validatePath(relativePath);
178
+ const content = await fs.readFile(fullPath);
179
+ return crypto.createHash("sha256").update(content).digest("hex");
180
+ }
181
+ escapeRegExp(string) {
182
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
183
+ }
184
+ matchesGlob(filename, pattern) {
185
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$", "i");
186
+ return regex.test(filename);
187
+ }
188
+ }
189
+ //# sourceMappingURL=file-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-manager.js","sourceRoot":"","sources":["../../src/filesystem/file-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { LockInfo, AcquireLockParams } from "../types/index.js";
2
+ export declare class LockManager {
3
+ initialize(): Promise<void>;
4
+ acquireLock(params: AcquireLockParams): Promise<{
5
+ granted: boolean;
6
+ lock?: LockInfo;
7
+ blockedBy?: LockInfo;
8
+ message: string;
9
+ }>;
10
+ releaseLock(workspaceId: string, filePath: string, userId: string): Promise<void>;
11
+ checkLock(workspaceId: string, filePath: string): Promise<LockInfo | null>;
12
+ getActiveLocks(workspaceId: string): Promise<LockInfo[]>;
13
+ private cleanupExpiredLocks;
14
+ forceReleaseAll(workspaceId: string): Promise<number>;
15
+ }