vskill 1.0.15 → 1.0.18

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 (220) hide show
  1. package/README.md +84 -9
  2. package/agents.json +3 -1
  3. package/dist/agents/agents-registry.d.ts +69 -3
  4. package/dist/agents/agents-registry.js +203 -0
  5. package/dist/agents/agents-registry.js.map +1 -1
  6. package/dist/api/client.d.ts +85 -0
  7. package/dist/api/client.js +193 -24
  8. package/dist/api/client.js.map +1 -1
  9. package/dist/commands/add-lockfile.d.ts +6 -0
  10. package/dist/commands/add-lockfile.js +10 -0
  11. package/dist/commands/add-lockfile.js.map +1 -1
  12. package/dist/commands/add.d.ts +7 -0
  13. package/dist/commands/add.js +110 -2
  14. package/dist/commands/add.js.map +1 -1
  15. package/dist/commands/auth.d.ts +23 -0
  16. package/dist/commands/auth.js +105 -11
  17. package/dist/commands/auth.js.map +1 -1
  18. package/dist/commands/eval/serve.d.ts +2 -0
  19. package/dist/commands/eval/serve.js +126 -4
  20. package/dist/commands/eval/serve.js.map +1 -1
  21. package/dist/commands/orgs.d.ts +21 -0
  22. package/dist/commands/orgs.js +164 -0
  23. package/dist/commands/orgs.js.map +1 -0
  24. package/dist/commands/skill.js +14 -1
  25. package/dist/commands/skill.js.map +1 -1
  26. package/dist/commands/whoami.d.ts +29 -0
  27. package/dist/commands/whoami.js +119 -0
  28. package/dist/commands/whoami.js.map +1 -0
  29. package/dist/discovery/github-tree.d.ts +23 -3
  30. package/dist/discovery/github-tree.js +172 -24
  31. package/dist/discovery/github-tree.js.map +1 -1
  32. package/dist/eval/anthropic-catalog.js +32 -2
  33. package/dist/eval/anthropic-catalog.js.map +1 -1
  34. package/dist/eval/batch-judge.js +1 -0
  35. package/dist/eval/batch-judge.js.map +1 -1
  36. package/dist/eval/llm.d.ts +1 -1
  37. package/dist/eval/llm.js +104 -2
  38. package/dist/eval/llm.js.map +1 -1
  39. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.d.ts +2 -0
  40. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.js +20 -0
  41. package/dist/eval-server/__tests__/helpers/studio-token-test-helpers.js.map +1 -0
  42. package/dist/eval-server/active-tenant-routes.d.ts +15 -0
  43. package/dist/eval-server/active-tenant-routes.js +101 -0
  44. package/dist/eval-server/active-tenant-routes.js.map +1 -0
  45. package/dist/eval-server/api-routes.js +206 -6
  46. package/dist/eval-server/api-routes.js.map +1 -1
  47. package/dist/eval-server/desktop-open-routes.d.ts +8 -0
  48. package/dist/eval-server/desktop-open-routes.js +64 -0
  49. package/dist/eval-server/desktop-open-routes.js.map +1 -0
  50. package/dist/eval-server/eval-server.js +90 -6
  51. package/dist/eval-server/eval-server.js.map +1 -1
  52. package/dist/eval-server/export-skill-routes.d.ts +9 -0
  53. package/dist/eval-server/export-skill-routes.js +81 -0
  54. package/dist/eval-server/export-skill-routes.js.map +1 -0
  55. package/dist/eval-server/git-routes.d.ts +1 -0
  56. package/dist/eval-server/git-routes.js +101 -4
  57. package/dist/eval-server/git-routes.js.map +1 -1
  58. package/dist/eval-server/install-engine-routes.d.ts +3 -16
  59. package/dist/eval-server/install-engine-routes.js +9 -124
  60. package/dist/eval-server/install-engine-routes.js.map +1 -1
  61. package/dist/eval-server/install-jobs.d.ts +41 -0
  62. package/dist/eval-server/install-jobs.js +161 -0
  63. package/dist/eval-server/install-jobs.js.map +1 -0
  64. package/dist/eval-server/install-skill-routes.d.ts +74 -11
  65. package/dist/eval-server/install-skill-routes.js +506 -79
  66. package/dist/eval-server/install-skill-routes.js.map +1 -1
  67. package/dist/eval-server/install-state-routes.d.ts +25 -0
  68. package/dist/eval-server/install-state-routes.js +125 -0
  69. package/dist/eval-server/install-state-routes.js.map +1 -0
  70. package/dist/eval-server/oauth-github-routes.d.ts +2 -0
  71. package/dist/eval-server/oauth-github-routes.js +505 -0
  72. package/dist/eval-server/oauth-github-routes.js.map +1 -0
  73. package/dist/eval-server/platform-proxy.d.ts +17 -1
  74. package/dist/eval-server/platform-proxy.js +125 -13
  75. package/dist/eval-server/platform-proxy.js.map +1 -1
  76. package/dist/eval-server/plugin-cli-routes.js +9 -9
  77. package/dist/eval-server/plugin-cli-routes.js.map +1 -1
  78. package/dist/eval-server/remove-skill-routes.d.ts +18 -0
  79. package/dist/eval-server/remove-skill-routes.js +145 -0
  80. package/dist/eval-server/remove-skill-routes.js.map +1 -0
  81. package/dist/eval-server/router.d.ts +17 -3
  82. package/dist/eval-server/router.js +166 -9
  83. package/dist/eval-server/router.js.map +1 -1
  84. package/dist/eval-server/settings-store.js +1 -1
  85. package/dist/eval-server/settings-store.js.map +1 -1
  86. package/dist/eval-server/supported-agents-routes.d.ts +6 -0
  87. package/dist/eval-server/supported-agents-routes.js +41 -0
  88. package/dist/eval-server/supported-agents-routes.js.map +1 -0
  89. package/dist/eval-server/utils/spawn-env.d.ts +1 -0
  90. package/dist/eval-server/utils/spawn-env.js +47 -0
  91. package/dist/eval-server/utils/spawn-env.js.map +1 -0
  92. package/dist/eval-ui/assets/AdvancedTab-D8zbE5fH.js +1 -0
  93. package/dist/eval-ui/assets/{CreateSkillPage-BmbvQEzE.js → CreateSkillPage-DOBhKdgr.js} +5 -5
  94. package/dist/eval-ui/assets/FindSkillsPalette-CyMmNPr-.js +2 -0
  95. package/dist/eval-ui/assets/GeneralTab-DYR9NWC4.js +1 -0
  96. package/dist/eval-ui/assets/PrivacyTab-CXIqQokl.js +1 -0
  97. package/dist/eval-ui/assets/SearchPaletteCore-Dn5gQJS_.js +14 -0
  98. package/dist/eval-ui/assets/SkillDetailPanel-DTrRnyyJ.js +1 -0
  99. package/dist/eval-ui/assets/UpdateDropdown-Cvr2fe0z.js +1 -0
  100. package/dist/eval-ui/assets/UpdatesTab-DwJIUDPX.js +1 -0
  101. package/dist/eval-ui/assets/core-DZAvsxlC.js +1 -0
  102. package/dist/eval-ui/assets/event-CDYWU2X3.js +1 -0
  103. package/dist/eval-ui/assets/globals-BRZwPAPF.js +49 -0
  104. package/dist/eval-ui/assets/globals-C3oEdsJh.css +1 -0
  105. package/dist/eval-ui/assets/index-D7M0Jdss.js +1 -0
  106. package/dist/eval-ui/assets/lifecycle-DSleOV-l.js +1 -0
  107. package/dist/eval-ui/assets/lifecycle-d1Sm9Hts.css +1 -0
  108. package/dist/eval-ui/assets/main-D2shn1dH.js +87 -0
  109. package/dist/eval-ui/assets/preferences-BHZXB5dL.css +1 -0
  110. package/dist/eval-ui/assets/preferences-BKv6X7fK.js +2 -0
  111. package/dist/eval-ui/assets/useDesktopBridge-DxVWbYqK.js +2 -0
  112. package/dist/eval-ui/index.html +4 -2
  113. package/dist/eval-ui/lifecycle.html +33 -0
  114. package/dist/eval-ui/preferences.html +34 -0
  115. package/dist/index.js +47 -1
  116. package/dist/index.js.map +1 -1
  117. package/dist/installer/bundle-files.d.ts +4 -0
  118. package/dist/installer/bundle-files.js +97 -0
  119. package/dist/installer/bundle-files.js.map +1 -0
  120. package/dist/installer/canonical.d.ts +31 -6
  121. package/dist/installer/canonical.js +50 -23
  122. package/dist/installer/canonical.js.map +1 -1
  123. package/dist/installer/clipboard-export.d.ts +19 -0
  124. package/dist/installer/clipboard-export.js +88 -0
  125. package/dist/installer/clipboard-export.js.map +1 -0
  126. package/dist/installer/frontmatter.js +1 -1
  127. package/dist/installer/frontmatter.js.map +1 -1
  128. package/dist/installer/multi-install.d.ts +43 -0
  129. package/dist/installer/multi-install.js +237 -0
  130. package/dist/installer/multi-install.js.map +1 -0
  131. package/dist/installer/transformers/aider.d.ts +2 -0
  132. package/dist/installer/transformers/aider.js +32 -0
  133. package/dist/installer/transformers/aider.js.map +1 -0
  134. package/dist/installer/transformers/continue-dev.d.ts +2 -0
  135. package/dist/installer/transformers/continue-dev.js +6 -0
  136. package/dist/installer/transformers/continue-dev.js.map +1 -0
  137. package/dist/installer/transformers/cursor.d.ts +2 -0
  138. package/dist/installer/transformers/cursor.js +24 -0
  139. package/dist/installer/transformers/cursor.js.map +1 -0
  140. package/dist/installer/transformers/github-copilot.d.ts +2 -0
  141. package/dist/installer/transformers/github-copilot.js +17 -0
  142. package/dist/installer/transformers/github-copilot.js.map +1 -0
  143. package/dist/installer/transformers/index.d.ts +78 -0
  144. package/dist/installer/transformers/index.js +13 -0
  145. package/dist/installer/transformers/index.js.map +1 -0
  146. package/dist/installer/transformers/junie.d.ts +2 -0
  147. package/dist/installer/transformers/junie.js +6 -0
  148. package/dist/installer/transformers/junie.js.map +1 -0
  149. package/dist/installer/transformers/kiro.d.ts +2 -0
  150. package/dist/installer/transformers/kiro.js +6 -0
  151. package/dist/installer/transformers/kiro.js.map +1 -0
  152. package/dist/installer/transformers/trae.d.ts +2 -0
  153. package/dist/installer/transformers/trae.js +6 -0
  154. package/dist/installer/transformers/trae.js.map +1 -0
  155. package/dist/installer/transformers/windsurf.d.ts +2 -0
  156. package/dist/installer/transformers/windsurf.js +12 -0
  157. package/dist/installer/transformers/windsurf.js.map +1 -0
  158. package/dist/installer/yaml-safe-mutate.d.ts +19 -0
  159. package/dist/installer/yaml-safe-mutate.js +184 -0
  160. package/dist/installer/yaml-safe-mutate.js.map +1 -0
  161. package/dist/lib/active-tenant.d.ts +36 -0
  162. package/dist/lib/active-tenant.js +120 -0
  163. package/dist/lib/active-tenant.js.map +1 -0
  164. package/dist/lib/github-fetch.d.ts +1 -0
  165. package/dist/lib/github-fetch.js +11 -1
  166. package/dist/lib/github-fetch.js.map +1 -1
  167. package/dist/lib/keychain.d.ts +15 -2
  168. package/dist/lib/keychain.js +156 -8
  169. package/dist/lib/keychain.js.map +1 -1
  170. package/dist/lib/migration/keychain-migration.d.ts +35 -0
  171. package/dist/lib/migration/keychain-migration.js +189 -0
  172. package/dist/lib/migration/keychain-migration.js.map +1 -0
  173. package/dist/lib/tenant-resolver.d.ts +38 -0
  174. package/dist/lib/tenant-resolver.js +79 -0
  175. package/dist/lib/tenant-resolver.js.map +1 -0
  176. package/dist/lockfile/types.d.ts +8 -0
  177. package/dist/sidecar/eval-ui-manifest.json +1 -0
  178. package/dist/sidecar/sea-config.json +57 -0
  179. package/dist/sidecar/sea-prep.blob +0 -0
  180. package/dist/sidecar/server.cjs +141627 -0
  181. package/dist/sidecar/vskill-version.txt +1 -0
  182. package/dist/studio/lib/ops-log.js +140 -57
  183. package/dist/studio/lib/ops-log.js.map +1 -1
  184. package/dist/studio/lib/provenance.js +3 -2
  185. package/dist/studio/lib/provenance.js.map +1 -1
  186. package/dist/studio/lib/query.d.ts +1 -0
  187. package/dist/studio/lib/query.js +7 -0
  188. package/dist/studio/lib/query.js.map +1 -0
  189. package/dist/studio/lib/scope-transfer.d.ts +16 -0
  190. package/dist/studio/lib/scope-transfer.js +52 -25
  191. package/dist/studio/lib/scope-transfer.js.map +1 -1
  192. package/dist/studio/routes/index.js +13 -1
  193. package/dist/studio/routes/index.js.map +1 -1
  194. package/dist/studio/routes/ops.js +31 -9
  195. package/dist/studio/routes/ops.js.map +1 -1
  196. package/dist/studio/routes/promote.js +16 -11
  197. package/dist/studio/routes/promote.js.map +1 -1
  198. package/dist/studio/routes/revert.js +14 -18
  199. package/dist/studio/routes/revert.js.map +1 -1
  200. package/dist/studio/routes/test-install.js +14 -11
  201. package/dist/studio/routes/test-install.js.map +1 -1
  202. package/dist/studio-runtime/lockfile.d.ts +51 -0
  203. package/dist/studio-runtime/lockfile.js +216 -0
  204. package/dist/studio-runtime/lockfile.js.map +1 -0
  205. package/dist/updater/source-fetcher.js +2 -2
  206. package/dist/updater/source-fetcher.js.map +1 -1
  207. package/dist/utils/skill-builder-detection.d.ts +14 -1
  208. package/dist/utils/skill-builder-detection.js +20 -8
  209. package/dist/utils/skill-builder-detection.js.map +1 -1
  210. package/dist/utils/skill-creator-detection.d.ts +10 -2
  211. package/dist/utils/skill-creator-detection.js +12 -43
  212. package/dist/utils/skill-creator-detection.js.map +1 -1
  213. package/package.json +17 -1
  214. package/dist/eval-ui/assets/FindSkillsPalette-D0Zjhm31.js +0 -2
  215. package/dist/eval-ui/assets/SearchPaletteCore-EhcN1xEa.js +0 -14
  216. package/dist/eval-ui/assets/SkillDetailPanel-B5J60ffv.js +0 -1
  217. package/dist/eval-ui/assets/UpdateDropdown-Celf0_Cr.js +0 -1
  218. package/dist/eval-ui/assets/index-BV7k6fdk.js +0 -124
  219. package/dist/eval-ui/assets/index-CKLqBL52.css +0 -1
  220. package/dist/eval-ui/assets/skill-studio-logo-CRyKgIrg.png +0 -0
