skillwiki 0.10.111 → 0.10.112

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 (40) hide show
  1. package/dist/{chunk-TDBUJUYH.js → chunk-25OXNFDV.js} +42 -18
  2. package/dist/{chunk-6OQLBNKR.js → chunk-ABRYBO4T.js} +29 -4
  3. package/dist/{chunk-OJJBZDPR.js → chunk-APKDTEVV.js} +178 -210
  4. package/dist/{chunk-5IBLXGLY.js → chunk-CKQPUHM2.js} +2 -2
  5. package/dist/chunk-DOHWNPAZ.js +60 -0
  6. package/dist/{chunk-GQWCTJTU.js → chunk-EE7W3GLA.js} +1 -1
  7. package/dist/{chunk-KWEYA3OE.js → chunk-FCA3RRNF.js} +1 -1
  8. package/dist/{chunk-MRPJ7X7W.js → chunk-IR2DI4Y5.js} +3 -3
  9. package/dist/chunk-K3M6OSUW.js +77 -0
  10. package/dist/{chunk-I6IRJCUJ.js → chunk-KJG2CSYD.js} +2 -2
  11. package/dist/chunk-LOVLZNLX.js +111 -0
  12. package/dist/{chunk-HBJLFPQP.js → chunk-Q6EZF37H.js} +8 -8
  13. package/dist/{chunk-JQV5KNPT.js → chunk-RR6VDWSI.js} +3 -3
  14. package/dist/{chunk-PYPPG7RF.js → chunk-SKOLE6US.js} +1 -1
  15. package/dist/{chunk-X5GL2FCU.js → chunk-TKX6PSWZ.js} +1 -1
  16. package/dist/cli.js +77 -33
  17. package/dist/{index-projection-2K672CBO.js → index-projection-UNZG5UJZ.js} +5 -5
  18. package/dist/{journal-QU5IDXQF.js → journal-5IW3TZ5F.js} +3 -3
  19. package/dist/{managed-write-preflight-GRSHQYKC.js → managed-write-preflight-L7IRQ76C.js} +7 -7
  20. package/dist/mcp-auth-env-X5MI5IX6.js +25 -0
  21. package/dist/{migrate-PIB4QA54.js → migrate-RO4BJP5T.js} +6 -5
  22. package/dist/mirror-H5BTBAON.js +151 -0
  23. package/dist/runtime-VMFYW2K3.js +20 -0
  24. package/dist/skillwiki-mcp.js +14 -12
  25. package/dist/sources-AOYP6ROO.js +10 -0
  26. package/dist/{watcher-MHG46J3R.js → watcher-MGHQD3UI.js} +20 -110
  27. package/package.json +1 -1
  28. package/skills/.claude-plugin/plugin.json +1 -1
  29. package/skills/.codex-plugin/plugin.json +1 -1
  30. package/skills/.cursor-plugin/plugin.json +1 -1
  31. package/skills/package.json +1 -1
  32. package/skills/skills/using-skillwiki/SKILL.md +1 -1
  33. package/skills/skills/wiki-init/SKILL.md +3 -0
  34. package/skills/using-skillwiki/SKILL.md +1 -1
  35. package/skills/wiki-init/SKILL.md +3 -0
  36. package/templates/launchd/com.karlchow.skillwiki-mirror-sse.plist +20 -0
  37. package/dist/chunk-I2J3IJB7.js +0 -54
  38. package/dist/runtime-GLMI5OCT.js +0 -15
  39. package/dist/sources-D2MGP246.js +0 -10
  40. package/dist/{chunk-UIEQIJXP.js → chunk-7POLZOFY.js} +42 -42
@@ -4,11 +4,11 @@ import {
4
4
  readPage,
5
5
  scanSensitiveContent,
6
6
  splitFrontmatter
7
- } from "./chunk-X5GL2FCU.js";
7
+ } from "./chunk-TKX6PSWZ.js";
8
8
  import {
9
9
  err,
10
10
  ok
11
- } from "./chunk-UIEQIJXP.js";
11
+ } from "./chunk-7POLZOFY.js";
12
12
 
13
13
  // src/utils/vault-path-safety.ts
