svamp-cli 0.2.80 → 0.2.82

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.
@@ -1,8 +1,8 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import os$1 from 'os';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import os from 'os';
2
2
  import fs, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as writeFile$1, rename, unlink } from 'fs/promises';
3
3
  import { readFileSync as readFileSync$1, mkdirSync, writeFileSync, renameSync, existsSync as existsSync$1, copyFileSync, unlinkSync as unlinkSync$1, watch, rmdirSync, readdirSync } from 'fs';
4
4
  import path__default, { join, dirname, resolve, basename } from 'path';
5
- import { fileURLToPath } from 'url';
5
+ import { URL as URL$1, fileURLToPath } from 'url';
6
6
  import { execFile, spawn as spawn$1, execSync as execSync$1 } from 'child_process';
7
7
  import { randomUUID as randomUUID$1 } from 'crypto';
8
8
  import { existsSync, readFileSync, writeFileSync as writeFileSync$1, mkdirSync as mkdirSync$1, appendFileSync, unlinkSync } from 'node:fs';
@@ -11,7 +11,7 @@ import { randomUUID, createHash } from 'node:crypto';
11
11
  import { join as join$1 } from 'node:path';
12
12
  import { spawn, execSync, execFile as execFile$1, execFileSync } from 'node:child_process';
13
13
  import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
14
- import os, { homedir, platform } from 'node:os';
14
+ import os$1, { homedir, platform } from 'node:os';
15
15
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
16
16
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
17
17
  import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
@@ -63,7 +63,7 @@ function parseWorkspaceFromToken(token) {
63
63
  return void 0;
64
64
  }
65
65
  }