@@ -0,0 +1,505 @@
1
+ // ---------------------------------------------------------------------------
2
+ // oauth-github-routes.ts — GitHub OAuth 2.0 Authorization Code flow.
3
+ //
4
+ // Why this replaces the Tauri IPC device-flow path (2026-05-11):
5
+ //
6
+ // 1. The vskill OAuth App at github.com/settings/developers has Device Flow
7
+ // DISABLED. Every device-code request returns
8
+ // `{"error":"device_flow_disabled"}` so the Tauri device-flow IPC was
9
+ // always going to silently fail at the GitHub layer.
10
+ //
11
+ // 2. The Tauri 2.x ACL was rejecting every custom #[tauri::command] at
12
+ // runtime because src-tauri/build.rs doesn't pass the command list to
13
+ // `Attributes::app_manifest(AppManifest::new().commands(&[...]))`. Even
14
+ // with a permission TOML correctly compiled into the binary, the runtime
15
+ // `allowed_commands` HashMap stayed empty for app-level commands. See
16
+ // `~/.claude/skills/tauri-desktop-release/SKILL.md` for the deep dive.
17
+ //
18
+ // 3. Doing OAuth through the sidecar HTTP layer sidesteps both issues:
19
+ // - JS in the WebView already authenticates to the sidecar via
20
+ // X-Studio-Token (the v1.0.30 fix). All it needs to do is POST/GET.
21
+ // - The browser callback first hits the registered verified-skill.com
22
+ // callback, which owns the GitHub client secret and exchanges the code.
23
+ // It then posts the resulting GitHub token back to the local sidecar.
24
+ //
25
+ // This mirrors how `claude-code` does it (Claude AI / Anthropic console
26
+ // login): localhost HTTP server starts the flow, validates the returning
27
+ // state, then stores the token in the OS keychain (with file fallback).
28
+ // See `anymodel-umb/repositories/antonoly/claude-code/services/oauth/`.
29
+ //
30
+ // Flow:
31
+ // 1. JS: POST /api/oauth/github/start
32
+ // → server generates random state (CSRF protection)
33
+ // → server stores {state → {expiresAt}} in in-memory map
34
+ // → server returns {state, authUrl}
35
+ // 2. JS: window.__TAURI__.shell.open(authUrl) (no IPC ACL needed —
36
+ // shell:allow-open is in default capabilities)
37
+ // 3. User authorizes in their browser → GitHub redirects to the registered
38
+ // verified-skill.com callback, which detects desktop state, exchanges
39
+ // the code using the platform client secret, and form-POSTs the token to
40
+ // http://localhost:<sidecar-port>/api/oauth/github/desktop-complete
41
+ // 4. Sidecar desktop-complete handler:
42
+ // → looks up state in map (404 if not found / expired)
43
+ // → stores access_token via keychain.setGithubToken()
44
+ // → marks the state as "ready" with the user info
45
+ // → returns a small HTML "close this tab" success page
46
+ // 5. JS polls GET /api/oauth/github/status?state=...
47
+ // → on "ready" → frontend shows signed-in chip
48
+ // → on "error" → frontend shows error toast
49
+ //
50
+ // Token storage is the SAME slot used by the old Tauri device-flow path
51
+ // (`~/.vskill/keys.env` or OS keychain), so existing token-consuming code
52
+ // in the studio (per-skill streams, account API) continues working.
53
+ // ---------------------------------------------------------------------------
54
+ import { randomBytes } from "node:crypto";
55
+ import { sendJson } from "./router.js";
56
+ // ---------------------------------------------------------------------------
57
+ // Config
58
+ // ---------------------------------------------------------------------------
59
+ // Public OAuth App client_id. Matches the value baked into the desktop
60
+ // binary at build time (src-tauri/build.rs reads GITHUB_OAUTH_CLIENT_ID env).
61
+ // PUBLIC value — no secret protection needed, intentionally hardcoded.
62
+ const GITHUB_OAUTH_CLIENT_ID = "Ov23li6H8OpvPfuhyDEt";
63
+ const GITHUB_AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
64
+ const GITHUB_USER_API = "https://api.github.com/user";
65
+ const REQUESTED_SCOPE = "repo read:user user:email";
66
+ const STATE_TTL_MS = 10 * 60 * 1000; // 10 minutes — GitHub auth pages
67
+ // allow ~15 min before user code expires
68
+ const STATE_GC_INTERVAL_MS = 60 * 1000;
69
+ // 2026-05-20: GitHub requires redirect_uri to exactly match the OAuth App's
70
+ // registered callback. The platform callback detects desktop state values,
71
+ // exchanges the code with its confidential client secret, then posts the
72
+ // GitHub access token back to the local sidecar.
73
+ const DESKTOP_BOUNCE_REDIRECT = "https://verified-skill.com/api/v1/auth/github/callback";
74
+ // Allow overriding the bounce URL via env (useful for local platform
75
+ // development against http://localhost:3000 etc.).
76
+ function getRedirectUri() {
77
+ return process.env.VSKILL_OAUTH_BOUNCE_URL || DESKTOP_BOUNCE_REDIRECT;
78
+ }
79
+ const flows = new Map();
80
+ function gcExpiredFlows() {
81
+ const now = Date.now();
82
+ for (const [state, flow] of flows) {
83
+ if (flow.expiresAt < now)
84
+ flows.delete(state);
85
+ }
86
+ }
87
+ setInterval(gcExpiredFlows, STATE_GC_INTERVAL_MS).unref();
88
+ // ---------------------------------------------------------------------------
89
+ // OAuth helpers
90
+ // ---------------------------------------------------------------------------
91
+ function base64UrlEncode(buf) {
92
+ return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
93
+ }
94
+ function generateState() {
95
+ return base64UrlEncode(randomBytes(32)); // 43 chars
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Helpers
99
+ // ---------------------------------------------------------------------------
100
+ /**
101
+ * The redirect_uri we send to GitHub. This must be the registered platform
102
+ * callback; the platform callback handles the localhost bounce for desktop
103
+ * states after GitHub redirects back to it.
104
+ */
105
+ function buildGitHubRedirectUri(_req) {
106
+ return getRedirectUri();
107
+ }
108
+ /**
109
+ * Extract the local sidecar port from the request host header so we can
110
+ * embed it in `state`. The platform bounce-redirect reads the port back
111
+ * out of state and forwards the user's browser to localhost:<port>.
112
+ */
113
+ function getLocalPort(req) {
114
+ const host = req.headers.host || "";
115
+ const m = host.match(/:(\d+)$/);
116
+ if (m) {
117
+ const n = Number.parseInt(m[1], 10);
118
+ if (Number.isFinite(n) && n > 0 && n <= 65535)
119
+ return n;
120
+ }
121
+ // No port in host → default 80. Should never happen for sidecar binds.
122
+ return 80;
123
+ }
124
+ /**
125
+ * Fetch the user's GitHub profile using the access token. Used to populate
126
+ * the signed-in chip in the studio UI.
127
+ */
128
+ async function fetchUserProfile(accessToken) {
129
+ const res = await fetch(GITHUB_USER_API, {
130
+ headers: {
131
+ "Authorization": `token ${accessToken}`,
132
+ "Accept": "application/vnd.github+json",
133
+ "User-Agent": "vskill-studio",
134
+ },
135
+ });
136
+ if (!res.ok) {
137
+ const text = await res.text().catch(() => "");
138
+ throw new Error(`GitHub user API failed: HTTP ${res.status}: ${text.slice(0, 200)}`);
139
+ }
140
+ const data = (await res.json());
141
+ return {
142
+ login: data.login,
143
+ id: data.id,
144
+ name: data.name,
145
+ email: data.email,
146
+ avatarUrl: data.avatar_url,
147
+ };
148
+ }
149
+ /**
150
+ * Persist the GitHub token to the same storage slot the existing keychain
151
+ * code uses (~/.vskill/keys.env or OS keychain). Lazy-imports keychain.ts
152
+ * so this file stays usable in environments where keychain isn't available
153
+ * (e.g., the test runner — keychain has hard ESM deps on native modules).
154
+ */
155
+ async function persistGithubToken(token) {
156
+ try {
157
+ const mod = await import("../lib/keychain.js");
158
+ const kc = mod.createKeychain();
159
+ kc.setGitHubToken(token);
160
+ return;
161
+ }
162
+ catch (err) {
163
+ // Fall back to manually writing keys.env so the next vskill launch can
164
+ // still read the token (the studio looks in both keychain and keys.env).
165
+ const fs = await import("node:fs");
166
+ const path = await import("node:path");
167
+ const os = await import("node:os");
168
+ const dir = path.join(os.homedir(), ".vskill");
169
+ fs.mkdirSync(dir, { recursive: true });
170
+ const file = path.join(dir, "keys.env");
171
+ let contents = "";
172
+ try {
173
+ contents = fs.readFileSync(file, "utf-8");
174
+ }
175
+ catch { /* fresh */ }
176
+ // Overwrite both canonical + legacy keys.
177
+ const lines = contents
178
+ .split("\n")
179
+ .filter((l) => !l.startsWith("github-oauth-token=") && !l.startsWith("github_token="));
180
+ lines.push(`github-oauth-token=${token}`);
181
+ lines.push(`github_token=${token}`);
182
+ fs.writeFileSync(file, lines.filter(Boolean).join("\n") + "\n", { mode: 0o600 });
183
+ // Surface the fallback so debugging is easy.
184
+ // eslint-disable-next-line no-console
185
+ console.warn(`[oauth-github] keychain.setGithubToken failed (${err.message}); wrote ${file} instead`);
186
+ }
187
+ }
188
+ async function readUrlEncodedBody(req) {
189
+ const MAX_BODY_SIZE = 64 * 1024;
190
+ const TIMEOUT_MS = 10_000;
191
+ return new Promise((resolve, reject) => {
192
+ const chunks = [];
193
+ let size = 0;
194
+ const timer = setTimeout(() => {
195
+ req.destroy();
196
+ reject(new Error("Request body timeout"));
197
+ }, TIMEOUT_MS);
198
+ req.on("data", (chunk) => {
199
+ size += chunk.length;
200
+ if (size > MAX_BODY_SIZE) {
201
+ clearTimeout(timer);
202
+ req.destroy();
203
+ reject(new Error("Request body too large"));
204
+ return;
205
+ }
206
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
207
+ });
208
+ req.on("end", () => {
209
+ clearTimeout(timer);
210
+ resolve(new URLSearchParams(Buffer.concat(chunks).toString()));
211
+ });
212
+ req.on("error", (err) => {
213
+ clearTimeout(timer);
214
+ reject(err);
215
+ });
216
+ });
217
+ }
218
+ // ---------------------------------------------------------------------------
219
+ // Route registration
220
+ // ---------------------------------------------------------------------------
221
+ export function registerOauthGithubRoutes(router) {
222
+ // -------------------------------------------------------------------------
223
+ // POST /api/oauth/github/start
224
+ //
225
+ // Initiates an OAuth flow. Returns the auth URL for the JS to open in the
226
+ // user's browser via Tauri's shell.open (or window.open for the CLI/web
227
+ // path — both are same-origin loopback so the redirect lands back here).
228
+ // -------------------------------------------------------------------------
229
+ router.post("/api/oauth/github/start", async (req, res) => {
230
+ // 2026-05-11 Option B: encode the sidecar's local port into state so
231
+ // the verified-skill.com bounce-redirect knows where to forward the
232
+ // user's browser. Format: <random base64url>.<port>. The same state
233
+ // round-trips through GitHub → platform → localhost callback.
234
+ const port = getLocalPort(req);
235
+ const state = `${generateState()}.${port}`;
236
+ const redirectUri = buildGitHubRedirectUri(req);
237
+ flows.set(state, {
238
+ expiresAt: Date.now() + STATE_TTL_MS,
239
+ status: "pending",
240
+ });
241
+ const authUrl = new URL(GITHUB_AUTHORIZE_URL);
242
+ authUrl.searchParams.set("client_id", GITHUB_OAUTH_CLIENT_ID);
243
+ authUrl.searchParams.set("redirect_uri", redirectUri);
244
+ authUrl.searchParams.set("scope", REQUESTED_SCOPE);
245
+ authUrl.searchParams.set("state", state);
246
+ // Force GitHub to always show the authorize page (even if previously
247
+ // authorized) — clearer UX when the user is debugging or rotating.
248
+ authUrl.searchParams.set("allow_signup", "true");
249
+ sendJson(res, {
250
+ state,
251
+ authUrl: authUrl.toString(),
252
+ expiresAt: flows.get(state).expiresAt,
253
+ });
254
+ });
255
+ // -------------------------------------------------------------------------
256
+ // GET /api/oauth/github/callback?error=...&state=...
257
+ //
258
+ // Browser-driven error callback. Success callbacks are now exchanged on the
259
+ // platform and delivered to /desktop-complete so the local sidecar never
260
+ // needs the confidential GitHub client secret.
261
+ // -------------------------------------------------------------------------
262
+ router.get("/api/oauth/github/callback", async (req, res) => {
263
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
264
+ const state = url.searchParams.get("state");
265
+ const errorParam = url.searchParams.get("error");
266
+ const errorDesc = url.searchParams.get("error_description");
267
+ if (errorParam) {
268
+ const flow = state ? flows.get(state) : null;
269
+ if (flow) {
270
+ flow.status = "error";
271
+ flow.errorMessage = `${errorParam}${errorDesc ? `: ${errorDesc}` : ""}`;
272
+ }
273
+ respondHtml(res, 400, errorPage(errorParam, errorDesc));
274
+ return;
275
+ }
276
+ if (!state) {
277
+ respondHtml(res, 400, errorPage("missing_params", "state not present in callback URL"));
278
+ return;
279
+ }
280
+ const flow = flows.get(state);
281
+ if (flow) {
282
+ flow.status = "error";
283
+ flow.errorMessage = "Outdated platform callback returned a code to the sidecar instead of posting a token.";
284
+ }
285
+ respondHtml(res, 400, errorPage("outdated_callback", "Skill Studio received an old callback shape. Click Sign in again so the platform can exchange the code securely."));
286
+ });
287
+ // -------------------------------------------------------------------------
288
+ // POST /api/oauth/github/desktop-complete
289
+ //
290
+ // Browser-driven completion from verified-skill.com. UNAUTHENTICATED
291
+ // (no X-Studio-Token header) and CSRF-protected by the random in-memory
292
+ // state. The GitHub access token arrives in an application/x-www-form-urlencoded
293
+ // body, not in the URL, so it does not land in browser history.
294
+ // -------------------------------------------------------------------------
295
+ router.post("/api/oauth/github/desktop-complete", async (req, res) => {
296
+ const body = await readUrlEncodedBody(req);
297
+ const state = body.get("state");
298
+ const accessToken = body.get("github_token");
299
+ if (!state || !accessToken) {
300
+ respondHtml(res, 400, errorPage("missing_params", "state or github_token not present in callback body"));
301
+ return;
302
+ }
303
+ const flow = flows.get(state);
304
+ if (!flow) {
305
+ respondHtml(res, 400, errorPage("expired_or_unknown_state", "This sign-in attempt expired or was never started. Close this tab and click Sign in again from Skill Studio."));
306
+ return;
307
+ }
308
+ if (flow.expiresAt < Date.now()) {
309
+ flows.delete(state);
310
+ respondHtml(res, 400, errorPage("expired", "This sign-in attempt expired. Try again."));
311
+ return;
312
+ }
313
+ if (flow.status !== "pending") {
314
+ // Already consumed — defensive.
315
+ respondHtml(res, 200, alreadyDonePage());
316
+ return;
317
+ }
318
+ try {
319
+ await persistGithubToken(accessToken);
320
+ const user = await fetchUserProfile(accessToken);
321
+ flow.status = "ready";
322
+ flow.user = user;
323
+ respondHtml(res, 200, successPage(user.login));
324
+ }
325
+ catch (err) {
326
+ const msg = err instanceof Error ? err.message : String(err);
327
+ flow.status = "error";
328
+ flow.errorMessage = msg;
329
+ // Log so a CI failure or upstream outage is visible in the sidecar log.
330
+ // eslint-disable-next-line no-console
331
+ console.error("[oauth-github] desktop completion failed:", msg);
332
+ respondHtml(res, 502, errorPage("completion_failed", msg));
333
+ }
334
+ });
335
+ // -------------------------------------------------------------------------
336
+ // GET /api/oauth/github/status?state=...
337
+ //
338
+ // Poll endpoint for the studio UI. Returns the flow's current status so
339
+ // the React UserDropdown can transition from "Signing in…" to either
340
+ // signed-in or error.
341
+ // -------------------------------------------------------------------------
342
+ router.get("/api/oauth/github/status", async (req, res) => {
343
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
344
+ const state = url.searchParams.get("state");
345
+ if (!state) {
346
+ sendJson(res, { status: "error", error: "missing state" }, 400);
347
+ return;
348
+ }
349
+ const flow = flows.get(state);
350
+ if (!flow) {
351
+ sendJson(res, { status: "expired" }, 200);
352
+ return;
353
+ }
354
+ if (flow.expiresAt < Date.now()) {
355
+ flows.delete(state);
356
+ sendJson(res, { status: "expired" }, 200);
357
+ return;
358
+ }
359
+ if (flow.status === "ready") {
360
+ // Keep the flow around for a few more polls so a slow UI doesn't miss
361
+ // it, but mark it consumed so re-polls keep returning ready.
362
+ sendJson(res, { status: "ready", user: flow.user }, 200);
363
+ return;
364
+ }
365
+ if (flow.status === "error") {
366
+ sendJson(res, { status: "error", error: flow.errorMessage ?? "unknown error" }, 200);
367
+ return;
368
+ }
369
+ sendJson(res, { status: "pending" }, 200);
370
+ });
371
+ // -------------------------------------------------------------------------
372
+ // GET /api/auth/me
373
+ //
374
+ // Returns the current signed-in user, looked up via the cached GitHub
375
+ // token in keychain/keys.env. Used by the studio UI on cold start to
376
+ // restore the signed-in chip without re-running the OAuth flow.
377
+ // -------------------------------------------------------------------------
378
+ router.get("/api/auth/me", async (_req, res) => {
379
+ try {
380
+ const mod = await import("../lib/keychain.js");
381
+ const kc = mod.createKeychain();
382
+ const token = kc.getGitHubToken();
383
+ if (!token) {
384
+ sendJson(res, { signedIn: false }, 200);
385
+ return;
386
+ }
387
+ try {
388
+ const user = await fetchUserProfile(token);
389
+ sendJson(res, { signedIn: true, user }, 200);
390
+ }
391
+ catch {
392
+ // Token present but invalid/expired — treat as signed-out so the UI
393
+ // doesn't get stuck. The next Sign in click will replace it.
394
+ sendJson(res, { signedIn: false, reason: "token_invalid" }, 200);
395
+ }
396
+ }
397
+ catch (err) {
398
+ sendJson(res, {
399
+ signedIn: false,
400
+ reason: "keychain_unavailable",
401
+ detail: err.message,
402
+ }, 200);
403
+ }
404
+ });
405
+ // -------------------------------------------------------------------------
406
+ // POST /api/auth/sign-out
407
+ //
408
+ // Deletes the cached GitHub token. The next /api/auth/me will return
409
+ // signedIn=false. Idempotent.
410
+ // -------------------------------------------------------------------------
411
+ router.post("/api/auth/sign-out", async (_req, res) => {
412
+ try {
413
+ const mod = await import("../lib/keychain.js");
414
+ const kc = mod.createKeychain();
415
+ kc.clearGitHubToken();
416
+ sendJson(res, { ok: true }, 200);
417
+ }
418
+ catch (err) {
419
+ sendJson(res, { ok: false, error: err.message }, 500);
420
+ }
421
+ });
422
+ }
423
+ // ---------------------------------------------------------------------------
424
+ // HTML responses for the browser-driven callback page
425
+ // ---------------------------------------------------------------------------
426
+ function respondHtml(res, status, html) {
427
+ res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
428
+ res.end(html);
429
+ }
430
+ function successPage(login) {
431
+ const safeLogin = String(login).replace(/[<>&"]/g, (c) => ({
432
+ "<": "&lt;",
433
+ ">": "&gt;",
434
+ "&": "&amp;",
435
+ '"': "&quot;",
436
+ }[c]));
437
+ return `<!doctype html>
438
+ <meta charset="utf-8">
439
+ <title>Signed in — Skill Studio</title>
440
+ <style>
441
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
442
+ margin: 0; padding: 2rem; display: grid; place-items: center;
443
+ min-height: 100vh; background: #fafafa; color: #1f2328; }
444
+ .card { max-width: 480px; padding: 2rem 2.5rem; background: white;
445
+ border-radius: 12px; border: 1px solid #d0d7de;
446
+ box-shadow: 0 8px 24px rgba(140,149,159,0.12); text-align: center; }
447
+ h1 { margin: 0 0 0.5rem; font-size: 1.5rem; font-weight: 600; }
448
+ p { margin: 0.5rem 0; line-height: 1.5; color: #57606a; }
449
+ code { background: #f6f8fa; padding: 0.125rem 0.375rem; border-radius: 4px;
450
+ font-family: ui-monospace, SFMono-Regular, monospace; }
451
+ .check { font-size: 3rem; margin-bottom: 1rem; }
452
+ </style>
453
+ <div class="card">
454
+ <div class="check">✓</div>
455
+ <h1>Signed in as <code>${safeLogin}</code></h1>
456
+ <p>You can close this tab and return to Skill Studio.</p>
457
+ </div>
458
+ <script>
459
+ // Try to auto-close. macOS Safari may block this; that's OK — the user can close manually.
460
+ setTimeout(() => { window.close(); }, 1500);
461
+ </script>`;
462
+ }
463
+ function alreadyDonePage() {
464
+ return `<!doctype html>
465
+ <meta charset="utf-8">
466
+ <title>Sign-in already completed</title>
467
+ <style>
468
+ body { font-family: -apple-system, sans-serif; margin: 0; padding: 2rem;
469
+ display: grid; place-items: center; min-height: 100vh;
470
+ background: #fafafa; color: #1f2328; }
471
+ .card { max-width: 480px; padding: 2rem 2.5rem; background: white;
472
+ border-radius: 12px; border: 1px solid #d0d7de; text-align: center; }
473
+ </style>
474
+ <div class="card">
475
+ <h1>Sign-in already completed</h1>
476
+ <p>You can close this tab.</p>
477
+ </div>`;
478
+ }
479
+ function errorPage(error, description) {
480
+ const safe = (s) => String(s ?? "")
481
+ .replace(/[<>&"]/g, (c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;" }[c]));
482
+ return `<!doctype html>
483
+ <meta charset="utf-8">
484
+ <title>Sign-in failed — Skill Studio</title>
485
+ <style>
486
+ body { font-family: -apple-system, sans-serif; margin: 0; padding: 2rem;
487
+ display: grid; place-items: center; min-height: 100vh;
488
+ background: #fafafa; color: #1f2328; }
489
+ .card { max-width: 520px; padding: 2rem 2.5rem; background: white;
490
+ border-radius: 12px; border: 1px solid #d0d7de;
491
+ box-shadow: 0 8px 24px rgba(140,149,159,0.12); text-align: center; }
492
+ h1 { margin: 0 0 0.5rem; font-size: 1.25rem; font-weight: 600; color: #cf222e; }
493
+ p { margin: 0.5rem 0; line-height: 1.5; color: #57606a; }
494
+ pre { background: #f6f8fa; padding: 0.75rem; border-radius: 6px;
495
+ font-family: ui-monospace, monospace; font-size: 0.8125rem;
496
+ text-align: left; overflow: auto; max-height: 200px; }
497
+ </style>
498
+ <div class="card">
499
+ <h1>Sign-in failed</h1>
500
+ <p><strong>${safe(error)}</strong></p>
501
+ ${description ? `<pre>${safe(description)}</pre>` : ""}
502
+ <p>Close this tab and try Sign in again from Skill Studio.</p>
503
+ </div>`;
504
+ }
505
+ //# sourceMappingURL=oauth-github-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-github-routes.js","sourceRoot":"","sources":["../../src/eval-server/oauth-github-routes.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,14 @@ import * as http from "node:http";
2
2
  export declare function getPlatformBaseUrl(): string;