14
14
  import { lstatSync, realpathSync } from "fs";
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseDotenvFile,
4
+ resolveSkillwikiHome
5
+ } from "./chunk-SKOLE6US.js";
6
+
7
+ // src/utils/mcp-auth-env.ts
8
+ import { join } from "path";
9
+ var MCP_URL_ENV = "SKILLWIKI_MCP_URL";
10
+ var MCP_TOKEN_ENV = "SKILLWIKI_MCP_TOKEN";
11
+ var MCP_HOST_ID_ENV = "SKILLWIKI_HOST_ID";
12
+ function nonEmptyEnvValue(value) {
13
+ if (typeof value !== "string") return void 0;
14
+ const trimmed = value.trim();
15
+ return trimmed.length > 0 ? trimmed : void 0;
16
+ }
17
+ function mcpAuthFromEnv(env) {
18
+ return {
19
+ url: nonEmptyEnvValue(env[MCP_URL_ENV]),
20
+ token: nonEmptyEnvValue(env[MCP_TOKEN_ENV]),
21
+ hostId: nonEmptyEnvValue(env[MCP_HOST_ID_ENV])
22
+ };
23
+ }
24
+ async function resolveMcpAuthEnv(input) {
25
+ const processAuth = mcpAuthFromEnv(input.env ?? process.env);
26
+ const dotenv = await parseDotenvFile(join(resolveSkillwikiHome(input.home, input.env), ".skillwiki", ".env"));
27
+ const fileAuth = mcpAuthFromEnv(dotenv);
28
+ return {
29
+ url: processAuth.url ?? fileAuth.url,
30
+ token: processAuth.token ?? fileAuth.token,
31
+ hostId: processAuth.hostId ?? fileAuth.hostId
32
+ };
33
+ }
34
+ async function mergeMcpAuthIntoEnv(home, env) {
35
+ const auth = await resolveMcpAuthEnv({ home, env });
36
+ const merged = { ...env };
37
+ if (auth.url && !nonEmptyEnvValue(env[MCP_URL_ENV])) merged[MCP_URL_ENV] = auth.url;
38
+ if (auth.token && !nonEmptyEnvValue(env[MCP_TOKEN_ENV])) merged[MCP_TOKEN_ENV] = auth.token;
39
+ if (auth.hostId && !nonEmptyEnvValue(env[MCP_HOST_ID_ENV])) merged[MCP_HOST_ID_ENV] = auth.hostId;
40
+ return merged;
41
+ }
42
+ function redactMcpSecret(text, secret) {
43
+ if (!secret || secret.length === 0 || !text.includes(secret)) return text;
44
+ return text.split(secret).join("[REDACTED]");
45
+ }
46
+ function isMcpOnlyLeaf(input) {
47
+ return !input.resolvedPath && Boolean(input.token) && !input.vaultSyncInstalled;
48
+ }
49
+
50
+ export {
51
+ MCP_URL_ENV,
52
+ MCP_TOKEN_ENV,
53
+ MCP_HOST_ID_ENV,
54
+ nonEmptyEnvValue,
55
+ mcpAuthFromEnv,
56
+ resolveMcpAuthEnv,
57
+ mergeMcpAuthIntoEnv,
58
+ redactMcpSecret,
59
+ isMcpOnlyLeaf
60
+ };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  err,
4
4
  ok
5
- } from "./chunk-UIEQIJXP.js";
5
+ } from "./chunk-7POLZOFY.js";
6
6
 
7
7
  // src/utils/atomic-write.ts
8
8
  import { randomBytes } from "crypto";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  atomicWriteText
4
- } from "./chunk-GQWCTJTU.js";
4
+ } from "./chunk-EE7W3GLA.js";
5
5
 
6
6
  // src/clip-outbox/journal.ts
7
7
  import { mkdir, readFile, open } from "fs/promises";
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  ROOT_INDEX_SECTION_ORDER,
4
4
  buildRootIndexUniverse
5
- } from "./chunk-I6IRJCUJ.js";
5
+ } from "./chunk-KJG2CSYD.js";
6
6
  import {
7
7
  atomicWriteText
8
- } from "./chunk-GQWCTJTU.js";
8
+ } from "./chunk-EE7W3GLA.js";
9
9
  import {
10
10
  err,
11
11
  ok
12
- } from "./chunk-UIEQIJXP.js";
12
+ } from "./chunk-7POLZOFY.js";
13
13
 
14
14
  // src/utils/index-projection.ts
