skillwiki 0.10.108 → 0.10.110

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 (30) hide show
  1. package/dist/{chunk-JCDAEZVD.js → chunk-5MV5SSRM.js} +1 -1
  2. package/dist/{chunk-Z4NCF6K6.js → chunk-6X6PPQNI.js} +481 -179
  3. package/dist/{chunk-DTTKCCQ4.js → chunk-KX5Y3GSX.js} +2 -2
  4. package/dist/{chunk-QU2IXZRN.js → chunk-NA3TZ4JN.js} +2 -2
  5. package/dist/{chunk-PGD3J2GE.js → chunk-QU2CKXM4.js} +49 -37
  6. package/dist/{chunk-GJC2CI3W.js → chunk-R2DJ7IJ5.js} +103 -0
  7. package/dist/{chunk-HMHMCPCE.js → chunk-WPJHDRTP.js} +1 -1
  8. package/dist/cli.js +135 -101
  9. package/dist/{index-projection-ESBJOBS7.js → index-projection-6XGESSHV.js} +3 -3
  10. package/dist/{managed-write-preflight-AYD3CJYD.js → managed-write-preflight-JPOBVVSU.js} +4 -4
  11. package/dist/skillwiki-mcp.js +7 -7
  12. package/dist/sources-CLOF5VAT.js +9 -0
  13. package/dist/vault-sync/scripts/wiki-snapshot.sh +20 -12
  14. package/package.json +1 -1
  15. package/skills/.claude-plugin/plugin.json +1 -1
  16. package/skills/.codex-plugin/plugin.json +1 -1
  17. package/skills/.cursor-plugin/plugin.json +1 -1
  18. package/skills/package.json +1 -1
  19. package/skills/skills/skillwiki-connect/SKILL.md +8 -0
  20. package/skills/skills/skillwiki-mcp/SKILL.md +8 -0
  21. package/skills/skills/using-skillwiki/SKILL.md +1 -1
  22. package/skills/skills/wiki-init/SKILL.md +12 -11
  23. package/skills/skillwiki-connect/SKILL.md +8 -0
  24. package/skills/skillwiki-mcp/SKILL.md +8 -0
  25. package/skills/using-skillwiki/SKILL.md +1 -1
  26. package/skills/wiki-init/SKILL.md +12 -11
  27. package/templates/launchd/com.karlchow.skillwiki-clip-outbox.plist +22 -0
  28. package/templates/web-clipper/llm-wiki-clippings.json +1 -1
  29. package/templates/web-clipper/readme.txt +13 -13
  30. package/dist/sources-QTQSZMST.js +0 -9
@@ -3,11 +3,11 @@ import {
3
3
  ROOT_INDEX_SECTION_ORDER,
4
4
  atomicWriteText,
5
5
  buildRootIndexUniverse
6
- } from "./chunk-HMHMCPCE.js";
6
+ } from "./chunk-WPJHDRTP.js";
7
7
  import {
8
8
  err,
9
9
  ok
10
- } from "./chunk-GJC2CI3W.js";
10
+ } from "./chunk-R2DJ7IJ5.js";
11
11
 
12
12
  // src/utils/index-projection.ts
13
13
  import { readFile } from "fs/promises";
@@ -8,7 +8,7 @@ import {
8
8
  readLogEvents,
9
9
  resolveExistingRegularFileInsideVault,
10
10
  writeLogEvent
11
- } from "./chunk-JCDAEZVD.js";
11
+ } from "./chunk-5MV5SSRM.js";
12
12
  import {
13
13
  ExitCode,
14
14
  RawSourceSchema,
@@ -19,7 +19,7 @@ import {
19
19
  scanSensitiveContent,
20
20
  scanVault,
21
21
  splitFrontmatter
22
- } from "./chunk-GJC2CI3W.js";
22
+ } from "./chunk-R2DJ7IJ5.js";
23
23
 
24
24
  // src/commands/sources.ts
