myaidev-method 0.2.7 → 0.2.9

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 (163) hide show
  1. package/.claude/agents/wordpress-admin.md +271 -0
  2. package/.env.example +0 -1
  3. package/COOLIFY_DEPLOYMENT.md +1 -1
  4. package/DEV_WORKFLOW_GUIDE.md +1 -1
  5. package/PACKAGE_FIXES_SUMMARY.md +319 -0
  6. package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
  7. package/PUBLISHING_GUIDE.md +1 -1
  8. package/README.md +7 -7
  9. package/USER_GUIDE.md +261 -1
  10. package/WORDPRESS_ADMIN_SCRIPTS.md +1 -1
  11. package/bin/cli.js +36 -0
  12. package/dist/server/.tsbuildinfo +1 -0
  13. package/dist/server/auth/controllers/AuthController.d.ts +34 -0
  14. package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
  15. package/dist/server/auth/controllers/AuthController.js +43 -0
  16. package/dist/server/auth/controllers/AuthController.js.map +1 -0
  17. package/dist/server/auth/example-usage.d.ts +53 -0
  18. package/dist/server/auth/example-usage.d.ts.map +1 -0
  19. package/dist/server/auth/example-usage.js +129 -0
  20. package/dist/server/auth/example-usage.js.map +1 -0
  21. package/dist/server/auth/index.d.ts +11 -0
  22. package/dist/server/auth/index.d.ts.map +1 -0
  23. package/dist/server/auth/index.js +15 -0
  24. package/dist/server/auth/index.js.map +1 -0
  25. package/dist/server/auth/layers.d.ts +19 -0
  26. package/dist/server/auth/layers.d.ts.map +1 -0
  27. package/dist/server/auth/layers.js +33 -0
  28. package/dist/server/auth/layers.js.map +1 -0
  29. package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
  30. package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
  31. package/dist/server/auth/middleware/authMiddleware.js +65 -0
  32. package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
  33. package/dist/server/auth/routes/authRoutes.d.ts +11 -0
  34. package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
  35. package/dist/server/auth/routes/authRoutes.js +213 -0
  36. package/dist/server/auth/routes/authRoutes.js.map +1 -0
  37. package/dist/server/auth/services/AuditLogService.d.ts +21 -0
  38. package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
  39. package/dist/server/auth/services/AuditLogService.js +28 -0
  40. package/dist/server/auth/services/AuditLogService.js.map +1 -0
  41. package/dist/server/auth/services/AuthService.d.ts +27 -0
  42. package/dist/server/auth/services/AuthService.d.ts.map +1 -0
  43. package/dist/server/auth/services/AuthService.js +246 -0
  44. package/dist/server/auth/services/AuthService.js.map +1 -0
  45. package/dist/server/auth/services/PasswordService.d.ts +12 -0
  46. package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
  47. package/dist/server/auth/services/PasswordService.js +31 -0
  48. package/dist/server/auth/services/PasswordService.js.map +1 -0
  49. package/dist/server/auth/services/SessionRepository.d.ts +24 -0
  50. package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
  51. package/dist/server/auth/services/SessionRepository.js +101 -0
  52. package/dist/server/auth/services/SessionRepository.js.map +1 -0
  53. package/dist/server/auth/services/TokenService.d.ts +12 -0
  54. package/dist/server/auth/services/TokenService.d.ts.map +1 -0
  55. package/dist/server/auth/services/TokenService.js +86 -0
  56. package/dist/server/auth/services/TokenService.js.map +1 -0
  57. package/dist/server/auth/services/UserRepository.d.ts +23 -0
  58. package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
  59. package/dist/server/auth/services/UserRepository.js +168 -0
  60. package/dist/server/auth/services/UserRepository.js.map +1 -0
  61. package/dist/server/auth/services/example.d.ts +26 -0
  62. package/dist/server/auth/services/example.d.ts.map +1 -0
  63. package/dist/server/auth/services/example.js +221 -0
  64. package/dist/server/auth/services/example.js.map +1 -0
  65. package/dist/server/auth/services/index.d.ts +6 -0
  66. package/dist/server/auth/services/index.d.ts.map +1 -0
  67. package/dist/server/auth/services/index.js +7 -0
  68. package/dist/server/auth/services/index.js.map +1 -0
  69. package/dist/server/database/db.d.ts +28 -0
  70. package/dist/server/database/db.d.ts.map +1 -0
  71. package/dist/server/database/db.js +91 -0
  72. package/dist/server/database/db.js.map +1 -0
  73. package/dist/server/database/schema.sql +95 -0
  74. package/dist/server/hono/app.d.ts +10 -0
  75. package/dist/server/hono/app.d.ts.map +1 -0
  76. package/dist/server/hono/app.js +26 -0
  77. package/dist/server/hono/app.js.map +1 -0
  78. package/dist/server/hono/routes.d.ts +12 -0
  79. package/dist/server/hono/routes.d.ts.map +1 -0
  80. package/dist/server/hono/routes.js +40 -0
  81. package/dist/server/hono/routes.js.map +1 -0
  82. package/dist/server/main.d.ts +2 -0
  83. package/dist/server/main.d.ts.map +1 -0
  84. package/dist/server/main.js +94 -0
  85. package/dist/server/main.js.map +1 -0
  86. package/dist/server/user-management/DirectoryService.d.ts +62 -0
  87. package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
  88. package/dist/server/user-management/DirectoryService.js +201 -0
  89. package/dist/server/user-management/DirectoryService.js.map +1 -0
  90. package/dist/server/user-management/LinuxUserService.d.ts +71 -0
  91. package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
  92. package/dist/server/user-management/LinuxUserService.js +192 -0
  93. package/dist/server/user-management/LinuxUserService.js.map +1 -0
  94. package/dist/server/user-management/QuotaService.d.ts +59 -0
  95. package/dist/server/user-management/QuotaService.d.ts.map +1 -0
  96. package/dist/server/user-management/QuotaService.js +148 -0
  97. package/dist/server/user-management/QuotaService.js.map +1 -0
  98. package/dist/server/user-management/UserManagementService.d.ts +74 -0
  99. package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
  100. package/dist/server/user-management/UserManagementService.js +122 -0
  101. package/dist/server/user-management/UserManagementService.js.map +1 -0
  102. package/dist/server/user-management/index.d.ts +26 -0
  103. package/dist/server/user-management/index.d.ts.map +1 -0
  104. package/dist/server/user-management/index.js +26 -0
  105. package/dist/server/user-management/index.js.map +1 -0
  106. package/dist/server/user-management/layers.d.ts +27 -0
  107. package/dist/server/user-management/layers.d.ts.map +1 -0
  108. package/dist/server/user-management/layers.js +37 -0
  109. package/dist/server/user-management/layers.js.map +1 -0
  110. package/dist/shared/types.d.ts +94 -0
  111. package/dist/shared/types.d.ts.map +1 -0
  112. package/dist/shared/types.js +32 -0
  113. package/dist/shared/types.js.map +1 -0
  114. package/package.json +26 -6
  115. package/src/lib/payloadcms-utils.js +5 -12
  116. package/src/server/auth/ARCHITECTURE.md +575 -0
  117. package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
  118. package/src/server/auth/QUICK_START.md +283 -0
  119. package/src/server/auth/README.md +290 -0
  120. package/src/server/auth/controllers/AuthController.ts +129 -0
  121. package/src/server/auth/example-usage.ts +159 -0
  122. package/src/server/auth/index.ts +19 -0
  123. package/src/server/auth/layers.ts +57 -0
  124. package/src/server/auth/middleware/authMiddleware.ts +118 -0
  125. package/src/server/auth/routes/authRoutes.ts +319 -0
  126. package/src/server/auth/services/AuditLogService.ts +81 -0
  127. package/src/server/auth/services/AuthService.ts +408 -0
  128. package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
  129. package/src/server/auth/services/PasswordService.ts +85 -0
  130. package/src/server/auth/services/README.md +361 -0
  131. package/src/server/auth/services/SessionRepository.ts +227 -0
  132. package/src/server/auth/services/TokenService.ts +174 -0
  133. package/src/server/auth/services/UserRepository.ts +318 -0
  134. package/src/server/auth/services/example.ts +346 -0
  135. package/src/server/auth/services/index.ts +6 -0
  136. package/src/server/database/db.ts +161 -0
  137. package/src/server/database/schema.sql +95 -0
  138. package/src/server/hono/app.ts +41 -0
  139. package/src/server/main.ts +115 -0
  140. package/src/server/user-management/DirectoryService.ts +348 -0
  141. package/src/server/user-management/LinuxUserService.ts +338 -0
  142. package/src/server/user-management/QuotaService.ts +256 -0
  143. package/src/server/user-management/README.md +333 -0
  144. package/src/server/user-management/UserManagementService.ts +335 -0
  145. package/src/server/user-management/index.ts +26 -0
  146. package/src/server/user-management/layers.ts +51 -0
  147. package/src/shared/types.ts +111 -0
  148. package/src/templates/claude/agents/payloadcms-publish.md +34 -14
  149. package/src/templates/codex/commands/myai-astro-publish.md +8 -2
  150. package/src/templates/codex/commands/myai-content-writer.md +8 -2
  151. package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
  152. package/src/templates/codex/commands/myai-dev-architect.md +8 -2
  153. package/src/templates/codex/commands/myai-dev-code.md +8 -2
  154. package/src/templates/codex/commands/myai-dev-docs.md +8 -2
  155. package/src/templates/codex/commands/myai-dev-review.md +8 -2
  156. package/src/templates/codex/commands/myai-dev-test.md +8 -2
  157. package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
  158. package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
  159. package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
  160. package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
  161. package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
  162. package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
  163. package/src/templates/docs/wordpress-troubleshoot.js +2 -2