15
15
  import { readFile } from "fs/promises";
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/clip-outbox/files.ts
4
+ import { constants } from "fs";
5
+ import { lstat, mkdir, open, realpath } from "fs/promises";
6
+ import { dirname, isAbsolute, relative, resolve, sep } from "path";
7
+ async function readRegular(root, path, maxBytes) {
8
+ const rel = relative(root, path);
9
+ if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || isAbsolute(rel)) throw new Error("unsafe input path");
10
+ let current = resolve(root);
11
+ for (const part of ["", ...rel.split(sep)]) {
12
+ current = resolve(current, part);
13
+ const st = await lstat(current);
14
+ if (st.isSymbolicLink()) throw new Error("symlink input refused");
15
+ if (current !== path && !st.isDirectory()) throw new Error("invalid input parent");
16
+ }
17
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
18
+ try {
19
+ const before = await handle.stat();
20
+ if (!before.isFile() || before.size > maxBytes) throw new Error("input type or size refused");
21
+ const bytes = Buffer.alloc(before.size + 1);
22
+ let bytesRead = 0;
23
+ while (bytesRead < bytes.length) {
24
+ const chunk = await handle.read(bytes, bytesRead, bytes.length - bytesRead, bytesRead);
25
+ if (chunk.bytesRead === 0) break;
26
+ bytesRead += chunk.bytesRead;
27
+ }
28
+ const after = await handle.stat();
29
+ if (await realpath(path) !== path) throw new Error("input path changed during read");
30
+ const leaf = await lstat(path);
31
+ if (leaf.ino !== before.ino || leaf.dev !== before.dev) throw new Error("input identity changed during read");
32
+ if (bytesRead !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs) throw new Error("input changed during read");
33
+ return bytes.subarray(0, bytesRead);
34
+ } finally {
35
+ await handle.close();
36
+ }
37
+ }
38
+ async function writeRegularExclusive(root, rel, body) {
39
+ const dest = resolve(root, ...rel.split("/"));
40
+ const checked = relative(root, dest);
41
+ if (!checked || checked.startsWith(`..${sep}`) || checked === ".." || isAbsolute(checked)) throw new Error("unsafe dest path");
42
+ let current = resolve(root);
43
+ const parts = checked.split(sep);
44
+ for (let i = 0; i < parts.length - 1; i++) {
45
+ current = resolve(current, parts[i]);
46
+ try {
47
+ const st = await lstat(current);
48
+ if (st.isSymbolicLink()) throw new Error("symlink dest refused");
49
+ if (!st.isDirectory()) throw new Error("invalid dest parent");
50
+ } catch (error) {
51
+ if (error.code !== "ENOENT") throw error;
52
+ await mkdir(current);
53
+ }
54
+ }
55
+ const leaf = resolve(current, parts[parts.length - 1]);
56
+ if (dirname(leaf) !== current) throw new Error("unsafe dest path");
57
+ try {
58
+ const handle = await open(leaf, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 420);
59
+ try {
60
+ await handle.writeFile(body);
61
+ } finally {
62
+ await handle.close();
63
+ }
64
+ return "created";
65
+ } catch (error) {
66
+ if (error.code !== "EEXIST") throw error;
67
+ const st = await lstat(leaf);
68
+ if (st.isSymbolicLink() || !st.isFile() || st.size !== body.length) return "conflict";
69
+ const existing = await readRegular(root, leaf, body.length);
70
+ return existing.equals(body) ? "idempotent" : "conflict";
71
+ }
72
+ }
73
+
74
+ export {
75
+ readRegular,
76
+ writeRegularExclusive
77
+ };
@@ -3,14 +3,14 @@ import {
3
3
  extractFrontmatter,
4
4
  scanSensitiveContent,
5
5
  scanVault
6
- } from "./chunk-X5GL2FCU.js";
6
+ } from "./chunk-TKX6PSWZ.js";
7
7
  import {
8
8
  MetaSchema,
9
9
  TypedKnowledgeSchema,
10
10
  detectSchema,
11
11
  err,
12
12
  ok
13
- } from "./chunk-UIEQIJXP.js";
13
+ } from "./chunk-7POLZOFY.js";
14
14
 
15
15
  // src/utils/index-universe.ts
