vskill 1.0.16 → 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 (206) hide show
  1. package/README.md +84 -9
  2. package/agents.json +3 -1
  3. package/dist/agents/agents-registry.d.ts +61 -0
  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 +17 -2
  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 +82 -5
  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 +4 -0
  68. package/dist/eval-server/install-state-routes.js +15 -1
  69. package/dist/eval-server/install-state-routes.js.map +1 -1
  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-CvdYq8Rr.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-Bf3PBC64.js → SearchPaletteCore-Dn5gQJS_.js} +2 -2
  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 +48 -12
  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/multi-install.d.ts +43 -0
  127. package/dist/installer/multi-install.js +237 -0
  128. package/dist/installer/multi-install.js.map +1 -0
  129. package/dist/installer/transformers/aider.d.ts +2 -0
  130. package/dist/installer/transformers/aider.js +32 -0
  131. package/dist/installer/transformers/aider.js.map +1 -0
  132. package/dist/installer/transformers/continue-dev.d.ts +2 -0
  133. package/dist/installer/transformers/continue-dev.js +6 -0
  134. package/dist/installer/transformers/continue-dev.js.map +1 -0
  135. package/dist/installer/transformers/cursor.d.ts +2 -0
  136. package/dist/installer/transformers/cursor.js +24 -0
  137. package/dist/installer/transformers/cursor.js.map +1 -0
  138. package/dist/installer/transformers/github-copilot.d.ts +2 -0
  139. package/dist/installer/transformers/github-copilot.js +17 -0
  140. package/dist/installer/transformers/github-copilot.js.map +1 -0
  141. package/dist/installer/transformers/index.d.ts +78 -0
  142. package/dist/installer/transformers/index.js +13 -0
  143. package/dist/installer/transformers/index.js.map +1 -0
  144. package/dist/installer/transformers/junie.d.ts +2 -0
  145. package/dist/installer/transformers/junie.js +6 -0
  146. package/dist/installer/transformers/junie.js.map +1 -0
  147. package/dist/installer/transformers/kiro.d.ts +2 -0
  148. package/dist/installer/transformers/kiro.js +6 -0
  149. package/dist/installer/transformers/kiro.js.map +1 -0
  150. package/dist/installer/transformers/trae.d.ts +2 -0
  151. package/dist/installer/transformers/trae.js +6 -0
  152. package/dist/installer/transformers/trae.js.map +1 -0
  153. package/dist/installer/transformers/windsurf.d.ts +2 -0
  154. package/dist/installer/transformers/windsurf.js +12 -0
  155. package/dist/installer/transformers/windsurf.js.map +1 -0
  156. package/dist/installer/yaml-safe-mutate.d.ts +19 -0
  157. package/dist/installer/yaml-safe-mutate.js +184 -0
  158. package/dist/installer/yaml-safe-mutate.js.map +1 -0
  159. package/dist/lib/active-tenant.d.ts +36 -0
  160. package/dist/lib/active-tenant.js +120 -0
  161. package/dist/lib/active-tenant.js.map +1 -0
  162. package/dist/lib/github-fetch.d.ts +1 -0
  163. package/dist/lib/github-fetch.js +11 -1
  164. package/dist/lib/github-fetch.js.map +1 -1
  165. package/dist/lib/keychain.d.ts +15 -2
  166. package/dist/lib/keychain.js +156 -8
  167. package/dist/lib/keychain.js.map +1 -1
  168. package/dist/lib/migration/keychain-migration.d.ts +35 -0
  169. package/dist/lib/migration/keychain-migration.js +189 -0
  170. package/dist/lib/migration/keychain-migration.js.map +1 -0
  171. package/dist/lib/tenant-resolver.d.ts +38 -0
  172. package/dist/lib/tenant-resolver.js +79 -0
  173. package/dist/lib/tenant-resolver.js.map +1 -0
  174. package/dist/lockfile/types.d.ts +8 -0
  175. package/dist/sidecar/eval-ui-manifest.json +1 -0
  176. package/dist/sidecar/sea-config.json +57 -0
  177. package/dist/sidecar/sea-prep.blob +0 -0
  178. package/dist/sidecar/server.cjs +141627 -0
  179. package/dist/sidecar/vskill-version.txt +1 -0
  180. package/dist/studio/lib/ops-log.js +140 -57
  181. package/dist/studio/lib/ops-log.js.map +1 -1
  182. package/dist/studio/lib/scope-transfer.d.ts +11 -1
  183. package/dist/studio/lib/scope-transfer.js +48 -24
  184. package/dist/studio/lib/scope-transfer.js.map +1 -1
  185. package/dist/studio/routes/index.js +10 -1
  186. package/dist/studio/routes/index.js.map +1 -1
  187. package/dist/studio/routes/ops.js +31 -7
  188. package/dist/studio/routes/ops.js.map +1 -1
  189. package/dist/studio/routes/promote.js +15 -8
  190. package/dist/studio/routes/promote.js.map +1 -1
  191. package/dist/studio/routes/revert.js +12 -1
  192. package/dist/studio/routes/revert.js.map +1 -1
  193. package/dist/studio/routes/test-install.js +13 -8
  194. package/dist/studio/routes/test-install.js.map +1 -1
  195. package/dist/studio-runtime/lockfile.d.ts +51 -0
  196. package/dist/studio-runtime/lockfile.js +216 -0
  197. package/dist/studio-runtime/lockfile.js.map +1 -0
  198. package/dist/updater/source-fetcher.js +2 -2
  199. package/dist/updater/source-fetcher.js.map +1 -1
  200. package/package.json +17 -1
  201. package/dist/eval-ui/assets/FindSkillsPalette-DsSgotS9.js +0 -2
  202. package/dist/eval-ui/assets/SkillDetailPanel-DAD2yJO-.js +0 -1
  203. package/dist/eval-ui/assets/UpdateDropdown-h5Hg3h7Z.js +0 -1
  204. package/dist/eval-ui/assets/index-CKLqBL52.css +0 -1
  205. package/dist/eval-ui/assets/index-JaDg6FlU.js +0 -124
  206. package/dist/eval-ui/assets/skill-studio-logo-CRyKgIrg.png +0 -0