3
3
  export declare function shouldProxyToPlatform(url: string | undefined): boolean;
4
4
  export declare function shouldInjectAuth(url: string | undefined): boolean;
5
- /** Test-only reset hook. */
5
+ /**
6
+ * Public invalidation hook so callers (e.g. an HTTP control endpoint, or a
7
+ * future SIGUSR1 handler) can drop the cached token immediately on auth
8
+ * state changes. Tests use this to reset between cases as well.
9
+ */
10
+ export declare function invalidatePlatformProxyTokenCache(): void;
11
+ /** @deprecated Prefer `invalidatePlatformProxyTokenCache`. Kept for any
12
+ * existing test imports until they migrate. */
6
13
  export declare function _resetPlatformProxyAuthCacheForTests(): void;
7
14
  export interface PickHeadersOptions {
8
15
  /** Request path (e.g. "/api/v1/tenants/abc/skills"). */
@@ -19,6 +26,15 @@ export declare function pickHeadersForUpstream(src: http.IncomingHttpHeaders, op
19
26
  * Resolves once the proxy pipeline finishes (either side ended) — callers
20
27
  * can `await` to know the response has been written, but typical use is
21
28
  * fire-and-forget within an HTTP server handler.
29
+ *
30
+ * SSE diagnostic logging (0838 / AC-US1-02):
31
+ * When `VSKILL_DEBUG_SSE=1`, every `text/event-stream`-shaped request emits
32
+ * proxy.sse.start{requestId, upstreamUrl} on dispatch
33
+ * proxy.sse.end{requestId, status, durationMs} on completion
34
+ * AND the response gets `X-Request-Id: <requestId>` so client-side debug
35
+ * logs (in useSkillUpdates) can be correlated with server-side logs.
36
+ * When the flag is unset (or "0"), no per-request logs and no header
37
+ * injection — preserves the production hot path.
22
38
  */
23
39
  export declare function proxyToPlatform(req: http.IncomingMessage, res: http.ServerResponse, baseUrl?: string): Promise<void>;
24
40
  export interface SubmitSkillUpdateEventInput {