16
16
  import { readFile } from "fs/promises";
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ExitCode,
4
+ err,
5
+ ipv4InCidr,
6
+ isBlockedHost,
7
+ ok
8
+ } from "./chunk-7POLZOFY.js";
9
+
10
+ // src/commands/fetch-guard.ts
11
+ var REDACT_PARAMS = /* @__PURE__ */ new Set(["api_key", "token", "key", "auth", "password", "secret", "access_token"]);
12
+ var PATH_TOKEN_RE = /[A-Fa-f0-9]{32,}|[A-Za-z0-9_\-]{40,}/g;
13
+ function runFetchGuard(input) {
14
+ return Promise.resolve(runFetchGuardSync(input));
15
+ }
16
+ function runFetchGuardSync(input) {
17
+ let u;
18
+ try {
19
+ u = new URL(input.url);
20
+ } catch {
21
+ return { exitCode: ExitCode.MALFORMED_URL, result: err("MALFORMED_URL", { url: input.url }) };
22
+ }
23
+ const sanitized = sanitizeUrl(u);
24
+ if (u.protocol !== "https:") {
25
+ return {
26
+ exitCode: ExitCode.SCHEME_REJECTED,
27
+ result: err("SCHEME_REJECTED", { sanitized_url: sanitized, scheme: u.protocol })
28
+ };
29
+ }
30
+ if (isBlockedHost(u.hostname)) {
31
+ return {
32
+ exitCode: ExitCode.HOST_BLOCKED,
33
+ result: err("HOST_BLOCKED", { sanitized_url: sanitized, host: u.hostname })
34
+ };
35
+ }
36
+ return { exitCode: ExitCode.OK, result: ok({ allowed: true, sanitized_url: sanitized, humanHint: `ALLOWED: ${sanitized}` }) };
37
+ }
38
+ function sanitizeUrl(u) {
39
+ const clone = new URL(u.toString());
40
+ if (clone.username || clone.password) {
41
+ clone.username = "";
42
+ clone.password = "";
43
+ }
44
+ for (const k of Array.from(clone.searchParams.keys())) {
45
+ if (REDACT_PARAMS.has(k.toLowerCase())) clone.searchParams.set(k, "REDACTED");
46
+ }
47
+ let s = clone.toString();
48
+ s = s.replace(PATH_TOKEN_RE, "REDACTED");
49
+ return s;
50
+ }
51
+
52
+ // src/clip-outbox/ssrf.ts
53
+ import { isIP } from "net";
54
+ import { lookup as dnsLookup } from "dns/promises";
55
+ var extraBlockedV4 = [
56
+ ["0.0.0.0", 8],
57
+ ["100.64.0.0", 10],
58
+ ["192.0.2.0", 24],
59
+ ["198.18.0.0", 15],
60
+ ["198.51.100.0", 24],
61
+ ["203.0.113.0", 24],
62
+ ["224.0.0.0", 4],
63
+ ["240.0.0.0", 4]
64
+ ];
65
+ function isBlockedAddress(address) {
66
+ if (isBlockedHost(address)) return true;
67
+ const version = isIP(address);
68
+ if (version === 4) {
69
+ return extraBlockedV4.some(([base, prefix]) => ipv4InCidr(address, base, prefix));
70
+ }
71
+ if (version === 6) {
72
+ const lower = address.toLowerCase();
73
+ return lower === "::" || lower === "::1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
74
+ }
75
+ return false;
76
+ }
77
+ async function assertPublicHttpsUrl(url, lookup = defaultLookup) {
78
+ const guard = runFetchGuardSync({ url });
79
+ if (!guard.result.ok) {
80
+ return { ok: false, reason: guard.result.error };
81
+ }
82
+ let parsed;
83
+ try {
84
+ parsed = new URL(url);
85
+ } catch {
86
+ return { ok: false, reason: "MALFORMED_URL" };
87
+ }
88
+ if (isBlockedAddress(parsed.hostname)) {
89
+ return { ok: false, reason: "HOST_BLOCKED" };
90
+ }
91
+ try {
92
+ const addresses = await lookup(parsed.hostname);
93
+ if (addresses.length === 0) return { ok: false, reason: "DNS_EMPTY" };
94
+ if (addresses.some(isBlockedAddress)) return { ok: false, reason: "HOST_BLOCKED" };
95
+ } catch {
96
+ return { ok: false, reason: "DNS_FAILED" };
97
+ }
98
+ return { ok: true, href: parsed.href };
99
+ }
100
+ async function defaultLookup(hostname) {
101
+ if (isIP(hostname)) return [hostname];
102
+ const v4 = await dnsLookup(hostname, { all: true, verbatim: true });
103
+ return v4.map((row) => row.address);
104
+ }
105
+
106
+ export {
107
+ runFetchGuard,
108
+ runFetchGuardSync,
109
+ assertPublicHttpsUrl,
110
+ defaultLookup
111
+ };
@@ -18,10 +18,10 @@ import {
18
18
  resolveExistingRegularFileInsideVault,
19
19
  stripFencedBlocks,
20
20
  writeLogEvent
21
- } from "./chunk-5IBLXGLY.js";
21
+ } from "./chunk-CKQPUHM2.js";
22
22
  import {
23
23
  buildRootIndexUniverse
24
- } from "./chunk-I6IRJCUJ.js";
24
+ } from "./chunk-KJG2CSYD.js";
25
25
  import {
26
26
  extractFrontmatter,
27
27
  filterGitIgnoredRelativePaths,
@@ -34,23 +34,23 @@ import {
34
34
  scanVault,
35
35
  splitFrontmatter,
36
36
  vaultIoConcurrency
37
- } from "./chunk-X5GL2FCU.js";
37
+ } from "./chunk-TKX6PSWZ.js";
38
+ import {
39
+ atomicWriteText
40
+ } from "./chunk-EE7W3GLA.js";
38
41
  import {
39
42
  parseDotenvFile,
40
43
  parseDotenvText,
41
44
  profileKey,
42
45
  resolveSkillwikiHome
43
- } from "./chunk-PYPPG7RF.js";
44
- import {
45
- atomicWriteText
46
- } from "./chunk-GQWCTJTU.js";
46
+ } from "./chunk-SKOLE6US.js";
47
47
  import {
48
48
  ExitCode,
49
49
  FleetManifestSchema,
50
50
  err,
51
51
  getErrorMessage,
52
52
  ok
53
- } from "./chunk-UIEQIJXP.js";
53
+ } from "./chunk-7POLZOFY.js";
54
54
 
