@tankpkg/cli 0.16.3 → 0.16.5

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/bin/tank.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as VERSION, i as USER_AGENT, l as setConfig, n as flushLogs, o as getConfig, s as getConfigDir, t as authFlowLog } from "../debug-logger-Ds5BG5rk.js";
2
+ import { a as VERSION, i as USER_AGENT, l as setConfig, n as flushLogs, o as getConfig, s as getConfigDir, t as authFlowLog } from "../debug-logger-BpF5Gl-T.js";
3
3
  import { t as logger } from "../logger-BhULz3Uz.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { Command } from "commander";
@@ -21,6 +21,7 @@ import { pipeline } from "node:stream/promises";
21
21
  import open from "open";
22
22
  import ignore from "ignore";
23
23
  import { fileURLToPath } from "node:url";
24
+ import { inspect } from "node:util";
24
25
  //#region \0rolldown/runtime.js
25
26
  var __defProp$1 = Object.defineProperty;
26
27
  var __exportAll = (all, no_symbols) => {
@@ -9067,18 +9068,31 @@ function writeLockfileWithResolvedGraph(lock, nodes, downloaded, onProgress) {
9067
9068
  lock.skills = sortedSkills;
9068
9069
  return lock;
9069
9070
  }
9070
- function mkdirSafeNoSymlinks(targetDir) {
9071
- const resolved = path.resolve(targetDir);
9072
- const segments = resolved.split(path.sep).filter(Boolean);
9073
- let current = resolved.startsWith("/") ? "/" : "";
9074
- for (const seg of segments) {
9075
- current = path.join(current, seg);
9076
- if (fs.existsSync(current)) {
9077
- const stat = fs.lstatSync(current);
9078
- if (stat.isSymbolicLink()) throw new TankIntegrityError(`Symlink in install path: ${current}`);
9079
- if (!stat.isDirectory()) throw new TankIntegrityError(`Non-directory in install path: ${current}`);
9080
- } else fs.mkdirSync(current);
9071
+ /**
9072
+ * Plan the ordered list of ancestor directories to create for `targetDir`,
9073
+ * from just below the filesystem root down to the leaf (the root itself is
9074
+ * never returned — it always exists and must not be `mkdir`'d).
9075
+ *
9076
+ * The path module is injectable so Windows drive/UNC root handling can be
9077
+ * verified on any host via `path.win32` without a real Windows machine.
9078
+ */
9079
+ function planMkdirSegments(targetDir, pathMod = path) {
9080
+ const resolved = pathMod.resolve(targetDir);
9081
+ const { root } = pathMod.parse(resolved);
9082
+ const dirs = [];
9083
+ let current = root;
9084
+ for (const seg of resolved.slice(root.length).split(pathMod.sep).filter(Boolean)) {
9085
+ current = pathMod.join(current, seg);
9086
+ dirs.push(current);
9081
9087
  }
9088
+ return dirs;
9089
+ }
9090
+ function mkdirSafeNoSymlinks(targetDir) {
9091
+ for (const current of planMkdirSegments(targetDir)) if (fs.existsSync(current)) {
9092
+ const stat = fs.lstatSync(current);
9093
+ if (stat.isSymbolicLink()) throw new TankIntegrityError(`Symlink in install path: ${current}`);
9094
+ if (!stat.isDirectory()) throw new TankIntegrityError(`Non-directory in install path: ${current}`);
9095
+ } else fs.mkdirSync(current);
9082
9096
  }
9083
9097
  function isDirNonEmpty(dir) {
9084
9098
  try {
@@ -9965,11 +9979,11 @@ function wrapMcpServerForSkill(options) {
9965
9979
  logger.warn(`MCP proxy wrapping skipped for ${options.skillName}: ${msg}`);
9966
9980
  }
9967
9981
  }
9968
- async function linkInstalledRoots(options) {
9969
- const { rootSkillNames, resolvedNodeByName, extractDirForSkill, directory, global, resolvedHome, homedir, dangerouslyNoTankProxy } = options;
9982
+ async function linkInstalledSkills(options) {
9983
+ const { skillNames, resolvedNodeByName, extractDirForSkill, directory, global, resolvedHome, homedir, dangerouslyNoTankProxy } = options;
9970
9984
  const agentSkillsBaseDir = global ? getGlobalAgentSkillsDir(resolvedHome) : path.join(directory, ".tank", "agent-skills");
9971
9985
  const linksDir = global ? path.join(resolvedHome, ".tank") : path.join(directory, ".tank");
9972
- for (const skillName of rootSkillNames) try {
9986
+ for (const skillName of skillNames) try {
9973
9987
  const node = resolvedNodeByName.get(skillName);
9974
9988
  if (!node) continue;
9975
9989
  const linkResult = linkSkillToAgents({
@@ -9993,7 +10007,7 @@ async function linkInstalledRoots(options) {
9993
10007
  dangerouslyNoTankProxy
9994
10008
  });
9995
10009
  } catch {
9996
- if (rootSkillNames.length === 1) logger.warn("Agent linking skipped (non-fatal)");
10010
+ if (skillNames.length === 1) logger.warn("Agent linking skipped (non-fatal)");
9997
10011
  else logger.warn(`Agent linking skipped for ${skillName} (non-fatal)`);
9998
10012
  }
9999
10013
  const detectedAgents = detectInstalledAgents(homedir);
@@ -10006,7 +10020,7 @@ async function linkInstalledRoots(options) {
10006
10020
  openclaw: "opencode"
10007
10021
  };
10008
10022
  const platforms = new Set(detectedAgents.map((a) => agentToPlatform[a.id]).filter(Boolean));
10009
- for (const skillName of rootSkillNames) {
10023
+ for (const skillName of skillNames) {
10010
10024
  if (!resolvedNodeByName.get(skillName)) continue;
10011
10025
  const skillDir = extractDirForSkill(skillName);
10012
10026
  const manifestPath = path.join(skillDir, "tank.json");
@@ -10057,8 +10071,8 @@ async function executeInstallPipeline(options) {
10057
10071
  global,
10058
10072
  homedir
10059
10073
  });
10060
- await linkInstalledRoots({
10061
- rootSkillNames,
10074
+ await linkInstalledSkills({
10075
+ skillNames: nodesToInstall.map((node) => node.name),
10062
10076
  resolvedNodeByName,
10063
10077
  extractDirForSkill,
10064
10078
  directory,
@@ -12084,6 +12098,37 @@ function printUserInfo(user) {
12084
12098
  logger.info(`Email: ${user.email ?? "unknown"}`);
12085
12099
  }
12086
12100
  //#endregion
12101
+ //#region src/lib/describe-error.ts
12102
+ /** Format a real Error, surfacing Node errno fields (code/syscall/path) when present. */
12103
+ function describeErrorObject(err) {
12104
+ const e = err;
12105
+ const base = err.message?.trim() || err.name || "Error";
12106
+ const tags = [
12107
+ e.code != null ? `code=${String(e.code)}` : null,
12108
+ e.syscall != null ? `syscall=${String(e.syscall)}` : null,
12109
+ e.path != null ? `path=${String(e.path)}` : null
12110
+ ].filter((tag) => tag !== null);
12111
+ return tags.length > 0 ? `${base} (${tags.join(", ")})` : base;
12112
+ }
12113
+ /**
12114
+ * Render an unknown thrown value as a non-empty, actionable string.
12115
+ *
12116
+ * Guarantees a useful message for every input: errno errors surface their
12117
+ * `code`/`syscall`/`path`, non-Error objects are inspected, and the result
12118
+ * never collapses to an empty string or a bare placeholder like `<none>`
12119
+ * (the symptom Windows users hit during `tank install`).
12120
+ */
12121
+ function describeError(err) {
12122
+ if (err instanceof Error) return describeErrorObject(err);
12123
+ if (err === null || err === void 0) return `${String(err)} (non-error thrown)`;
12124
+ if (typeof err === "object") try {
12125
+ return `${err.constructor?.name ?? "object"}: ${inspect(err, { depth: 2 })}`;
12126
+ } catch {
12127
+ return Object.prototype.toString.call(err);
12128
+ }
12129
+ return `${typeof err} ${String(err)}`;
12130
+ }
12131
+ //#endregion
12087
12132
  //#region src/lib/install-suggestions.ts
12088
12133
  /**
12089
12134
  * Best-effort fuzzy lookup of similar skill names. Hits the public /api/v1/search
@@ -12304,7 +12349,7 @@ program.command("install").alias("i").description("Install one or more skills fr
12304
12349
  ...proxyOpt
12305
12350
  });
12306
12351
  } catch (err) {
12307
- const msg = err instanceof Error ? err.message : String(err);
12352
+ const msg = describeError(err);
12308
12353
  console.error(`Install failed: ${msg}`);
12309
12354
  process.exit(1);
12310
12355
  }
@@ -12327,7 +12372,7 @@ program.command("install").alias("i").description("Install one or more skills fr
12327
12372
  ...proxyOpt
12328
12373
  });
12329
12374
  } catch (err) {
12330
- const msg = err instanceof Error ? err.message : String(err);
12375
+ const msg = describeError(err);
12331
12376
  failures.push({
12332
12377
  target,
12333
12378
  error: msg,