@@ -0,0 +1 @@
1
+ 1.0.18
@@ -10,74 +10,162 @@
10
10
  //
11
11
  // In-process subscribe()/listOps()/deleteOp() (tombstone) — no cross-process IPC.
12
12
  // ---------------------------------------------------------------------------
13
- import { openSync, writeSync, closeSync, mkdirSync, existsSync, readFileSync } from "node:fs";
13
+ import { promises as fs, existsSync, statSync, renameSync } from "node:fs";
14
14
  import { dirname, join } from "node:path";
15
15
  import { homedir } from "node:os";
16
16
  import { EventEmitter } from "node:events";
17
- const APPEND_FLAGS = "a";
18
17
  const emitter = new EventEmitter();
19
- emitter.setMaxListeners(0);
18
+ // Subscriber count is bounded by active SSE clients (~UI tabs); 100 is far
19
+ // above realistic load, and unlike `setMaxListeners(0)` it lets a real leak
20
+ // surface as a Node warning instead of silently growing.
21
+ emitter.setMaxListeners(100);
22
+ // Default rotation threshold for studio-ops.jsonl. Override via env to tune
23
+ // for long-running studios. 5 MiB ≈ ~25k ops at ~200 B each.
24
+ const ROTATE_BYTES_DEFAULT = 5 * 1024 * 1024;
25
+ function getRotateBytes() {
26
+ const raw = process.env.VSKILL_OPS_LOG_ROTATE_BYTES;
27
+ if (!raw)
28
+ return ROTATE_BYTES_DEFAULT;
29
+ const n = Number(raw);
30
+ return Number.isFinite(n) && n > 0 ? n : ROTATE_BYTES_DEFAULT;
31
+ }
20
32
  export function getLogPath() {
21
33
  return process.env.VSKILL_OPS_LOG_PATH || join(homedir(), ".vskill", "studio-ops.jsonl");
22
34
  }