55
55
  // src/utils/managed-write-preflight.ts
56
56
  import { existsSync as existsSync11 } from "fs";
@@ -8,20 +8,20 @@ import {
8
8
  readLogEvents,
9
9
  resolveExistingRegularFileInsideVault,
10
10
  writeLogEvent
11
- } from "./chunk-5IBLXGLY.js";
11
+ } from "./chunk-CKQPUHM2.js";
12
12
  import {
13
13
  extractFrontmatter,
14
14
  readPage,
15
15
  scanSensitiveContent,
16
16
  scanVault,
17
17
  splitFrontmatter
18
- } from "./chunk-X5GL2FCU.js";
18
+ } from "./chunk-TKX6PSWZ.js";
19
19
  import {
20
20
  ExitCode,
21
21
  RawSourceSchema,
22
22
  err,
23
23
  ok
24
- } from "./chunk-UIEQIJXP.js";
24
+ } from "./chunk-7POLZOFY.js";
25
25
 
26
26
  // src/commands/sources.ts
27
27
  import { readFile as readFile4 } from "fs/promises";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  isClipOutboxKey,
4
4
  isVaultSyncKey
5
- } from "./chunk-UIEQIJXP.js";
5
+ } from "./chunk-7POLZOFY.js";
6
6
 
7
7
  // src/utils/home.ts
8
8
  import { homedir } from "os";
@@ -3,7 +3,7 @@ import {
3
3
  err,
4
4
  getErrorMessage,
5
5
  ok
6
- } from "./chunk-UIEQIJXP.js";
6
+ } from "./chunk-7POLZOFY.js";
7
7
 
8
8
  // src/parsers/frontmatter.ts
9
9
  import yaml from "js-yaml";