66
- function getHyphaServerUrl() {
66
+ function getHyphaServerUrl$1() {
67
67
  return process.env.HYPHA_SERVER_URL || "http://localhost:9527";
68
68
  }
69
69
 
@@ -1393,7 +1393,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
1393
1393
  const tunnels = handlers.tunnels;
1394
1394
  if (!tunnels) throw new Error("Tunnel management not available");
1395
1395
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
1396
- const { FrpcTunnel } = await import('./frpc-j60b46eU.mjs');
1396
+ const { FrpcTunnel } = await import('./frpc-DPfGAdlj.mjs');
1397
1397
  const tunnel = new FrpcTunnel({
1398
1398
  name: params.name,
1399
1399
  ports: params.ports,
@@ -5350,13 +5350,33 @@ function shouldIsolate(input) {
5350
5350
  return input.optionsSecurityContext !== null && input.optionsSecurityContext !== void 0;
5351
5351
  }
5352
5352
 
5353
- const HYPHA_PROXY_BASE_URL = "https://proxy.hypha.aicell.io";
5353
+ const EXAMPLE_HYPHA_PROXY_URL = "https://proxy.hypha.aicell.io";
5354
5354
  const MODE_KEY = "SVAMP_CLAUDE_PROXY";
5355
+ const HYPHA_PROXY_URL_KEY = "SVAMP_HYPHA_PROXY_URL";
5355
5356
  const MANAGED_KEYS = /* @__PURE__ */ new Set([
5356
5357
  MODE_KEY,
5358
+ HYPHA_PROXY_URL_KEY,
5357
5359
  "ANTHROPIC_BASE_URL",
5358
5360
  "ANTHROPIC_API_KEY"
5359
5361
  ]);
5362
+ function normalizeProxyUrl(raw) {
5363
+ const trimmed = raw.trim().replace(/\/+$/, "");
5364
+ if (trimmed.endsWith("/v1")) {
5365
+ throw new Error(
5366
+ "Hypha proxy URL must not end in /v1 \u2014 the SDK appends it, producing double /v1/v1/messages"
5367
+ );
5368
+ }
5369
+ return trimmed;
5370
+ }
5371
+ function resolveHyphaProxyUrl() {
5372
+ const configured = (process.env[HYPHA_PROXY_URL_KEY] || "").trim();
5373
+ if (!configured) return void 0;
5374
+ try {
5375
+ return normalizeProxyUrl(configured);
5376
+ } catch {
5377
+ return void 0;
5378
+ }
5379
+ }
5360
5380
  function envFilePath() {
5361
5381
  const svampHome = process.env.SVAMP_HOME || join$1(homedir(), ".svamp");
5362
5382
  return join$1(svampHome, ".env");
@@ -5417,11 +5437,13 @@ function getClaudeAuthStatus() {
5417
5437
  const mode = currentMode();
5418
5438
  if (mode === "hypha") {
5419
5439
  const token = process.env.HYPHA_TOKEN;
5440
+ const proxyUrl = resolveHyphaProxyUrl();
5420
5441
  return {
5421
5442
  mode,
5422
- baseUrl: HYPHA_PROXY_BASE_URL,
5443
+ baseUrl: proxyUrl,
5423
5444
  apiKeyPreview: redactKey(token),
5424
- hyphaTokenMissing: !token
5445
+ hyphaTokenMissing: !token,
5446
+ hyphaProxyUrlMissing: !proxyUrl
5425
5447
  };
5426
5448
  }
5427
5449
  if (mode === "custom") {
@@ -5433,9 +5455,15 @@ function getClaudeAuthStatus() {
5433
5455
  }
5434
5456
  return { mode: "login" };
5435
5457
  }
5436
- function setClaudeAuthHyphaProxy() {
5458
+ function setClaudeAuthHyphaProxy(url) {
5459
+ if (!url || !url.trim()) {
5460
+ throw new Error(
5461
+ `A Hypha proxy URL is required (no default). Example: svamp daemon auth use-hypha-proxy ${EXAMPLE_HYPHA_PROXY_URL}`
5462
+ );
5463
+ }
5437
5464
  updateEnvFile({
5438
5465
  [MODE_KEY]: "hypha",
5466
+ [HYPHA_PROXY_URL_KEY]: normalizeProxyUrl(url),
5439
5467
  // Hypha mode resolves token live at spawn — no stored copy.
5440
5468
  ANTHROPIC_BASE_URL: void 0,
5441
5469
  ANTHROPIC_API_KEY: void 0
@@ -5444,6 +5472,7 @@ function setClaudeAuthHyphaProxy() {
5444
5472
  function setClaudeAuthLogin() {
5445
5473
  updateEnvFile({
5446
5474
  [MODE_KEY]: void 0,
5475
+ [HYPHA_PROXY_URL_KEY]: void 0,
5447
5476
  ANTHROPIC_BASE_URL: void 0,
5448
5477
  ANTHROPIC_API_KEY: void 0
5449
5478
  });
@@ -5459,6 +5488,7 @@ function setClaudeAuthCustom(baseUrl, apiKey) {
5459
5488
  }
5460
5489
  updateEnvFile({
5461
5490
  [MODE_KEY]: "custom",
5491
+ [HYPHA_PROXY_URL_KEY]: void 0,
5462
5492
  ANTHROPIC_BASE_URL: trimmed,
5463
5493
  ANTHROPIC_API_KEY: apiKey
5464
5494
  });
@@ -5466,15 +5496,21 @@ function setClaudeAuthCustom(baseUrl, apiKey) {
5466
5496
  function applyClaudeProxyEnv(spawnEnv) {
5467
5497
  const mode = currentMode();
5468
5498
  if (mode === "hypha") {
5499
+ const proxyUrl = resolveHyphaProxyUrl();
5500
+ if (!proxyUrl) {
5501
+ delete spawnEnv.ANTHROPIC_BASE_URL;
5502
+ delete spawnEnv.ANTHROPIC_API_KEY;
5503
+ return `hypha mode but ${HYPHA_PROXY_URL_KEY} not configured \u2014 run \`svamp daemon auth use-hypha-proxy <URL>\`; falling back to login`;
5504
+ }
5469
5505
  const token = process.env.HYPHA_TOKEN;
5470
5506
  if (!token) {
5471
5507
  delete spawnEnv.ANTHROPIC_BASE_URL;
5472
5508
  delete spawnEnv.ANTHROPIC_API_KEY;
5473
5509
  return "hypha mode but HYPHA_TOKEN missing \u2014 falling back to login";
5474
5510
  }
5475
- spawnEnv.ANTHROPIC_BASE_URL = HYPHA_PROXY_BASE_URL;
5511
+ spawnEnv.ANTHROPIC_BASE_URL = proxyUrl;
5476
5512
  spawnEnv.ANTHROPIC_API_KEY = token;
5477
- return `hypha proxy (${HYPHA_PROXY_BASE_URL})`;
5513
+ return `hypha proxy (${proxyUrl})`;
5478
5514
  }
5479
5515
  if (mode === "custom") {
5480
5516
  const url = process.env.ANTHROPIC_BASE_URL;
@@ -5496,21 +5532,68 @@ const MANAGED_ENV_KEYS = Array.from(MANAGED_KEYS);
5496
5532
 
5497
5533
  var claudeAuth = /*#__PURE__*/Object.freeze({
5498
5534
  __proto__: null,
5499
- HYPHA_PROXY_BASE_URL: HYPHA_PROXY_BASE_URL,
5535
+ EXAMPLE_HYPHA_PROXY_URL: EXAMPLE_HYPHA_PROXY_URL,
5500
5536
  MANAGED_ENV_KEYS: MANAGED_ENV_KEYS,
5501
5537
  applyClaudeProxyEnv: applyClaudeProxyEnv,
5502
5538
  getClaudeAuthStatus: getClaudeAuthStatus,
5539
+ resolveHyphaProxyUrl: resolveHyphaProxyUrl,
5503
5540
  setClaudeAuthCustom: setClaudeAuthCustom,
5504
5541
  setClaudeAuthHyphaProxy: setClaudeAuthHyphaProxy,
5505
5542
  setClaudeAuthLogin: setClaudeAuthLogin,
5506
5543
  updateEnvFile: updateEnvFile
5507
5544
  });
5508
5545
 
5509
- const DEFAULT_WEB_BASE = "https://svamp.hypha.aicell.io";
5546
+ function getHyphaServerUrl() {
5547
+ const raw = (process.env.HYPHA_SERVER_URL || "").trim();
5548
+ return raw ? raw.replace(/\/+$/, "") : void 0;
5549
+ }
5550
+ function getHyphaHostname() {
5551
+ const url = getHyphaServerUrl();
5552
+ if (!url) return void 0;
5553
+ try {
5554
+ return new URL$1(url).hostname || void 0;
5555
+ } catch {
5556
+ return void 0;
5557
+ }
5558
+ }
5559
+ function deriveOrThrow(prefix, envVar, purpose) {
5560
+ const host = getHyphaHostname();
5561
+ if (host) return prefix ? `${prefix}.${host}` : host;
5562
+ throw new Error(
5563
+ `Cannot determine ${purpose}: set ${envVar} explicitly, or set HYPHA_SERVER_URL so it can be derived. No hostname is hardcoded as a fallback.`
5564
+ );
5565
+ }
5566
+ function getFrpsSubdomainHost() {
5567
+ const explicit = (process.env.FRPS_SUBDOMAIN_HOST || "").trim();
5568
+ if (explicit) return explicit;
5569
+ return deriveOrThrow("svc", "FRPS_SUBDOMAIN_HOST", "frps subdomain host");
5570
+ }
5571
+ function getFrpsServerAddr() {
5572
+ const explicit = (process.env.FRPS_SERVER_ADDR || "").trim();
5573
+ if (explicit) return explicit;
5574
+ return `frps-ctrl.${getFrpsSubdomainHost()}`;
5575
+ }
5576
+ function getSvampWebBaseUrl() {
5577
+ const explicit = (process.env.SVAMP_WEB_URL || "").trim();
5578
+ if (explicit) return explicit.replace(/\/+$/, "");
5579
+ const host = getHyphaHostname();
5580
+ if (host) return `https://svamp.${host}`;
5581
+ throw new Error(
5582
+ "Cannot determine Svamp web base URL: set SVAMP_WEB_URL explicitly, or set HYPHA_SERVER_URL so it can be derived (https://svamp.<host>). No hostname is hardcoded as a fallback."
5583
+ );
5584
+ }
5585
+ function getSkillsServerUrl() {
5586
+ const explicit = (process.env.HYPHA_SKILLS_SERVER || "").trim();
5587
+ if (explicit) return explicit.replace(/\/+$/, "");
5588
+ const server = getHyphaServerUrl();
5589
+ if (server) return server;
5590
+ throw new Error(
5591
+ "Cannot determine skills marketplace server: set HYPHA_SKILLS_SERVER or HYPHA_SERVER_URL. No hostname is hardcoded as a fallback."
5592
+ );
5593
+ }
5594
+
5510
5595
  function getShareBaseUrl() {
5511
- const fromEnv = (process.env.SVAMP_WEB_URL || "").trim();
5512
- const base = fromEnv || DEFAULT_WEB_BASE;
5513
- return base.replace(/\/+$/, "");
5596
+ return getSvampWebBaseUrl();
5514
5597
  }
5515
5598
  function buildSessionShareUrl(input) {
5516
5599
  const params = new URLSearchParams();
@@ -5893,6 +5976,170 @@ function escapeHtml(s) {
5893
5976
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5894
5977
  }
5895
5978
 
5979
+ const SKILLS_WORKSPACE = process.env.HYPHA_SKILLS_WORKSPACE || "hypha-cloud";
5980
+ const SKILLS_COLLECTION = process.env.HYPHA_SKILLS_COLLECTION || "marketplace";
5981
+ const SKILLS_DIR = join(os.homedir(), ".claude", "skills");
5982
+ function getSkillsServer() {
5983
+ return getSkillsServerUrl();
5984
+ }
5985
+ function getArtifactBaseUrl() {
5986
+ return `${getSkillsServer()}/${SKILLS_WORKSPACE}/artifacts`;
5987
+ }
5988
+ async function fetchWithTimeout(url, options = {}, timeoutMs = 3e4) {
5989
+ const controller = new AbortController();
5990
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
5991
+ try {
5992
+ return await fetch(url, { ...options, signal: controller.signal });
5993
+ } catch (err) {
5994
+ if (err.name === "AbortError") throw new Error(`Request timed out after ${timeoutMs}ms: ${url}`);
5995
+ throw err;
5996
+ } finally {
5997
+ clearTimeout(timer);
5998
+ }
5999
+ }
6000
+ function parseFrontmatter(content) {
6001
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
6002
+ if (!match) return null;
6003
+ const yaml = match[1];
6004
+ const result = {};
6005
+ let currentKey = null;
6006
+ let nestedObj = null;
6007
+ for (const line of yaml.split("\n")) {
6008
+ if (!line.trim() || line.trim().startsWith("#")) continue;
6009
+ const indentMatch = line.match(/^(\s*)/);
6010
+ const indent = indentMatch ? indentMatch[1].length : 0;
6011
+ if (indent > 0 && currentKey && nestedObj !== null) {
6012
+ const kvMatch2 = line.trim().match(/^([^:]+):\s*(.*)$/);
6013
+ if (kvMatch2) {
6014
+ nestedObj[kvMatch2[1].trim()] = kvMatch2[2].trim().replace(/^["']|["']$/g, "");
6015
+ }
6016
+ continue;
6017
+ }
6018
+ if (nestedObj !== null && currentKey) {
6019
+ result[currentKey] = nestedObj;
6020
+ nestedObj = null;
6021
+ currentKey = null;
6022
+ }
6023
+ const kvMatch = line.match(/^([^:]+):\s*(.*)$/);
6024
+ if (!kvMatch) continue;
6025
+ const key = kvMatch[1].trim();
6026
+ const value = kvMatch[2].trim();
6027
+ if (!value) {
6028
+ currentKey = key;
6029
+ nestedObj = {};
6030
+ } else {
6031
+ result[key] = value.replace(/^["']|["']$/g, "");
6032
+ }
6033
+ }
6034
+ if (nestedObj !== null && currentKey) {
6035
+ result[currentKey] = nestedObj;
6036
+ }
6037
+ if (!result.name || typeof result.name !== "string") return null;
6038
+ if (!result.description || typeof result.description !== "string") return null;
6039
+ return result;
6040
+ }
6041
+ async function searchSkills(query) {
6042
+ const base = getArtifactBaseUrl();
6043
+ const filters = encodeURIComponent(JSON.stringify({ type: "skill" }));
6044
+ const url = `${base}/${SKILLS_COLLECTION}/children?keywords=${encodeURIComponent(query)}&filters=${filters}&limit=50`;
6045
+ const resp = await fetchWithTimeout(url);
6046
+ if (!resp.ok) {
6047
+ if (resp.status === 404) return [];
6048
+ throw new Error(`Search failed: ${resp.status} ${resp.statusText}`);
6049
+ }
6050
+ const data = await resp.json();
6051
+ return normalizeSkillList(Array.isArray(data) ? data : data.items || []);
6052
+ }
6053
+ async function listAllSkills(offset = 0, limit = 100) {
6054
+ const base = getArtifactBaseUrl();
6055
+ const filters = encodeURIComponent(JSON.stringify({ type: "skill" }));
6056
+ const url = `${base}/${SKILLS_COLLECTION}/children?filters=${filters}&offset=${offset}&limit=${limit}`;
6057
+ const resp = await fetchWithTimeout(url);
6058
+ if (!resp.ok) {
6059
+ if (resp.status === 404) return [];
6060
+ throw new Error(`List failed: ${resp.status} ${resp.statusText}`);
6061
+ }
6062
+ const data = await resp.json();
6063
+ return normalizeSkillList(Array.isArray(data) ? data : data.items || []);
6064
+ }
6065
+ async function getSkillInfo(skillAlias) {
6066
+ const base = getArtifactBaseUrl();
6067
+ const url = `${base}/${skillAlias}`;
6068
+ const resp = await fetchWithTimeout(url);
6069
+ if (!resp.ok) {
6070
+ if (resp.status === 404) return null;
6071
+ throw new Error(`Get skill failed: ${resp.status} ${resp.statusText}`);
6072
+ }
6073
+ const data = await resp.json();
6074
+ return normalizeSkill(data);
6075
+ }
6076
+ async function listSkillFiles(skillAlias, dir = "") {
6077
+ const base = getArtifactBaseUrl();
6078
+ const pathPart = dir ? `/${dir}/` : "/";
6079
+ const url = `${base}/${skillAlias}/files${pathPart}`;
6080
+ const resp = await fetchWithTimeout(url);
6081
+ if (!resp.ok) {
6082
+ throw new Error(`List files failed: ${resp.status} ${resp.statusText}`);
6083
+ }
6084
+ const data = await resp.json();
6085
+ return Array.isArray(data) ? data : data.items || [];
6086
+ }
6087
+ async function downloadSkillFile(skillAlias, filePath) {
6088
+ const base = getArtifactBaseUrl();
6089
+ const url = `${base}/${skillAlias}/files/${filePath}`;
6090
+ const resp = await fetchWithTimeout(url, { redirect: "follow" }, 6e4);
6091
+ if (!resp.ok) {
6092
+ throw new Error(`Download failed for ${filePath}: ${resp.status} ${resp.statusText}`);
6093
+ }
6094
+ return await resp.text();
6095
+ }
6096
+ async function downloadSkillZip(skillAlias) {
6097
+ const base = getArtifactBaseUrl();
6098
+ const url = `${base}/${skillAlias}/create-zip-file`;
6099
+ const resp = await fetchWithTimeout(url, {}, 12e4);
6100
+ if (!resp.ok) {
6101
+ throw new Error(`Zip download failed: ${resp.status} ${resp.statusText}`);
6102
+ }
6103
+ const ab = await resp.arrayBuffer();
6104
+ return Buffer.from(ab);
6105
+ }
6106
+ function normalizeSkillList(items) {
6107
+ return items.map(normalizeSkill).filter((s) => s !== null);
6108
+ }
6109
+ function normalizeSkill(item) {
6110
+ if (!item) return null;
6111
+ const manifest = item.manifest || {};
6112
+ return {
6113
+ alias: item.alias || manifest.name || item.id || "unknown",
6114
+ name: manifest.name || item.alias || "unknown",
6115
+ description: manifest.description || "",
6116
+ author: manifest.metadata?.author || manifest.author,
6117
+ version: manifest.metadata?.version || manifest.version,
6118
+ tags: manifest.tags,
6119
+ created_at: item.created_at,
6120
+ last_modified: item.last_modified,
6121
+ versions: item.versions,
6122
+ git_url: item.git_url
6123
+ };
6124
+ }
6125
+
6126
+ var api = /*#__PURE__*/Object.freeze({
6127
+ __proto__: null,
6128
+ SKILLS_COLLECTION: SKILLS_COLLECTION,
6129
+ SKILLS_DIR: SKILLS_DIR,
6130
+ SKILLS_WORKSPACE: SKILLS_WORKSPACE,
6131
+ downloadSkillFile: downloadSkillFile,
6132
+ downloadSkillZip: downloadSkillZip,
6133
+ fetchWithTimeout: fetchWithTimeout,
6134
+ getArtifactBaseUrl: getArtifactBaseUrl,
6135
+ getSkillInfo: getSkillInfo,
6136
+ getSkillsServer: getSkillsServer,
6137
+ listAllSkills: listAllSkills,
6138
+ listSkillFiles: listSkillFiles,
6139
+ parseFrontmatter: parseFrontmatter,
6140
+ searchSkills: searchSkills
6141
+ });
6142
+
5896
6143
  const DEFAULT_PROBE_INTERVAL_S = 10;
5897
6144
  const DEFAULT_PROBE_TIMEOUT_S = 5;
5898
6145
  const DEFAULT_PROBE_FAILURE_THRESHOLD = 3;
@@ -6617,7 +6864,7 @@ function resolveContextWindow(opts) {
6617
6864
  return candidate;
6618
6865
  }
6619
6866
 
6620
- const SVAMP_HOME$1 = process.env.SVAMP_HOME || join$1(os.homedir(), ".svamp");
6867
+ const SVAMP_HOME$1 = process.env.SVAMP_HOME || join$1(os$1.homedir(), ".svamp");
6621
6868
  function generateHookSettings(portOrOptions = {}) {
6622
6869
  const opts = typeof portOrOptions === "number" ? { sessionStartPort: portOrOptions } : portOrOptions;
6623
6870
  const hooksDir = join$1(SVAMP_HOME$1, "tmp", "hooks");
@@ -6811,7 +7058,7 @@ function checkTruncation(format, tail, fileSize, head) {
6811
7058
 
6812
7059
  const __filename$1 = fileURLToPath(import.meta.url);
6813
7060
  const __dirname$1 = dirname(__filename$1);
6814
- const CLAUDE_SKILLS_DIR = join(os$1.homedir(), ".claude", "skills");
7061
+ const CLAUDE_SKILLS_DIR = join(os.homedir(), ".claude", "skills");
6815
7062
  function looksLikeClaudeError(line) {
6816
7063
  const l = line.toLowerCase();
6817
7064
  return l.includes("api error") || l.includes("request rejected") || l.includes("error:") || l.includes("overloaded") || l.includes("rate limit") || l.includes("unauthorized") || l.includes("forbidden") || /\b(4\d\d|5\d\d)\b/.test(l) && (l.includes("status") || l.includes("error") || l.includes("rejected"));
@@ -6892,7 +7139,7 @@ async function installSkillFromEndpoint(name, baseUrl) {
6892
7139
  }
6893
7140
  }
6894
7141
  async function installSkillFromMarketplace(name) {
6895
- const BASE = `https://hypha.aicell.io/hypha-cloud/artifacts/${name}`;
7142
+ const BASE = `${getArtifactBaseUrl()}/${name}`;
6896
7143
  async function collectFiles(dir = "") {
6897
7144
  const url = dir ? `${BASE}/files/${dir}` : `${BASE}/files/`;
6898
7145
  const resp = await fetch(url, { signal: AbortSignal.timeout(15e3) });
@@ -7013,7 +7260,7 @@ function preventMachineSleep(logger) {
7013
7260
  }
7014
7261
  async function fetchMarketplaceSkillVersion(name) {
7015
7262
  try {
7016
- const url = `https://hypha.aicell.io/hypha-cloud/artifacts/${name}/files/SKILL.md`;
7263
+ const url = `${getArtifactBaseUrl()}/${name}/files/SKILL.md`;
7017
7264
  const resp = await fetch(url, { signal: AbortSignal.timeout(1e4) });
7018
7265
  if (!resp.ok) return null;
7019
7266
  const md = await resp.text();
@@ -7035,7 +7282,7 @@ async function ensureAutoInstalledSkills(logger) {
7035
7282
  },
7036
7283
  {
7037
7284
  name: "hypha",
7038
- install: () => installSkillFromEndpoint("hypha", "https://hypha.aicell.io/ws/agent-skills/")
7285
+ install: () => installSkillFromEndpoint("hypha", `${getSkillsServer()}/ws/agent-skills/`)
7039
7286
  // hypha skill ships from a different endpoint without a version probe yet.
7040
7287
  },
7041
7288
  {
@@ -7097,14 +7344,14 @@ function loadEnvFile(path) {
7097
7344
  return true;
7098
7345
  }
7099
7346
  function loadDotEnv() {
7100
- const svampEnv = join(process.env.SVAMP_HOME || os$1.homedir() + "/.svamp", ".env");
7347
+ const svampEnv = join(process.env.SVAMP_HOME || os.homedir() + "/.svamp", ".env");
7101
7348
  if (!loadEnvFile(svampEnv)) {
7102
- const hyphaEnv = join(process.env.HYPHA_HOME || os$1.homedir() + "/.hypha", ".env");
7349
+ const hyphaEnv = join(process.env.HYPHA_HOME || os.homedir() + "/.hypha", ".env");
7103
7350
  loadEnvFile(hyphaEnv);
7104
7351
  }
7105
7352
  }
7106
7353
  loadDotEnv();
7107
- const SVAMP_HOME = process.env.SVAMP_HOME || join(os$1.homedir(), ".svamp");
7354
+ const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
7108
7355
  const DAEMON_STATE_FILE = join(SVAMP_HOME, "daemon.state.json");
7109
7356
  const DAEMON_LOCK_FILE = join(SVAMP_HOME, "daemon.lock");
7110
7357
  const LOGS_DIR = join(SVAMP_HOME, "logs");
@@ -7933,7 +8180,7 @@ async function startDaemon(options) {
7933
8180
  machineId = readFileSync$1(machineIdFile, "utf-8").trim();
7934
8181
  }
7935
8182
  if (!machineId) {
7936
- machineId = `machine-${os$1.hostname()}-${randomUUID$1().slice(0, 8)}`;
8183
+ machineId = `machine-${os.hostname()}-${randomUUID$1().slice(0, 8)}`;
7937
8184
  try {
7938
8185
  writeFileSync(machineIdFile, machineId, "utf-8");
7939
8186
  } catch {
@@ -7976,7 +8223,7 @@ async function startDaemon(options) {
7976
8223
  const list = loadExposedTunnels().filter((t) => t.name !== name);
7977
8224
  saveExposedTunnels(list);
7978
8225
  }
7979
- const { ServeManager } = await import('./serveManager-DTR_TDIp.mjs');
8226
+ const { ServeManager } = await import('./serveManager-bhh_rQ5M.mjs');
7980
8227
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
7981
8228
  ensureAutoInstalledSkills(logger).catch(() => {
7982
8229
  });
@@ -8162,10 +8409,10 @@ async function startDaemon(options) {
8162
8409
  var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2, killAndWaitForExit = killAndWaitForExit2, buildIsolationConfig = buildIsolationConfig2;
8163
8410
  let sessionMetadata = {
8164
8411
  path: directory,
8165
- host: os$1.hostname(),
8412
+ host: os.hostname(),
8166
8413
  version: "0.1.0",
8167
8414
  machineId,
8168
- homeDir: os$1.homedir(),
8415
+ homeDir: os.homedir(),
8169
8416
  svampHomeDir: SVAMP_HOME,
8170
8417
  svampLibDir: join(__dirname$1, ".."),
8171
8418
  svampToolsDir: join(__dirname$1, "..", "tools"),
@@ -9747,10 +9994,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
9747
9994
  var parseBashPermission = parseBashPermission2, shouldAutoAllow = shouldAutoAllow2;
9748
9995
  let sessionMetadata = {
9749
9996
  path: directory,
9750
- host: os$1.hostname(),
9997
+ host: os.hostname(),
9751
9998
  version: "0.1.0",
9752
9999
  machineId,
9753
- homeDir: os$1.homedir(),
10000
+ homeDir: os.homedir(),
9754
10001
  svampHomeDir: SVAMP_HOME,
9755
10002
  svampLibDir: join(__dirname$1, ".."),
9756
10003
  svampToolsDir: join(__dirname$1, "..", "tools"),
@@ -10445,7 +10692,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
10445
10692
  logger.log(`Failed to detect isolation capabilities: ${err}`);
10446
10693
  isolationCapabilities = { available: [], preferred: null, details: { nono: { found: false }, srt: { found: false }, bwrap: { found: false }, docker: { found: false }, podman: { found: false } } };
10447
10694
  }
10448
- const defaultHomeDir = existsSync$1("/data") ? "/data" : os$1.homedir();
10695
+ const defaultHomeDir = existsSync$1("/data") ? "/data" : os.homedir();
10449
10696
  const persistedMachineMeta = loadPersistedMachineMetadata(SVAMP_HOME);
10450
10697
  if (persistedMachineMeta) {
10451
10698
  logger.log(`Restored machine metadata (sharing=${!!persistedMachineMeta.sharing}, securityContextConfig=${!!persistedMachineMeta.securityContextConfig}, injectPlatformGuidance=${persistedMachineMeta.injectPlatformGuidance})`);
@@ -10474,8 +10721,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
10474
10721
  }
10475
10722
  }
10476
10723
  const machineMetadata = {
10477
- host: os$1.hostname(),
10478
- platform: os$1.platform(),
10724
+ host: os.hostname(),
10725
+ platform: os.platform(),
10479
10726
  svampVersion: "0.1.0 (hypha)",
10480
10727
  homeDir: defaultHomeDir,
10481
10728
  svampHomeDir: SVAMP_HOME,
@@ -10549,7 +10796,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
10549
10796
  const specs = loadExposedTunnels();
10550
10797
  if (specs.length === 0) return;
10551
10798
  logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
10552
- const { FrpcTunnel } = await import('./frpc-j60b46eU.mjs');
10799
+ const { FrpcTunnel } = await import('./frpc-DPfGAdlj.mjs');
10553
10800
  for (const spec of specs) {
10554
10801
  if (tunnels.has(spec.name)) continue;
10555
10802
  try {
@@ -10601,6 +10848,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
10601
10848
  // Legacy; debug service uses session index now
10602
10849
  });
10603
10850
  logger.log(`Debug service registered: svamp-debug-${machineId}`);
10851
+ const supervised = process.env.SVAMP_SUPERVISED === "1";
10852
+ const localState = {
10853
+ pid: process.pid,
10854
+ startTime: (/* @__PURE__ */ new Date()).toISOString(),
10855
+ version: DAEMON_VERSION,
10856
+ hyphaServerUrl,
10857
+ workspace: server.config.workspace,
10858
+ machineId,
10859
+ hyphaClientId: server.config.client_id,
10860
+ supervised
10861
+ };
10862
+ writeDaemonStateFile(localState);
10863
+ logger.log("Daemon state file written \u2014 daemon is up; restoring mounts/sessions in background");
10604
10864
  await serveManager.restore();
10605
10865
  const persistedSessions = loadPersistedSessions();
10606
10866
  try {
@@ -10762,18 +11022,6 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
10762
11022
  } catch (err) {
10763
11023
  logger.log("Could not generate token (server may not support it):", err);
10764
11024
  }
10765
- const supervised = process.env.SVAMP_SUPERVISED === "1";
10766
- const localState = {
10767
- pid: process.pid,
10768
- startTime: (/* @__PURE__ */ new Date()).toISOString(),
10769
- version: DAEMON_VERSION,
10770
- hyphaServerUrl,
10771
- workspace: server.config.workspace,
10772
- machineId,
10773
- hyphaClientId: server.config.client_id,
10774
- supervised
10775
- };
10776
- writeDaemonStateFile(localState);
10777
11025
  console.log("Svamp daemon started successfully!");
10778
11026
  console.log(` Machine ID: ${machineId}`);
10779
11027
  console.log(` Hypha server: ${hyphaServerUrl}`);
@@ -11252,7 +11500,7 @@ async function restartDaemon() {
11252
11500
  function daemonStatus() {
11253
11501
  const state = readDaemonStateFile();
11254
11502
  if (!state) {
11255
- const plistPath = join(os$1.homedir(), "Library", "LaunchAgents", "io.hypha.svamp.daemon.plist");
11503
+ const plistPath = join(os.homedir(), "Library", "LaunchAgents", "io.hypha.svamp.daemon.plist");
11256
11504
  if (existsSync$1(plistPath)) {
11257
11505
  console.log("Status: Not running (launchd service installed \u2014 may be starting)");
11258
11506
  } else {
@@ -11291,4 +11539,4 @@ var run = /*#__PURE__*/Object.freeze({
11291
11539
  stopDaemon: stopDaemon
11292
11540
  });
11293
11541
 
11294
- export { DefaultTransport$1 as D, GeminiTransport$1 as G, ServeAuth as S, registerSessionService as a, stopDaemon as b, connectToHypha as c, daemonStatus as d, resolveSecurityContext as e, buildSecurityContextFromFlags as f, getHyphaServerUrl as g, hasCookieToken as h, buildSessionShareUrl as i, buildMachineShareUrl as j, generateHookSettings as k, loadSecurityContextConfig as l, mergeSecurityContexts as m, normalizeAllowedUser as n, acpBackend as o, acpAgentConfig as p, codexMcpBackend as q, registerMachineService as r, startDaemon as s, claudeAuth as t, run as u };
11542
+ export { buildSessionShareUrl as A, buildMachineShareUrl as B, generateHookSettings as C, DefaultTransport$1 as D, acpBackend as E, acpAgentConfig as F, codexMcpBackend as G, GeminiTransport$1 as H, claudeAuth as I, api as J, run as K, ServeAuth as S, registerSessionService as a, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerAddr as f, getHyphaServerUrl$1 as g, getHyphaServerUrl as h, hasCookieToken as i, getSkillsServer as j, SKILLS_WORKSPACE as k, SKILLS_COLLECTION as l, fetchWithTimeout as m, searchSkills as n, SKILLS_DIR as o, parseFrontmatter as p, getSkillInfo as q, registerMachineService as r, startDaemon as s, downloadSkillFile as t, listSkillFiles as u, normalizeAllowedUser as v, loadSecurityContextConfig as w, resolveSecurityContext as x, buildSecurityContextFromFlags as y, mergeSecurityContexts as z };
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
2
2
  import os from 'node:os';
3
3
  import { resolve, join } from 'node:path';
4
4
  import { existsSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService, k as generateHookSettings } from './run-W8JZkXIf.mjs';
5
+ import { c as connectToHypha, a as registerSessionService, C as generateHookSettings } from './run-BvocESxe.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -54,7 +54,7 @@ async function handleServeCommand() {
54
54
  }
55
55
  }
56
56
  async function serveAdd(args, machineId) {
57
- const { connectAndGetMachine } = await import('./commands-C6lrExeN.mjs');
57
+ const { connectAndGetMachine } = await import('./commands-BSfZg4aa.mjs');
58
58
  const pos = positionalArgs(args);
59
59
  const name = pos[0];
60
60
  if (!name) {
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
93
93
  }
94
94
  }
95
95
  async function serveApply(args, machineId) {
96
- const { connectAndGetMachine } = await import('./commands-C6lrExeN.mjs');
96
+ const { connectAndGetMachine } = await import('./commands-BSfZg4aa.mjs');
97
97
  const fs = await import('fs');
98
98
  const yaml = await import('yaml');
99
99
  const file = positionalArgs(args)[0];
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
182
182
  }
183
183
  }
184
184
  async function serveRemove(args, machineId) {
185
- const { connectAndGetMachine } = await import('./commands-C6lrExeN.mjs');
185
+ const { connectAndGetMachine } = await import('./commands-BSfZg4aa.mjs');
186
186
  const pos = positionalArgs(args);
187
187
  const name = pos[0];
188
188
  if (!name) {
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
202
202
  }
203
203
  }
204
204
  async function serveList(args, machineId) {
205
- const { connectAndGetMachine } = await import('./commands-C6lrExeN.mjs');
205
+ const { connectAndGetMachine } = await import('./commands-BSfZg4aa.mjs');
206
206
  const all = hasFlag(args, "--all", "-a");
207
207
  const json = hasFlag(args, "--json");
208
208
  const sessionId = getFlag(args, "--session");
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
235
235
  }
236
236
  }
237
237
  async function serveInfo(machineId) {
238
- const { connectAndGetMachine } = await import('./commands-C6lrExeN.mjs');
238
+ const { connectAndGetMachine } = await import('./commands-BSfZg4aa.mjs');
239
239
  const { machine, server } = await connectAndGetMachine(machineId);
240
240
  try {
241
241
  const info = await machine.serveInfo();
@@ -4,7 +4,7 @@ import * as fs from 'fs';
4
4
  import * as http from 'http';
5
5
  import * as net from 'net';
6
6
  import * as path from 'path';
7
- import { S as ServeAuth, h as hasCookieToken } from './run-W8JZkXIf.mjs';
7
+ import { h as getHyphaServerUrl, S as ServeAuth, i as hasCookieToken } from './run-BvocESxe.mjs';
8
8
  import 'os';
9
9
  import 'fs/promises';
10
10
  import 'url';
@@ -83,7 +83,11 @@ class ServeManager {
83
83
  constructor(svampHome, logger, hyphaServerUrl) {
84
84
  this.persistFile = path.join(svampHome, "serve-mounts.json");
85
85
  this.log = logger || ((msg) => console.log(`[SERVE] ${msg}`));
86
- this.hyphaServerUrl = hyphaServerUrl || process.env.HYPHA_SERVER_URL || "https://hypha.aicell.io";
86
+ const resolvedServerUrl = hyphaServerUrl || getHyphaServerUrl();
87
+ if (!resolvedServerUrl) {
88
+ throw new Error("ServeManager requires a Hypha server URL \u2014 set HYPHA_SERVER_URL.");
89
+ }
90
+ this.hyphaServerUrl = resolvedServerUrl;
87
91
  this.auth = new ServeAuth({ hyphaServerUrl: this.hyphaServerUrl });
88
92
  }
89
93
  // ── Public API ───────────────────────────────────────────────────────
@@ -708,7 +712,7 @@ class ServeManager {
708
712
  const mount = this.mounts.get(mountName);
709
713
  const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, `static-${subdomainSafe}-${mount.linkToken}`]]) : void 0;
710
714
  try {
711
- const { FrpcTunnel } = await import('./frpc-j60b46eU.mjs');
715
+ const { FrpcTunnel } = await import('./frpc-DPfGAdlj.mjs');
712
716
  let tunnel;
713
717
  tunnel = new FrpcTunnel({
714
718
  name: tunnelName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.2.80",
3
+ "version": "0.2.82",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",