@@ -0,0 +1,71 @@
1
+ import { Effect, Context, Layer } from "effect";
2
+ /**
3
+ * Linux User Management Service
4
+ *
5
+ * Handles creation, management, and deletion of Linux system users
6
+ * for multi-user isolation in MyAIDev Method web server.
7
+ *
8
+ * Security Features:
9
+ * - No sudo/root access granted to created users
10
+ * - Limited shell access (rbash or nologin)
11
+ * - Home directory isolation
12
+ * - Resource limits via ulimit
13
+ * - User groups for permission management
14
+ */
15
+ export interface LinuxUser {
16
+ username: string;
17
+ uid: number;
18
+ gid: number;
19
+ homeDir: string;
20
+ shell: string;
21
+ created: boolean;
22
+ }
23
+ export interface CreateLinuxUserOptions {
24
+ username: string;
25
+ shell?: "/bin/rbash" | "/usr/sbin/nologin" | "/bin/bash";
26
+ createHome?: boolean;
27
+ groups?: string[];
28
+ }
29
+ export interface LinuxUserError {
30
+ readonly _tag: "LinuxUserError";
31
+ readonly message: string;
32
+ readonly cause?: unknown;
33
+ }
34
+ declare const LinuxUserService_base: Context.TagClass<LinuxUserService, "LinuxUserService", {
35
+ /**
36
+ * Create a new Linux system user
37
+ */
38
+ readonly createUser: (options: CreateLinuxUserOptions) => Effect.Effect<LinuxUser, LinuxUserError>;
39
+ /**
40
+ * Check if a Linux user exists
41
+ */
42
+ readonly userExists: (username: string) => Effect.Effect<boolean, LinuxUserError>;
43
+ /**
44
+ * Get Linux user information
45
+ */
46
+ readonly getUserInfo: (username: string) => Effect.Effect<LinuxUser, LinuxUserError>;
47
+ /**
48
+ * Delete a Linux system user
49
+ */
50
+ readonly deleteUser: (username: string, removeHome?: boolean) => Effect.Effect<void, LinuxUserError>;
51
+ /**
52
+ * Set resource limits for a user
53
+ */
54
+ readonly setResourceLimits: (username: string, limits: ResourceLimits) => Effect.Effect<void, LinuxUserError>;
55
+ /**
56
+ * Sanitize username for Linux system use
57
+ * Converts email-based usernames to valid Linux usernames
58
+ */
59
+ readonly sanitizeUsername: (username: string) => Effect.Effect<string, LinuxUserError>;
60
+ }>;
61
+ export declare class LinuxUserService extends LinuxUserService_base {
62
+ static Live: Layer.Layer<LinuxUserService, never, never>;
63
+ }
64
+ export interface ResourceLimits {
65
+ maxOpenFiles?: number;
66
+ maxProcesses?: number;
67
+ maxMemoryKB?: number;
68
+ maxCPUTime?: number;
69
+ }
70
+ export {};
71
+ //# sourceMappingURL=LinuxUserService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinuxUserService.d.ts","sourceRoot":"","sources":["../../../src/server/user-management/LinuxUserService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGhD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,WAAW,CAAC;IACzD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;;IAWG;;OAEG;yBACkB,CACnB,OAAO,EAAE,sBAAsB,KAC5B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC;IAE7C;;OAEG;yBACkB,CACnB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC;IAE3C;;OAEG;0BACmB,CACpB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC;IAE7C;;OAEG;yBACkB,CACnB,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,OAAO,KACjB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC;IAExC;;OAEG;gCACyB,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC;IAExC;;;OAGG;+BACwB,CACzB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;;AA9C9C,qBAAa,gBAAiB,SAAQ,qBAgDnC;IACD,MAAM,CAAC,IAAI,8CA2OR;CACJ;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
@@ -0,0 +1,192 @@
1
+ import { Effect, Context, Layer } from "effect";
2
+ import { execSync } from "child_process";
3
+ const LinuxUserError = (message, cause) => ({
4
+ _tag: "LinuxUserError",
5
+ message,
6
+ cause,
7
+ });
8
+ export class LinuxUserService extends Context.Tag("LinuxUserService")() {
9
+ static Live = Layer.succeed(this, {
10
+ createUser: (options) => Effect.gen(function* () {
11
+ const { username, shell = "/bin/rbash", createHome = true, groups = [] } = options;
12
+ // Validate username format (Linux username requirements)
13
+ if (!/^[a-z_][a-z0-9_-]{0,31}$/.test(username)) {
14
+ return yield* Effect.fail(LinuxUserError(`Invalid Linux username format: ${username}. Must start with lowercase letter or underscore, contain only lowercase letters, digits, underscores, and hyphens, and be 1-32 characters long.`));
15
+ }
16
+ // Check if user already exists
17
+ const exists = yield* Effect.tryPromise({
18
+ try: async () => {
19
+ try {
20
+ execSync(`id -u ${username}`, { stdio: "ignore" });
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ },
27
+ catch: (error) => LinuxUserError("Failed to check if user exists", error),
28
+ });
29
+ if (exists) {
30
+ return yield* Effect.fail(LinuxUserError(`Linux user ${username} already exists`));
31
+ }
32
+ // Create user command
33
+ const createHomeFlag = createHome ? "--create-home" : "--no-create-home";
34
+ const groupsFlag = groups.length > 0 ? `--groups ${groups.join(",")}` : "";
35
+ const command = `sudo useradd ${createHomeFlag} --shell ${shell} ${groupsFlag} ${username}`;
36
+ yield* Effect.tryPromise({
37
+ try: async () => {
38
+ execSync(command, { stdio: "pipe" });
39
+ },
40
+ catch: (error) => LinuxUserError(`Failed to create Linux user ${username}`, error),
41
+ });
42
+ // Get user info
43
+ const userInfo = yield* Effect.tryPromise({
44
+ try: async () => {
45
+ const uidOutput = execSync(`id -u ${username}`, {
46
+ encoding: "utf-8",
47
+ }).trim();
48
+ const gidOutput = execSync(`id -g ${username}`, {
49
+ encoding: "utf-8",
50
+ }).trim();
51
+ const homeDir = createHome ? `/home/${username}` : "";
52
+ return {
53
+ username,
54
+ uid: parseInt(uidOutput, 10),
55
+ gid: parseInt(gidOutput, 10),
56
+ homeDir,
57
+ shell,
58
+ created: true,
59
+ };
60
+ },
61
+ catch: (error) => LinuxUserError(`Failed to get user info for ${username}`, error),
62
+ });
63
+ return userInfo;
64
+ }),
65
+ userExists: (username) => Effect.tryPromise({
66
+ try: async () => {
67
+ try {
68
+ execSync(`id -u ${username}`, { stdio: "ignore" });
69
+ return true;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ },
75
+ catch: (error) => LinuxUserError("Failed to check if user exists", error),
76
+ }),
77
+ getUserInfo: (username) => Effect.gen(function* () {
78
+ const exists = yield* Effect.tryPromise({
79
+ try: async () => {
80
+ try {
81
+ execSync(`id -u ${username}`, { stdio: "ignore" });
82
+ return true;
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ },
88
+ catch: (error) => LinuxUserError("Failed to check if user exists", error),
89
+ });
90
+ if (!exists) {
91
+ return yield* Effect.fail(LinuxUserError(`Linux user ${username} does not exist`));
92
+ }
93
+ const userInfo = yield* Effect.tryPromise({
94
+ try: async () => {
95
+ const uidOutput = execSync(`id -u ${username}`, {
96
+ encoding: "utf-8",
97
+ }).trim();
98
+ const gidOutput = execSync(`id -g ${username}`, {
99
+ encoding: "utf-8",
100
+ }).trim();
101
+ // Get shell from /etc/passwd
102
+ const passwdLine = execSync(`getent passwd ${username}`, {
103
+ encoding: "utf-8",
104
+ }).trim();
105
+ const shell = passwdLine.split(":")[6] || "/bin/bash";
106
+ // Get home directory
107
+ const homeDir = passwdLine.split(":")[5] || `/home/${username}`;
108
+ return {
109
+ username,
110
+ uid: parseInt(uidOutput, 10),
111
+ gid: parseInt(gidOutput, 10),
112
+ homeDir,
113
+ shell,
114
+ created: true,
115
+ };
116
+ },
117
+ catch: (error) => LinuxUserError(`Failed to get user info for ${username}`, error),
118
+ });
119
+ return userInfo;
120
+ }),
121
+ deleteUser: (username, removeHome = true) => Effect.gen(function* () {
122
+ // Check if user exists
123
+ const exists = yield* Effect.tryPromise({
124
+ try: async () => {
125
+ try {
126
+ execSync(`id -u ${username}`, { stdio: "ignore" });
127
+ return true;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ },
133
+ catch: (error) => LinuxUserError("Failed to check if user exists", error),
134
+ });
135
+ if (!exists) {
136
+ return yield* Effect.fail(LinuxUserError(`Linux user ${username} does not exist`));
137
+ }
138
+ // Delete user
139
+ const removeHomeFlag = removeHome ? "--remove" : "";
140
+ const command = `sudo userdel ${removeHomeFlag} ${username}`;
141
+ yield* Effect.tryPromise({
142
+ try: async () => {
143
+ execSync(command, { stdio: "pipe" });
144
+ },
145
+ catch: (error) => LinuxUserError(`Failed to delete Linux user ${username}`, error),
146
+ });
147
+ }),
148
+ setResourceLimits: (username, limits) => Effect.gen(function* () {
149
+ // Create limits configuration file
150
+ const limitsContent = `
151
+ # Resource limits for ${username}
152
+ ${username} soft nofile ${limits.maxOpenFiles || 1024}
153
+ ${username} hard nofile ${limits.maxOpenFiles || 2048}
154
+ ${username} soft nproc ${limits.maxProcesses || 256}
155
+ ${username} hard nproc ${limits.maxProcesses || 512}
156
+ ${username} soft as ${limits.maxMemoryKB || 2097152}
157
+ ${username} hard as ${limits.maxMemoryKB || 4194304}
158
+ ${username} soft cpu ${limits.maxCPUTime || 60}
159
+ ${username} hard cpu ${limits.maxCPUTime || 120}
160
+ `.trim();
161
+ const limitsFilePath = `/etc/security/limits.d/${username}.conf`;
162
+ yield* Effect.tryPromise({
163
+ try: async () => {
164
+ // Write limits file (requires sudo)
165
+ execSync(`echo '${limitsContent}' | sudo tee ${limitsFilePath} > /dev/null`, {
166
+ stdio: "pipe",
167
+ });
168
+ },
169
+ catch: (error) => LinuxUserError(`Failed to set resource limits for ${username}`, error),
170
+ });
171
+ }),
172
+ sanitizeUsername: (username) => Effect.gen(function* () {
173
+ // Convert to lowercase
174
+ let sanitized = username.toLowerCase();
175
+ // Replace invalid characters with underscores
176
+ sanitized = sanitized.replace(/[^a-z0-9_-]/g, "_");
177
+ // Ensure starts with letter or underscore
178
+ if (!/^[a-z_]/.test(sanitized)) {
179
+ sanitized = `u_${sanitized}`;
180
+ }
181
+ // Truncate to 32 characters
182
+ sanitized = sanitized.substring(0, 32);
183
+ // Remove trailing hyphens or underscores
184
+ sanitized = sanitized.replace(/[-_]+$/, "");
185
+ if (sanitized.length === 0) {
186
+ return yield* Effect.fail(LinuxUserError(`Could not sanitize username: ${username} results in empty string`));
187
+ }
188
+ return sanitized;
189
+ }),
190
+ });
191
+ }
192
+ //# sourceMappingURL=LinuxUserService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinuxUserService.js","sourceRoot":"","sources":["../../../src/server/user-management/LinuxUserService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { Effect, Context, Layer } from "effect";
2
+ /**
3
+ * Quota Management Service
4
+ *
5
+ * Manages disk quotas and resource limits for Linux users.
6
+ * Provides storage limits to prevent individual users from
7
+ * consuming excessive disk space.
8
+ *
9
+ * Features:
10
+ * - Disk quota management (requires quota system on host)
11
+ * - Directory size tracking
12
+ * - Resource limit enforcement
13
+ * - Usage monitoring
14
+ */
15
+ export interface DiskQuota {
16
+ softLimitMB: number;
17
+ hardLimitMB: number;
18
+ }
19
+ export interface QuotaUsage {
20
+ usedMB: number;
21
+ softLimitMB: number;
22
+ hardLimitMB: number;
23
+ percentUsed: number;
24
+ isOverSoftLimit: boolean;
25
+ isOverHardLimit: boolean;
26
+ }
27
+ export interface QuotaError {
28
+ readonly _tag: "QuotaError";
29
+ readonly message: string;
30
+ readonly cause?: unknown;
31
+ }
32
+ declare const QuotaService_base: Context.TagClass<QuotaService, "QuotaService", {
33
+ /**
34
+ * Set disk quota for a user
35
+ * Note: Requires quota support on the filesystem
36
+ */
37
+ readonly setDiskQuota: (username: string, quota: DiskQuota) => Effect.Effect<void, QuotaError>;
38
+ /**
39
+ * Get current quota usage for a user
40
+ */
41
+ readonly getQuotaUsage: (username: string) => Effect.Effect<QuotaUsage, QuotaError>;
42
+ /**
43
+ * Check if quota system is available
44
+ */
45
+ readonly isQuotaAvailable: () => Effect.Effect<boolean, QuotaError>;
46
+ /**
47
+ * Get directory size for a user (fallback if quota not available)
48
+ */
49
+ readonly getDirectorySize: (dirPath: string) => Effect.Effect<number, QuotaError>;
50
+ /**
51
+ * Enforce storage limits (alternative to disk quotas)
52
+ */
53
+ readonly checkStorageLimit: (username: string, homeDir: string, limitMB: number) => Effect.Effect<boolean, QuotaError>;
54
+ }>;
55
+ export declare class QuotaService extends QuotaService_base {
56
+ static Live: Layer.Layer<QuotaService, never, never>;
57
+ }
58
+ export {};
59
+ //# sourceMappingURL=QuotaService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuotaService.d.ts","sourceRoot":"","sources":["../../../src/server/user-management/QuotaService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGhD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;;IAWG;;;OAGG;2BACoB,CACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC;IAEpC;;OAEG;4BACqB,CACtB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC;IAE1C;;OAEG;+BACwB,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;IAEnE;;OAEG;+BACwB,CACzB,OAAO,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;IAEtC;;OAEG;gCACyB,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;;AAtC3C,qBAAa,YAAa,SAAQ,iBAwC/B;IACD,MAAM,CAAC,IAAI,0CA0KR;CACJ"}
@@ -0,0 +1,148 @@
1
+ import { Effect, Context, Layer } from "effect";
2
+ import { execSync } from "child_process";
3
+ const QuotaError = (message, cause) => ({
4
+ _tag: "QuotaError",
5
+ message,
6
+ cause,
7
+ });
8
+ export class QuotaService extends Context.Tag("QuotaService")() {
9
+ static Live = Layer.succeed(this, {
10
+ setDiskQuota: (username, quota) => Effect.gen(function* () {
11
+ // Check if quota is available
12
+ const quotaAvailable = yield* Effect.tryPromise({
13
+ try: async () => {
14
+ try {
15
+ execSync("which setquota", { stdio: "ignore" });
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ },
22
+ catch: (error) => QuotaError("Failed to check quota availability", error),
23
+ });
24
+ if (!quotaAvailable) {
25
+ return yield* Effect.fail(QuotaError("Quota system not available. Install quota tools: sudo apt-get install quota"));
26
+ }
27
+ // Set quota using setquota command
28
+ // Format: setquota -u username soft_blocks hard_blocks soft_inodes hard_inodes filesystem
29
+ const softBlocks = quota.softLimitMB * 1024; // Convert MB to blocks (1 block = 1KB)
30
+ const hardBlocks = quota.hardLimitMB * 1024;
31
+ const softInodes = 10000; // Reasonable inode limit
32
+ const hardInodes = 15000;
33
+ const filesystem = "/"; // Primary filesystem
34
+ yield* Effect.tryPromise({
35
+ try: async () => {
36
+ const command = `sudo setquota -u ${username} ${softBlocks} ${hardBlocks} ${softInodes} ${hardInodes} ${filesystem}`;
37
+ execSync(command, { stdio: "pipe" });
38
+ },
39
+ catch: (error) => QuotaError(`Failed to set disk quota for ${username}`, error),
40
+ });
41
+ }),
42
+ getQuotaUsage: (username) => Effect.gen(function* () {
43
+ // Check if quota is available
44
+ const quotaAvailable = yield* Effect.tryPromise({
45
+ try: async () => {
46
+ try {
47
+ execSync("which quota", { stdio: "ignore" });
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ },
54
+ catch: (error) => QuotaError("Failed to check quota availability", error),
55
+ });
56
+ if (!quotaAvailable) {
57
+ // Fallback: Calculate directory size
58
+ const homeDir = `/home/${username}`;
59
+ const usedMB = yield* Effect.tryPromise({
60
+ try: async () => {
61
+ const output = execSync(`sudo du -sm ${homeDir}`, {
62
+ encoding: "utf-8",
63
+ });
64
+ return parseInt(output.split("\t")[0] || "0", 10);
65
+ },
66
+ catch: (error) => QuotaError(`Failed to calculate directory size for ${username}`, error),
67
+ });
68
+ return {
69
+ usedMB,
70
+ softLimitMB: 1024, // Default 1GB soft limit
71
+ hardLimitMB: 2048, // Default 2GB hard limit
72
+ percentUsed: (usedMB / 2048) * 100,
73
+ isOverSoftLimit: usedMB > 1024,
74
+ isOverHardLimit: usedMB > 2048,
75
+ };
76
+ }
77
+ // Get quota usage using quota command
78
+ const usage = yield* Effect.tryPromise({
79
+ try: async () => {
80
+ const output = execSync(`sudo quota -u ${username} --show-mntpoint`, {
81
+ encoding: "utf-8",
82
+ });
83
+ // Parse quota output
84
+ // Expected format:
85
+ // Disk quotas for user username (uid 1001):
86
+ // Filesystem blocks quota limit grace files quota limit grace
87
+ // /dev/sda1 1024 1024000 2048000 100 10000 15000
88
+ const lines = output.trim().split("\n");
89
+ if (lines.length < 3) {
90
+ throw new Error("Invalid quota output format");
91
+ }
92
+ const dataLine = (lines[2] || "").trim().split(/\s+/);
93
+ const usedBlocks = parseInt(dataLine[1] || "0", 10);
94
+ const softBlocks = parseInt(dataLine[2] || "0", 10);
95
+ const hardBlocks = parseInt(dataLine[3] || "0", 10);
96
+ const usedMB = Math.round(usedBlocks / 1024);
97
+ const softLimitMB = Math.round(softBlocks / 1024);
98
+ const hardLimitMB = Math.round(hardBlocks / 1024);
99
+ return {
100
+ usedMB,
101
+ softLimitMB,
102
+ hardLimitMB,
103
+ percentUsed: (usedMB / hardLimitMB) * 100,
104
+ isOverSoftLimit: usedMB > softLimitMB,
105
+ isOverHardLimit: usedMB > hardLimitMB,
106
+ };
107
+ },
108
+ catch: (error) => QuotaError(`Failed to get quota usage for ${username}`, error),
109
+ });
110
+ return usage;
111
+ }),
112
+ isQuotaAvailable: () => Effect.tryPromise({
113
+ try: async () => {
114
+ try {
115
+ execSync("which quota", { stdio: "ignore" });
116
+ execSync("which setquota", { stdio: "ignore" });
117
+ return true;
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ },
123
+ catch: (error) => QuotaError("Failed to check quota availability", error),
124
+ }),
125
+ getDirectorySize: (dirPath) => Effect.tryPromise({
126
+ try: async () => {
127
+ const output = execSync(`sudo du -sm ${dirPath}`, {
128
+ encoding: "utf-8",
129
+ });
130
+ return parseInt(output.split("\t")[0] || "0", 10);
131
+ },
132
+ catch: (error) => QuotaError(`Failed to get directory size for ${dirPath}`, error),
133
+ }),
134
+ checkStorageLimit: (username, homeDir, limitMB) => Effect.gen(function* () {
135
+ const usedMB = yield* Effect.tryPromise({
136
+ try: async () => {
137
+ const output = execSync(`sudo du -sm ${homeDir}`, {
138
+ encoding: "utf-8",
139
+ });
140
+ return parseInt(output.split("\t")[0] || "0", 10);
141
+ },
142
+ catch: (error) => QuotaError(`Failed to check storage for ${username}`, error),
143
+ });
144
+ return usedMB <= limitMB;
145
+ }),
146
+ });
147
+ }
148
+ //# sourceMappingURL=QuotaService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuotaService.js","sourceRoot":"","sources":["../../../src/server/user-management/QuotaService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { Effect, Context, Layer } from "effect";
2
+ import { LinuxUserService, LinuxUser } from "./LinuxUserService";
3
+ import { DirectoryService } from "./DirectoryService";
4
+ import { QuotaService, DiskQuota } from "./QuotaService";
5
+ /**
6
+ * User Management Service
7
+ *
8
+ * High-level orchestration service that coordinates Linux user creation,
9
+ * directory setup, and resource management for multi-user environments.
10
+ *
11
+ * This service integrates:
12
+ * - LinuxUserService: System user creation and management
13
+ * - DirectoryService: Home directory and Claude config setup
14
+ * - QuotaService: Disk quotas and resource limits
15
+ *
16
+ * Workflow:
17
+ * 1. Sanitize and validate username
18
+ * 2. Create Linux system user
19
+ * 3. Set up home directory structure
20
+ * 4. Configure Claude Code environment
21
+ * 5. Apply resource limits and quotas
22
+ * 6. Return complete user setup information
23
+ */
24
+ export interface CreateUserRequest {
25
+ username: string;
26
+ email: string;
27
+ claudeApiKey?: string;
28
+ diskQuotaMB?: number;
29
+ shell?: "/bin/rbash" | "/usr/sbin/nologin" | "/bin/bash";
30
+ }
31
+ export interface UserSetupResult {
32
+ linuxUser: LinuxUser;
33
+ homeDir: string;
34
+ claudeConfigPath: string;
35
+ workspacePath: string;
36
+ quotaApplied: boolean;
37
+ limitsApplied: boolean;
38
+ }
39
+ export interface UserManagementError {
40
+ readonly _tag: "UserManagementError";
41
+ readonly message: string;
42
+ readonly cause?: unknown;
43
+ }
44
+ declare const UserManagementService_base: Context.TagClass<UserManagementService, "UserManagementService", {
45
+ /**
46
+ * Create a complete user environment
47
+ * - Creates Linux user
48
+ * - Sets up home directory
49
+ * - Configures Claude Code
50
+ * - Applies quotas and limits
51
+ */
52
+ readonly createUser: (request: CreateUserRequest) => Effect.Effect<UserSetupResult, UserManagementError>;
53
+ /**
54
+ * Delete user and clean up all resources
55
+ */
56
+ readonly deleteUser: (username: string) => Effect.Effect<void, UserManagementError>;
57
+ /**
58
+ * Get user information
59
+ */
60
+ readonly getUserInfo: (username: string) => Effect.Effect<LinuxUser, UserManagementError>;
61
+ /**
62
+ * Update user quotas
63
+ */
64
+ readonly updateQuota: (username: string, quota: DiskQuota) => Effect.Effect<void, UserManagementError>;
65
+ /**
66
+ * Check if username is available
67
+ */
68
+ readonly isUsernameAvailable: (username: string) => Effect.Effect<boolean, UserManagementError>;
69
+ }>;
70
+ export declare class UserManagementService extends UserManagementService_base {
71
+ static Live: Layer.Layer<UserManagementService, never, LinuxUserService | DirectoryService | QuotaService>;
72
+ }
73
+ export {};
74
+ //# sourceMappingURL=UserManagementService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserManagementService.d.ts","sourceRoot":"","sources":["../../../src/server/user-management/UserManagementService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAgB,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;;IAcG;;;;;;OAMG;yBACkB,CACnB,OAAO,EAAE,iBAAiB,KACvB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,mBAAmB,CAAC;IAExD;;OAEG;yBACkB,CACnB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAE7C;;OAEG;0BACmB,CACpB,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAC;IAElD;;OAEG;0BACmB,CACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAE7C;;OAEG;kCAC2B,CAC5B,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC;;AAzCpD,qBAAa,qBAAsB,SAAQ,0BA2CxC;IACD,MAAM,CAAC,IAAI,gGAwOT;CACH"}