package/dist/cli.js CHANGED
@@ -1,8 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- runFetchGuard,
4
- runFetchGuardSync
5
- } from "./chunk-I2J3IJB7.js";
6
2
  import {
7
3
  buildDeleteIntent,
8
4
  normalizeVaultRelPath,
@@ -16,9 +12,6 @@ import {
16
12
  DEFAULT_MCP_URL,
17
13
  DEFAULT_NO_DECISION_STREAK,
18
14
  GateError,
19
- MCP_HOST_ID_ENV,
20
- MCP_TOKEN_ENV,
21
- MCP_URL_ENV,
22
15
  PUSH_NOT_IN_PROFILE,
23
16
  SATELLITE_STALE_MS,
24
17
  buildVectorIndex,
@@ -31,17 +24,13 @@ import {
31
24
  guardProtectedVaultWrite,
32
25
  isEventLedgerPath,
33
26
  isFailedRunStatus,
34
- mcpAuthFromEnv,
35
27
  measureDirtyVolume,
36
28
  memoryAuthorityTiersRank,
37
- mergeMcpAuthIntoEnv,
38
- nonEmptyEnvValue,
39
29
  pruneVectorIndex,
40
30
  readCliPackageJson,
41
31
  readSatelliteLatestRunAt,
42
32
  readSatelliteLatestRunFromText,
43
33
  readVaultSyncConfig,
44
- redactMcpSecret,
45
34
  reindexPageInVectorIndex,
46
35
  renderIndexUpsert,
47
36
  renderProjectIndex,
@@ -75,7 +64,7 @@ import {
75
64
  upsertIndexEntry,
76
65
  vectorIndexStatus,
77
66
  withMcpClient
78
- } from "./chunk-OJJBZDPR.js";
67
+ } from "./chunk-APKDTEVV.js";
79
68
  import {
80
69
  applySourceDisposition,
81
70
  decodeSourceActionApproval,
@@ -83,7 +72,7 @@ import {
83
72
  inventorySources,
84
73
  planSourceDisposition,
85
74
  runSourcesPending
86
- } from "./chunk-JQV5KNPT.js";
75
+ } from "./chunk-RR6VDWSI.js";
87
76
  import {
88
77
  normalizeDistTag,
89
78
  readCache,
@@ -96,7 +85,7 @@ import {
96
85
  UNMANAGED_START,
97
86
  renderRootIndex,
98
87
  writeRootIndexProjection
99
- } from "./chunk-MRPJ7X7W.js";
88
+ } from "./chunk-IR2DI4Y5.js";
100
89
  import {
101
90
  FLEET_REL_PATH,
102
91
  REDACTED_MALFORMED_REFERENCE,
@@ -184,7 +173,7 @@ import {
184
173
  stageVaultContentChanges,
185
174
  supersedeStaleReviewRequiredJournals,
186
175
  taxonomyCommentForPage
187
- } from "./chunk-HBJLFPQP.js";
176
+ } from "./chunk-Q6EZF37H.js";
188
177
  import {
189
178
  authorizeRawOperation,
190
179
  buildSourceReferenceIndex,
@@ -201,11 +190,11 @@ import {
201
190
  resolveExistingRegularFileInsideVault,
202
191
  validateLogEvent,
203
192
  writeLogEvent
204
- } from "./chunk-5IBLXGLY.js";
193
+ } from "./chunk-CKQPUHM2.js";
205
194
  import {
206
195
  assertTargetInsideVault,
207
196
  prepareTypedPage
208
- } from "./chunk-I6IRJCUJ.js";
197
+ } from "./chunk-KJG2CSYD.js";
209
198
  import {
210
199
  extractFrontmatter,
211
200
  mapWithConcurrency,
@@ -217,25 +206,38 @@ import {
217
206
  scanVault,
218
207
  splitFrontmatter,
219
208
  vaultIoConcurrency
220
- } from "./chunk-X5GL2FCU.js";
209
+ } from "./chunk-TKX6PSWZ.js";
210
+ import {
211
+ MCP_HOST_ID_ENV,
212
+ MCP_TOKEN_ENV,
213
+ MCP_URL_ENV,
214
+ mcpAuthFromEnv,
215
+ mergeMcpAuthIntoEnv,
216
+ nonEmptyEnvValue,
217
+ redactMcpSecret
218
+ } from "./chunk-DOHWNPAZ.js";
221
219
  import {
222
220
  CLIP_MIGRATION_HINT,
223
221
  refreshInstalledClipPlist
224
- } from "./chunk-TDBUJUYH.js";
222
+ } from "./chunk-25OXNFDV.js";
223
+ import {
224
+ atomicWriteText
225
+ } from "./chunk-EE7W3GLA.js";
225
226
  import {
226
227
  CLIP_NOT_CONFIGURED,
227
228
  loadClipOutboxConfig
228
- } from "./chunk-6OQLBNKR.js";
229
+ } from "./chunk-ABRYBO4T.js";
230
+ import {
231
+ runFetchGuard,
232
+ runFetchGuardSync
233
+ } from "./chunk-LOVLZNLX.js";
229
234
  import {
230
235
  parseDotenvFile,
231
236
  parseDotenvText,
232
237
  profileKey,
233
238
  resolveSkillwikiHome,
234
239
  writeDotenv
235
- } from "./chunk-PYPPG7RF.js";
236
- import {
237
- atomicWriteText
238
- } from "./chunk-GQWCTJTU.js";
240
+ } from "./chunk-SKOLE6US.js";
239
241
  import {
240
242
  ExitCode,
241
243
  MetaSchema,
@@ -245,7 +247,7 @@ import {
245
247
  err,
246
248
  ok,
247
249
  resolveSessionKind
248
- } from "./chunk-UIEQIJXP.js";
250
+ } from "./chunk-7POLZOFY.js";
249
251
 
250
252
  // src/cli.ts
251
253
  import { join as join44 } from "path";
@@ -3583,7 +3585,7 @@ async function runClipOutboxStatus(home) {
3583
3585
  result: ok({ enabled: false, humanHint: CLIP_NOT_CONFIGURED })
3584
3586
  };
3585
3587
  }
