skillvault-publisher 0.11.2 → 0.12.0

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 (47) hide show
  1. package/dist/index.js +411 -114
  2. package/package.json +6 -2
  3. package/dist/commands/analytics.js +0 -75
  4. package/dist/commands/audit.js +0 -96
  5. package/dist/commands/changelog.js +0 -78
  6. package/dist/commands/check-session.js +0 -117
  7. package/dist/commands/customers.js +0 -53
  8. package/dist/commands/grants.js +0 -69
  9. package/dist/commands/info.js +0 -30
  10. package/dist/commands/init.js +0 -83
  11. package/dist/commands/investigate.js +0 -455
  12. package/dist/commands/invite.js +0 -198
  13. package/dist/commands/licenses.js +0 -65
  14. package/dist/commands/link.js +0 -112
  15. package/dist/commands/list.js +0 -80
  16. package/dist/commands/login.js +0 -137
  17. package/dist/commands/logout.js +0 -11
  18. package/dist/commands/publish-all.js +0 -336
  19. package/dist/commands/publish.js +0 -341
  20. package/dist/commands/register.js +0 -118
  21. package/dist/commands/report.js +0 -45
  22. package/dist/commands/revoke-grant.js +0 -49
  23. package/dist/commands/scan-output.js +0 -201
  24. package/dist/commands/search.js +0 -89
  25. package/dist/commands/session-cleanup.js +0 -108
  26. package/dist/commands/session-common.js +0 -180
  27. package/dist/commands/session-keepalive.js +0 -215
  28. package/dist/commands/skill-delete.js +0 -58
  29. package/dist/commands/skill-status.js +0 -63
  30. package/dist/commands/skill-unarchive.js +0 -51
  31. package/dist/commands/unlink.js +0 -67
  32. package/dist/commands/update.js +0 -221
  33. package/dist/commands/watchtower.js +0 -109
  34. package/dist/commands/watermark-decode.js +0 -105
  35. package/dist/commands/webhook.js +0 -77
  36. package/dist/commands/whoami.js +0 -118
  37. package/dist/commands/workspaces.js +0 -108
  38. package/dist/credentials.js +0 -185
  39. package/dist/fingerprint.js +0 -51
  40. package/dist/grant-cache.js +0 -112
  41. package/dist/hooks-manager.js +0 -202
  42. package/dist/pdf-report.js +0 -265
  43. package/dist/projects-registry.js +0 -192
  44. package/dist/publisher-workspace.js +0 -165
  45. package/dist/publisher-workspaces-registry.js +0 -133
  46. package/dist/scope.js +0 -100
  47. package/dist/session.js +0 -103