23
- function ensureDir(path) {
24
- const dir = dirname(path);
25
- if (!existsSync(dir))
26
- mkdirSync(dir, { recursive: true });
35
+ async function ensureDir(path) {
36
+ // mkdir(recursive:true) is idempotent — no need for an existsSync TOCTOU pre-check.
37
+ await fs.mkdir(dirname(path), { recursive: true });
38
+ }
39
+ /**
40
+ * Rotate `studio-ops.jsonl` → `studio-ops.1.jsonl` when the live log exceeds
41
+ * the configured threshold. Best-effort: a rotation failure must not block
42
+ * the append. Compaction of tombstones happens implicitly because rotated
43
+ * files are kept (only `listOps` reads the live file; rotated history is
44
+ * forensic-only, not surfaced via the API).
45
+ */
46
+ function maybeRotate(path) {
47
+ try {
48
+ if (!existsSync(path))
49
+ return;
50
+ const size = statSync(path).size;
51
+ if (size < getRotateBytes())
52
+ return;
53
+ renameSync(path, path.replace(/\.jsonl$/, ".1.jsonl"));
54
+ }
55
+ catch {
56
+ /* rotation is best-effort */
57
+ }
27
58
  }
28
59
  export async function appendOp(op) {
29
60
  const path = getLogPath();
30
- ensureDir(path);
61
+ await ensureDir(path);
62
+ maybeRotate(path);
31
63
  const line = JSON.stringify(op) + "\n";
32
- // Single openSync + writeSync + closeSync atomic for small writes under POSIX O_APPEND.
33
- const fd = openSync(path, APPEND_FLAGS);
64
+ // POSIX guarantees writes < PIPE_BUF (4 KiB) are atomic with O_APPEND;
65
+ // our ops are well under that. fs.appendFile uses O_APPEND under the hood.
66
+ await fs.appendFile(path, line);
67
+ emitter.emit("op", op);
68
+ }
69
+ const TAIL_CHUNK_BYTES = 64 * 1024;
70
+ const DEFAULT_LIMIT = 50;
71
+ /**
72
+ * Read JSONL backwards from EOF in 64 KiB chunks until we have `limit`
73
+ * non-tombstoned ops or hit the start of the file. Tombstones are appended
74
+ * AFTER the op they invalidate, so reading newest-first naturally surfaces
75
+ * tombstone IDs before the ops they apply to.
76
+ *
77
+ * Memory: O(chunk + limit). Time: O(limit) for typical limits (50–500),
78
+ * vs. O(file-size) for the previous full-file approach.
79
+ */
80
+ async function readTailOps(path, limit, before) {
81
+ let fh;
34
82
  try {
35
- writeSync(fd, line);
83
+ fh = await fs.open(path, "r");
36
84
  }
37
- finally {
38
- closeSync(fd);
85
+ catch (err) {
86
+ if (err.code === "ENOENT")
87
+ return [];
88
+ throw err;
39
89
  }
40
- emitter.emit("op", op);
41
- }
42
- function readAllLines(path) {
43
- if (!existsSync(path))
44
- return [];
45
- const raw = readFileSync(path, "utf-8");
46
- const out = [];
47
- for (const line of raw.split("\n")) {
48
- const trimmed = line.trim();
49
- if (!trimmed)
50
- continue;
51
- try {
52
- out.push(JSON.parse(trimmed));
53
- }
54
- catch {
55
- // Skip malformed line log corruption shouldn't crash listOps.
90
+ try {
91
+ const { size } = await fh.stat();
92
+ if (size === 0)
93
+ return [];
94
+ const tombstoned = new Set();
95
+ const ops = [];
96
+ let pos = size;
97
+ let carry = ""; // Partial line stitched onto the next (older) chunk.
98
+ while (pos > 0 && ops.length < limit) {
99
+ const readLen = Math.min(TAIL_CHUNK_BYTES, pos);
100
+ pos -= readLen;
101
+ const buf = Buffer.alloc(readLen);
102
+ await fh.read(buf, 0, readLen, pos);
103
+ // chunk_text + carry from previous (newer) chunk = continuous bytes,
104
+ // because chunks are read newest→oldest and `carry` was the partial
105
+ // line at the START of the previous chunk.
106
+ const text = buf.toString("utf-8") + carry;
107
+ // The first newline splits a partial line at the start of `text`
108
+ // (which continues into older bytes still on disk) from complete
109
+ // lines after it. At pos === 0 there is no older data — every
110
+ // segment is a complete line.
111
+ let firstNL;
112
+ let lines;
113
+ if (pos === 0) {
114
+ carry = "";
115
+ lines = text.split("\n");
116
+ }
117
+ else {
118
+ firstNL = text.indexOf("\n");
119
+ if (firstNL === -1) {
120
+ // No newline at all — the entire chunk continues into older bytes.
121
+ carry = text;
122
+ continue;
123
+ }
124
+ carry = text.slice(0, firstNL);
125
+ lines = text.slice(firstNL + 1).split("\n");
126
+ }
127
+ // Process newest→oldest within this batch.
128
+ for (let i = lines.length - 1; i >= 0 && ops.length < limit; i--) {
129
+ const trimmed = lines[i].trim();
130
+ if (!trimmed)
131
+ continue;
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(trimmed);
135
+ }
136
+ catch {
137
+ continue;
138
+ }
139
+ if (parsed.tombstone === true) {
140
+ const id = parsed.id;
141
+ if (id)
142
+ tombstoned.add(id);
143
+ continue;
144
+ }
145
+ const op = parsed;
146
+ if (typeof op.ts !== "number")
147
+ continue;
148
+ if (tombstoned.has(op.id))
149
+ continue;
150
+ if (before != null && op.ts >= before)
151
+ continue;
152
+ ops.push(op);
153
+ }
56
154
  }
155
+ // Tail-read order is approximately newest-first but `ts` may not be
156
+ // strictly monotonic w.r.t. file position (clock skew, parallel writes
157
+ // — both rare but possible). Re-sort to honor the contract.
158
+ ops.sort((a, b) => b.ts - a.ts);
159
+ return ops.slice(0, limit);
160
+ }
161
+ finally {
162
+ await fh.close();
57
163
  }
58
- return out;
59
164
  }
60
165
  export async function listOps(opts = {}) {
61
166
  const path = getLogPath();
62
- const all = readAllLines(path);
63
- const tombstoned = new Set();
64
- for (const entry of all) {
65
- if (entry.tombstone === true && entry.id) {
66
- tombstoned.add(entry.id);
67
- }
68
- }
69
- let ops = all
70
- .filter((e) => !e.tombstone && typeof e.ts === "number")
71
- .filter((op) => !tombstoned.has(op.id));
72
- // Newest first.
73
- ops.sort((a, b) => b.ts - a.ts);
74
- if (opts.before != null) {
75
- ops = ops.filter((op) => op.ts < opts.before);
76
- }
77
- if (opts.limit != null) {
78
- ops = ops.slice(0, opts.limit);
79
- }
80
- return ops;
167
+ const limit = opts.limit ?? DEFAULT_LIMIT;
168
+ return readTailOps(path, limit, opts.before);
81
169
  }
82
170
  export function subscribe(fn) {
83
171
  emitter.on("op", fn);
@@ -85,15 +173,10 @@ export function subscribe(fn) {
85
173
  }
86
174
  export async function deleteOp(id) {
87
175
  const path = getLogPath();
88
- ensureDir(path);
176
+ await ensureDir(path);
177
+ maybeRotate(path);
89
178
  const line = JSON.stringify({ id, tombstone: true }) + "\n";
90
- const fd = openSync(path, APPEND_FLAGS);
91
- try {
92
- writeSync(fd, line);
93
- }
94
- finally {
95
- closeSync(fd);
96
- }
179
+ await fs.appendFile(path, line);
97
180
  emitter.emit("delete", id);
98
181
  }
99
182
  //# sourceMappingURL=ops-log.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ops-log.js","sourceRoot":"","sources":["../../../src/studio/lib/ops-log.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAC9E,oDAAoD;AACpD,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,iDAAiD;AACjD,EAAE;AACF,kFAAkF;AAClF,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;AACnC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAE3B,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAY;IACzC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACvC,0FAA0F;IAC1F,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC;AAOD,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAiC,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAuB,EAAE;IACrD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,IAAK,KAAuB,CAAC,SAAS,KAAK,IAAI,IAAK,KAAuB,CAAC,EAAE,EAAE,CAAC;YAC/E,UAAU,CAAC,GAAG,CAAE,KAAuB,CAAC,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,IAAI,GAAG,GAAe,GAAG;SACtB,MAAM,CAAC,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAE,CAAmB,CAAC,SAAS,IAAI,OAAQ,CAAc,CAAC,EAAE,KAAK,QAAQ,CAAC;SACvG,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1C,gBAAgB;IAChB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAEhC,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACxB,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,MAAO,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAA0B;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrB,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAU;IACvC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAA0B,CAAC,GAAG,IAAI,CAAC;IACpF,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"ops-log.js","sourceRoot":"","sources":["../../../src/studio/lib/ops-log.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,16 @@ export declare function resolveScopePath(scope: SkillScope, root: string, skill:
29
29
  * Count files written recursively into a directory — used for the SSE
30
30
  * `copied` and `deleted` event payloads. Returns 0 for missing dirs so
31
31
  * callers can use it on paths that may have been removed mid-flight.
32
+ *
33
+ * Skips symlinks (lstat) to avoid following loops, and caps depth at
34
+ * MAX_RECURSION_DEPTH so a pathological tree can't blow the call stack.
32
35
  */
33
- export declare function countFiles(dir: string): number;
36
+ export declare function countFiles(dir: string, depth?: number): number;
37
+ /**
38
+ * Pre-flight check that a transfer can succeed. Throws MissingSourceError /
39
+ * CollisionError without touching the filesystem. Routes call this BEFORE
40
+ * opening an SSE stream so failure cases return clean HTTP 404 / 409 with
41
+ * a JSON body instead of opening SSE then immediately emitting an error.
42
+ */
43
+ export declare function validatePaths(sourcePath: string, destPath: string, overwrite: boolean): void;
34
44
  export declare function transfer(req: TransferRequest, emit: SSEEmit): Promise<TransferResult>;
@@ -13,8 +13,12 @@
13
13
  // Provenance sidecar (.vskill-meta.json) must NEVER leak out of OWN scope —
14
14
  // filtered inside transfer before invoking copy.
15
15
  // ---------------------------------------------------------------------------
16
- import { existsSync, rmSync, readdirSync, statSync, copyFileSync, mkdirSync, writeFileSync } from "node:fs";
16
+ import { existsSync, rmSync, readdirSync, copyFileSync, mkdirSync, writeFileSync } from "node:fs";
17
17
  import { join } from "node:path";
18
+ // Cap recursion depth on user-content trees we don't fully control.
19
+ // 32 is well above any realistic skill nesting (plan.md skills have 0–3
20
+ // levels) and safely below Node's default call-stack budget.
21
+ const MAX_RECURSION_DEPTH = 32;
18
22
  import { copyPluginFiltered, shouldSkipFromCommands, } from "../../shared/copy-plugin-filtered.js";
19
23
  import { resolveSourceLink } from "../../eval-server/source-link.js";
20
24
  export class CollisionError extends Error {
@@ -49,17 +53,23 @@ export function resolveScopePath(scope, root, skill, home) {
49
53
  * Count files written recursively into a directory — used for the SSE
50
54
  * `copied` and `deleted` event payloads. Returns 0 for missing dirs so
51
55
  * callers can use it on paths that may have been removed mid-flight.
56
+ *
57
+ * Skips symlinks (lstat) to avoid following loops, and caps depth at
58
+ * MAX_RECURSION_DEPTH so a pathological tree can't blow the call stack.
52
59
  */
53
- export function countFiles(dir) {
60
+ export function countFiles(dir, depth = 0) {
54
61
  if (!existsSync(dir))
55
62
  return 0;
63
+ if (depth >= MAX_RECURSION_DEPTH)
64
+ return 0;
56
65
  let n = 0;
57
- for (const entry of readdirSync(dir)) {
58
- const full = join(dir, entry);
59
- const st = statSync(full);
60
- if (st.isDirectory())
61
- n += countFiles(full);
62
- else if (st.isFile())
66
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
67
+ if (entry.isSymbolicLink())
68
+ continue;
69
+ const full = join(dir, entry.name);
70
+ if (entry.isDirectory())
71
+ n += countFiles(full, depth + 1);
72
+ else if (entry.isFile())
63
73
  n += 1;
64
74
  }
65
75
  return n;
@@ -69,36 +79,50 @@ export function countFiles(dir) {
69
79
  * copyPluginFiltered this does not do the plugin-root flattening, because
70
80
  * OWN → INSTALLED|GLOBAL is a straight skill-dir copy with one exclusion.
71
81
  */
72
- function copyOwnSkillFiltered(sourceDir, targetDir, relBase = "") {
82
+ function copyOwnSkillFiltered(sourceDir, targetDir, relBase = "", depth = 0) {
83
+ if (depth >= MAX_RECURSION_DEPTH)
84
+ return;
73
85
  mkdirSync(targetDir, { recursive: true });
74
- for (const entry of readdirSync(sourceDir)) {
86
+ for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
75
87
  // 0809: filter the copied-skill source-link sidecar at the OWN scope root
76
88
  // alongside the existing .vskill-meta.json filter. Sidecars are re-derived
77
89
  // by the destination's `transfer()` post-copy step, never copied through.
78
90
  // Asymmetric with copyPluginFiltered (which strips at any depth) on purpose:
79
91
  // OWN scope is a single skill dir, so sidecars only ever exist at the root.
80
- if (!relBase && (entry === ".vskill-meta.json" || entry === ".vskill-source.json"))
92
+ if (!relBase && (entry.name === ".vskill-meta.json" || entry.name === ".vskill-source.json"))
93
+ continue;
94
+ // Skip symlinks defensively — copying through them risks loops and
95
+ // unexpected escapes from the skill dir.
96
+ if (entry.isSymbolicLink())
81
97
  continue;
82
- const relPath = relBase ? `${relBase}/${entry}` : entry;
83
- const sourcePath = join(sourceDir, entry);
84
- const st = statSync(sourcePath);
85
- if (st.isDirectory()) {
86
- copyOwnSkillFiltered(sourcePath, join(targetDir, entry), relPath);
98
+ const relPath = relBase ? `${relBase}/${entry.name}` : entry.name;
99
+ const sourcePath = join(sourceDir, entry.name);
100
+ if (entry.isDirectory()) {
101
+ copyOwnSkillFiltered(sourcePath, join(targetDir, entry.name), relPath, depth + 1);
87
102
  }
88
- else if (st.isFile() && !shouldSkipFromCommands(relPath)) {
89
- copyFileSync(sourcePath, join(targetDir, entry));
103
+ else if (entry.isFile() && !shouldSkipFromCommands(relPath)) {
104
+ copyFileSync(sourcePath, join(targetDir, entry.name));
90
105
  }
91
106
  }
92
107
  }
108
+ /**
109
+ * Pre-flight check that a transfer can succeed. Throws MissingSourceError /
110
+ * CollisionError without touching the filesystem. Routes call this BEFORE
111
+ * opening an SSE stream so failure cases return clean HTTP 404 / 409 with
112
+ * a JSON body instead of opening SSE then immediately emitting an error.
113
+ */
114
+ export function validatePaths(sourcePath, destPath, overwrite) {
115
+ if (!existsSync(sourcePath))
116
+ throw new MissingSourceError(sourcePath);
117
+ if (existsSync(destPath) && !overwrite)
118
+ throw new CollisionError(destPath);
119
+ }
93
120
  export async function transfer(req, emit) {
94
121
  const sourcePath = resolveScopePath(req.fromScope, req.root, req.skill, req.home);
95
122
  const destPath = resolveScopePath(req.toScope, req.root, req.skill, req.home);
96
- if (!existsSync(sourcePath)) {
97
- throw new MissingSourceError(sourcePath);
98
- }
99
- if (existsSync(destPath) && !req.overwrite) {
100
- throw new CollisionError(destPath);
101
- }
123
+ // Re-validate inside transfer too — callers may call transfer() directly
124
+ // (not via the route layer) and expect the throw contract.
125
+ validatePaths(sourcePath, destPath, req.overwrite ?? false);
102
126
  // If overwrite, clear the destination first so stale files aren't left behind.
103
127
  if (existsSync(destPath) && req.overwrite) {
104
128
  rmSync(destPath, { recursive: true, force: true });
@@ -1 +1 @@
1
- {"version":3,"file":"scope-transfer.js","sourceRoot":"","sources":["../../../src/studio/lib/scope-transfer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-transfer.js","sourceRoot":"","sources":["../../../src/studio/lib/scope-transfer.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,9 @@ import { registerDetectEnginesRoute } from "../../eval-server/detect-engines-rou
14
14
  import { registerInstallEngineRoutes } from "../../eval-server/install-engine-routes.js";
15
15
  import { registerInstallSkillRoutes } from "../../eval-server/install-skill-routes.js";
16
16
  import { registerInstallStateRoutes } from "../../eval-server/install-state-routes.js";
17
+ import { registerSupportedAgentsRoutes } from "../../eval-server/supported-agents-routes.js";
18
+ import { registerExportSkillRoutes } from "../../eval-server/export-skill-routes.js";
19
+ import { registerRemoveSkillRoutes } from "../../eval-server/remove-skill-routes.js";
17
20
  export function registerScopeTransferRoutes(router, root) {
18
21
  registerPromoteRoute(router, root);
19
22
  registerTestInstallRoute(router, root);
@@ -21,8 +24,14 @@ export function registerScopeTransferRoutes(router, root) {
21
24
  registerOpsRoutes(router);
22
25
  registerDetectEnginesRoute(router, root);
23
26
  registerInstallEngineRoutes(router, root);
24
- registerInstallSkillRoutes(router);
27
+ registerInstallSkillRoutes(router, root);
25
28
  // 0827 — per-skill install-state for the panel's scope picker.
26
29
  registerInstallStateRoutes(router, root);
30
+ // 0845 — cross-tool install targets (every supported agent, detected or not).
31
+ registerSupportedAgentsRoutes(router);
32
+ // 0845 T-015 — Tier-3 clipboard export (ChatGPT, v0, bolt.new).
33
+ registerExportSkillRoutes(router);
34
+ // 0850 — per-agent skill remove from the InstallTargetsModal.
35
+ registerRemoveSkillRoutes(router, root);
27
36
  }
28
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/studio/routes/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wDAAwD;AACxD,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,qEAAqE;AACrE,cAAc;AACd,8EAA8E;AAG9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAEvF,MAAM,UAAU,2BAA2B,CAAC,MAAc,EAAE,IAAY;IACtE,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,wBAAwB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,2BAA2B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1C,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACnC,+DAA+D;IAC/D,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/studio/routes/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wDAAwD;AACxD,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,qEAAqE;AACrE,cAAc;AACd,8EAA8E;AAG9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AAErF,MAAM,UAAU,2BAA2B,CAAC,MAAc,EAAE,IAAY;IACtE,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,wBAAwB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,2BAA2B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1C,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,+DAA+D;IAC/D,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzC,8EAA8E;IAC9E,6BAA6B,CAAC,MAAM,CAAC,CAAC;IACtC,gEAAgE;IAChE,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClC,8DAA8D;IAC9D,yBAAyB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC"}
@@ -9,6 +9,34 @@ import { sendJson } from "../../eval-server/router.js";
9
9
  import { initSSE, sendSSE } from "../../eval-server/sse-helpers.js";
10
10
  import { listOps, subscribe, deleteOp } from "../lib/ops-log.js";
11
11
  import { parseQuery } from "../lib/query.js";
12
+ // One ticker fans out to every connected SSE client instead of one timer
13
+ // per connection. Lazily started on first subscriber, stopped when the
14
+ // last leaves. `.unref()` so an idle ticker doesn't block process exit.
15
+ const HEARTBEAT_MS = 3000;
16
+ const heartbeatClients = new Set();
17
+ let heartbeatTimer = null;
18
+ function startHeartbeatIfNeeded() {
19
+ if (heartbeatTimer || heartbeatClients.size === 0)
20
+ return;
21
+ heartbeatTimer = setInterval(() => {
22
+ const ts = Date.now();
23
+ for (const res of heartbeatClients) {
24
+ try {
25
+ sendSSE(res, "heartbeat", { ts });
26
+ }
27
+ catch {
28
+ // Stream closed mid-fan-out — drop it now to avoid a stuck client
29
+ // keeping the timer alive after the connection ended.
30
+ heartbeatClients.delete(res);
31
+ }
32
+ }
33
+ if (heartbeatClients.size === 0 && heartbeatTimer) {
34
+ clearInterval(heartbeatTimer);
35
+ heartbeatTimer = null;
36
+ }
37
+ }, HEARTBEAT_MS);
38
+ heartbeatTimer.unref();
39
+ }
12
40
  export function registerOpsRoutes(router) {
13
41
  // GET /api/studio/ops?limit=50&before=<ts>
14
42
  router.get("/api/studio/ops", async (req, res) => {
@@ -34,14 +62,10 @@ export function registerOpsRoutes(router) {
34
62
  // Writing after stream close — safe to swallow.
35
63
  }
36
64
  });
37
- const heartbeat = setInterval(() => {
38
- try {
39
- sendSSE(res, "heartbeat", { ts: Date.now() });
40
- }
41
- catch { }
42
- }, 3000);
65
+ heartbeatClients.add(res);
66
+ startHeartbeatIfNeeded();
43
67
  const cleanup = () => {
44
- clearInterval(heartbeat);
68
+ heartbeatClients.delete(res);
45
69
  unsub();
46
70
  };
47
71
  req.on("close", cleanup);
@@ -1 +1 @@
1
- {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../../src/studio/routes/ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../../src/studio/routes/ops.ts"],"names":[],"mappings":"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"}
@@ -7,10 +7,9 @@
7
7
  // ---------------------------------------------------------------------------
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { homedir } from "node:os";
10
- import { existsSync } from "node:fs";
11
10
  import { sendJson } from "../../eval-server/router.js";
12
11
  import { initSSE, sendSSE, sendSSEDone } from "../../eval-server/sse-helpers.js";
13
- import { transfer, CollisionError, MissingSourceError, resolveScopePath, } from "../lib/scope-transfer.js";
12
+ import { transfer, validatePaths, CollisionError, MissingSourceError, resolveScopePath, } from "../lib/scope-transfer.js";
14
13
  import { writeProvenance } from "../lib/provenance.js";
15
14
  import { appendOp } from "../lib/ops-log.js";
16
15
  import { parseQuery } from "../lib/query.js";
@@ -26,13 +25,21 @@ export function registerPromoteRoute(router, root, home = homedir()) {
26
25
  const opId = randomUUID();
27
26
  // Pre-validate BEFORE opening the SSE stream so collisions and missing-
28
27
  // source errors return clean HTTP status codes (409 / 404) per AC-US1-03.
29
- if (!existsSync(sourcePath)) {
30
- sendJson(res, { ok: false, code: "missing-source", path: sourcePath }, 404, req);
31
- return;
28
+ // Single source of truth: validatePaths is the same check transfer()
29
+ // re-runs internally no double-stat in the happy path.
30
+ try {
31
+ validatePaths(sourcePath, destPath, overwrite);
32
32
  }
33
- if (existsSync(destPath) && !overwrite) {
34
- sendJson(res, { ok: false, code: "collision", path: destPath }, 409, req);
35
- return;
33
+ catch (err) {
34
+ if (err instanceof MissingSourceError) {
35
+ sendJson(res, { ok: false, code: "missing-source", path: err.path }, 404, req);
36
+ return;
37
+ }
38
+ if (err instanceof CollisionError) {
39
+ sendJson(res, { ok: false, code: "collision", path: err.path }, 409, req);
40
+ return;
41
+ }
42
+ throw err;
36
43
  }
37
44
  const emit = (e) => {
38
45
  if (!res.headersSent)
@@ -1 +1 @@
1
- {"version":3,"file":"promote.js","sourceRoot":"","sources":["../../../src/studio/routes/promote.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"promote.js","sourceRoot":"","sources":["../../../src/studio/routes/promote.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,14 @@
5
5
  // AC-US3-03/04 (provenance-gated delete — missing .vskill-meta.json → 400
6
6
  // {code:"no-provenance"}, no fs change; the original promote
7
7
  // op in ops-log is preserved — a new revert op is appended)
8
+ //
9
+ // Semantics: revert is mechanically an OWN-only delete. The SSE `destPath` /
10
+ // op-log `paths.dest` carry `provenance.sourcePath` as the LOGICAL target
11
+ // (where the skill should reappear once OWN is removed and the underlying
12
+ // installed/global scope becomes visible again). If that source has been
13
+ // uninstalled or modified between promote and revert, the user ends up with
14
+ // no skill at all — we record `sourceExists` in the op-log details so an
15
+ // audit can detect this case post-hoc.
8
16
  // ---------------------------------------------------------------------------
9
17
  import { randomUUID } from "node:crypto";
10
18
  import { homedir } from "node:os";
@@ -39,6 +47,9 @@ export function registerRevertRoute(router, root, home = homedir()) {
39
47
  // Capture target scope for logging BEFORE the delete.
40
48
  const toScope = provenance.promotedFrom;
41
49
  const filesDeleted = countFiles(ownDir);
50
+ // Record whether the logical reveal target still exists on disk.
51
+ // False = revert leaves the user with no copy of the skill anywhere.
52
+ const sourceExists = existsSync(provenance.sourcePath);
42
53
  emit({
43
54
  type: "started",
44
55
  opId,
@@ -62,7 +73,7 @@ export function registerRevertRoute(router, root, home = homedir()) {
62
73
  toScope,
63
74
  paths: { source: ownDir, dest: provenance.sourcePath },
64
75
  actor: "studio-ui",
65
- details: { filesDeleted },
76
+ details: { filesDeleted, sourceExists },
66
77
  };
67
78
  await appendOp(op);
68
79
  sendSSEDone(res, { type: "done", opId, destPath: ownDir });