3586
- const { readJournal: readJournal2 } = await import("./journal-QU5IDXQF.js");
3588
+ const { readJournal: readJournal2 } = await import("./journal-5IW3TZ5F.js");
3587
3589
  let queue;
3588
3590
  try {
3589
3591
  queue = (await readJournal2(resolved.config.stateRoot)).map(({ captureId, state, reason, attempts }) => ({ captureId, state, reason, attempts }));
@@ -3601,7 +3603,7 @@ async function runClipOutboxStatus(home) {
3601
3603
  }
3602
3604
  async function runClipOutboxInstall(input) {
3603
3605
  try {
3604
- const { migrateClipOutbox } = await import("./migrate-PIB4QA54.js");
3606
+ const { migrateClipOutbox } = await import("./migrate-RO4BJP5T.js");
3605
3607
  return { exitCode: ExitCode.OK, result: ok(await migrateClipOutbox(input)) };
3606
3608
  } catch (e) {
3607
3609
  return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: e instanceof Error ? e.message : "clip migration failed" }) };
@@ -3614,10 +3616,10 @@ async function runClipOutboxWatch(home) {
3614
3616
  let client;
3615
3617
  try {
3616
3618
  const { S3Client: S3Client3 } = await import("@aws-sdk/client-s3");
3617
- const { loadClipCredentials, validateRuntime } = await import("./runtime-GLMI5OCT.js");
3619
+ const { loadClipCredentials, validateRuntime } = await import("./runtime-VMFYW2K3.js");
3618
3620
  const { createS3CreateOnlyPut, parseDestination } = await import("./upload-HTE62TOV.js");
3619
3621
  const { postReadyHint } = await import("./ready-S7F53Q2D.js");
3620
- const { startClipWorker } = await import("./watcher-MHG46J3R.js");
3622
+ const { startClipWorker } = await import("./watcher-MGHQD3UI.js");
3621
3623
  const cfg = resolved.config;
3622
3624
  const credentials = await loadClipCredentials(home, cfg);
3623
3625
  await validateRuntime(home, cfg, credentials.s3.endpoint);
@@ -3653,6 +3655,47 @@ async function runClipOutboxWatch(home) {
3653
3655
  client?.destroy();
3654
3656
  }
3655
3657
  }
3658
+ async function runClipOutboxMirrorWatch(home) {
3659
+ const resolved = await loadClipOutboxConfig(home);
3660
+ if (!resolved.ok || !resolved.enabled) return { exitCode: ExitCode.INVALID_CONFIG_VALUE, result: err("INVALID_CONFIG_VALUE", { message: resolved.ok ? CLIP_NOT_CONFIGURED : resolved.message }) };
3661
+ let client;
3662
+ const abort = new AbortController();
3663
+ try {
3664
+ const { S3Client: S3Client3 } = await import("@aws-sdk/client-s3");
3665
+ const { loadClipCredentials, validateRuntime } = await import("./runtime-VMFYW2K3.js");
3666
+ const { createS3GetObject, eventsUrlFromMcp, parseDestinationForMirror, startMirrorListener } = await import("./mirror-H5BTBAON.js");
3667
+ const { resolveMcpAuthEnv } = await import("./mcp-auth-env-X5MI5IX6.js");
3668
+ const cfg = resolved.config;
3669
+ const credentials = await loadClipCredentials(home, cfg);
3670
+ await validateRuntime(home, cfg, credentials.s3.endpoint);
3671
+ const auth = await resolveMcpAuthEnv({ home });
3672
+ if (!auth.url || !auth.token) throw new Error("MCP bearer required for inbound SSE");
3673
+ const destination = parseDestinationForMirror(cfg.destination);
3674
+ client = new S3Client3({ endpoint: credentials.s3.endpoint, region: credentials.s3.region, credentials: credentials.s3, forcePathStyle: true, maxAttempts: 1 });
3675
+ const done = new Promise((resolve9) => {
3676
+ abort.signal.addEventListener("abort", () => resolve9(), { once: true });
3677
+ });
3678
+ process.once("SIGTERM", () => abort.abort());
3679
+ process.once("SIGINT", () => abort.abort());
3680
+ await Promise.race([
3681
+ startMirrorListener({
3682
+ home,
3683
+ config: cfg,
3684
+ eventsUrl: eventsUrlFromMcp(auth.url),
3685
+ mcpToken: auth.token,
3686
+ getObject: createS3GetObject({ client, bucket: destination.bucket, prefix: destination.prefix, maxBytes: cfg.maxBundleBytes }),
3687
+ signal: abort.signal
3688
+ }),
3689
+ done
3690
+ ]);
3691
+ return { exitCode: ExitCode.OK, result: ok({ humanHint: "Inbound SSE listener stopped." }) };
3692
+ } catch {
3693
+ return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { message: "inbound SSE listener startup failed; check MCP bearer, clip credentials, frozen mirror and wiki-push isolation" }) };
3694
+ } finally {
3695
+ abort.abort();
3696
+ client?.destroy();
3697
+ }
3698
+ }
3656
3699
 
