spacecode 2.12.1 → 2.12.4

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.
package/dist/repl.mjs CHANGED
@@ -5792,6 +5792,10 @@ async function readSkills(fs6, home, report) {
5792
5792
  const path13 = `${root}/${dir}/${rel}`;
5793
5793
  const content = await readOptional(fs6, path13, report);
5794
5794
  if (content === null) continue;
5795
+ if (content.trim() === "") {
5796
+ report.notes.push(`Skill ${parsed.name}: ${rel} not imported (empty file); the skill still loads without it`);
5797
+ continue;
5798
+ }
5795
5799
  const size = Buffer.byteLength(content, "utf8");
5796
5800
  const name = siblingAssetName(parsed.name, rel);
5797
5801
  if (size > MAX_ASSET_BYTES || slugs.has(name) || siblings.length >= MAX_SKILL_FILES || content.includes("\0")) {
@@ -6904,6 +6908,164 @@ var init_mcp_rpc_pending = __esm({
6904
6908
  }
6905
6909
  });
6906
6910
 
6911
+ // ../agent/src/import/mcp-sse-parser.ts
6912
+ function createSseParser(onData) {
6913
+ let buffer = "";
6914
+ let dataLines = [];
6915
+ let skipLf = false;
6916
+ function handleLine(line) {
6917
+ if (line === "") {
6918
+ if (dataLines.length === 0) return;
6919
+ const payload = dataLines.join("\n");
6920
+ dataLines = [];
6921
+ onData(payload);
6922
+ return;
6923
+ }
6924
+ if (line.startsWith(":")) return;
6925
+ const colon = line.indexOf(":");
6926
+ if (colon === -1) {
6927
+ if (line === "data") dataLines.push("");
6928
+ return;
6929
+ }
6930
+ if (line.slice(0, colon) !== "data") return;
6931
+ const value = line.slice(colon + 1);
6932
+ dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
6933
+ }
6934
+ return {
6935
+ push(chunk) {
6936
+ buffer += chunk;
6937
+ let start = 0;
6938
+ for (let i = 0; i < buffer.length; i++) {
6939
+ const ch = buffer[i];
6940
+ if (skipLf && i === start && ch === "\n") {
6941
+ skipLf = false;
6942
+ start = i + 1;
6943
+ continue;
6944
+ }
6945
+ skipLf = false;
6946
+ if (ch === "\n") {
6947
+ handleLine(buffer.slice(start, i));
6948
+ start = i + 1;
6949
+ } else if (ch === "\r") {
6950
+ handleLine(buffer.slice(start, i));
6951
+ start = i + 1;
6952
+ if (buffer[i + 1] === "\n") i++, start++;
6953
+ else if (i + 1 === buffer.length) skipLf = true;
6954
+ }
6955
+ }
6956
+ buffer = buffer.slice(start);
6957
+ },
6958
+ end() {
6959
+ buffer = "";
6960
+ dataLines = [];
6961
+ skipLf = false;
6962
+ }
6963
+ };
6964
+ }
6965
+ var init_mcp_sse_parser = __esm({
6966
+ "../agent/src/import/mcp-sse-parser.ts"() {
6967
+ "use strict";
6968
+ }
6969
+ });
6970
+
6971
+ // ../agent/src/import/mcp-http-transport.ts
6972
+ function createHttpTransport(opts) {
6973
+ const aborter = new AbortController();
6974
+ let sessionId = null;
6975
+ let closed = false;
6976
+ let reported = false;
6977
+ const fail = (reason) => {
6978
+ if (closed || reported) return;
6979
+ reported = true;
6980
+ closed = true;
6981
+ aborter.abort();
6982
+ opts.onClose(reason);
6983
+ };
6984
+ const deliver = (parsed) => {
6985
+ for (const m2 of Array.isArray(parsed) ? parsed : [parsed]) opts.onMessage(m2);
6986
+ };
6987
+ const headers = () => ({
6988
+ ...opts.headers,
6989
+ "content-type": "application/json",
6990
+ accept: "application/json, text/event-stream",
6991
+ "mcp-protocol-version": opts.protocolVersion,
6992
+ ...sessionId !== null ? { "mcp-session-id": sessionId } : {}
6993
+ });
6994
+ async function readStream(res) {
6995
+ if (!res.body) return;
6996
+ const decoder = new TextDecoder();
6997
+ const parser = createSseParser((data) => {
6998
+ let parsed;
6999
+ try {
7000
+ parsed = JSON.parse(data);
7001
+ } catch (err) {
7002
+ if (!(err instanceof SyntaxError)) throw err;
7003
+ return;
7004
+ }
7005
+ deliver(parsed);
7006
+ });
7007
+ for await (const chunk of res.body) parser.push(decoder.decode(chunk, { stream: true }));
7008
+ parser.push(decoder.decode());
7009
+ parser.end();
7010
+ }
7011
+ async function post(message) {
7012
+ const hadSession = sessionId !== null;
7013
+ let res;
7014
+ try {
7015
+ res = await opts.fetch(opts.url, { method: "POST", headers: headers(), body: JSON.stringify(message), signal: aborter.signal });
7016
+ } catch (err) {
7017
+ return fail(`request failed: ${err instanceof Error ? err.name : "error"}`);
7018
+ }
7019
+ if (closed) return;
7020
+ if (res.status === 401 || res.status === 403) return fail("needs authorization (OAuth is not supported)");
7021
+ if (res.status === 404 && hadSession) return fail("session expired");
7022
+ if (res.status >= 400) return fail(`HTTP ${res.status}`);
7023
+ const sid = res.headers.get("mcp-session-id");
7024
+ if (sessionId === null && sid) sessionId = sid;
7025
+ if (res.status === 202) return;
7026
+ const type = (res.headers.get("content-type") ?? "").toLowerCase();
7027
+ try {
7028
+ if (type.startsWith("text/event-stream")) await readStream(res);
7029
+ else if (type.startsWith("application/json")) {
7030
+ let parsed;
7031
+ try {
7032
+ parsed = JSON.parse(await res.text());
7033
+ } catch (err) {
7034
+ if (!(err instanceof SyntaxError)) throw err;
7035
+ return fail("invalid JSON in response");
7036
+ }
7037
+ deliver(parsed);
7038
+ }
7039
+ } catch (err) {
7040
+ fail(`request failed: ${err instanceof Error ? err.name : "error"}`);
7041
+ }
7042
+ }
7043
+ return {
7044
+ send(message) {
7045
+ if (closed) throw new Error(CLOSED);
7046
+ void post(message);
7047
+ },
7048
+ async close() {
7049
+ if (closed) return;
7050
+ closed = true;
7051
+ aborter.abort();
7052
+ if (sessionId === null) return;
7053
+ try {
7054
+ await opts.fetch(opts.url, { method: "DELETE", headers: { ...opts.headers, "mcp-protocol-version": opts.protocolVersion, "mcp-session-id": sessionId } });
7055
+ } catch {
7056
+ }
7057
+ }
7058
+ };
7059
+ }
7060
+ var CLOSED;
7061
+ var init_mcp_http_transport = __esm({
7062
+ "../agent/src/import/mcp-http-transport.ts"() {
7063
+ "use strict";
7064
+ init_mcp_sse_parser();
7065
+ CLOSED = "MCP server connection is closed";
7066
+ }
7067
+ });
7068
+
6907
7069
  // ../agent/src/import/mcp-rpc.ts
6908
7070
  function createRpcSession(deps) {
6909
7071
  const pending = new PendingRequests();
@@ -7152,6 +7314,8 @@ var init_src = __esm({
7152
7314
  init_claude_json_mcp();
7153
7315
  init_mcp_rpc_message();
7154
7316
  init_mcp_rpc_pending();
7317
+ init_mcp_sse_parser();
7318
+ init_mcp_http_transport();
7155
7319
  init_mcp_rpc();
7156
7320
  init_mcp_tool_result();
7157
7321
  init_mcp_client();
@@ -7626,7 +7790,7 @@ async function adaptInstructions(deps, original) {
7626
7790
  const existingOrig = await client.get("instructions", "global.orig");
7627
7791
  if (current && existingOrig && existingOrig.content === original && current.description === marker) {
7628
7792
  log(" unchanged instructions/global (already adapted from this source)");
7629
- return true;
7793
+ return "unchanged";
7630
7794
  }
7631
7795
  const input = prepareAdaptation(original);
7632
7796
  if (input.sections.length === 0) {
@@ -7733,7 +7897,8 @@ ${USAGE}`);
7733
7897
  const report = await readClaudeAssets({ fs: deps.fs, home: deps.homedir(), projectDirs });
7734
7898
  deps.log(`Claude Code setup (project "${projectKey(projectDirs[projectDirs.length - 1] ?? deps.cwd)}"):`);
7735
7899
  for (const n of report.notes) deps.log(` note: ${n}`);
7736
- for (const a of report.assets) deps.log(` ${dryRun ? "would import" : "found"} ${a.kind}/${a.name} (${a.content.length} chars) ${a.source}`);
7900
+ if (dryRun) for (const a of report.assets) deps.log(` would import ${a.kind}/${a.name} (${a.content.length} chars) ${a.source}`);
7901
+ else if (report.assets.length > 0) deps.log(` found ${report.assets.length} item(s): ${summarizeKinds(report.assets)}`);
7737
7902
  for (const e of report.errors) deps.log(` ERROR ${e.path}: ${e.message}`);
7738
7903
  if (report.assets.length === 0) {
7739
7904
  deps.log("Nothing to import.");
@@ -7762,8 +7927,10 @@ ${USAGE}`);
7762
7927
  for (const a of report.assets) {
7763
7928
  try {
7764
7929
  if (adapt && a.kind === "instructions" && a.name === "global") {
7765
- if (await adaptInstructions({ client, log: deps.log, confirm: deps.confirm, assumeYes: flags.includes("--yes") }, a.content)) {
7766
- imported++;
7930
+ const adapted = await adaptInstructions({ client, log: deps.log, confirm: deps.confirm, assumeYes: flags.includes("--yes") }, a.content);
7931
+ if (adapted) {
7932
+ if (adapted === "unchanged") skipped++;
7933
+ else imported++;
7767
7934
  continue;
7768
7935
  }
7769
7936
  }
@@ -7801,6 +7968,11 @@ ${USAGE}`);
7801
7968
  deps.log(`Done: ${imported} imported, ${skipped} unchanged${failed ? ", some items need attention (see above)" : ""}.`);
7802
7969
  return failed ? 1 : 0;
7803
7970
  }
7971
+ function summarizeKinds(assets) {
7972
+ const counts = /* @__PURE__ */ new Map();
7973
+ for (const a of assets) counts.set(a.kind, (counts.get(a.kind) ?? 0) + 1);
7974
+ return [...counts].map(([kind, n]) => `${n} ${kind}`).join(", ");
7975
+ }
7804
7976
  var USAGE;
7805
7977
  var init_import_claude = __esm({
7806
7978
  "src/import-claude.ts"() {
@@ -7809,7 +7981,7 @@ var init_import_claude = __esm({
7809
7981
  init_api_key();
7810
7982
  init_profile_runtime();
7811
7983
  init_import_adapt();
7812
- USAGE = "Usage: spacecode import-claude [--dry-run] [--overwrite] [--no-adapt] [--yes] [--restore-original]";
7984
+ USAGE = "Usage: spacecode import claude [--dry-run] [--overwrite] [--no-adapt] [--yes] [--restore-original]";
7813
7985
  }
7814
7986
  });
7815
7987
 
@@ -7924,6 +8096,18 @@ var init_stdio_transport = __esm({
7924
8096
  function approvalEnv(env3) {
7925
8097
  return Object.entries(env3).map(([k2, v2]) => `${k2}=${v2}`).sort();
7926
8098
  }
8099
+ function approvalKey(def) {
8100
+ const envNames = approvalEnv(def.env);
8101
+ return def.transport === "http" ? { command: `mcp-http:${def.url}`, args: [], envNames } : { command: def.command, args: def.args, envNames };
8102
+ }
8103
+ function isSafeMcpUrl(url) {
8104
+ try {
8105
+ const u3 = new URL(url);
8106
+ return u3.protocol === "https:" || u3.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(u3.hostname);
8107
+ } catch {
8108
+ return false;
8109
+ }
8110
+ }
7927
8111
  function mcpToolName(server, tool) {
7928
8112
  return `mcp__${sanitize2(server)}__${sanitize2(tool)}`;
7929
8113
  }
@@ -7981,11 +8165,10 @@ var init_mcp_manager = __esm({
7981
8165
  }
7982
8166
  for (const def of this.deps.defs) {
7983
8167
  try {
7984
- if (def.transport !== "stdio") continue;
7985
- const envNames = approvalEnv(def.env);
7986
- if (!await this.deps.isStdioApproved({ command: def.command, args: def.args, envNames })) continue;
7987
- if (!expandServerEnv({ command: def.command, args: def.args, env: def.env }, this.deps.env).ok) continue;
7988
- const hit = getCachedTools(this.cache, serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames }, tc.hash));
8168
+ const key = approvalKey(def);
8169
+ if (!await this.deps.isStdioApproved(key)) continue;
8170
+ if (!expandServerEnv({ command: def.transport === "http" ? def.url : def.command, args: def.args, env: def.env }, this.deps.env).ok) continue;
8171
+ const hit = getCachedTools(this.cache, serverCacheKey({ name: def.name, ...key }, tc.hash));
7989
8172
  if (hit) this.cachedTools.set(def.name, this.usable(def.name, hit).tools);
7990
8173
  } catch (err) {
7991
8174
  this.cacheNotes.push(`${def.name}: cached tools skipped (${err instanceof Error ? err.message : String(err)})`);
@@ -8038,7 +8221,7 @@ var init_mcp_manager = __esm({
8038
8221
  if (!tc) return Promise.resolve();
8039
8222
  this.cacheWrite = this.cacheWrite.then(async () => {
8040
8223
  try {
8041
- const key = serverCacheKey({ name: def.name, command: def.command, args: def.args, envNames: approvalEnv(def.env) }, tc.hash);
8224
+ const key = serverCacheKey({ name: def.name, ...approvalKey(def) }, tc.hash);
8042
8225
  const next = putCachedTools(this.cache, key, tools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })));
8043
8226
  if (next === this.cache) return;
8044
8227
  await tc.write(serializeToolCache(next));
@@ -8053,38 +8236,56 @@ var init_mcp_manager = __esm({
8053
8236
  const name = def.name;
8054
8237
  const skip = (reason) => void this.servers.set(name, { name, def, status: "skipped", reason });
8055
8238
  const fail = (reason) => void this.servers.set(name, { name, def, status: "failed", reason });
8056
- if (def.transport !== "stdio") return skip("http servers are not supported yet");
8057
- const envNames = approvalEnv(def.env);
8239
+ const http = def.transport === "http";
8240
+ if (http && !isSafeMcpUrl(def.url)) return skip("its URL must be https (or localhost)");
8241
+ const key = approvalKey(def);
8058
8242
  let approved;
8059
8243
  try {
8060
- approved = await this.deps.isStdioApproved({ command: def.command, args: def.args, envNames });
8244
+ approved = await this.deps.isStdioApproved(key);
8061
8245
  } catch (err) {
8062
8246
  return fail(`approval check failed: ${err instanceof Error ? err.message : String(err)}`);
8063
8247
  }
8064
- if (!approved) return skip(`its command is not approved on this machine (${def.origin} definition)`);
8065
- const expanded = expandServerEnv({ command: def.command, args: def.args, env: def.env }, this.deps.env);
8248
+ if (!approved) return skip(`its ${http ? "URL and headers are" : "command is"} not approved on this machine (${def.origin} definition)`);
8249
+ const expanded = expandServerEnv({ command: http ? def.url : def.command, args: def.args, env: def.env }, this.deps.env);
8066
8250
  if (!expanded.ok) return skip(`set ${expanded.missing.join(", ")} in your environment`);
8251
+ if (http && !isSafeMcpUrl(expanded.command)) return skip("its URL must be https (or localhost)");
8067
8252
  this.servers.set(name, { name, def, status: "connecting" });
8068
8253
  let transport;
8069
8254
  try {
8070
8255
  let session2;
8071
8256
  let closeReason = null;
8072
- transport = (this.deps.start ?? startStdioTransport)({
8073
- command: expanded.command,
8074
- args: expanded.args,
8075
- env: expanded.env,
8076
- cwd: this.deps.cwd,
8077
- onMessage: (m2) => void session2.handleMessage(m2),
8078
- onClose: (reason) => {
8079
- closeReason = reason;
8080
- session2.close(reason);
8081
- const cur = this.servers.get(name);
8082
- if (cur?.status === "ready") this.servers.set(name, { name, def, status: "failed", reason: `server ${reason}` });
8083
- }
8084
- });
8085
- const t = transport;
8257
+ const onClose = (reason) => {
8258
+ closeReason = reason;
8259
+ session2.close(reason);
8260
+ const cur = this.servers.get(name);
8261
+ if (cur?.status === "ready") this.servers.set(name, { name, def, status: "failed", reason: `server ${reason}` });
8262
+ };
8263
+ let send;
8264
+ if (http) {
8265
+ const t = createHttpTransport({
8266
+ url: expanded.command,
8267
+ headers: expanded.env,
8268
+ fetch: this.deps.fetch ?? ((url, init) => fetch(url, { ...init, redirect: "error" })),
8269
+ protocolVersion: MCP_PROTOCOL_VERSION,
8270
+ onMessage: (m2) => void session2.handleMessage(m2),
8271
+ onClose
8272
+ });
8273
+ transport = t;
8274
+ send = (m2) => t.send(m2);
8275
+ } else {
8276
+ const t = (this.deps.start ?? startStdioTransport)({
8277
+ command: expanded.command,
8278
+ args: expanded.args,
8279
+ env: expanded.env,
8280
+ cwd: this.deps.cwd,
8281
+ onMessage: (m2) => void session2.handleMessage(m2),
8282
+ onClose
8283
+ });
8284
+ transport = t;
8285
+ send = (m2) => t.send(m2);
8286
+ }
8086
8287
  session2 = createRpcSession({
8087
- send: (m2) => t.send(m2),
8288
+ send,
8088
8289
  setTimer: (fn, ms) => setTimeout(fn, ms),
8089
8290
  clearTimer: (h) => clearTimeout(h)
8090
8291
  });
@@ -8208,10 +8409,10 @@ var init_mcp_manager = __esm({
8208
8409
  async function runApproveMcp(deps) {
8209
8410
  const { defs, statusLines } = await loadMcpDefs(deps);
8210
8411
  if (!deps.quiet) for (const l of statusLines) deps.log(l);
8211
- const stdio = defs.filter((d) => d.transport === "stdio");
8412
+ const stdio = defs;
8212
8413
  const todo = [];
8213
8414
  for (const d of stdio) {
8214
- const key = { command: d.command, args: d.args, envNames: approvalEnv(d.env) };
8415
+ const key = approvalKey(d);
8215
8416
  if (await deps.trust.stdioStatus(key) === "unapproved") todo.push(d);
8216
8417
  }
8217
8418
  if (todo.length === 0) {
@@ -8226,13 +8427,18 @@ async function runApproveMcp(deps) {
8226
8427
  ${todo.length} imported MCP server(s) want to run a command on this machine. Nothing spawns until you approve it (you can also run \`spacecode approve-mcp\` later).`);
8227
8428
  let approved = 0;
8228
8429
  for (const d of todo) {
8229
- deps.log(`
8430
+ deps.log(
8431
+ d.transport === "http" ? `
8432
+ MCP server ${show(d.name)} (${d.origin} definition) wants to connect from this machine to:
8433
+ ${show(d.url)}
8434
+ headers it will send (NAME=where the value comes from): ${approvalEnv(d.env).join(", ") || "(none)"}` : `
8230
8435
  MCP server ${show(d.name)} (${d.origin} definition) wants to run on this machine:
8231
8436
  ${show(d.command)} ${d.args.map(show).join(" ")}
8232
- environment it will receive (NAME=where the value comes from): ${approvalEnv(d.env).join(", ") || "(none)"}`);
8233
- const answer = (await deps.ask("Approve this command? [y/N] ") ?? "").trim().toLowerCase();
8437
+ environment it will receive (NAME=where the value comes from): ${approvalEnv(d.env).join(", ") || "(none)"}`
8438
+ );
8439
+ const answer = (await deps.ask("Approve? [y/N] ") ?? "").trim().toLowerCase();
8234
8440
  if (answer === "y" || answer === "yes") {
8235
- await deps.trust.approveStdio({ command: d.command, args: d.args, envNames: approvalEnv(d.env) });
8441
+ await deps.trust.approveStdio(approvalKey(d));
8236
8442
  deps.log(" approved");
8237
8443
  approved++;
8238
8444
  } else deps.log(" not approved");
@@ -35864,7 +36070,7 @@ var require_websocket_server = __commonJS({
35864
36070
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
35865
36071
  var RUNNING = 0;
35866
36072
  var CLOSING = 1;
35867
- var CLOSED = 2;
36073
+ var CLOSED2 = 2;
35868
36074
  var WebSocketServer2 = class extends EventEmitter3 {
35869
36075
  /**
35870
36076
  * Create a `WebSocketServer` instance.
@@ -35993,7 +36199,7 @@ var require_websocket_server = __commonJS({
35993
36199
  * @public
35994
36200
  */
35995
36201
  close(cb) {
35996
- if (this._state === CLOSED) {
36202
+ if (this._state === CLOSED2) {
35997
36203
  if (cb) {
35998
36204
  this.once("close", () => {
35999
36205
  cb(new Error("The server is not running"));
@@ -36217,7 +36423,7 @@ var require_websocket_server = __commonJS({
36217
36423
  };
36218
36424
  }
36219
36425
  function emitClose(server) {
36220
- server._state = CLOSED;
36426
+ server._state = CLOSED2;
36221
36427
  server.emit("close");
36222
36428
  }
36223
36429
  function socketOnError() {
@@ -104173,7 +104379,7 @@ Skills are loaded on demand with the skill tool; bodies are not in the prompt. /
104173
104379
  (a) => a.kind === "skill" && (a.name === name || a.name.startsWith(`${name}.`) && a.description.startsWith(SIBLING_PREFIX))
104174
104380
  );
104175
104381
  for (const a of assets) await deps.client.delete("skill", a.name);
104176
- return `Deleted skill "${name}" and ${assets.length - 1} file(s) from the profile. Re-running import-claude would import it again.`;
104382
+ return `Deleted skill "${name}" and ${assets.length - 1} file(s) from the profile. Re-running "spacecode import claude" would import it again.`;
104177
104383
  }
104178
104384
  return "Usage: /skills | /skills show <name> [file] | /skills delete <name>";
104179
104385
  }
@@ -105893,7 +106099,13 @@ function clearOnResize(stdout) {
105893
106099
 
105894
106100
  // src/index.tsx
105895
106101
  var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
105896
- if (process.argv[2] === "import-claude") {
106102
+ if (process.argv[2] === "import" || process.argv[2] === "import-claude") {
106103
+ const importArgs = process.argv[2] === "import" ? process.argv.slice(4) : process.argv.slice(3);
106104
+ const importAgent = process.argv[2] === "import" ? process.argv[3] : "claude";
106105
+ if (importAgent !== "claude") {
106106
+ console.error(`Unknown agent "${importAgent ?? ""}". Supported: claude`);
106107
+ process.exit(2);
106108
+ }
105897
106109
  const { runImportClaude: runImportClaude2 } = await Promise.resolve().then(() => (init_import_claude(), import_claude_exports));
105898
106110
  const { readFile: readFile12, readdir: readdir6 } = await import("node:fs/promises");
105899
106111
  const { execFile: execFile6 } = await import("node:child_process");
@@ -105901,7 +106113,7 @@ if (process.argv[2] === "import-claude") {
105901
106113
  execFile6("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() }, (err, stdout) => resolve2(err ? null : stdout.trim() || null));
105902
106114
  });
105903
106115
  const code = await runImportClaude2({
105904
- argv: process.argv.slice(2),
106116
+ argv: ["import-claude", ...importArgs],
105905
106117
  env: process.env,
105906
106118
  fs: { readFile: (p) => readFile12(p, "utf-8"), readdir: (p) => readdir6(p) },
105907
106119
  readFile: (p) => readFile12(p, "utf-8"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecode",
3
- "version": "2.12.1",
3
+ "version": "2.12.4",
4
4
  "description": "SpaceCode - launch cloud dev environments with AI coding agents",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",