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.
- package/dist/{chunk-JCDAEZVD.js → chunk-5MV5SSRM.js} +1 -1
- package/dist/{chunk-Z4NCF6K6.js → chunk-6X6PPQNI.js} +481 -179
- package/dist/{chunk-DTTKCCQ4.js → chunk-KX5Y3GSX.js} +2 -2
- package/dist/{chunk-QU2IXZRN.js → chunk-NA3TZ4JN.js} +2 -2
- package/dist/{chunk-PGD3J2GE.js → chunk-QU2CKXM4.js} +49 -37
- package/dist/{chunk-GJC2CI3W.js → chunk-R2DJ7IJ5.js} +103 -0
- package/dist/{chunk-HMHMCPCE.js → chunk-WPJHDRTP.js} +1 -1
- package/dist/cli.js +135 -101
- package/dist/{index-projection-ESBJOBS7.js → index-projection-6XGESSHV.js} +3 -3
- package/dist/{managed-write-preflight-AYD3CJYD.js → managed-write-preflight-JPOBVVSU.js} +4 -4
- package/dist/skillwiki-mcp.js +7 -7
- package/dist/sources-CLOF5VAT.js +9 -0
- package/dist/vault-sync/scripts/wiki-snapshot.sh +20 -12
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/.cursor-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/skills/skillwiki-connect/SKILL.md +8 -0
- package/skills/skills/skillwiki-mcp/SKILL.md +8 -0
- package/skills/skills/using-skillwiki/SKILL.md +1 -1
- package/skills/skills/wiki-init/SKILL.md +12 -11
- package/skills/skillwiki-connect/SKILL.md +8 -0
- package/skills/skillwiki-mcp/SKILL.md +8 -0
- package/skills/using-skillwiki/SKILL.md +1 -1
- package/skills/wiki-init/SKILL.md +12 -11
- package/templates/launchd/com.karlchow.skillwiki-clip-outbox.plist +22 -0
- package/templates/web-clipper/llm-wiki-clippings.json +1 -1
- package/templates/web-clipper/readme.txt +13 -13
- 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-
|
|
6
|
+
} from "./chunk-WPJHDRTP.js";
|
|
7
7
|
import {
|
|
8
8
|
err,
|
|
9
9
|
ok
|
|
10
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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 (
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
6905
|
+
const profile = readSnapshotProfile(resolve6(snapshotProfile));
|
|
6895
6906
|
const fromProfile = profile.WIKI_GIT_WORKTREE ?? profile.SNAPSHOT_WORKTREE ?? profile.GIT_DIR;
|
|
6896
|
-
return fromProfile ?
|
|
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((
|
|
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 =
|
|
7106
|
-
let convergenceVault = input.convergenceVault &&
|
|
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
|
|
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 =
|
|
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 =
|
|
7316
|
+
const mutationVault = resolve7(input.vault);
|
|
7306
7317
|
const lock = acquireManagedWriteLock(mutationVault, input.command, {
|
|
7307
|
-
gitStateVault: input.convergenceVault ?
|
|
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,
|