3657
3700
  // src/commands/connect.ts
3658
3701
  import { chmod, mkdir as mkdir5, readFile as readFile6, stat as stat3 } from "fs/promises";
@@ -4132,7 +4175,7 @@ ${fmRewritten}
4132
4175
  }
4133
4176
  let indexUpdated = false;
4134
4177
  if (!isRaw) {
4135
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-2K672CBO.js");
4178
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-UNZG5UJZ.js");
4136
4179
  const before = await readFile7(join19(input.vault, "index.md"), "utf8").catch(() => "");
4137
4180
  const fullTarget = relPath.replace(/\.md$/, "");
4138
4181
  const bare = fullTarget.split("/").pop() ?? fullTarget;
@@ -4255,7 +4298,7 @@ async function runRemove(input) {
4255
4298
  if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
4256
4299
  const { readFile: readFile23 } = await import("fs/promises");
4257
4300
  const { join: pathJoin } = await import("path");
4258
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-2K672CBO.js");
4301
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-UNZG5UJZ.js");
4259
4302
  const before = await readFile23(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
4260
4303
  const fullTarget = relPath.replace(/\.md$/, "");
4261
4304
  const bare = fullTarget.split("/").pop() ?? fullTarget;
@@ -11263,7 +11306,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
11263
11306
  if (dirty) {
11264
11307
  return emit(dirty, void 0, { postCommit: false });
11265
11308
  }
11266
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-GRSHQYKC.js");
11309
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-L7IRQ76C.js");
11267
11310
  const run = await runManagedWriteTransaction2({
11268
11311
  vault,
11269
11312
  command,
@@ -11910,7 +11953,8 @@ program.command("doctor").description("diagnose skillwiki setup issues").option(
11910
11953
  var clipOutboxCmd = program.command("clip-outbox").description("dedicated file-level web-clip outbox");
11911
11954
  clipOutboxCmd.command("status").description("show whether the clip-outbox channel is configured").action(async () => emit(await runClipOutboxStatus(resolveSkillwikiHome())));
11912
11955
  clipOutboxCmd.command("watch").description("run the enabled dedicated outbox worker using macOS FSEvents").action(async () => emit(await runClipOutboxWatch(resolveSkillwikiHome())));
11913
- clipOutboxCmd.command("install-or-migrate").description("refresh the packaged launchd plist without enabling the channel").option("--load", "attended launchd bootstrap; requires existing enabled=true", false).option("--conditional-create-proven", "attest the deployed SeaweedFS If-None-Match:* race test passed", false).action(async (opts) => emit(await runClipOutboxInstall({ home: resolveSkillwikiHome(), load: opts.load, conditionalCreateProven: opts.conditionalCreateProven })));
11956
+ clipOutboxCmd.command("mirror-watch").description("copy admitted raw/articles and raw/assets from storage into the frozen mirror using MCP SSE").action(async () => emit(await runClipOutboxMirrorWatch(resolveSkillwikiHome())));
11957
+ clipOutboxCmd.command("install-or-migrate").description("refresh the packaged launchd plist without enabling the channel").option("--load", "attended clip watcher bootstrap; requires existing enabled=true", false).option("--load-mirror", "attended inbound SSE bootstrap; requires existing enabled=true; never auto-loaded on upgrade", false).option("--conditional-create-proven", "attest the deployed SeaweedFS If-None-Match:* race test passed", false).action(async (opts) => emit(await runClipOutboxInstall({ home: resolveSkillwikiHome(), load: opts.load, loadMirror: opts.loadMirror, conditionalCreateProven: opts.conditionalCreateProven })));
11914
11958
  program.command("connect").description(CONNECT_DESCRIPTION).option("--from-file <path>", "chat-attached env file (not Drive / \u4E91\u76D8)").option("--from-stdin", "read env file from stdin (no echo)").option("--dry-run", "validate without writing ~/.skillwiki/.env").option("--force", "overwrite a different existing token").action(async (opts) => emit(await runConnect({
11915
11959
  home: resolveSkillwikiHome(),
11916
11960
  fromFile: opts.fromFile,