teamcopilot 0.0.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.
Files changed (209) hide show
  1. package/.env.example +10 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +131 -0
  4. package/bin/teamcopilot.js +281 -0
  5. package/dist/auth/index.js +189 -0
  6. package/dist/change-user-role.js +77 -0
  7. package/dist/chat/index.js +849 -0
  8. package/dist/constants.js +2 -0
  9. package/dist/create-user.js +98 -0
  10. package/dist/cronjob/index.js +16 -0
  11. package/dist/cronjob/resource-reconciliation.js +33 -0
  12. package/dist/delete-user.js +66 -0
  13. package/dist/frontend/assets/abap-CRCWOmpq.js +1 -0
  14. package/dist/frontend/assets/apex-DnsZk_dE.js +1 -0
  15. package/dist/frontend/assets/azcli-1IWB1ccx.js +1 -0
  16. package/dist/frontend/assets/bat-DPkNLes8.js +1 -0
  17. package/dist/frontend/assets/bicep-Corcdgou.js +2 -0
  18. package/dist/frontend/assets/cameligo-CGrWLZr3.js +1 -0
  19. package/dist/frontend/assets/clojure-D9WOWImG.js +1 -0
  20. package/dist/frontend/assets/codicon-DCmgc-ay.ttf +0 -0
  21. package/dist/frontend/assets/coffee-B7EJu28W.js +1 -0
  22. package/dist/frontend/assets/cpp-SEyurbux.js +1 -0
  23. package/dist/frontend/assets/csharp-BoL64M5l.js +1 -0
  24. package/dist/frontend/assets/csp-C46ZqvIl.js +1 -0
  25. package/dist/frontend/assets/css-DQU6DXDx.js +3 -0
  26. package/dist/frontend/assets/cssMode-BDT3WbVs.js +4 -0
  27. package/dist/frontend/assets/cypher-D84EuPTj.js +1 -0
  28. package/dist/frontend/assets/dart-D8lhlL1r.js +1 -0
  29. package/dist/frontend/assets/dockerfile-DLk6rpji.js +1 -0
  30. package/dist/frontend/assets/ecl-BO6FnfXk.js +1 -0
  31. package/dist/frontend/assets/editor.worker-B4pQIWZD.js +12 -0
  32. package/dist/frontend/assets/elixir-BRjLKONM.js +1 -0
  33. package/dist/frontend/assets/flow9-Cac8vKd7.js +1 -0
  34. package/dist/frontend/assets/freemarker2-C7-hEgID.js +3 -0
  35. package/dist/frontend/assets/fsharp-fd1GTHhf.js +1 -0
  36. package/dist/frontend/assets/go-O9LJTZXk.js +1 -0
  37. package/dist/frontend/assets/graphql-LQdxqEYJ.js +1 -0
  38. package/dist/frontend/assets/handlebars-4cwTkPir.js +1 -0
  39. package/dist/frontend/assets/hcl-DxDQ3s82.js +1 -0
  40. package/dist/frontend/assets/html-YNfE1Q0A.js +1 -0
  41. package/dist/frontend/assets/htmlMode-opTQ1HoB.js +4 -0
  42. package/dist/frontend/assets/index-DWyaVa1h.js +782 -0
  43. package/dist/frontend/assets/index-lXrsgeTF.css +1 -0
  44. package/dist/frontend/assets/ini-BvajGCUy.js +1 -0
  45. package/dist/frontend/assets/java-SYsfObOQ.js +1 -0
  46. package/dist/frontend/assets/javascript-BEwGzk7T.js +1 -0
  47. package/dist/frontend/assets/jsonMode-CGhIS5Al.js +10 -0
  48. package/dist/frontend/assets/julia-DQXNmw_w.js +1 -0
  49. package/dist/frontend/assets/kotlin-qQ0MG-9I.js +1 -0
  50. package/dist/frontend/assets/less-GGFNNJHn.js +2 -0
  51. package/dist/frontend/assets/lexon-Canl7DCW.js +1 -0
  52. package/dist/frontend/assets/liquid-QekTGCGJ.js +1 -0
  53. package/dist/frontend/assets/lua-D28Ae8-K.js +1 -0
  54. package/dist/frontend/assets/m3-DPitgjJI.js +1 -0
  55. package/dist/frontend/assets/markdown-B811l8j2.js +1 -0
  56. package/dist/frontend/assets/mdx-BAVDaB7v.js +1 -0
  57. package/dist/frontend/assets/mips-CdjsipkG.js +1 -0
  58. package/dist/frontend/assets/msdax-CYqgjx_P.js +1 -0
  59. package/dist/frontend/assets/mysql-BHd6q0vd.js +1 -0
  60. package/dist/frontend/assets/objective-c-B1aVtJYH.js +1 -0
  61. package/dist/frontend/assets/pascal-BhNW15KB.js +1 -0
  62. package/dist/frontend/assets/pascaligo-5jv8CcQD.js +1 -0
  63. package/dist/frontend/assets/perl-DlYyT36c.js +1 -0
  64. package/dist/frontend/assets/pgsql-Dy0bjov7.js +1 -0
  65. package/dist/frontend/assets/php-120yhfDK.js +1 -0
  66. package/dist/frontend/assets/pla-CjnFlu4u.js +1 -0
  67. package/dist/frontend/assets/postiats-CQpG440k.js +1 -0
  68. package/dist/frontend/assets/powerquery-DdJtto1Z.js +1 -0
  69. package/dist/frontend/assets/powershell-Bu_VLpJB.js +1 -0
  70. package/dist/frontend/assets/protobuf-IBS6jZEB.js +2 -0
  71. package/dist/frontend/assets/pug-kFxLfcjb.js +1 -0
  72. package/dist/frontend/assets/python-BQlHw7XO.js +1 -0
  73. package/dist/frontend/assets/qsharp-q7JyzKFN.js +1 -0
  74. package/dist/frontend/assets/r-BIFz-_sK.js +1 -0
  75. package/dist/frontend/assets/razor-Be3Wwc2E.js +1 -0
  76. package/dist/frontend/assets/redis-CHOsPHWR.js +1 -0
  77. package/dist/frontend/assets/redshift-CBifECDb.js +1 -0
  78. package/dist/frontend/assets/restructuredtext-CghPJEOS.js +1 -0
  79. package/dist/frontend/assets/ruby-CYWGW-b1.js +1 -0
  80. package/dist/frontend/assets/rust-DMDD0SHb.js +1 -0
  81. package/dist/frontend/assets/sb-BYAiYHFx.js +1 -0
  82. package/dist/frontend/assets/scala-Bqvq8jcR.js +1 -0
  83. package/dist/frontend/assets/scheme-Dhb-2j9p.js +1 -0
  84. package/dist/frontend/assets/scss-CTwUZ5N7.js +3 -0
  85. package/dist/frontend/assets/shell-CsDZo4DB.js +1 -0
  86. package/dist/frontend/assets/solidity-CME5AdoB.js +1 -0
  87. package/dist/frontend/assets/sophia-RYC1BQQz.js +1 -0
  88. package/dist/frontend/assets/sparql-KEyrF7De.js +1 -0
  89. package/dist/frontend/assets/sql-BdTr02Mf.js +1 -0
  90. package/dist/frontend/assets/st-C7iG7M4S.js +1 -0
  91. package/dist/frontend/assets/swift-D7IUmUK8.js +1 -0
  92. package/dist/frontend/assets/systemverilog-DgMryOEJ.js +1 -0
  93. package/dist/frontend/assets/tcl-PloMZuKG.js +1 -0
  94. package/dist/frontend/assets/tsMode-CIBFoN3z.js +11 -0
  95. package/dist/frontend/assets/twig-BfRIq3la.js +1 -0
  96. package/dist/frontend/assets/typescript-BuV9wEIE.js +1 -0
  97. package/dist/frontend/assets/typespec-CzxlYoT_.js +1 -0
  98. package/dist/frontend/assets/vb-BwAE3J76.js +1 -0
  99. package/dist/frontend/assets/wgsl-B_1kOXbF.js +298 -0
  100. package/dist/frontend/assets/xml-DcDKYaM4.js +1 -0
  101. package/dist/frontend/assets/yaml-CuBNmOuI.js +1 -0
  102. package/dist/frontend/index.html +14 -0
  103. package/dist/frontend/logo.svg +50 -0
  104. package/dist/index.js +169 -0
  105. package/dist/logging.js +30 -0
  106. package/dist/opencode-auth/index.js +122 -0
  107. package/dist/opencode-server.js +91 -0
  108. package/dist/prisma/client.js +38 -0
  109. package/dist/reset-password.js +73 -0
  110. package/dist/rotate-jwt-secret.js +20 -0
  111. package/dist/scripts/prisma-workspace.js +34 -0
  112. package/dist/skills/index.js +311 -0
  113. package/dist/types/permissions.js +2 -0
  114. package/dist/types/shared/permissions.js +17 -0
  115. package/dist/types/shared/skill.js +17 -0
  116. package/dist/types/shared/workflow-files.js +17 -0
  117. package/dist/types/shared/workflow.js +17 -0
  118. package/dist/types/skill.js +2 -0
  119. package/dist/types/workflow-files.js +2 -0
  120. package/dist/types/workflow.js +2 -0
  121. package/dist/users/index.js +22 -0
  122. package/dist/utils/approval-snapshot-common.js +596 -0
  123. package/dist/utils/assert.js +20 -0
  124. package/dist/utils/chat-session.js +44 -0
  125. package/dist/utils/cli-bootstrap.js +26 -0
  126. package/dist/utils/index.js +95 -0
  127. package/dist/utils/jwt-secret.js +63 -0
  128. package/dist/utils/opencode-auth.js +126 -0
  129. package/dist/utils/opencode-client.js +109 -0
  130. package/dist/utils/password-policy.js +12 -0
  131. package/dist/utils/permission-common.js +280 -0
  132. package/dist/utils/redact.js +108 -0
  133. package/dist/utils/resource-access.js +37 -0
  134. package/dist/utils/resource-file-routes.js +115 -0
  135. package/dist/utils/resource-files.js +572 -0
  136. package/dist/utils/runtime-paths.js +61 -0
  137. package/dist/utils/session-abort.js +52 -0
  138. package/dist/utils/skill-approval-snapshot.js +39 -0
  139. package/dist/utils/skill-files.js +17 -0
  140. package/dist/utils/skill-permissions.js +15 -0
  141. package/dist/utils/skill.js +217 -0
  142. package/dist/utils/user-role.js +14 -0
  143. package/dist/utils/workflow-approval-snapshot.js +38 -0
  144. package/dist/utils/workflow-files.js +17 -0
  145. package/dist/utils/workflow-interruption.js +50 -0
  146. package/dist/utils/workflow-permissions.js +27 -0
  147. package/dist/utils/workflow-runner.js +414 -0
  148. package/dist/utils/workflow.js +158 -0
  149. package/dist/utils/workspace-sync.js +204 -0
  150. package/dist/workflows/index.js +751 -0
  151. package/dist/workspace_files/.opencode/opencode.json +17 -0
  152. package/dist/workspace_files/.opencode/package.json +14 -0
  153. package/dist/workspace_files/.opencode/plugins/createSkill.ts +339 -0
  154. package/dist/workspace_files/.opencode/plugins/createWorkflow.ts +345 -0
  155. package/dist/workspace_files/.opencode/plugins/findSimilarWorkflow.ts +173 -0
  156. package/dist/workspace_files/.opencode/plugins/findSkill.ts +211 -0
  157. package/dist/workspace_files/.opencode/plugins/getSkillContent.ts +135 -0
  158. package/dist/workspace_files/.opencode/plugins/honeytoken-protection.ts +64 -0
  159. package/dist/workspace_files/.opencode/plugins/listAvailableSkills.ts +93 -0
  160. package/dist/workspace_files/.opencode/plugins/listAvailableWorkflows.ts +93 -0
  161. package/dist/workspace_files/.opencode/plugins/python-protection.ts +184 -0
  162. package/dist/workspace_files/.opencode/plugins/runWorkflow.ts +168 -0
  163. package/dist/workspace_files/.opencode/tsconfig.json +16 -0
  164. package/dist/workspace_files/AGENTS.md +483 -0
  165. package/dist/workspace_files/package-lock.json +167 -0
  166. package/dist/workspace_files/package.json +5 -0
  167. package/package.json +86 -0
  168. package/prisma/migrations/20260203040755_init/migration.sql +20 -0
  169. package/prisma/migrations/20260204034845_replace_google_auth_with_email_password/migration.sql +25 -0
  170. package/prisma/migrations/20260207022226_add_user_role/migration.sql +25 -0
  171. package/prisma/migrations/20260210161254_add_workflow_runs/migration.sql +16 -0
  172. package/prisma/migrations/20260211050606_adds_workflow_table/migration.sql +40 -0
  173. package/prisma/migrations/20260211050750_adds_fkey_constraint/migration.sql +21 -0
  174. package/prisma/migrations/20260211051912_removes_workflow_table/migration.sql +34 -0
  175. package/prisma/migrations/20260211052238_changes_workflow_id_to_slug/migration.sql +27 -0
  176. package/prisma/migrations/20260212051912_add_output_to_workflow_runs/migration.sql +2 -0
  177. package/prisma/migrations/20260213073006_add_chat_sessions/migration.sql +13 -0
  178. package/prisma/migrations/20260216053202_add_chat_sessions_opencode_session_id_idx/migration.sql +2 -0
  179. package/prisma/migrations/20260216053237_drop_redundant_chat_sessions_opencode_idx/migration.sql +2 -0
  180. package/prisma/migrations/20260219060705_makes/migration.sql +24 -0
  181. package/prisma/migrations/20260222040542_add_workflow_execution_permissions/migration.sql +18 -0
  182. package/prisma/migrations/20260222040815_remove_workflow_execution_permissions/migration.sql +10 -0
  183. package/prisma/migrations/20260222041348_add_workflow_execution_permissions_final/migration.sql +17 -0
  184. package/prisma/migrations/20260222041741_rename_to_tool_execution_permissions/migration.sql +30 -0
  185. package/prisma/migrations/20260222041826_simplify_tool_execution_permissions/migration.sql +29 -0
  186. package/prisma/migrations/20260222041950_add_fields_for_standalone_permissions/migration.sql +32 -0
  187. package/prisma/migrations/20260222042954_simplify_tool_permissions_table/migration.sql +27 -0
  188. package/prisma/migrations/20260223073902_add_workflow_run_permissions_tables/migration.sql +23 -0
  189. package/prisma/migrations/20260225025151_add_workflow_metadata/migration.sql +16 -0
  190. package/prisma/migrations/20260225031035_merge_workflow_permissions_into_metadata/migration.sql +44 -0
  191. package/prisma/migrations/20260225031752_removes_default_for_run_permission_mode/migration.sql +20 -0
  192. package/prisma/migrations/20260225033603_remove_workflow_metadata_user_fkeys/migration.sql +18 -0
  193. package/prisma/migrations/20260225043032_restore_workflow_metadata_user_fkeys/migration.sql +20 -0
  194. package/prisma/migrations/20260225091423_add_workflow_approved_snapshots/migration.sql +28 -0
  195. package/prisma/migrations/20260226032121_add_is_approved_to_workflow_metadata/migration.sql +21 -0
  196. package/prisma/migrations/20260226032444_undoes_last_db_change/migration.sql +26 -0
  197. package/prisma/migrations/20260227120000_remove_snapshot_hash_from_approved_snapshots/migration.sql +16 -0
  198. package/prisma/migrations/20260228071125_adds_workspace_path_to_snapshot_table/migration.sql +22 -0
  199. package/prisma/migrations/20260228071217_modifies_index_and_removes_default_value/migration.sql +22 -0
  200. package/prisma/migrations/20260228071710_undoes_previous/migration.sql +27 -0
  201. package/prisma/migrations/20260228105022_add_must_change_password_first_login/migration.sql +20 -0
  202. package/prisma/migrations/20260301115439_add_workflow_run_log_refs/migration.sql +8 -0
  203. package/prisma/migrations/20260301122557_add_workflow_aborted_sessions/migration.sql +5 -0
  204. package/prisma/migrations/20260302045545_move_workflow_run_log_refs_into_workflow_runs/migration.sql +17 -0
  205. package/prisma/migrations/20260303040318_add_skill_tables/migration.sql +61 -0
  206. package/prisma/migrations/20260303051533_unify_resource_permissions/migration.sql +97 -0
  207. package/prisma/migrations/20260303064255_unify_resource_metadata_and_snapshots/migration.sql +179 -0
  208. package/prisma/migrations/migration_lock.toml +3 -0
  209. package/prisma/schema.prisma +147 -0
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dotenv_1 = __importDefault(require("dotenv"));
7
+ dotenv_1.default.config();
8
+ const promises_1 = require("node:readline/promises");
9
+ const node_process_1 = require("node:process");
10
+ const bcryptjs_1 = __importDefault(require("bcryptjs"));
11
+ const client_1 = __importDefault(require("./prisma/client"));
12
+ const password_policy_1 = require("./utils/password-policy");
13
+ const cli_bootstrap_1 = require("./utils/cli-bootstrap");
14
+ async function resolveEmailArgOrPrompt() {
15
+ const argEmail = process.argv[2];
16
+ if (argEmail && argEmail.trim().length > 0) {
17
+ return argEmail.trim();
18
+ }
19
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
20
+ try {
21
+ while (true) {
22
+ const answer = (await rl.question('Enter email: ')).trim();
23
+ if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(answer)) {
24
+ return answer;
25
+ }
26
+ console.error('Invalid email address. Please try again.');
27
+ }
28
+ }
29
+ finally {
30
+ rl.close();
31
+ }
32
+ }
33
+ async function resolveTempPasswordOrPrompt() {
34
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
35
+ try {
36
+ while (true) {
37
+ const answer = (await rl.question(`Enter temporary password (min ${password_policy_1.MIN_PASSWORD_LENGTH} chars): `)).trim();
38
+ if ((0, password_policy_1.isPasswordValid)(answer)) {
39
+ return answer;
40
+ }
41
+ console.error(`${(0, password_policy_1.getPasswordPolicyErrorMessage)()}. Please try again.`);
42
+ }
43
+ }
44
+ finally {
45
+ rl.close();
46
+ }
47
+ }
48
+ async function main() {
49
+ await (0, cli_bootstrap_1.bootstrapCliDatabaseAccess)();
50
+ const email = await resolveEmailArgOrPrompt();
51
+ const tempPassword = await resolveTempPasswordOrPrompt();
52
+ const user = await client_1.default.users.findUnique({ where: { email } });
53
+ if (!user) {
54
+ console.error(`No user found with email: ${email}`);
55
+ process.exit(1);
56
+ }
57
+ const passwordHash = await bcryptjs_1.default.hash(tempPassword, 12);
58
+ await client_1.default.users.update({
59
+ where: { id: user.id },
60
+ data: {
61
+ password_hash: passwordHash,
62
+ must_change_password: true,
63
+ reset_token: null,
64
+ reset_token_expires_at: null
65
+ }
66
+ });
67
+ console.log(`Temporary password set for ${user.email}. Password change is required on next sign in.`);
68
+ await client_1.default.$disconnect();
69
+ }
70
+ main().catch((err) => {
71
+ console.error(err);
72
+ process.exit(1);
73
+ });
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dotenv_1 = __importDefault(require("dotenv"));
7
+ dotenv_1.default.config();
8
+ const client_1 = __importDefault(require("./prisma/client"));
9
+ const cli_bootstrap_1 = require("./utils/cli-bootstrap");
10
+ const jwt_secret_1 = require("./utils/jwt-secret");
11
+ async function main() {
12
+ await (0, cli_bootstrap_1.bootstrapCliJwtAccess)();
13
+ await (0, jwt_secret_1.rotateJwtSecret)();
14
+ console.log("JWT secret rotated. Existing tokens are now invalid.");
15
+ await client_1.default.$disconnect();
16
+ }
17
+ main().catch((err) => {
18
+ console.error(err);
19
+ process.exit(1);
20
+ });
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const dotenv_1 = __importDefault(require("dotenv"));
7
+ const child_process_1 = require("child_process");
8
+ const workspace_sync_1 = require("../utils/workspace-sync");
9
+ const runtime_paths_1 = require("../utils/runtime-paths");
10
+ dotenv_1.default.config();
11
+ const args = process.argv.slice(2);
12
+ if (args.length === 0) {
13
+ console.error("Usage: npm run prisma -- <prisma args>");
14
+ process.exit(1);
15
+ }
16
+ const databaseUrl = (0, workspace_sync_1.getWorkspaceDatabaseUrl)();
17
+ const prismaCliEntrypoint = require.resolve("prisma/build/index.js", {
18
+ paths: [(0, runtime_paths_1.getPackageRoot)()],
19
+ });
20
+ const child = (0, child_process_1.spawn)(process.execPath, [prismaCliEntrypoint, ...args, "--schema", (0, runtime_paths_1.getPrismaSchemaPath)()], {
21
+ cwd: (0, runtime_paths_1.getPackageRoot)(),
22
+ stdio: "inherit",
23
+ env: {
24
+ ...process.env,
25
+ DATABASE_URL: databaseUrl,
26
+ },
27
+ });
28
+ child.on("exit", (code, signal) => {
29
+ if (signal) {
30
+ process.kill(process.pid, signal);
31
+ return;
32
+ }
33
+ process.exit(code ?? 1);
34
+ });
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const express_1 = __importDefault(require("express"));
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const multer_1 = __importDefault(require("multer"));
11
+ const client_1 = __importDefault(require("../prisma/client"));
12
+ const index_1 = require("../utils/index");
13
+ const skill_1 = require("../utils/skill");
14
+ const skill_permissions_1 = require("../utils/skill-permissions");
15
+ const permission_common_1 = require("../utils/permission-common");
16
+ const skill_files_1 = require("../utils/skill-files");
17
+ const resource_file_routes_1 = require("../utils/resource-file-routes");
18
+ const skill_approval_snapshot_1 = require("../utils/skill-approval-snapshot");
19
+ const resource_access_1 = require("../utils/resource-access");
20
+ const router = express_1.default.Router({ mergeParams: true });
21
+ const uploadTmpDir = path_1.default.join(os_1.default.tmpdir(), "teamcopilot-skill-uploads");
22
+ fs_1.default.mkdirSync(uploadTmpDir, { recursive: true });
23
+ const skillFileUpload = (0, multer_1.default)({ dest: uploadTmpDir, limits: { files: 1 } });
24
+ function normalizeSkillNameOrSlug(value) {
25
+ return value
26
+ .toLowerCase()
27
+ .trim()
28
+ .replace(/[^a-z0-9\s-]/g, "")
29
+ .replace(/\s+/g, "-")
30
+ .replace(/-+/g, "-");
31
+ }
32
+ async function getSkillEditorAccess(slug, userId) {
33
+ const accessSummary = await (0, resource_access_1.getResourceAccessSummary)("skill", slug, userId);
34
+ const skillStatus = accessSummary.is_approved ? "approved" : "pending";
35
+ return {
36
+ can_view: accessSummary.can_view,
37
+ can_edit: accessSummary.can_edit,
38
+ editor_status: skillStatus,
39
+ };
40
+ }
41
+ async function assertCanViewSkillFiles(slug, userId) {
42
+ const access = await getSkillEditorAccess(slug, userId);
43
+ if (!access.can_view) {
44
+ throw {
45
+ status: 403,
46
+ message: "You do not have permission to view this skill"
47
+ };
48
+ }
49
+ }
50
+ async function assertCanEditSkillFiles(slug, userId) {
51
+ const access = await getSkillEditorAccess(slug, userId);
52
+ if (!access.can_edit) {
53
+ throw {
54
+ status: 403,
55
+ message: "You do not have permission to edit this skill"
56
+ };
57
+ }
58
+ }
59
+ router.post("/", (0, index_1.apiHandler)(async (req, res) => {
60
+ const body = req.body;
61
+ const rawName = typeof body.name === "string" ? body.name : "";
62
+ const normalizedName = normalizeSkillNameOrSlug(rawName);
63
+ const unifiedName = normalizedName;
64
+ if (!unifiedName) {
65
+ throw {
66
+ status: 400,
67
+ message: "name is required"
68
+ };
69
+ }
70
+ await (0, skill_1.createSkill)({
71
+ slug: unifiedName,
72
+ createdByUserId: req.userId,
73
+ });
74
+ res.status(201).json({ success: true });
75
+ }, true));
76
+ router.get("/", (0, index_1.apiHandler)(async (req, res) => {
77
+ const slugs = (0, skill_1.listSkillSlugs)();
78
+ const skills = [];
79
+ const creatorIds = new Set();
80
+ const metadataBySlug = new Map();
81
+ for (const slug of slugs) {
82
+ const metadata = await (0, skill_1.getOrCreateSkillMetadataAndEnsurePermission)(slug);
83
+ metadataBySlug.set(slug, metadata);
84
+ if (metadata.created_by_user_id) {
85
+ creatorIds.add(metadata.created_by_user_id);
86
+ }
87
+ }
88
+ const creators = creatorIds.size > 0
89
+ ? await client_1.default.users.findMany({
90
+ where: {
91
+ id: { in: Array.from(creatorIds) }
92
+ },
93
+ select: {
94
+ id: true,
95
+ name: true,
96
+ email: true,
97
+ }
98
+ })
99
+ : [];
100
+ const creatorNameById = new Map(creators.map((creator) => [creator.id, creator.name]));
101
+ const creatorEmailById = new Map(creators.map((creator) => [creator.id, creator.email]));
102
+ for (const slug of slugs) {
103
+ const metadata = metadataBySlug.get(slug);
104
+ if (!metadata)
105
+ continue;
106
+ const { manifest } = await (0, skill_1.readSkillManifestAndEnsurePermissions)(slug);
107
+ const accessSummary = await (0, resource_access_1.getResourceAccessSummary)("skill", slug, req.userId);
108
+ if (!accessSummary.can_view) {
109
+ continue;
110
+ }
111
+ const createdByUserId = metadata.created_by_user_id;
112
+ skills.push({
113
+ slug,
114
+ name: manifest.name,
115
+ description: manifest.description,
116
+ created_by_user_id: createdByUserId,
117
+ created_by_user_name: createdByUserId ? (creatorNameById.get(createdByUserId) ?? null) : null,
118
+ created_by_user_email: createdByUserId ? (creatorEmailById.get(createdByUserId) ?? null) : null,
119
+ approved_by_user_id: metadata.approved_by_user_id,
120
+ is_approved: accessSummary.is_approved,
121
+ can_view: accessSummary.can_view,
122
+ can_edit: accessSummary.can_edit,
123
+ permission_mode: accessSummary.permission_mode,
124
+ is_locked_due_to_missing_users: accessSummary.is_locked_due_to_missing_users,
125
+ });
126
+ }
127
+ res.json({ skills });
128
+ }, true));
129
+ router.get("/:slug", (0, index_1.apiHandler)(async (req, res) => {
130
+ const slug = req.params.slug;
131
+ await assertCanViewSkillFiles(slug, req.userId);
132
+ const { metadata, manifest } = await (0, skill_1.readSkillManifestAndEnsurePermissions)(slug);
133
+ const createdByUserId = metadata.created_by_user_id ?? null;
134
+ const approvedByUserId = metadata.approved_by_user_id ?? null;
135
+ const userIds = [createdByUserId, approvedByUserId].filter((id) => typeof id === "string");
136
+ const users = userIds.length > 0
137
+ ? await client_1.default.users.findMany({
138
+ where: { id: { in: userIds } },
139
+ select: { id: true, name: true, email: true }
140
+ })
141
+ : [];
142
+ const usersById = new Map(users.map((user) => [user.id, user]));
143
+ const creator = createdByUserId ? (usersById.get(createdByUserId) ?? null) : null;
144
+ const approver = approvedByUserId ? (usersById.get(approvedByUserId) ?? null) : null;
145
+ const accessSummary = await (0, resource_access_1.getResourceAccessSummary)("skill", slug, req.userId);
146
+ const permission = await (0, skill_permissions_1.getSkillAccessPermissionWithUsers)(slug);
147
+ const permissionMode = (0, permission_common_1.assertCommonPermissionMode)(permission.permission_mode, "skill access");
148
+ const permissions = permissionMode === "everyone"
149
+ ? { mode: "everyone" }
150
+ : { mode: "restricted", allowed_user_ids: permission.allowedUsers.map((row) => row.user_id) };
151
+ res.json({
152
+ skill: {
153
+ slug,
154
+ name: manifest.name,
155
+ created_by_user_id: createdByUserId,
156
+ created_by_user_name: creator?.name ?? null,
157
+ created_by_user_email: creator?.email ?? null,
158
+ approved_by_user_id: approvedByUserId,
159
+ is_approved: accessSummary.is_approved,
160
+ approved_by_user_name: approver?.name ?? null,
161
+ approved_by_user_email: approver?.email ?? null,
162
+ can_view: accessSummary.can_view,
163
+ can_edit: accessSummary.can_edit,
164
+ permission_mode: accessSummary.permission_mode,
165
+ is_locked_due_to_missing_users: accessSummary.is_locked_due_to_missing_users,
166
+ permissions,
167
+ allowed_users_resolved: permission.allowedUsers.map((row) => ({
168
+ user_id: row.user.id,
169
+ name: row.user.name,
170
+ email: row.user.email,
171
+ is_owner: row.user.id === metadata.created_by_user_id,
172
+ is_approver: row.user.id === metadata.approved_by_user_id
173
+ })),
174
+ }
175
+ });
176
+ }, true));
177
+ router.post("/:slug/approve", (0, index_1.apiHandler)(async (req, res) => {
178
+ const slug = req.params.slug;
179
+ const approvalResult = await (0, skill_approval_snapshot_1.approveSkillWithSnapshot)(slug, req.userId);
180
+ const { metadata: approvedMetadata } = await (0, skill_1.readSkillManifestAndEnsurePermissions)(slug);
181
+ await (0, skill_permissions_1.addApproverToSkillAccessPermissionsIfRestricted)(slug, req.userId, approvedMetadata.created_by_user_id);
182
+ res.json({
183
+ skill: {
184
+ slug,
185
+ approved_by_user_id: approvalResult.approved_by_user_id,
186
+ is_approved: true,
187
+ snapshot_hash: approvalResult.snapshot_hash,
188
+ snapshot_file_count: approvalResult.snapshot_file_count
189
+ }
190
+ });
191
+ }, true));
192
+ router.post("/:slug/reject-restore", (0, index_1.apiHandler)(async (req, res) => {
193
+ const slug = req.params.slug;
194
+ const result = await (0, skill_approval_snapshot_1.restoreSkillToApprovedSnapshot)(slug, req.userId);
195
+ res.json({
196
+ skill: {
197
+ slug,
198
+ restored_file_count: result.restored_file_count,
199
+ snapshot_hash: result.snapshot_hash,
200
+ }
201
+ });
202
+ }, true));
203
+ router.get("/:slug/approval-diff", (0, index_1.apiHandler)(async (req, res) => {
204
+ const slug = req.params.slug;
205
+ if (req.role !== "Engineer") {
206
+ throw {
207
+ status: 403,
208
+ message: "Only Engineers can review approval diffs"
209
+ };
210
+ }
211
+ await (0, skill_1.readSkillManifestAndEnsurePermissions)(slug);
212
+ const previousSnapshot = await (0, skill_approval_snapshot_1.loadApprovedSkillSnapshotFromDb)(slug);
213
+ const currentSnapshot = (0, skill_approval_snapshot_1.collectCurrentSkillSnapshot)(slug);
214
+ const diff = (0, skill_approval_snapshot_1.buildSkillApprovalDiffResponse)(previousSnapshot, currentSnapshot);
215
+ res.json(diff);
216
+ }, true));
217
+ (0, resource_file_routes_1.registerResourceFileRoutes)({
218
+ router,
219
+ uploadMiddleware: skillFileUpload.single("file"),
220
+ assertCanView: assertCanViewSkillFiles,
221
+ ensureResourceExists: async (slug) => {
222
+ await (0, skill_1.readSkillManifestAndEnsurePermissions)(slug);
223
+ },
224
+ getEditorAccess: getSkillEditorAccess,
225
+ assertCanEdit: assertCanEditSkillFiles,
226
+ listDirectory: skill_files_1.listSkillDirectory,
227
+ readFileContent: skill_files_1.readSkillFileContent,
228
+ saveFileContent: skill_files_1.saveSkillFileContent,
229
+ createFileOrFolder: skill_files_1.createSkillFileOrFolder,
230
+ uploadFileFromTempPath: skill_files_1.uploadSkillFileFromTempPath,
231
+ renamePath: skill_files_1.renameSkillPath,
232
+ deletePath: skill_files_1.deleteSkillPath,
233
+ skipResponseSanitizationForFileContentRead: false,
234
+ });
235
+ const updateSkillPermissionsHandler = (0, index_1.apiHandler)(async (req, res) => {
236
+ const slug = req.params.slug;
237
+ const metadata = await (0, skill_1.getOrCreateSkillMetadataAndEnsurePermission)(slug);
238
+ const approvalState = await (0, skill_approval_snapshot_1.getSkillSnapshotApprovalState)(slug);
239
+ if (!approvalState.is_current_code_approved) {
240
+ throw {
241
+ status: 403,
242
+ message: "Skill must be approved before updating access permissions"
243
+ };
244
+ }
245
+ const currentSummary = await (0, resource_access_1.getResourceAccessSummary)("skill", slug, req.userId);
246
+ if (!currentSummary.can_edit) {
247
+ throw {
248
+ status: 403,
249
+ message: currentSummary.is_locked_due_to_missing_users
250
+ ? "Skill permissions cannot be modified because no allowed users remain"
251
+ : "You do not have permission to modify skill access permissions"
252
+ };
253
+ }
254
+ const { mode, allowed_user_ids } = req.body;
255
+ if (mode !== "restricted" && mode !== "everyone") {
256
+ throw {
257
+ status: 400,
258
+ message: 'mode must be "restricted" or "everyone"'
259
+ };
260
+ }
261
+ const updatedPermission = mode === "everyone"
262
+ ? await (0, skill_permissions_1.setSkillAccessPermissions)(slug, { mode: "everyone" }, metadata.created_by_user_id)
263
+ : await (0, skill_permissions_1.setSkillAccessPermissions)(slug, {
264
+ mode: "restricted",
265
+ allowed_user_ids: Array.isArray(allowed_user_ids) ? allowed_user_ids.map((id) => String(id)) : []
266
+ }, metadata.created_by_user_id);
267
+ const updatedSummary = await (0, resource_access_1.getResourceAccessSummary)("skill", slug, req.userId);
268
+ const updatedPermissionMode = (0, permission_common_1.assertCommonPermissionMode)(updatedPermission.permission_mode, "skill access");
269
+ const permissions = updatedPermissionMode === "everyone"
270
+ ? { mode: "everyone" }
271
+ : { mode: "restricted", allowed_user_ids: updatedPermission.allowedUsers.map((row) => row.user_id) };
272
+ res.json({
273
+ skill: {
274
+ slug,
275
+ ...updatedSummary,
276
+ permissions,
277
+ allowed_users_resolved: updatedPermission.allowedUsers.map((row) => ({
278
+ user_id: row.user.id,
279
+ name: row.user.name,
280
+ email: row.user.email,
281
+ is_owner: row.user.id === metadata.created_by_user_id,
282
+ is_approver: row.user.id === metadata.approved_by_user_id
283
+ }))
284
+ }
285
+ });
286
+ }, true);
287
+ // PATCH /api/skills/:slug/permissions - Update permissions (canonical)
288
+ router.patch("/:slug/permissions", updateSkillPermissionsHandler);
289
+ router.delete("/:slug", (0, index_1.apiHandler)(async (req, res) => {
290
+ const slug = req.params.slug;
291
+ const metadata = await (0, skill_1.getOrCreateSkillMetadataAndEnsurePermission)(slug);
292
+ const creatorUserId = metadata.created_by_user_id ?? null;
293
+ const creator = creatorUserId
294
+ ? await client_1.default.users.findUnique({
295
+ where: { id: creatorUserId },
296
+ select: { id: true }
297
+ })
298
+ : null;
299
+ const isOwner = creatorUserId === req.userId;
300
+ const hasNoCreatorUser = creator === null;
301
+ const isEngineer = req.role === "Engineer";
302
+ if (!isOwner && !(hasNoCreatorUser && isEngineer)) {
303
+ throw {
304
+ status: 403,
305
+ message: "Only the skill owner can delete this skill. Engineers can only delete skills whose owner no longer exists."
306
+ };
307
+ }
308
+ await (0, skill_1.deleteSkill)(slug);
309
+ res.json({ success: true });
310
+ }, true));
311
+ exports.default = router;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../permissions"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../skill"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../workflow-files"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../workflow"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const express_1 = __importDefault(require("express"));
7
+ const client_1 = __importDefault(require("../prisma/client"));
8
+ const index_1 = require("../utils/index");
9
+ const router = express_1.default.Router({ mergeParams: true });
10
+ router.get("/", (0, index_1.apiHandler)(async (_req, res) => {
11
+ const users = await client_1.default.users.findMany({
12
+ orderBy: { name: "asc" },
13
+ select: {
14
+ id: true,
15
+ name: true,
16
+ email: true,
17
+ role: true
18
+ }
19
+ });
20
+ res.json({ users });
21
+ }, true));
22
+ exports.default = router;