package/dist/index.js CHANGED
@@ -237,10 +237,10 @@ var require_utils = __commonJS({
237
237
  sum += a.length;
238
238
  }
239
239
  const res = new Uint8Array(sum);
240
- for (let i = 0, pad3 = 0; i < arrays.length; i++) {
240
+ for (let i = 0, pad4 = 0; i < arrays.length; i++) {
241
241
  const a = arrays[i];
242
- res.set(a, pad3);
243
- pad3 += a.length;
242
+ res.set(a, pad4);
243
+ pad4 += a.length;
244
244
  }
245
245
  return res;
246
246
  }
@@ -1769,10 +1769,10 @@ var require_utils2 = __commonJS({
1769
1769
  sum += a.length;
1770
1770
  }
1771
1771
  const res = new Uint8Array(sum);
1772
- for (let i = 0, pad3 = 0; i < arrays.length; i++) {
1772
+ for (let i = 0, pad4 = 0; i < arrays.length; i++) {
1773
1773
  const a = arrays[i];
1774
- res.set(a, pad3);
1775
- pad3 += a.length;
1774
+ res.set(a, pad4);
1775
+ pad4 += a.length;
1776
1776
  }
1777
1777
  return res;
1778
1778
  }
@@ -3891,11 +3891,11 @@ function __metadata(metadataKey, metadataValue) {
3891
3891
  }
3892
3892
  function __awaiter(thisArg, _arguments, P, generator) {
3893
3893
  function adopt(value) {
3894
- return value instanceof P ? value : new P(function(resolve10) {
3895
- resolve10(value);
3894
+ return value instanceof P ? value : new P(function(resolve11) {
3895
+ resolve11(value);
3896
3896
  });
3897
3897
  }
3898
- return new (P || (P = Promise))(function(resolve10, reject) {
3898
+ return new (P || (P = Promise))(function(resolve11, reject) {
3899
3899
  function fulfilled(value) {
3900
3900
  try {
3901
3901
  step(generator.next(value));
@@ -3911,7 +3911,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
3911
3911
  }
3912
3912
  }
3913
3913
  function step(result) {
3914
- result.done ? resolve10(result.value) : adopt(result.value).then(fulfilled, rejected);
3914
+ result.done ? resolve11(result.value) : adopt(result.value).then(fulfilled, rejected);
3915
3915
  }
3916
3916
  step((generator = generator.apply(thisArg, _arguments || [])).next());
3917
3917
  });
@@ -4102,14 +4102,14 @@ function __asyncValues(o) {
4102
4102
  }, i);
4103
4103
  function verb(n) {
4104
4104
  i[n] = o[n] && function(v) {
4105
- return new Promise(function(resolve10, reject) {
4106
- v = o[n](v), settle(resolve10, reject, v.done, v.value);
4105
+ return new Promise(function(resolve11, reject) {
4106
+ v = o[n](v), settle(resolve11, reject, v.done, v.value);
4107
4107
  });
4108
4108
  };
4109
4109
  }
4110
- function settle(resolve10, reject, d, v) {
4110
+ function settle(resolve11, reject, d, v) {
4111
4111
  Promise.resolve(v).then(function(v2) {
4112
- resolve10({ value: v2, done: d });
4112
+ resolve11({ value: v2, done: d });
4113
4113
  }, reject);
4114
4114
  }
4115
4115
  }
@@ -5054,9 +5054,9 @@ var require_clone = __commonJS({
5054
5054
  } else if (_instanceof(parent2, nativeSet)) {
5055
5055
  child = new nativeSet();
5056
5056
  } else if (_instanceof(parent2, nativePromise)) {
5057
- child = new nativePromise(function(resolve10, reject) {
5057
+ child = new nativePromise(function(resolve11, reject) {
5058
5058
  parent2.then(function(value) {
5059
- resolve10(_clone(value, depth2 - 1));
5059
+ resolve11(_clone(value, depth2 - 1));
5060
5060
  }, function(err) {
5061
5061
  reject(_clone(err, depth2 - 1));
5062
5062
  });
@@ -145431,7 +145431,7 @@ var require_pdfkit = __commonJS({
145431
145431
  return `${this.ref.id} 0 R`;
145432
145432
  }
145433
145433
  };
145434
- var pad3 = (str, length) => (Array(length + 1).join("0") + str).slice(-length);
145434
+ var pad4 = (str, length) => (Array(length + 1).join("0") + str).slice(-length);
145435
145435
  var escapableRe = /[\n\r\t\b\f()\\]/g;
145436
145436
  var escapable = {
145437
145437
  "\n": "\\n",
@@ -145487,7 +145487,7 @@ var require_pdfkit = __commonJS({
145487
145487
  } else if (object instanceof PDFAbstractReference || object instanceof PDFTree || object instanceof SpotColor) {
145488
145488
  return object.toString();
145489
145489
  } else if (object instanceof Date) {
145490
- let string = `D:${pad3(object.getUTCFullYear(), 4)}` + pad3(object.getUTCMonth() + 1, 2) + pad3(object.getUTCDate(), 2) + pad3(object.getUTCHours(), 2) + pad3(object.getUTCMinutes(), 2) + pad3(object.getUTCSeconds(), 2) + "Z";
145490
+ let string = `D:${pad4(object.getUTCFullYear(), 4)}` + pad4(object.getUTCMonth() + 1, 2) + pad4(object.getUTCDate(), 2) + pad4(object.getUTCHours(), 2) + pad4(object.getUTCMinutes(), 2) + pad4(object.getUTCSeconds(), 2) + "Z";
145491
145491
  if (encryptFn) {
145492
145492
  string = encryptFn(Buffer.from(string, "ascii")).toString("binary");
145493
145493
  string = string.replace(escapableRe, (c) => escapable[c]);
@@ -151595,7 +151595,7 @@ function confColor(c) {
151595
151595
  return SV.sirenRed500;
151596
151596
  }
151597
151597
  async function generatePdfReport(data, outputPath) {
151598
- return new Promise((resolve10, reject) => {
151598
+ return new Promise((resolve11, reject) => {
151599
151599
  const doc = new import_pdfkit.default({
151600
151600
  size: "A4",
151601
151601
  margins: { top: 72, bottom: 0, left: 56, right: 56 },
@@ -151742,7 +151742,7 @@ async function generatePdfReport(data, outputPath) {
151742
151742
  yPos += 72;
151743
151743
  drawPageFooter(doc, leftMargin, contentWidth, pageWidth, pageNum);
151744
151744
  doc.end();
151745
- stream.on("finish", resolve10);
151745
+ stream.on("finish", resolve11);
151746
151746
  stream.on("error", reject);
151747
151747
  });
151748
151748
  }
@@ -151804,7 +151804,7 @@ var init_pdf_report = __esm({
151804
151804
  });
151805
151805
 
151806
151806
  // dist/index.js
151807
- import { Command as Command35 } from "commander";
151807
+ import { Command as Command36 } from "commander";
151808
151808
  import { readFileSync as readFileSync18 } from "node:fs";
151809
151809
  import { fileURLToPath } from "node:url";
151810
151810
  import { dirname as dirname5, join as join17 } from "node:path";
@@ -151813,6 +151813,7 @@ import { dirname as dirname5, join as join17 } from "node:path";
151813
151813
  import { Command } from "commander";
151814
151814
  import chalk from "chalk";
151815
151815
  import { randomBytes } from "node:crypto";
151816
+ import { createInterface } from "node:readline/promises";
151816
151817
 
151817
151818
  // dist/credentials.js
151818
151819
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, rmSync, existsSync as existsSync2, openSync as openSync2, closeSync as closeSync2, unlinkSync as unlinkSync2 } from "node:fs";
@@ -151861,7 +151862,43 @@ function removeKeypair() {
151861
151862
  }
151862
151863
 
151863
151864
  // dist/commands/login.js
151864
- var loginCommand = new Command("login").description("Authenticate with the Skill Vault server").option("--email <email>", "Your publisher account email").option("--token <token>", "Authenticate with a publisher session JWT (Bearer token from dashboard)").option("--license-key <key>", "Authenticate with a license key (customer)").option("--code <code>", "Redeem an invite code after login").option("--server <url>", "Server URL", "https://api.getskillvault.com").action(async (options) => {
151865
+ async function chooseFromMultiple(available, publisherFlag) {
151866
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
151867
+ if (publisherFlag) {
151868
+ const match = available.find((p) => p.publisher_id === publisherFlag);
151869
+ if (match)
151870
+ return match;
151871
+ console.error(chalk.red(`Publisher ${publisherFlag} not in your accessible list.`));
151872
+ }
151873
+ console.error(chalk.red("Multiple publishers available for this email. Pass --publisher <id> to choose:"));
151874
+ for (const p of available) {
151875
+ console.error(chalk.dim(` --publisher ${p.publisher_id} (${p.publisher_name || "unnamed"}, role: ${p.role})`));
151876
+ }
151877
+ process.exit(1);
151878
+ }
151879
+ console.log();
151880
+ console.log(chalk.bold(" This email has access to multiple publishers:"));
151881
+ console.log();
151882
+ available.forEach((p, idx) => {
151883
+ const roleColor = p.role === "owner" ? chalk.green : p.role === "admin" ? chalk.cyan : chalk.dim;
151884
+ console.log(` ${chalk.bold(`[${idx + 1}]`)} ${p.publisher_name || p.publisher_id} ${chalk.dim(`(${p.publisher_id})`)} ${roleColor(p.role)}`);
151885
+ });
151886
+ console.log();
151887
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
151888
+ try {
151889
+ while (true) {
151890
+ const answer = (await rl.question(` Pick a publisher [1-${available.length}]: `)).trim();
151891
+ const idx = parseInt(answer, 10);
151892
+ if (Number.isInteger(idx) && idx >= 1 && idx <= available.length) {
151893
+ return available[idx - 1];
151894
+ }
151895
+ console.log(chalk.red(` Invalid choice \u2014 enter a number between 1 and ${available.length}`));
151896
+ }
151897
+ } finally {
151898
+ rl.close();
151899
+ }
151900
+ }
151901
+ var loginCommand = new Command("login").description("Authenticate with the Skill Vault server").option("--email <email>", "Your publisher account email").option("--token <token>", "Authenticate with a publisher session JWT (Bearer token from dashboard)").option("--license-key <key>", "Authenticate with a license key (customer)").option("--code <code>", "Redeem an invite code after login").option("--publisher <id>", "Disambiguate when your email has access to multiple publisher accounts").option("--server <url>", "Server URL", "https://api.getskillvault.com").action(async (options) => {
151865
151902
  try {
151866
151903
  const serverUrl = options.server;
151867
151904
  const publisherToken = options.token || process.env.SKILLVAULT_TOKEN || null;
@@ -151881,22 +151918,52 @@ var loginCommand = new Command("login").description("Authenticate with the Skill
151881
151918
  publisherIdentity = { publisher_id: me.publisher_id, email: me.email, name: me.name };
151882
151919
  } else if (options.email) {
151883
151920
  console.log(chalk.dim(`Authenticating as ${options.email}...`));
151921
+ const body2 = { email: options.email };
151922
+ if (options.publisher)
151923
+ body2.publisher_id = options.publisher;
151884
151924
  const loginRes = await fetch(`${serverUrl}/auth/login`, {
151885
151925
  method: "POST",
151886
151926
  headers: { "Content-Type": "application/json" },
151887
- body: JSON.stringify({ email: options.email })
151927
+ body: JSON.stringify(body2)
151888
151928
  });
151889
151929
  if (!loginRes.ok) {
151890
- const err = await loginRes.json().catch(() => ({ message: loginRes.statusText }));
151891
- console.error(chalk.red(`Login failed: ${err.message}`));
151930
+ const err = await loginRes.json().catch(() => null);
151931
+ const reason = err?.message || err?.error || loginRes.statusText || `HTTP ${loginRes.status}`;
151932
+ console.error(chalk.red(`Login failed: ${reason}`));
151892
151933
  if (loginRes.status === 404) {
151893
151934
  console.error(chalk.dim("No account found. Create one at https://app.getskillvault.com/login"));
151935
+ console.error(chalk.dim("Or ask a team owner to add your email via `skillvault-publisher team add`."));
151894
151936
  }
151895
151937
  process.exit(1);
151896
151938
  }
151897
151939
  const loginData = await loginRes.json();
151898
- sessionToken = loginData.session_token;
151899
- publisherIdentity = { publisher_id: loginData.publisher_id, email: loginData.email, name: loginData.name };
151940
+ if ("multiple_choice" in loginData && loginData.multiple_choice) {
151941
+ const chosen = await chooseFromMultiple(loginData.available_publishers, options.publisher);
151942
+ const retryRes = await fetch(`${serverUrl}/auth/login`, {
151943
+ method: "POST",
151944
+ headers: { "Content-Type": "application/json" },
151945
+ body: JSON.stringify({ email: options.email, publisher_id: chosen.publisher_id })
151946
+ });
151947
+ if (!retryRes.ok) {
151948
+ const err = await retryRes.json().catch(() => null);
151949
+ const reason = err?.message || err?.error || retryRes.statusText;
151950
+ console.error(chalk.red(`Login retry failed: ${reason}`));
151951
+ process.exit(1);
151952
+ }
151953
+ const retryData = await retryRes.json();
151954
+ sessionToken = retryData.session_token;
151955
+ publisherIdentity = { publisher_id: retryData.publisher_id, email: retryData.email, name: retryData.name };
151956
+ if (retryData.role && retryData.role !== "owner") {
151957
+ console.log(chalk.dim(` Logged in as ${chalk.bold(retryData.role)} of ${chosen.publisher_name || retryData.publisher_id}`));
151958
+ }
151959
+ } else {
151960
+ const single = loginData;
151961
+ sessionToken = single.session_token;
151962
+ publisherIdentity = { publisher_id: single.publisher_id, email: single.email, name: single.name };
151963
+ if (single.role && single.role !== "owner") {
151964
+ console.log(chalk.dim(` Logged in as ${chalk.bold(single.role)} of ${single.publisher_id}`));
151965
+ }
151966
+ }
151900
151967
  } else if (!options.licenseKey) {
151901
151968
  console.error(chalk.red("Publisher identity required."));
151902
151969
  console.error("");
@@ -152003,7 +152070,7 @@ var registerCommand = new Command2("register").description("Create a new publish
152003
152070
  let verified = false;
152004
152071
  const maxAttempts = 120;
152005
152072
  for (let i = 0; i < maxAttempts; i++) {
152006
- await new Promise((resolve10) => setTimeout(resolve10, 3e3));
152073
+ await new Promise((resolve11) => setTimeout(resolve11, 3e3));
152007
152074
  try {
152008
152075
  const statusRes = await fetch(`${serverUrl}/publishers/${publisher_id}/verification-status`);
152009
152076
  if (statusRes.ok) {
@@ -152094,7 +152161,7 @@ var logoutCommand = new Command3("logout").description("Remove stored credential
152094
152161
  import { Command as Command4 } from "commander";
152095
152162
  import chalk5 from "chalk";
152096
152163
  import { readFileSync as readFileSync6, existsSync as existsSync6, statSync as statSync3 } from "node:fs";
152097
- import { resolve as resolve4, join as join6 } from "node:path";
152164
+ import { resolve as resolve5, join as join6 } from "node:path";
152098
152165
  import { createHash as createHash3 } from "node:crypto";
152099
152166
 
152100
152167
  // ../shared/dist/crypto.js
@@ -152212,7 +152279,7 @@ function readVaultMetadata(data) {
152212
152279
  // ../shared/dist/packer.js
152213
152280
  import { readFileSync as readFileSync3, readdirSync, existsSync as existsSync3 } from "node:fs";
152214
152281
  import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
152215
- import { join as join3, relative } from "node:path";
152282
+ import { join as join3, relative, resolve as resolve2, isAbsolute, sep } from "node:path";
152216
152283
  import { createHash as createHash2 } from "node:crypto";
152217
152284
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
152218
152285
  "node_modules",
@@ -152245,10 +152312,12 @@ function collectFiles(dir, base, ignoreSet) {
152245
152312
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
152246
152313
  if (ignoreSet.has(entry.name))
152247
152314
  continue;
152315
+ if (entry.isSymbolicLink())
152316
+ continue;
152248
152317
  const fullPath = join3(dir, entry.name);
152249
152318
  if (entry.isDirectory()) {
152250
152319
  entries.push(...collectFiles(fullPath, base, ignoreSet));
152251
- } else {
152320
+ } else if (entry.isFile()) {
152252
152321
  entries.push({
152253
152322
  path: relative(base, fullPath),
152254
152323
  content: readFileSync3(fullPath)
@@ -152529,17 +152598,57 @@ function extractPotentialHeartbeats(content) {
152529
152598
  return values;
152530
152599
  }
152531
152600
 
152601
+ // ../shared/dist/skill-name.js
152602
+ var MAX_SKILL_NAME_LENGTH = 64;
152603
+ var ALLOWED_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
152604
+ var InvalidSkillNameError = class extends Error {
152605
+ reason;
152606
+ name;
152607
+ constructor(reason, name) {
152608
+ super(`invalid skill name: ${reason}`);
152609
+ this.reason = reason;
152610
+ this.name = name;
152611
+ this.name = "InvalidSkillNameError";
152612
+ }
152613
+ };
152614
+ function validateSkillName(name) {
152615
+ if (typeof name !== "string") {
152616
+ throw new InvalidSkillNameError("must be a string", String(name));
152617
+ }
152618
+ if (name.length === 0) {
152619
+ throw new InvalidSkillNameError("must not be empty", name);
152620
+ }
152621
+ if (name.length > MAX_SKILL_NAME_LENGTH) {
152622
+ throw new InvalidSkillNameError(`exceeds ${MAX_SKILL_NAME_LENGTH} characters`, name);
152623
+ }
152624
+ for (let i = 0; i < name.length; i++) {
152625
+ const code = name.charCodeAt(i);
152626
+ if (code < 32 || code === 127) {
152627
+ throw new InvalidSkillNameError(`contains control character (0x${code.toString(16).padStart(2, "0")})`, name);
152628
+ }
152629
+ }
152630
+ if (name.includes("..")) {
152631
+ throw new InvalidSkillNameError('contains parent-directory segment "..", which is not allowed in path components', name);
152632
+ }
152633
+ if (name.includes("/") || name.includes("\\")) {
152634
+ throw new InvalidSkillNameError("contains a path separator, which is not allowed in skill names", name);
152635
+ }
152636
+ if (!ALLOWED_PATTERN.test(name)) {
152637
+ throw new InvalidSkillNameError(`must match ${ALLOWED_PATTERN.source} (ASCII letters, digits, dashes, underscores; must start with a letter or digit)`, name);
152638
+ }
152639
+ }
152640
+
152532
152641
  // dist/session.js
152533
152642
  import chalk4 from "chalk";
152534
152643
 
152535
152644
  // dist/publisher-workspace.js
152536
152645
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync4 } from "node:fs";
152537
- import { join as join4, dirname as dirname3, resolve as resolve2 } from "node:path";
152646
+ import { join as join4, dirname as dirname3, resolve as resolve3 } from "node:path";
152538
152647
  import { homedir as homedir3 } from "node:os";
152539
152648
  var MANIFEST_DIRNAME = ".skillvault-publisher";
152540
152649
  var MANIFEST_FILENAME = "workspace.json";
152541
152650
  function workspaceRootsFor(dir) {
152542
- const abs = resolve2(dir);
152651
+ const abs = resolve3(dir);
152543
152652
  const manifestPath = join4(abs, MANIFEST_DIRNAME, MANIFEST_FILENAME);
152544
152653
  return {
152545
152654
  dir: abs,
@@ -152586,7 +152695,7 @@ function saveWorkspace(dir, manifest) {
152586
152695
  writeFileSync4(manifestPath, JSON.stringify(manifest, null, 2), { mode: 420 });
152587
152696
  }
152588
152697
  function findWorkspace(cwd) {
152589
- const start = resolve2(cwd ?? process.cwd());
152698
+ const start = resolve3(cwd ?? process.cwd());
152590
152699
  const home = homedir3();
152591
152700
  let dir = start;
152592
152701
  while (true) {
@@ -152658,8 +152767,9 @@ async function sessionFetch(ctx, path, options) {
152658
152767
  process.exit(1);
152659
152768
  }
152660
152769
  if (!res.ok) {
152661
- const err = await res.json().catch(() => ({ message: res.statusText }));
152662
- process.stderr.write(chalk4.red(`Error: ${err.message}
152770
+ const err = await res.json().catch(() => null);
152771
+ const reason = err?.message || err?.error || res.statusText || `HTTP ${res.status}`;
152772
+ process.stderr.write(chalk4.red(`Error: ${reason}
152663
152773
  `));
152664
152774
  process.exit(1);
152665
152775
  }
@@ -152696,7 +152806,7 @@ function requireCommandContext(opts = {}) {
152696
152806
 
152697
152807
  // dist/publisher-workspaces-registry.js
152698
152808
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, statSync as statSync2 } from "node:fs";
152699
- import { dirname as dirname4, join as join5, resolve as resolve3 } from "node:path";
152809
+ import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
152700
152810
  import { homedir as homedir4 } from "node:os";
152701
152811
  var REGISTRY_VERSION = 1;
152702
152812
  var MANIFEST_DIRNAME2 = ".skillvault-publisher";
@@ -152730,7 +152840,7 @@ function listWorkspaces() {
152730
152840
  const live = [];
152731
152841
  let pruned = false;
152732
152842
  for (const entry of reg.workspaces) {
152733
- const path = resolve3(entry.path);
152843
+ const path = resolve4(entry.path);
152734
152844
  let stillThere = false;
152735
152845
  try {
152736
152846
  const st = statSync2(path);
@@ -152755,8 +152865,8 @@ function listWorkspaces() {
152755
152865
  }
152756
152866
  function registerWorkspace(entry) {
152757
152867
  const reg = readRegistry();
152758
- const normalized = { ...entry, path: resolve3(entry.path) };
152759
- const idx = reg.workspaces.findIndex((w) => resolve3(w.path) === normalized.path);
152868
+ const normalized = { ...entry, path: resolve4(entry.path) };
152869
+ const idx = reg.workspaces.findIndex((w) => resolve4(w.path) === normalized.path);
152760
152870
  if (idx >= 0) {
152761
152871
  reg.workspaces[idx] = normalized;
152762
152872
  } else {
@@ -152766,9 +152876,9 @@ function registerWorkspace(entry) {
152766
152876
  }
152767
152877
  function unregisterWorkspace(path) {
152768
152878
  const reg = readRegistry();
152769
- const target = resolve3(path);
152879
+ const target = resolve4(path);
152770
152880
  const before = reg.workspaces.length;
152771
- reg.workspaces = reg.workspaces.filter((w) => resolve3(w.path) !== target);
152881
+ reg.workspaces = reg.workspaces.filter((w) => resolve4(w.path) !== target);
152772
152882
  if (reg.workspaces.length !== before)
152773
152883
  writeRegistry(reg);
152774
152884
  }
@@ -152798,9 +152908,9 @@ var publishCommand = new Command4("publish").description("Encrypt and publish a
152798
152908
  const config = ctx.config;
152799
152909
  const workspaceCtx = resolveWorkspaceContext({
152800
152910
  workspaceFlag: options.workspace,
152801
- cwd: directory ? resolve4(directory) : process.cwd()
152911
+ cwd: directory ? resolve5(directory) : process.cwd()
152802
152912
  });
152803
- const dirPath = options.workspace ? resolve4(options.workspace) : directory ? resolve4(directory) : workspaceCtx?.dir ?? process.cwd();
152913
+ const dirPath = options.workspace ? resolve5(options.workspace) : directory ? resolve5(directory) : workspaceCtx?.dir ?? process.cwd();
152804
152914
  if (!existsSync6(dirPath) || !statSync3(dirPath).isDirectory()) {
152805
152915
  process.stderr.write(chalk5.red(`Error: "${dirPath}" is not a valid directory
152806
152916
  `));
@@ -152821,6 +152931,17 @@ var publishCommand = new Command4("publish").description("Encrypt and publish a
152821
152931
  process.stderr.write(chalk5.red("Error: Skill name required \u2014 set in SKILL.md frontmatter or use --name\n"));
152822
152932
  process.exit(1);
152823
152933
  }
152934
+ try {
152935
+ validateSkillName(skillName);
152936
+ } catch (err) {
152937
+ if (err instanceof InvalidSkillNameError) {
152938
+ process.stderr.write(chalk5.red(`Error: ${err.message}
152939
+ `));
152940
+ process.stderr.write(chalk5.dim("Skill names must be ASCII letters, digits, dashes, or underscores (1-64 chars).\n"));
152941
+ process.exit(1);
152942
+ }
152943
+ throw err;
152944
+ }
152824
152945
  if (!description) {
152825
152946
  process.stderr.write(chalk5.red("Error: Description required \u2014 set in SKILL.md frontmatter or use --description\n"));
152826
152947
  process.exit(1);
@@ -153092,7 +153213,7 @@ import chalk6 from "chalk";
153092
153213
  var searchCommand = new Command5("search").description("Search for skills in the registry").argument("<query>", "Search query").option("--type <type>", "Filter by capability type").option("--publisher <publisher>", "Filter by publisher name or ID").option("--mine", "Show only your own published skills").option("--json", "Output results as JSON").action(async (query, options) => {
153093
153214
  try {
153094
153215
  const config = getConfig();
153095
- const serverUrl = config?.server_url ?? "http://localhost:3001";
153216
+ const serverUrl = config?.server_url ?? "https://api.getskillvault.com";
153096
153217
  let publisherFilter = options.publisher;
153097
153218
  if (options.mine) {
153098
153219
  if (!config) {
@@ -153253,7 +153374,7 @@ var licensesCommand = new Command7("licenses").description("List your active ski
153253
153374
  import { Command as Command8 } from "commander";
153254
153375
  import chalk9 from "chalk";
153255
153376
  import { readFileSync as readFileSync8, existsSync as existsSync7, statSync as statSync4 } from "node:fs";
153256
- import { resolve as resolve5, join as join7 } from "node:path";
153377
+ import { resolve as resolve6, join as join7 } from "node:path";
153257
153378
  import { createHash as createHash4 } from "node:crypto";
153258
153379
  function parseFrontmatter2(content) {
153259
153380
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -153304,7 +153425,7 @@ var updateCommand = new Command8("update").description("Push a new version of an
153304
153425
  try {
153305
153426
  const ctx = requireSession();
153306
153427
  const config = ctx.config;
153307
- const dirPath = resolve5(directory);
153428
+ const dirPath = resolve6(directory);
153308
153429
  if (!existsSync7(dirPath) || !statSync4(dirPath).isDirectory()) {
153309
153430
  process.stderr.write(chalk9.red(`Error: "${dirPath}" is not a valid directory
153310
153431
  `));
@@ -153487,6 +153608,14 @@ var whoamiCommand = new Command9("whoami").description("Show current user, activ
153487
153608
  const ctx = requireSession();
153488
153609
  const config = ctx.config;
153489
153610
  const fingerprint = getDeviceFingerprint();
153611
+ let authMe = null;
153612
+ try {
153613
+ const meRes = await fetch(`${config.server_url}/auth/me`, { headers: ctx.headers });
153614
+ if (meRes.ok) {
153615
+ authMe = await meRes.json();
153616
+ }
153617
+ } catch {
153618
+ }
153490
153619
  const response = await fetch(`${config.server_url}/agent/status?agent_id=${encodeURIComponent(config.agent_id)}`, {
153491
153620
  headers: ctx.headers
153492
153621
  });
@@ -153495,29 +153624,48 @@ var whoamiCommand = new Command9("whoami").description("Show current user, activ
153495
153624
  console.log(JSON.stringify({
153496
153625
  host_id: config.host_id,
153497
153626
  agent_id: config.agent_id,
153498
- ...config.publisher_id ? { publisher_id: config.publisher_id } : {},
153499
- ...config.email ? { email: config.email } : {},
153627
+ ...authMe ? {
153628
+ publisher_id: authMe.publisher_id,
153629
+ publisher_name: authMe.publisher_name,
153630
+ email: authMe.email,
153631
+ role: authMe.role,
153632
+ is_owner: authMe.is_owner
153633
+ } : {
153634
+ ...config.publisher_id ? { publisher_id: config.publisher_id } : {},
153635
+ ...config.email ? { email: config.email } : {}
153636
+ },
153500
153637
  device_fingerprint: fingerprint,
153501
153638
  server_url: config.server_url,
153502
- server_status: "unreachable"
153639
+ server_status: "agent_status_unreachable"
153503
153640
  }, null, 2));
153504
153641
  return;
153505
153642
  }
153506
153643
  console.log();
153507
153644
  console.log(chalk10.bold(" Skill Vault Identity"));
153508
153645
  console.log();
153509
- if (config.publisher_id) {
153510
- console.log(` ${"Publisher ID:".padEnd(22)} ${config.publisher_id}`);
153511
- }
153512
- if (config.email) {
153513
- console.log(` ${"Email:".padEnd(22)} ${config.email}`);
153646
+ if (authMe) {
153647
+ const roleColor = authMe.role === "owner" ? chalk10.green : authMe.role === "admin" ? chalk10.cyan : chalk10.dim;
153648
+ const roleLabel = authMe.role || (authMe.is_owner ? "owner" : "member");
153649
+ console.log(` ${"You:".padEnd(22)} ${authMe.email} ${roleColor(`(${roleLabel})`)}`);
153650
+ const publisherDisplay = authMe.publisher_name ? `${authMe.publisher_name} ${chalk10.dim(`(${authMe.publisher_id})`)}` : authMe.publisher_id;
153651
+ console.log(` ${"Publisher:".padEnd(22)} ${publisherDisplay}`);
153652
+ if (!authMe.is_owner) {
153653
+ console.log(` ${chalk10.dim(" (you are a team member of this publisher)".padEnd(22))}`);
153654
+ }
153655
+ } else {
153656
+ if (config.publisher_id) {
153657
+ console.log(` ${"Publisher ID:".padEnd(22)} ${config.publisher_id}`);
153658
+ }
153659
+ if (config.email) {
153660
+ console.log(` ${"Email:".padEnd(22)} ${config.email}`);
153661
+ }
153514
153662
  }
153515
153663
  console.log(` ${"Host ID:".padEnd(22)} ${config.host_id}`);
153516
153664
  console.log(` ${"Agent ID:".padEnd(22)} ${config.agent_id}`);
153517
153665
  console.log(` ${"Device Fingerprint:".padEnd(22)} ${fingerprint}`);
153518
153666
  console.log(` ${"Server:".padEnd(22)} ${config.server_url}`);
153519
153667
  console.log();
153520
- console.log(chalk10.yellow(` Server unreachable (${response.status}). Showing local config only.`));
153668
+ console.log(chalk10.yellow(` Status endpoint unreachable (${response.status}); skipped grants list.`));
153521
153669
  console.log();
153522
153670
  return;
153523
153671
  }
@@ -153538,11 +153686,22 @@ var whoamiCommand = new Command9("whoami").description("Show current user, activ
153538
153686
  console.log();
153539
153687
  console.log(chalk10.bold(" Skill Vault Identity"));
153540
153688
  console.log();
153541
- if (config.publisher_id) {
153542
- console.log(` ${"Publisher ID:".padEnd(22)} ${config.publisher_id}`);
153543
- }
153544
- if (config.email) {
153545
- console.log(` ${"Email:".padEnd(22)} ${config.email}`);
153689
+ if (authMe) {
153690
+ const roleColor = authMe.role === "owner" ? chalk10.green : authMe.role === "admin" ? chalk10.cyan : chalk10.dim;
153691
+ const roleLabel = authMe.role || (authMe.is_owner ? "owner" : "member");
153692
+ console.log(` ${"You:".padEnd(22)} ${authMe.email} ${roleColor(`(${roleLabel})`)}`);
153693
+ const publisherDisplay = authMe.publisher_name ? `${authMe.publisher_name} ${chalk10.dim(`(${authMe.publisher_id})`)}` : authMe.publisher_id;
153694
+ console.log(` ${"Publisher:".padEnd(22)} ${publisherDisplay}`);
153695
+ if (!authMe.is_owner) {
153696
+ console.log(` ${chalk10.dim(" (you are a team member of this publisher)".padEnd(22))}`);
153697
+ }
153698
+ } else {
153699
+ if (config.publisher_id) {
153700
+ console.log(` ${"Publisher ID:".padEnd(22)} ${config.publisher_id}`);
153701
+ }
153702
+ if (config.email) {
153703
+ console.log(` ${"Email:".padEnd(22)} ${config.email}`);
153704
+ }
153546
153705
  }
153547
153706
  console.log(` ${"Host ID:".padEnd(22)} ${data.host_id ?? config.host_id}`);
153548
153707
  console.log(` ${"Agent ID:".padEnd(22)} ${data.agent_id ?? config.agent_id}`);
@@ -153627,7 +153786,7 @@ var reportCommand = new Command10("report").description("Report a telemetry even
153627
153786
  import { Command as Command11 } from "commander";
153628
153787
  import chalk12 from "chalk";
153629
153788
  import { existsSync as existsSync8, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
153630
- import { resolve as resolve6, join as join8 } from "node:path";
153789
+ import { resolve as resolve7, join as join8 } from "node:path";
153631
153790
  function toTitleCase(name) {
153632
153791
  return name.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
153633
153792
  }
@@ -153664,7 +153823,7 @@ var initCommand = new Command11("init").description("Scaffold a new skill direct
153664
153823
  process.stderr.write(chalk12.dim(" skillvault-publisher init my-awesome-skill\n"));
153665
153824
  process.exit(1);
153666
153825
  }
153667
- const dirPath = resolve6(name);
153826
+ const dirPath = resolve7(name);
153668
153827
  if (existsSync8(dirPath)) {
153669
153828
  process.stderr.write(chalk12.red(`Error: Directory "${name}" already exists.
153670
153829
  `));
@@ -153779,11 +153938,9 @@ var listCommand = new Command13("list").description("List your published skills"
153779
153938
  headers: ctx.headers
153780
153939
  });
153781
153940
  if (!response.ok) {
153782
- const err = await response.json().catch(() => ({
153783
- error: "unknown",
153784
- message: response.statusText
153785
- }));
153786
- process.stderr.write(chalk14.red(`Failed to list skills: ${err.message}
153941
+ const err = await response.json().catch(() => null);
153942
+ const reason = err?.message || err?.error || response.statusText || `HTTP ${response.status}`;
153943
+ process.stderr.write(chalk14.red(`Failed to list skills: ${reason}
153787
153944
  `));
153788
153945
  process.exit(1);
153789
153946
  }
@@ -153842,7 +153999,7 @@ var listCommand = new Command13("list").description("List your published skills"
153842
153999
  import { Command as Command14 } from "commander";
153843
154000
  import chalk15 from "chalk";
153844
154001
  import { readFileSync as readFileSync9, existsSync as existsSync9, statSync as statSync5, readdirSync as readdirSync2 } from "node:fs";
153845
- import { resolve as resolve7, join as join9, basename } from "node:path";
154002
+ import { resolve as resolve8, join as join9, basename } from "node:path";
153846
154003
  import { createHash as createHash6 } from "node:crypto";
153847
154004
  function parseFrontmatter3(content) {
153848
154005
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -153990,7 +154147,7 @@ var publishAllCommand = new Command14("publish-all").description("Discover and p
153990
154147
  skillDirs = entries.map((e) => e.path);
153991
154148
  console.log(chalk15.dim(`Iterating ${skillDirs.length} registered workspace${skillDirs.length === 1 ? "" : "s"}` + (options.current ? " (current account only)" : "") + "..."));
153992
154149
  } else {
153993
- const rootDir = resolve7(directory);
154150
+ const rootDir = resolve8(directory);
153994
154151
  if (!existsSync9(rootDir) || !statSync5(rootDir).isDirectory()) {
153995
154152
  process.stderr.write(chalk15.red(`Error: "${rootDir}" is not a valid directory
153996
154153
  `));
@@ -154270,12 +154427,12 @@ var inviteListCommand = new Command15("list").description("List all invites").op
154270
154427
  }
154271
154428
  console.log(`Invites (${invites.length}):
154272
154429
  `);
154273
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
154274
- console.log(chalk16.dim(`${pad3("Code", 14)} ${pad3("Email", 30)} ${pad3("Status", 10)} ${pad3("Skills", 25)} ${pad3("Created", 12)}`));
154430
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
154431
+ console.log(chalk16.dim(`${pad4("Code", 14)} ${pad4("Email", 30)} ${pad4("Status", 10)} ${pad4("Skills", 25)} ${pad4("Created", 12)}`));
154275
154432
  console.log(chalk16.dim("\u2500".repeat(95)));
154276
154433
  for (const inv of invites) {
154277
154434
  const statusColor = inv.status === "redeemed" ? chalk16.green : inv.status === "expired" ? chalk16.dim : chalk16.yellow;
154278
- console.log(`${pad3(inv.code, 14)} ${pad3(inv.customer_email || "(open)", 30)} ${statusColor(pad3(inv.status, 10))} ${pad3(inv.capabilities.join(", ") || "(none)", 25)} ${pad3(new Date(inv.created_at).toLocaleDateString(), 12)}`);
154435
+ console.log(`${pad4(inv.code, 14)} ${pad4(inv.customer_email || "(open)", 30)} ${statusColor(pad4(inv.status, 10))} ${pad4(inv.capabilities.join(", ") || "(none)", 25)} ${pad4(new Date(inv.created_at).toLocaleDateString(), 12)}`);
154279
154436
  }
154280
154437
  } catch (err) {
154281
154438
  process.stderr.write(chalk16.red(`Error: ${err instanceof Error ? err.message : String(err)}
@@ -155014,18 +155171,18 @@ var grantsCommand = new Command23("grants").description("List capability grants
155014
155171
  console.log();
155015
155172
  console.log(chalk20.bold(" Capability Grants"));
155016
155173
  console.log();
155017
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
155018
- const header = `${pad3("Grant ID", 22)} ${pad3("Skill", 24)} ${pad3("Customer", 22)} ${pad3("Status", 10)} ${pad3("Granted", 12)} ${"Expires"}`;
155174
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
155175
+ const header = `${pad4("Grant ID", 22)} ${pad4("Skill", 24)} ${pad4("Customer", 22)} ${pad4("Status", 10)} ${pad4("Granted", 12)} ${"Expires"}`;
155019
155176
  console.log(` ${chalk20.dim(header)}`);
155020
155177
  console.log(` ${chalk20.dim("\u2500".repeat(header.length))}`);
155021
155178
  for (const grant of grants) {
155022
155179
  const statusColor = grant.status === "active" ? chalk20.green : grant.status === "revoked" ? chalk20.red : grant.status === "expired" ? chalk20.yellow : chalk20.white;
155023
155180
  const row = [
155024
- pad3(grant.id, 22),
155025
- pad3(grant.capability, 24),
155026
- pad3(grant.customer_id || "-", 22),
155027
- statusColor(pad3(grant.status, 10)),
155028
- pad3(new Date(grant.created_at).toLocaleDateString(), 12),
155181
+ pad4(grant.id, 22),
155182
+ pad4(grant.capability, 24),
155183
+ pad4(grant.customer_id || "-", 22),
155184
+ statusColor(pad4(grant.status, 10)),
155185
+ pad4(new Date(grant.created_at).toLocaleDateString(), 12),
155029
155186
  grant.expires_at ? new Date(grant.expires_at).toLocaleDateString() : "never"
155030
155187
  ].join(" ");
155031
155188
  console.log(` ${row}`);
@@ -155101,18 +155258,18 @@ var customersCommand = new Command25("customers").description("List your custome
155101
155258
  console.log();
155102
155259
  console.log(chalk22.bold(" Customers"));
155103
155260
  console.log();
155104
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
155105
- const header = `${pad3("Email", 32)} ${pad3("Skills", 8)} ${pad3("Loads", 8)} ${pad3("Status", 10)} Last Active`;
155261
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
155262
+ const header = `${pad4("Email", 32)} ${pad4("Skills", 8)} ${pad4("Loads", 8)} ${pad4("Status", 10)} Last Active`;
155106
155263
  console.log(` ${chalk22.dim(header)}`);
155107
155264
  console.log(` ${chalk22.dim("\u2500".repeat(header.length))}`);
155108
155265
  for (const c of customers) {
155109
155266
  const statusColor = c.status === "active" ? chalk22.green : chalk22.dim;
155110
155267
  const lastActive = c.last_active_at ? new Date(c.last_active_at).toLocaleDateString() : chalk22.dim("never");
155111
155268
  const row = [
155112
- pad3(c.email, 32),
155113
- pad3(String(c.skills_count), 8),
155114
- pad3(String(c.total_loads), 8),
155115
- statusColor(pad3(c.status, 10)),
155269
+ pad4(c.email, 32),
155270
+ pad4(String(c.skills_count), 8),
155271
+ pad4(String(c.total_loads), 8),
155272
+ statusColor(pad4(c.status, 10)),
155116
155273
  lastActive
155117
155274
  ].join(" ");
155118
155275
  console.log(` ${row}`);
@@ -155164,15 +155321,15 @@ var analyticsCommand = new Command26("analytics").description("View publisher an
155164
155321
  console.log();
155165
155322
  console.log(chalk23.bold(" Top Skills"));
155166
155323
  console.log();
155167
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
155168
- const header = `${pad3("Skill", 28)} ${pad3("Decryptions", 14)} Active Licenses`;
155324
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
155325
+ const header = `${pad4("Skill", 28)} ${pad4("Decryptions", 14)} Active Licenses`;
155169
155326
  console.log(` ${chalk23.dim(header)}`);
155170
155327
  console.log(` ${chalk23.dim("\u2500".repeat(header.length))}`);
155171
155328
  for (const skill of data.top_skills) {
155172
155329
  const stats = statsMap.get(skill.name);
155173
155330
  const row = [
155174
- pad3(skill.name, 28),
155175
- pad3(String(skill.decryptions), 14),
155331
+ pad4(skill.name, 28),
155332
+ pad4(String(skill.decryptions), 14),
155176
155333
  String(stats?.active_licenses ?? "-")
155177
155334
  ].join(" ");
155178
155335
  console.log(` ${row}`);
@@ -155305,8 +155462,8 @@ var auditCommand = new Command28("audit").description("View audit log events").o
155305
155462
  console.log();
155306
155463
  console.log(chalk25.bold(" Audit Log"));
155307
155464
  console.log();
155308
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
155309
- const header = `${pad3("Event Type", 22)} ${pad3("Skill", 24)} ${pad3("Agent", 20)} ${"Time"}`;
155465
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
155466
+ const header = `${pad4("Event Type", 22)} ${pad4("Skill", 24)} ${pad4("Agent", 20)} ${"Time"}`;
155310
155467
  console.log(` ${chalk25.dim(header)}`);
155311
155468
  console.log(` ${chalk25.dim("\u2500".repeat(header.length))}`);
155312
155469
  for (const event of events) {
@@ -155314,9 +155471,9 @@ var auditCommand = new Command28("audit").description("View audit log events").o
155314
155471
  const agent = event.agent_id ? event.agent_id.slice(0, 18) : "-";
155315
155472
  const time = new Date(event.created_at).toLocaleString();
155316
155473
  const row = [
155317
- pad3(event.event_type, 22),
155318
- pad3(skill, 24),
155319
- pad3(agent, 20),
155474
+ pad4(event.event_type, 22),
155475
+ pad4(skill, 24),
155476
+ pad4(agent, 20),
155320
155477
  time
155321
155478
  ].join(" ");
155322
155479
  console.log(` ${row}`);
@@ -155771,7 +155928,7 @@ var watchtowerCommand = new Command30("watchtower").description("Watchtower secu
155771
155928
  return;
155772
155929
  }
155773
155930
  const { summary } = data;
155774
- const pad3 = (s, n) => s.padEnd(n).slice(0, n);
155931
+ const pad4 = (s, n) => s.padEnd(n).slice(0, n);
155775
155932
  console.log();
155776
155933
  console.log(chalk27.bold(" Watchtower \u2014 Security Overview"));
155777
155934
  console.log();
@@ -155780,28 +155937,28 @@ var watchtowerCommand = new Command30("watchtower").description("Watchtower secu
155780
155937
  console.log();
155781
155938
  console.log(chalk27.bold(" Flagged Customers"));
155782
155939
  console.log();
155783
- const header = `${pad3("Agent ID", 20)} ${pad3("Risk Score", 12)} ${pad3("Level", 10)} ${pad3("Strikes", 9)} ${pad3("Status", 12)} Skills`;
155940
+ const header = `${pad4("Agent ID", 20)} ${pad4("Risk Score", 12)} ${pad4("Level", 10)} ${pad4("Strikes", 9)} ${pad4("Status", 12)} Skills`;
155784
155941
  console.log(` ${chalk27.dim(header)}`);
155785
155942
  console.log(` ${chalk27.dim("\u2500".repeat(header.length))}`);
155786
155943
  for (const c of data.flagged_customers) {
155787
155944
  const agentShort = c.agent_id.length > 18 ? c.agent_id.slice(0, 18) + "\u2026" : c.agent_id;
155788
155945
  let levelColored;
155789
155946
  if (c.risk_level === "critical") {
155790
- levelColored = chalk27.red.bold(pad3(c.risk_level, 10));
155947
+ levelColored = chalk27.red.bold(pad4(c.risk_level, 10));
155791
155948
  } else if (c.risk_level === "high") {
155792
- levelColored = chalk27.red(pad3(c.risk_level, 10));
155949
+ levelColored = chalk27.red(pad4(c.risk_level, 10));
155793
155950
  } else if (c.risk_level === "medium") {
155794
- levelColored = chalk27.yellow(pad3(c.risk_level, 10));
155951
+ levelColored = chalk27.yellow(pad4(c.risk_level, 10));
155795
155952
  } else {
155796
- levelColored = chalk27.dim(pad3(c.risk_level, 10));
155953
+ levelColored = chalk27.dim(pad4(c.risk_level, 10));
155797
155954
  }
155798
- const statusColored = c.status === "suspended" ? chalk27.red(pad3(c.status, 12)) : pad3(c.status, 12);
155955
+ const statusColored = c.status === "suspended" ? chalk27.red(pad4(c.status, 12)) : pad4(c.status, 12);
155799
155956
  const skills = c.capabilities.length > 0 ? c.capabilities.join(", ") : chalk27.dim("none");
155800
155957
  const row = [
155801
- pad3(agentShort, 20),
155802
- pad3(String(c.risk_score), 12),
155958
+ pad4(agentShort, 20),
155959
+ pad4(String(c.risk_score), 12),
155803
155960
  levelColored,
155804
- pad3(String(c.strike_count), 9),
155961
+ pad4(String(c.strike_count), 9),
155805
155962
  statusColored,
155806
155963
  skills
155807
155964
  ].join(" ");
@@ -155812,7 +155969,7 @@ var watchtowerCommand = new Command30("watchtower").description("Watchtower secu
155812
155969
  console.log();
155813
155970
  console.log(chalk27.bold(" Recent Security Events"));
155814
155971
  console.log();
155815
- const header = `${pad3("Event Type", 28)} ${pad3("Skill", 24)} ${pad3("Agent", 20)} Time`;
155972
+ const header = `${pad4("Event Type", 28)} ${pad4("Skill", 24)} ${pad4("Agent", 20)} Time`;
155816
155973
  console.log(` ${chalk27.dim(header)}`);
155817
155974
  console.log(` ${chalk27.dim("\u2500".repeat(header.length))}`);
155818
155975
  const events = data.recent_events.slice(0, 10);
@@ -155821,9 +155978,9 @@ var watchtowerCommand = new Command30("watchtower").description("Watchtower secu
155821
155978
  const agent = e.agent_id ? e.agent_id.slice(0, 18) : chalk27.dim("-");
155822
155979
  const time = new Date(e.created_at).toLocaleString();
155823
155980
  const row = [
155824
- pad3(e.event_type, 28),
155825
- pad3(skill, 24),
155826
- pad3(agent, 20),
155981
+ pad4(e.event_type, 28),
155982
+ pad4(skill, 24),
155983
+ pad4(agent, 20),
155827
155984
  time
155828
155985
  ].join(" ");
155829
155986
  console.log(` ${row}`);
@@ -155936,7 +156093,7 @@ var watermarkDecodeCommand = new Command31("watermark-decode").description("Anal
155936
156093
  import { Command as Command32 } from "commander";
155937
156094
  import chalk29 from "chalk";
155938
156095
  import { existsSync as existsSync14, readFileSync as readFileSync17, statSync as statSync6 } from "node:fs";
155939
- import { resolve as resolve8, join as join15 } from "node:path";
156096
+ import { resolve as resolve9, join as join15 } from "node:path";
155940
156097
  function parseFrontmatter4(content) {
155941
156098
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
155942
156099
  if (!match)
@@ -155958,7 +156115,7 @@ function parseFrontmatter4(content) {
155958
156115
  var linkCommand = new Command32("link").description("Link a skill source directory to your publisher account").argument("[directory]", "Skill directory to link (defaults to CWD)").option("--workspace <path>", "Explicit workspace directory (precedence over positional)").option("--force", "Overwrite an existing link to a different publisher_id").option("--name <name>", "Override skill name (defaults to SKILL.md frontmatter name)").option("--capability <cap>", "Override capability_name (advanced \u2014 usually matches skill name)").action(async (directory, options) => {
155959
156116
  try {
155960
156117
  const ctx = requireSession();
155961
- const dirPath = options.workspace ? resolve8(options.workspace) : directory ? resolve8(directory) : process.cwd();
156118
+ const dirPath = options.workspace ? resolve9(options.workspace) : directory ? resolve9(directory) : process.cwd();
155962
156119
  if (!existsSync14(dirPath) || !statSync6(dirPath).isDirectory()) {
155963
156120
  process.stderr.write(chalk29.red(`Error: "${dirPath}" is not a valid directory
155964
156121
  `));
@@ -156022,10 +156179,10 @@ var linkCommand = new Command32("link").description("Link a skill source directo
156022
156179
  import { Command as Command33 } from "commander";
156023
156180
  import chalk30 from "chalk";
156024
156181
  import { existsSync as existsSync15, rmSync as rmSync2, statSync as statSync7 } from "node:fs";
156025
- import { resolve as resolve9, join as join16 } from "node:path";
156182
+ import { resolve as resolve10, join as join16 } from "node:path";
156026
156183
  var unlinkCommand = new Command33("unlink").description("Remove the workspace link from a skill source directory").argument("[directory]", "Skill directory to unlink (defaults to CWD)").option("--workspace <path>", "Explicit workspace directory (precedence over positional)").option("--keep-registry", "Remove the manifest file but leave the registry entry").action(async (directory, options) => {
156027
156184
  try {
156028
- const dirPath = options.workspace ? resolve9(options.workspace) : directory ? resolve9(directory) : process.cwd();
156185
+ const dirPath = options.workspace ? resolve10(options.workspace) : directory ? resolve10(directory) : process.cwd();
156029
156186
  if (!existsSync15(dirPath) || !statSync7(dirPath).isDirectory()) {
156030
156187
  process.stderr.write(chalk30.red(`Error: "${dirPath}" is not a valid directory
156031
156188
  `));
@@ -156160,10 +156317,149 @@ workspacesCommand.command("find").description("Print the absolute path of a work
156160
156317
  }
156161
156318
  });
156162
156319
 
156320
+ // dist/commands/team.js
156321
+ import { Command as Command35 } from "commander";
156322
+ import chalk32 from "chalk";
156323
+ function formatRole(role) {
156324
+ if (role === "owner")
156325
+ return chalk32.green("owner");
156326
+ if (role === "admin")
156327
+ return chalk32.cyan("admin");
156328
+ return chalk32.dim("member");
156329
+ }
156330
+ function formatStatus(status) {
156331
+ if (status === "active")
156332
+ return chalk32.green("active");
156333
+ if (status === "pending")
156334
+ return chalk32.yellow("pending");
156335
+ return chalk32.dim(status);
156336
+ }
156337
+ function pad3(s, n) {
156338
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, "");
156339
+ if (visible.length >= n)
156340
+ return s;
156341
+ return s + " ".repeat(n - visible.length);
156342
+ }
156343
+ var teamCommand = new Command35("team").description("Manage your publisher team members").option("--json", "Output as JSON").action(async (options) => {
156344
+ try {
156345
+ const ctx = requireSession();
156346
+ const data = await sessionFetch(ctx, `/publishers/${ctx.publisherId}/team`);
156347
+ const members = data.members || [];
156348
+ if (options.json) {
156349
+ console.log(JSON.stringify(members, null, 2));
156350
+ return;
156351
+ }
156352
+ if (members.length === 0) {
156353
+ console.log();
156354
+ console.log(chalk32.dim(" No team members yet. Invite someone:"));
156355
+ console.log(chalk32.cyan(" skillvault-publisher team add <email> [--role member|admin]"));
156356
+ console.log();
156357
+ return;
156358
+ }
156359
+ console.log();
156360
+ console.log(chalk32.bold(` Team members (${members.length})`));
156361
+ console.log();
156362
+ const header = `${pad3(chalk32.dim("Email"), 36)} ${pad3(chalk32.dim("Role"), 14)} ${pad3(chalk32.dim("Status"), 14)} ${chalk32.dim("Member ID")}`;
156363
+ console.log(` ${header}`);
156364
+ console.log(` ${chalk32.dim("\u2500".repeat(80))}`);
156365
+ for (const m of members) {
156366
+ const row = `${pad3(m.email, 36)} ${pad3(formatRole(m.role), 14)} ${pad3(formatStatus(m.status), 14)} ${chalk32.dim(m.id)}`;
156367
+ console.log(` ${row}`);
156368
+ }
156369
+ console.log();
156370
+ console.log(chalk32.dim(` Logged in as publisher ${ctx.publisherId}`));
156371
+ console.log();
156372
+ } catch (err) {
156373
+ const message = err instanceof Error ? err.message : String(err);
156374
+ process.stderr.write(chalk32.red(`team list failed: ${message}
156375
+ `));
156376
+ process.exit(1);
156377
+ }
156378
+ });
156379
+ teamCommand.command("add").description("Invite someone to your publisher team. Sends a magic-link email.").argument("<email>", "The email address to invite").option("--role <role>", "Role: member (default) or admin", "member").option("--name <name>", "Display name for the invitee (optional)").option("--json", "Output as JSON").action(async (email, options) => {
156380
+ try {
156381
+ if (!["member", "admin"].includes(options.role)) {
156382
+ process.stderr.write(chalk32.red(`Invalid role "${options.role}". Use --role member or --role admin.
156383
+ `));
156384
+ process.exit(1);
156385
+ }
156386
+ const ctx = requireSession();
156387
+ const member = await sessionFetch(ctx, `/publishers/${ctx.publisherId}/team`, {
156388
+ method: "POST",
156389
+ body: { email, role: options.role, name: options.name || null }
156390
+ });
156391
+ if (options.json) {
156392
+ console.log(JSON.stringify(member, null, 2));
156393
+ return;
156394
+ }
156395
+ console.log();
156396
+ console.log(chalk32.green(` \u2713 Invited ${email} as ${formatRole(options.role)}`));
156397
+ console.log(chalk32.dim(` Member ID: ${member.id}`));
156398
+ console.log(chalk32.dim(` Status: ${formatStatus(member.status)}`));
156399
+ console.log();
156400
+ console.log(chalk32.dim(" A magic-link invitation email has been sent. The new member can"));
156401
+ console.log(chalk32.dim(" click the link to activate, then run:"));
156402
+ console.log(chalk32.cyan(` skillvault-publisher login --email ${email}`));
156403
+ console.log();
156404
+ } catch (err) {
156405
+ const message = err instanceof Error ? err.message : String(err);
156406
+ process.stderr.write(chalk32.red(`team add failed: ${message}
156407
+ `));
156408
+ process.exit(1);
156409
+ }
156410
+ });
156411
+ teamCommand.command("remove").description("Remove a team member by email or member ID").argument("<email-or-id>", "The member email address or member ID (tm_xxx)").option("--yes", "Skip confirmation").option("--json", "Output as JSON").action(async (target, options) => {
156412
+ try {
156413
+ const ctx = requireSession();
156414
+ const listData = await sessionFetch(ctx, `/publishers/${ctx.publisherId}/team`);
156415
+ const members = listData.members || [];
156416
+ const member = members.find((m) => m.id === target || m.email === target);
156417
+ if (!member) {
156418
+ process.stderr.write(chalk32.red(`No team member found matching "${target}".
156419
+ `));
156420
+ process.stderr.write(chalk32.dim("Run `skillvault-publisher team` to see the current list.\n"));
156421
+ process.exit(1);
156422
+ }
156423
+ if (member.role === "owner") {
156424
+ process.stderr.write(chalk32.red("Cannot remove the owner of a publisher account.\n"));
156425
+ process.exit(1);
156426
+ }
156427
+ if (member.status === "removed") {
156428
+ process.stderr.write(chalk32.yellow(`${member.email} is already removed.
156429
+ `));
156430
+ process.exit(0);
156431
+ }
156432
+ if (!options.yes) {
156433
+ console.log();
156434
+ console.log(chalk32.yellow.bold(" Warning: ") + "This will revoke " + chalk32.bold(member.email) + `'s access to publisher ${ctx.publisherId}.`);
156435
+ console.log();
156436
+ console.log(` Email: ${chalk32.cyan(member.email)}`);
156437
+ console.log(` Role: ${formatRole(member.role)}`);
156438
+ console.log(` Status: ${formatStatus(member.status)}`);
156439
+ console.log(` Added: ${chalk32.dim(new Date(member.created_at).toLocaleDateString())}`);
156440
+ console.log();
156441
+ console.log(chalk32.dim(" Add --yes to confirm."));
156442
+ console.log();
156443
+ process.exit(0);
156444
+ }
156445
+ await sessionFetch(ctx, `/publishers/${ctx.publisherId}/team/${member.id}`, { method: "DELETE" });
156446
+ if (options.json) {
156447
+ console.log(JSON.stringify({ removed: true, member_id: member.id, email: member.email }, null, 2));
156448
+ return;
156449
+ }
156450
+ console.log(chalk32.green(` \u2713 Removed ${member.email} from the team`));
156451
+ } catch (err) {
156452
+ const message = err instanceof Error ? err.message : String(err);
156453
+ process.stderr.write(chalk32.red(`team remove failed: ${message}
156454
+ `));
156455
+ process.exit(1);
156456
+ }
156457
+ });
156458
+
156163
156459
  // dist/index.js
156164
156460
  var __dirname2 = dirname5(fileURLToPath(import.meta.url));
156165
156461
  var pkg = JSON.parse(readFileSync18(join17(__dirname2, "..", "package.json"), "utf8"));
156166
- var program = new Command35();
156462
+ var program = new Command36();
156167
156463
  program.name("skillvault-publisher").description("SkillVault publisher CLI \u2014 publish, manage, and distribute encrypted skills").version(pkg.version).addHelpText("after", `
156168
156464
 
156169
156465
  Getting Started:
@@ -156208,6 +156504,7 @@ program.addCommand(watermarkDecodeCommand);
156208
156504
  program.addCommand(linkCommand);
156209
156505
  program.addCommand(unlinkCommand);
156210
156506
  program.addCommand(workspacesCommand);
156507
+ program.addCommand(teamCommand);
156211
156508
  program.parse();
156212
156509
  /*! Bundled license information:
156213
156510