25
25
  import { readFile as readFile4 } from "fs/promises";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  atomicWriteText,
4
4
  buildRootIndexUniverse
5
- } from "./chunk-HMHMCPCE.js";
5
+ } from "./chunk-WPJHDRTP.js";
6
6
  import {
7
7
  authorizeRawOperation,
8
8
  buildSourceReferenceIndex,
@@ -22,7 +22,7 @@ import {
22
22
  resolveExistingRegularFileInsideVault,
23
23
  stripFencedBlocks,
24
24
  writeLogEvent
25
- } from "./chunk-JCDAEZVD.js";
25
+ } from "./chunk-5MV5SSRM.js";
26
26
  import {
27
27
  ExitCode,
28
28
  FleetManifestSchema,
@@ -30,6 +30,7 @@ import {
30
30
  extractFrontmatter,
31
31
  filterGitIgnoredRelativePaths,
32
32
  getErrorMessage,
33
+ isClipOutboxKey,
33
34
  isVaultSyncKey,
34
35
  mapWithConcurrency,
35
36
  ok,
@@ -41,11 +42,27 @@ import {
41
42
  scanVault,
42
43
  splitFrontmatter,
43
44
  vaultIoConcurrency
44
- } from "./chunk-GJC2CI3W.js";
45
+ } from "./chunk-R2DJ7IJ5.js";
46
+
47
+ // src/utils/home.ts
48
+ import { homedir } from "os";
49
+ import { resolve } from "path";
50
+ function resolveSkillwikiHome(home, env = process.env) {
51
+ for (const candidate of [home, env.HOME, env.USERPROFILE]) {
52
+ if (candidate?.trim()) return resolve(candidate);
53
+ }
54
+ try {
55
+ const fallback = homedir();
56
+ if (fallback?.trim()) return resolve(fallback);
57
+ } catch {
58
+ throw new Error("Could not resolve SkillWiki home directory");
59
+ }
60
+ throw new Error("Could not resolve SkillWiki home directory");
61
+ }
45
62
 
46
63
  // src/utils/managed-write-preflight.ts
47
64
  import { existsSync as existsSync11 } from "fs";
48
- import { join as join21, resolve as resolve6 } from "path";
65
+ import { join as join21, resolve as resolve7 } from "path";
49
66
 
50
67
  // src/commands/fleet.ts
51
68
  import { readFile as readFile2 } from "fs/promises";
@@ -88,7 +105,7 @@ function parseDotenvText(text) {
88
105
  if (eq <= 0) continue;
89
106
  const key = line.slice(0, eq).trim();
90
107
  const value = line.slice(eq + 1).trim();
91
- if (!_whitelist.has(key) && !isValidWikiProfileKey(key) && !isVaultSyncKey(key)) continue;
108
+ if (!_whitelist.has(key) && !isValidWikiProfileKey(key) && !isVaultSyncKey(key) && !isClipOutboxKey(key)) continue;
92
109
  if (value.length === 0) continue;
93
110
  out[key] = value;
94
111
  }
@@ -175,7 +192,7 @@ async function runFleetValidate(input) {
175
192
  }
176
193
  async function runFleetContext(input) {
177
194
  const env = input.env ?? process.env;
178
- const home = input.home ?? env.HOME ?? "";
195
+ const home = resolveSkillwikiHome(input.home, env);
179
196
  const cwd = input.cwd ?? process.cwd();
180
197
  const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
181
198
  const user = input.user ?? safeEnvValue(env.USER) ?? safeUserName();
@@ -297,7 +314,7 @@ async function runFleetContext(input) {
297
314
  }
298
315
  function fleetContextEnv(input) {
299
316
  const env = input.env ?? process.env;
300
- const home = input.home ?? env.HOME ?? "";
317
+ const home = resolveSkillwikiHome(input.home, env);
301
318
  const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
302
319
  const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
303
320
  const file = input.file ?? (vault ? join(vault, FLEET_REL_PATH) : void 0);
@@ -2414,7 +2431,7 @@ async function runTopicMapCheck(input) {
2414
2431
  // src/commands/dedup.ts
2415
2432
  import { createHash as createHash3 } from "crypto";
2416
2433
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2417
- import { dirname as dirname3, join as join10, posix, resolve } from "path";
2434
+ import { dirname as dirname3, join as join10, posix, resolve as resolve2 } from "path";
2418
2435
 
2419
2436
  // src/utils/rclone.ts
2420
2437
  import { execFile } from "child_process";
@@ -2649,7 +2666,7 @@ async function runDedup(input) {
2649
2666
  const manifest = safeEntries.length > 0 ? {
2650
2667
  version: 1,
2651
2668
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
2652
- vault: resolve(input.vault),
2669
+ vault: resolve2(input.vault),
2653
2670
  entries: safeEntries
2654
2671
  } : void 0;
2655
2672
  const remoteDeleteCount = safeEntries.reduce((count, entry) => count + entry.duplicates.length, 0);
@@ -2843,11 +2860,11 @@ function readManifest(path) {
2843
2860
  }
2844
2861
  }
2845
2862
  function validateManifestLocalState(vault, manifest) {
2846
- if (resolve(manifest.vault) !== resolve(vault)) {
2863
+ if (resolve2(manifest.vault) !== resolve2(vault)) {
2847
2864
  return err("APPROVAL_INVALID", {
2848
2865
  message: "dedup manifest belongs to a different vault",
2849
2866
  manifest_vault: manifest.vault,
2850
- vault: resolve(vault)
2867
+ vault: resolve2(vault)
2851
2868
  });
2852
2869
  }
2853
2870
  for (const entry of manifest.entries) {
@@ -2929,7 +2946,7 @@ async function runRawBodyDedup(vault, scan, pageTextCache) {
2929
2946
 
2930
2947
  // src/commands/audit.ts
2931
2948
  import { readFile as readFile11, stat as stat2 } from "fs/promises";
2932
- import { dirname as dirname4, resolve as resolve2, join as join11 } from "path";
2949
+ import { dirname as dirname4, resolve as resolve3, join as join11 } from "path";
2933
2950
  async function runAudit(input) {
2934
2951
  let text;
2935
2952
  try {
@@ -2941,7 +2958,7 @@ async function runAudit(input) {
2941
2958
  if (!fm.ok) return { exitCode: ExitCode.INVALID_FRONTMATTER, result: fm };
2942
2959
  const split = splitFrontmatter(text);
2943
2960
  const body = split.ok ? split.data.body : text;
2944
- const vault = await findVaultRoot(dirname4(resolve2(input.file)));
2961
+ const vault = await findVaultRoot(dirname4(resolve3(input.file)));
2945
2962
  if (!vault) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID") };
2946
2963
  const markers = extractCitationMarkers(body);
2947
2964
  const resolved = await Promise.all(markers.map(async (m) => {
@@ -3043,7 +3060,7 @@ async function validateCompoundReferences(vault, existingScan, pageTextCache) {
3043
3060
  // src/commands/path-too-long.ts
3044
3061
  import { existsSync as existsSync3 } from "fs";
3045
3062
  import { mkdir as mkdir4, readFile as readFile12, rename as rename3, unlink as unlink2 } from "fs/promises";
3046
- import { dirname as dirname5, join as join12, posix as posix2, resolve as resolve3 } from "path";
3063
+ import { dirname as dirname5, join as join12, posix as posix2, resolve as resolve4 } from "path";
3047
3064
  var MAX_PATH_LENGTH = 240;
3048
3065
  var WINDOWS_ABSOLUTE_PATH_LIMIT = 259;
3049
3066
  async function runPathTooLong(input) {
@@ -3136,7 +3153,7 @@ function findPathTooLongViolations(pages, maxLength) {
3136
3153
  }
3137
3154
  function maxFixPathLength(vault) {
3138
3155
  if (process.platform !== "win32") return MAX_PATH_LENGTH;
3139
- const root = resolve3(vault);
3156
+ const root = resolve4(vault);
3140
3157
  const separatorBudget = root.endsWith("\\") || root.endsWith("/") ? 0 : 1;
3141
3158
  const absoluteSafeRelLength = WINDOWS_ABSOLUTE_PATH_LIMIT - root.length - separatorBudget;
3142
3159
  return Math.max(1, Math.min(MAX_PATH_LENGTH, absoluteSafeRelLength));
@@ -5723,7 +5740,7 @@ function probeRemoteHealth(input) {
5723
5740
  // src/utils/vault-sync-helper.ts
5724
5741
  import { spawnSync } from "child_process";
5725
5742
  import { existsSync as existsSync7 } from "fs";
5726
- import { homedir, platform } from "os";
5743
+ import { platform } from "os";
5727
5744
  import { dirname as dirname6, join as join16 } from "path";
5728
5745
  import { fileURLToPath } from "url";
5729
5746
  var HELPER_NAME = "wiki-pull-with-auto-resolve.sh";
@@ -5746,13 +5763,7 @@ function candidateHelperPaths(input = { vault: "" }) {
5746
5763
  paths.push(join16(here, "..", "..", "vault-sync", "scripts", HELPER_NAME));
5747
5764
  paths.push(join16(here, "..", "..", "..", "vault-sync", "scripts", HELPER_NAME));
5748
5765
  }
5749
- const home = input.home ?? env.HOME ?? env.USERPROFILE ?? (() => {
5750
- try {
5751
- return homedir();
5752
- } catch {
5753
- return void 0;
5754
- }
5755
- })();
5766
+ const home = resolveSkillwikiHome(input.home, env);
5756
5767
  if (home) {
5757
5768
  const xdg = env.XDG_DATA_HOME;
5758
5769
  const isDarwin = platform() === "darwin";
@@ -5942,7 +5953,7 @@ function runSyncStatus(input) {
5942
5953
  output.stashes = stashes;
5943
5954
  }
5944
5955
  if (input.includeRemoteHealth) {
5945
- const home = input.home ?? process.env.HOME ?? "";
5956
+ const home = resolveSkillwikiHome(input.home);
5946
5957
  const remote_health = probeRemoteHealth({
5947
5958
  vaultPath: vault,
5948
5959
  home,
@@ -6527,7 +6538,7 @@ import {
6527
6538
  writeFileSync as writeFileSync4
6528
6539
  } from "fs";
6529
6540
  import { hostname } from "os";
6530
- import { dirname as dirname7, join as join18, resolve as resolve4 } from "path";
6541
+ import { dirname as dirname7, join as join18, resolve as resolve5 } from "path";
6531
6542
  function managedWriteLockPath(vault) {
6532
6543
  const gitPath = git(vault, ["rev-parse", "--git-path", "vault-sync/managed-write.lock"]);
6533
6544
  if (gitPath) return gitPath.startsWith("/") ? gitPath : join18(vault, gitPath);
@@ -6572,7 +6583,7 @@ function hasLocalOwnerProof(vault, record) {
6572
6583
  }
6573
6584
  function reclaimDeadManagedWriteLockOwner(vault, options = {}) {
6574
6585
  const path = managedWriteLockPath(vault);
6575
- const gitStateVault = resolve4(options.gitStateVault ?? vault);
6586
+ const gitStateVault = resolve5(options.gitStateVault ?? vault);
6576
6587
  if (!existsSync9(path)) return ok({ reclaimed: false });
6577
6588
  const record = readLockRecord(path);
6578
6589
  if (!record) {
@@ -6859,7 +6870,7 @@ function supersedeStaleReviewRequiredJournals(vault, opts = {}) {
6859
6870
 
6860
6871
  // src/utils/snapshot-worktree.ts
6861
6872
  import { readFileSync as readFileSync8 } from "fs";
6862
- import { join as join20, resolve as resolve5 } from "path";
6873
+ import { join as join20, resolve as resolve6 } from "path";
6863
6874
  function readSkillWikiConfig(path) {
6864
6875
  try {
6865
6876
  return parseDotenvText(readFileSync8(path, "utf8"));
@@ -6888,12 +6899,12 @@ function resolveConfiguredSnapshotWorktree(home) {
6888
6899
  if (!home) return void 0;
6889
6900
  const config = readSkillWikiConfig(join20(home, ".skillwiki", ".env"));
6890
6901
  const explicit = config["vault_sync.snapshot_worktree"];
6891
- if (explicit) return resolve5(explicit);
6902
+ if (explicit) return resolve6(explicit);
6892
6903
  const snapshotProfile = config["vault_sync.snapshot_profile"];
6893
6904
  if (!snapshotProfile) return void 0;
6894
- const profile = readSnapshotProfile(resolve5(snapshotProfile));
6905
+ const profile = readSnapshotProfile(resolve6(snapshotProfile));
6895
6906
  const fromProfile = profile.WIKI_GIT_WORKTREE ?? profile.SNAPSHOT_WORKTREE ?? profile.GIT_DIR;
6896
- return fromProfile ? resolve5(fromProfile) : void 0;
6907
+ return fromProfile ? resolve6(fromProfile) : void 0;
6897
6908
  }
6898
6909
 
6899
6910
  // src/utils/managed-write-preflight.ts
@@ -6910,7 +6921,7 @@ var DEFAULT_DEPS = {
6910
6921
  converge: (input) => runVaultSyncPullHelper(input),
6911
6922
  resolveConfiguredSnapshotWorktree,
6912
6923
  syncPeers: runSyncPeers,
6913
- sleepMs: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
6924
+ sleepMs: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
6914
6925
  nowMs: () => Date.now(),
6915
6926
  logHeartbeat: (message) => {
6916
6927
  process.stderr.write(`${message}
@@ -7102,8 +7113,8 @@ function isGitVault(vault) {
7102
7113
  return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
7103
7114
  }
7104
7115
  async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
7105
- const mutationVault = resolve6(input.vault);
7106
- let convergenceVault = input.convergenceVault && resolve6(input.convergenceVault) !== mutationVault ? resolve6(input.convergenceVault) : void 0;
7116
+ const mutationVault = resolve7(input.vault);
7117
+ let convergenceVault = input.convergenceVault && resolve7(input.convergenceVault) !== mutationVault ? resolve7(input.convergenceVault) : void 0;
7107
7118
  let convergenceSource = convergenceVault ? "explicit" : "single-path";
7108
7119
  const mutationBlocker = preflightBlocker(mutationVault);
7109
7120
  if (mutationBlocker) {
@@ -7181,7 +7192,7 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
7181
7192
  };
7182
7193
  }
7183
7194
  if (!convergenceVault && host.role === "snapshotter" && host.protected === true) {
7184
- const home = input.home ?? input.env?.HOME ?? process.env.HOME ?? "";
7195
+ const home = resolveSkillwikiHome(input.home, input.env);
7185
7196
  const configured = (deps.resolveConfiguredSnapshotWorktree ?? resolveConfiguredSnapshotWorktree)(home);
7186
7197
  if (!configured) {
7187
7198
  return {
@@ -7193,7 +7204,7 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
7193
7204
  })
7194
7205
  };
7195
7206
  }
7196
- convergenceVault = resolve6(configured);
7207
+ convergenceVault = resolve7(configured);
7197
7208
  convergenceSource = "configured";
7198
7209
  if (convergenceVault === mutationVault) {
7199
7210
  return {
@@ -7302,9 +7313,9 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
7302
7313
  };
7303
7314
  }
7304
7315
  async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
7305
- const mutationVault = resolve6(input.vault);
7316
+ const mutationVault = resolve7(input.vault);
7306
7317
  const lock = acquireManagedWriteLock(mutationVault, input.command, {
7307
- gitStateVault: input.convergenceVault ? resolve6(input.convergenceVault) : mutationVault
7318
+ gitStateVault: input.convergenceVault ? resolve7(input.convergenceVault) : mutationVault
7308
7319
  });
7309
7320
  if (!lock.ok) {
7310
7321
  return { exitCode: ExitCode.SYNC_LOCK_HELD, result: lock };
@@ -7357,6 +7368,7 @@ async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
7357
7368
  }
7358
7369
 
7359
7370
  export {
7371
+ resolveSkillwikiHome,
7360
7372
  readLastOp,
7361
7373
  appendLastOp,
7362
7374
  clearLastOp,
@@ -419,6 +419,106 @@ function parseVaultSyncKeyValue(key, value) {
419
419
  const first = parsed.error.issues[0];
420
420
  return { ok: false, message: first?.message ?? "invalid value" };
421
421
  }
422
+ var clipBool = z.enum(["true", "false"]);
423
+ var clipIntString = (min, max) => z.string().regex(/^[0-9]+$/, "must be a decimal integer").refine((s) => {
424
+ const n = Number(s);
425
+ return Number.isInteger(n) && n >= min && n <= max;
426
+ }, { message: `must be an integer ${min}\u2013${max}` });
427
+ var clipHttpsUrl = z.string().min(1).refine((s) => {
428
+ try {
429
+ const u = new URL(s);
430
+ return u.protocol === "https:" && u.username === "" && u.password === "";
431
+ } catch {
432
+ return false;
433
+ }
434
+ }, { message: "must be an https URL without userinfo" });
435
+ var clipCredentialRef = z.string().min(1).max(128).regex(
436
+ /^[a-zA-Z0-9_.-]+$/,
437
+ "must be a credential reference name, never a secret value"
438
+ );
439
+ var CLIP_IMAGE_TYPES = ["png", "jpeg", "gif", "webp"];
440
+ var CLIP_ALLOWED_PREFIXES = "raw/articles,raw/assets";
441
+ var CLIP_OUTBOX_DEFAULTS = {
442
+ enabled: "false",
443
+ vault_id: "central",
444
+ allowed_prefixes: CLIP_ALLOWED_PREFIXES,
445
+ destination: "seaweed-wiki:cloud/wiki",
446
+ mode: "event-watch",
447
+ poll_fallback_enabled: "true",
448
+ reconcile_seconds: "3600",
449
+ debounce_ms: "500",
450
+ stable_hash_ms: "500",
451
+ max_article_bytes: "2097152",
452
+ max_images: "64",
453
+ max_image_bytes: "20971520",
454
+ max_bundle_bytes: "104857600",
455
+ max_pixels: "40000000",
456
+ download_concurrency: "4",
457
+ image_types: CLIP_IMAGE_TYPES.join(","),
458
+ max_redirects: "5",
459
+ request_timeout_ms: "30000",
460
+ localize_timeout_ms: "120000",
461
+ watcher_label: "com.karlchow.skillwiki-clip-outbox"
462
+ };
463
+ var ClipOutboxConfigSchema = z.object({
464
+ "clip_outbox.enabled": clipBool,
465
+ "clip_outbox.root": absolutePath,
466
+ "clip_outbox.state_root": absolutePath,
467
+ "clip_outbox.mirror_root": absolutePath,
468
+ "clip_outbox.d8_note_root": absolutePath,
469
+ "clip_outbox.d8_note_absent": clipBool,
470
+ "clip_outbox.vault_id": z.literal("central"),
471
+ "clip_outbox.allowed_prefixes": z.string().refine((s) => {
472
+ const parts = s.split(",").map((p) => p.trim()).filter(Boolean).sort();
473
+ return parts.join(",") === "raw/articles,raw/assets";
474
+ }, { message: "must be exactly raw/articles,raw/assets" }),
475
+ "clip_outbox.destination": z.literal("seaweed-wiki:cloud/wiki"),
476
+ "clip_outbox.mode": z.literal("event-watch"),
477
+ "clip_outbox.poll_fallback_enabled": clipBool,
478
+ "clip_outbox.reconcile_seconds": clipIntString(1, 86400),
479
+ "clip_outbox.debounce_ms": clipIntString(100, 5e3),
480
+ "clip_outbox.stable_hash_ms": clipIntString(100, 5e3),
481
+ "clip_outbox.max_article_bytes": clipIntString(1, 2097152),
482
+ "clip_outbox.max_images": clipIntString(0, 64),
483
+ "clip_outbox.max_image_bytes": clipIntString(1, 20971520),
484
+ "clip_outbox.max_bundle_bytes": clipIntString(1, 104857600),
485
+ "clip_outbox.max_pixels": clipIntString(1, 4e7),
486
+ "clip_outbox.download_concurrency": clipIntString(1, 4),
487
+ "clip_outbox.image_types": z.string().refine((s) => {
488
+ const parts = s.split(",").map((p) => p.trim()).filter(Boolean);
489
+ if (parts.length === 0) return false;
490
+ const allowed = new Set(CLIP_IMAGE_TYPES);
491
+ const seen = /* @__PURE__ */ new Set();
492
+ for (const p of parts) {
493
+ if (!allowed.has(p) || seen.has(p)) return false;
494
+ seen.add(p);
495
+ }
496
+ return true;
497
+ }, { message: "must be a unique subset of png,jpeg,gif,webp" }),
498
+ "clip_outbox.max_redirects": clipIntString(0, 5),
499
+ "clip_outbox.request_timeout_ms": clipIntString(1, 3e4),
500
+ "clip_outbox.localize_timeout_ms": clipIntString(1, 12e4),
501
+ "clip_outbox.ready_url": clipHttpsUrl,
502
+ "clip_outbox.s3_credential_ref": clipCredentialRef,
503
+ "clip_outbox.ready_credential_ref": clipCredentialRef,
504
+ "clip_outbox.watcher_label": z.literal("com.karlchow.skillwiki-clip-outbox")
505
+ }).strict();
506
+ var CLIP_OUTBOX_KEYS = Object.keys(ClipOutboxConfigSchema.shape);
507
+ function isClipOutboxKey(key) {
508
+ return CLIP_OUTBOX_KEYS.includes(key);
509
+ }
510
+ function parseClipOutboxKeyValue(key, value) {
511
+ if (!isClipOutboxKey(key)) {
512
+ return { ok: false, message: `unknown clip_outbox key: ${key}` };
513
+ }
514
+ const fieldSchema = ClipOutboxConfigSchema.shape[key];
515
+ const parsed = fieldSchema.safeParse(value);
516
+ if (parsed.success) {
517
+ return { ok: true };
518
+ }
519
+ const first = parsed.error.issues[0];
520
+ return { ok: false, message: first?.message ?? "invalid value" };
521
+ }
422
522
  function detectSchema(fm) {
423
523
  const COMPOUND_TYPES = /* @__PURE__ */ new Set(["lesson", "pattern", "antipattern", "gotcha"]);
424
524
  if (typeof fm.type === "string" && COMPOUND_TYPES.has(fm.type) && "project" in fm) return { schema: "compound" };
@@ -834,6 +934,9 @@ export {
834
934
  FleetManifestSchema,
835
935
  isVaultSyncKey,
836
936
  parseVaultSyncKeyValue,
937
+ CLIP_OUTBOX_DEFAULTS,
938
+ isClipOutboxKey,
939
+ parseClipOutboxKeyValue,
837
940
  detectSchema,
838
941
  isBlockedHost,
839
942
  getErrorMessage,
@@ -8,7 +8,7 @@ import {
8
8
  ok,
9
9
  scanSensitiveContent,
10
10
  scanVault
11
- } from "./chunk-GJC2CI3W.js";
11
+ } from "./chunk-R2DJ7IJ5.js";
12
12
 
13
13
  // src/utils/atomic-write.ts
14
14
  import { randomBytes } from "crypto";