@xopcai/xopc 0.0.23 → 0.0.25

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 (170) hide show
  1. package/dist/extensions/feishu/xopc.extension.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/agents-C_bPhtBs.js +216 -0
  4. package/dist/gateway/static/root/assets/agents-C_bPhtBs.js.map +1 -0
  5. package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js +2 -0
  6. package/dist/gateway/static/root/assets/apps-page-DbzO48lg.js.map +1 -0
  7. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js → attachment-preview-renderer-CxMJMbD2.js} +4 -4
  8. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CebH7fCz.js.map → attachment-preview-renderer-CxMJMbD2.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js → attachment-process-heavy-EFXPGfWk.js} +6 -6
  10. package/dist/gateway/static/root/assets/{attachment-process-heavy-Dbf1--O6.js.map → attachment-process-heavy-EFXPGfWk.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-utils-core-Dt6UxMPV.js → attachment-utils-core-ECbeoa9H.js} +1 -1
  12. package/dist/gateway/static/root/assets/attachment-utils-core-ECbeoa9H.js.map +1 -0
  13. package/dist/gateway/static/root/assets/channels-settings-CeGoU9v8.js +9 -0
  14. package/dist/gateway/static/root/assets/{channels-settings-CGzrrBlT.js.map → channels-settings-CeGoU9v8.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/cn-BMCV0OMB.js +2 -0
  16. package/dist/gateway/static/root/assets/cn-BMCV0OMB.js.map +1 -0
  17. package/dist/gateway/static/root/assets/cron-page-DpEYUvxB.js +2 -0
  18. package/dist/gateway/static/root/assets/{cron-page-BGCdDf2w.js.map → cron-page-DpEYUvxB.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/cron-utils-Cvv0F3pa.js +3 -0
  20. package/dist/gateway/static/root/assets/{cron-utils-Dnks4wWv.js.map → cron-utils-Cvv0F3pa.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/dist-C41N3YrO.js +2 -0
  22. package/dist/gateway/static/root/assets/{dist-BG1ChbY9.js.map → dist-C41N3YrO.js.map} +1 -1
  23. package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js → docx-preview-F-jKDMNv.js} +2 -2
  24. package/dist/gateway/static/root/assets/{docx-preview-DxcHM3sR.js.map → docx-preview-F-jKDMNv.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js → excel-worksheet-utils-DPfAinZn.js} +1 -1
  26. package/dist/gateway/static/root/assets/{excel-worksheet-utils-Dk66snKA.js.map → excel-worksheet-utils-DPfAinZn.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/extension-debug-page-CkkYZjNP.js +2 -0
  28. package/dist/gateway/static/root/assets/{extension-debug-page-CRC16AbL.js.map → extension-debug-page-CkkYZjNP.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/extension-page-BjUIPVNG.js +2 -0
  30. package/dist/gateway/static/root/assets/{extension-page-BagrJnbm.js.map → extension-page-BjUIPVNG.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js +2 -0
  32. package/dist/gateway/static/root/assets/extension-settings-page-CwuFDOdk.js.map +1 -0
  33. package/dist/gateway/static/root/assets/index-DwzwDCjW.js +150 -0
  34. package/dist/gateway/static/root/assets/index-DwzwDCjW.js.map +1 -0
  35. package/dist/gateway/static/root/assets/index-dhtHG1nU.css +1 -0
  36. package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js → jszip.min-CL3dfyxs.js} +1 -1
  37. package/dist/gateway/static/root/assets/{jszip.min-DVUfmhpE.js.map → jszip.min-CL3dfyxs.js.map} +1 -1
  38. package/dist/gateway/static/root/assets/logs-page-BtwGPuw2.js +2 -0
  39. package/dist/gateway/static/root/assets/{logs-page-Bo9EsE_D.js.map → logs-page-BtwGPuw2.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{pdf--jE7rvON.js → pdf-CX6ji-QC.js} +1 -1
  41. package/dist/gateway/static/root/assets/{pdf--jE7rvON.js.map → pdf-CX6ji-QC.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/sessions-page-4rKFDn2k.js +2 -0
  43. package/dist/gateway/static/root/assets/{sessions-page-CDgXq8qp.js.map → sessions-page-4rKFDn2k.js.map} +1 -1
  44. package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js +2 -0
  45. package/dist/gateway/static/root/assets/settings-page-iYLSxQYc.js.map +1 -0
  46. package/dist/gateway/static/root/assets/skills-page-_siDuHeF.js +3 -0
  47. package/dist/gateway/static/root/assets/{skills-page-BRS5qYTw.js.map → skills-page-_siDuHeF.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/vendor-swr-B5fPo7KK.js +2 -0
  49. package/dist/gateway/static/root/assets/{vendor-swr-Dp4nzp5h.js.map → vendor-swr-B5fPo7KK.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js → xlsx-CPtvfmVF.js} +1 -1
  51. package/dist/gateway/static/root/assets/{xlsx-DVk38js7.js.map → xlsx-CPtvfmVF.js.map} +1 -1
  52. package/dist/gateway/static/root/index.html +5 -4
  53. package/dist/package.js +1 -1
  54. package/dist/src/agent/image/tool-model-config.js +2 -2
  55. package/dist/src/agent/image/tool-model-config.js.map +1 -1
  56. package/dist/src/agent/memory/dreaming/config.d.ts +20 -0
  57. package/dist/src/agent/memory/dreaming/config.js +44 -0
  58. package/dist/src/agent/memory/dreaming/config.js.map +1 -0
  59. package/dist/src/agent/memory/dreaming/constants.d.ts +8 -0
  60. package/dist/src/agent/memory/dreaming/constants.js +14 -0
  61. package/dist/src/agent/memory/dreaming/constants.js.map +1 -0
  62. package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +22 -0
  63. package/dist/src/agent/memory/dreaming/deep-promotion.js +337 -0
  64. package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -0
  65. package/dist/src/agent/memory/dreaming/preview.d.ts +26 -0
  66. package/dist/src/agent/memory/dreaming/preview.js +176 -0
  67. package/dist/src/agent/memory/dreaming/preview.js.map +1 -0
  68. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +45 -0
  69. package/dist/src/agent/memory/dreaming/short-term-store.js +187 -0
  70. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -0
  71. package/dist/src/agent/orchestration/agent-orchestrator.js +17 -0
  72. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  73. package/dist/src/agent/service.d.ts +6 -0
  74. package/dist/src/agent/service.js +52 -0
  75. package/dist/src/agent/service.js.map +1 -1
  76. package/dist/src/agent/tools/dreaming-tool.d.ts +7 -0
  77. package/dist/src/agent/tools/dreaming-tool.js +102 -0
  78. package/dist/src/agent/tools/dreaming-tool.js.map +1 -0
  79. package/dist/src/agent/tools/execute-code-tool.js +1 -1
  80. package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
  81. package/dist/src/agent/tools/factory.js +5 -0
  82. package/dist/src/agent/tools/factory.js.map +1 -1
  83. package/dist/src/agent/tools/index.d.ts +1 -0
  84. package/dist/src/agent/tools/index.js +2 -1
  85. package/dist/src/agent/tools/memory-tool.js +9 -2
  86. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  87. package/dist/src/cli/commands/extension-dev.d.ts +2 -0
  88. package/dist/src/cli/commands/extension-dev.js +196 -0
  89. package/dist/src/cli/commands/extension-dev.js.map +1 -0
  90. package/dist/src/cli/commands/extension-marketplace.d.ts +4 -0
  91. package/dist/src/cli/commands/extension-marketplace.js +145 -0
  92. package/dist/src/cli/commands/extension-marketplace.js.map +1 -0
  93. package/dist/src/cli/commands/extension-pack.d.ts +2 -0
  94. package/dist/src/cli/commands/extension-pack.js +242 -0
  95. package/dist/src/cli/commands/extension-pack.js.map +1 -0
  96. package/dist/src/cli/commands/extension.js +13 -0
  97. package/dist/src/cli/commands/extension.js.map +1 -1
  98. package/dist/src/cli/index.js +5 -1
  99. package/dist/src/cli/index.js.map +1 -1
  100. package/dist/src/config/schema.d.ts +39 -0
  101. package/dist/src/config/schema.js +17 -2
  102. package/dist/src/config/schema.js.map +1 -1
  103. package/dist/src/extensions/api.d.ts +6 -1
  104. package/dist/src/extensions/api.js +30 -0
  105. package/dist/src/extensions/api.js.map +1 -1
  106. package/dist/src/extensions/engine-check.d.ts +14 -0
  107. package/dist/src/extensions/engine-check.js +148 -0
  108. package/dist/src/extensions/engine-check.js.map +1 -0
  109. package/dist/src/extensions/loader.js +23 -0
  110. package/dist/src/extensions/loader.js.map +1 -1
  111. package/dist/src/extensions/marketplace.d.ts +24 -0
  112. package/dist/src/extensions/marketplace.js +98 -0
  113. package/dist/src/extensions/marketplace.js.map +1 -0
  114. package/dist/src/extensions/normalize-manifest.js +16 -4
  115. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  116. package/dist/src/extensions/sdk/index.d.ts +2 -0
  117. package/dist/src/extensions/sdk/index.js +2 -1
  118. package/dist/src/extensions/sdk/index.js.map +1 -1
  119. package/dist/src/extensions/sdk/testing.d.ts +49 -3
  120. package/dist/src/extensions/sdk/testing.js +174 -5
  121. package/dist/src/extensions/sdk/testing.js.map +1 -1
  122. package/dist/src/extensions/types/core.d.ts +5 -0
  123. package/dist/src/extensions/types/manifest.d.ts +17 -0
  124. package/dist/src/extensions/when-context.d.ts +6 -0
  125. package/dist/src/extensions/when-context.js +28 -0
  126. package/dist/src/extensions/when-context.js.map +1 -0
  127. package/dist/src/extensions/when-expression.d.ts +11 -0
  128. package/dist/src/extensions/when-expression.js +215 -0
  129. package/dist/src/extensions/when-expression.js.map +1 -0
  130. package/dist/src/gateway/hono/app.js +1 -1
  131. package/dist/src/gateway/hono/app.js.map +1 -1
  132. package/dist/src/gateway/hono/lib/config-payload.d.ts +13 -0
  133. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +28 -0
  134. package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
  135. package/dist/src/gateway/hono/routes/config.js +48 -0
  136. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  137. package/dist/src/gateway/hono/routes/dreaming.d.ts +3 -0
  138. package/dist/src/gateway/hono/routes/dreaming.js +198 -0
  139. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -0
  140. package/dist/src/gateway/hono/routes/index.js +2 -0
  141. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  142. package/dist/src/gateway/lock.js +1 -1
  143. package/dist/src/gateway/service.js +7 -0
  144. package/dist/src/gateway/service.js.map +1 -1
  145. package/dist/src/providers/index.d.ts +6 -3
  146. package/dist/src/providers/index.js +12 -23
  147. package/dist/src/providers/index.js.map +1 -1
  148. package/package.json +2 -1
  149. package/dist/gateway/static/root/assets/agents-BY_DaknM.js +0 -216
  150. package/dist/gateway/static/root/assets/agents-BY_DaknM.js.map +0 -1
  151. package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js +0 -2
  152. package/dist/gateway/static/root/assets/apps-page-BO3nQbJY.js.map +0 -1
  153. package/dist/gateway/static/root/assets/attachment-utils-core-Dt6UxMPV.js.map +0 -1
  154. package/dist/gateway/static/root/assets/channels-settings-CGzrrBlT.js +0 -9
  155. package/dist/gateway/static/root/assets/cron-page-BGCdDf2w.js +0 -2
  156. package/dist/gateway/static/root/assets/cron-utils-Dnks4wWv.js +0 -3
  157. package/dist/gateway/static/root/assets/dist-BG1ChbY9.js +0 -2
  158. package/dist/gateway/static/root/assets/extension-debug-page-CRC16AbL.js +0 -2
  159. package/dist/gateway/static/root/assets/extension-page-BagrJnbm.js +0 -2
  160. package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js +0 -2
  161. package/dist/gateway/static/root/assets/extension-settings-page-DEpxRKKK.js.map +0 -1
  162. package/dist/gateway/static/root/assets/index-CYVs9x8D.css +0 -1
  163. package/dist/gateway/static/root/assets/index-KNzRh6gu.js +0 -150
  164. package/dist/gateway/static/root/assets/index-KNzRh6gu.js.map +0 -1
  165. package/dist/gateway/static/root/assets/logs-page-Bo9EsE_D.js +0 -2
  166. package/dist/gateway/static/root/assets/sessions-page-CDgXq8qp.js +0 -2
  167. package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js +0 -2
  168. package/dist/gateway/static/root/assets/settings-page-BWMTFST6.js.map +0 -1
  169. package/dist/gateway/static/root/assets/skills-page-BRS5qYTw.js +0 -3
  170. package/dist/gateway/static/root/assets/vendor-swr-Dp4nzp5h.js +0 -2
@@ -0,0 +1,28 @@
1
+ import { getAllProviders, init_providers, isProviderConfiguredSync } from "../providers/index.js";
2
+ //#region src/extensions/when-context.ts
3
+ init_providers();
4
+ /**
5
+ * Flat payload for `GET /api/context` and client-side `when` evaluation.
6
+ */
7
+ function buildWhenContextSnapshot(config, loader) {
8
+ const out = {
9
+ platform: process.platform,
10
+ isElectron: Boolean(process.versions.electron),
11
+ gatewayAuthenticated: true,
12
+ sessionActive: false,
13
+ agentRunning: false
14
+ };
15
+ for (const p of getAllProviders()) out[`hasProvider.${p}`] = isProviderConfiguredSync(p);
16
+ const channels = config.channels;
17
+ if (channels && typeof channels === "object") for (const [id, raw] of Object.entries(channels)) if (raw && typeof raw === "object" && raw !== null && "enabled" in raw) out[`hasChannel.${id}`] = raw.enabled === true;
18
+ else out[`hasChannel.${id}`] = Boolean(raw);
19
+ if (loader) try {
20
+ loader.setConfig(config);
21
+ for (const ext of loader.discoverExtensions()) out[`extensionEnabled.${ext.id}`] = true;
22
+ } catch {}
23
+ return out;
24
+ }
25
+ //#endregion
26
+ export { buildWhenContextSnapshot };
27
+
28
+ //# sourceMappingURL=when-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"when-context.js","names":[],"sources":["../../../src/extensions/when-context.ts"],"sourcesContent":["import type { Config as SurfaceConfig } from '../config/config-surface.js';\nimport { getAllProviders, isProviderConfiguredSync } from '../providers/index.js';\nimport type { ExtensionLoader } from './loader.js';\n\n/**\n * Flat payload for `GET /api/context` and client-side `when` evaluation.\n */\nexport function buildWhenContextSnapshot(\n config: SurfaceConfig,\n loader: ExtensionLoader | null,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {\n platform: process.platform,\n isElectron: Boolean(process.versions.electron),\n gatewayAuthenticated: true,\n sessionActive: false,\n agentRunning: false,\n };\n for (const p of getAllProviders()) {\n out[`hasProvider.${p}`] = isProviderConfiguredSync(p);\n }\n const channels = config.channels;\n if (channels && typeof channels === 'object') {\n for (const [id, raw] of Object.entries(channels)) {\n if (raw && typeof raw === 'object' && raw !== null && 'enabled' in raw) {\n out[`hasChannel.${id}`] = (raw as { enabled?: boolean }).enabled === true;\n } else {\n out[`hasChannel.${id}`] = Boolean(raw);\n }\n }\n }\n if (loader) {\n try {\n loader.setConfig(config);\n for (const ext of loader.discoverExtensions()) {\n out[`extensionEnabled.${ext.id}`] = true;\n }\n } catch {\n /* non-fatal for context snapshot */\n }\n }\n return out;\n}\n"],"mappings":";;gBACkF;;;;AAMlF,SAAgB,yBACd,QACA,QACyB;CACzB,MAAM,MAA+B;EACnC,UAAU,QAAQ;EAClB,YAAY,QAAQ,QAAQ,SAAS,SAAS;EAC9C,sBAAsB;EACtB,eAAe;EACf,cAAc;EACf;AACD,MAAK,MAAM,KAAK,iBAAiB,CAC/B,KAAI,eAAe,OAAO,yBAAyB,EAAE;CAEvD,MAAM,WAAW,OAAO;AACxB,KAAI,YAAY,OAAO,aAAa,SAClC,MAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,SAAS,CAC9C,KAAI,OAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,aAAa,IACjE,KAAI,cAAc,QAAS,IAA8B,YAAY;KAErE,KAAI,cAAc,QAAQ,QAAQ,IAAI;AAI5C,KAAI,OACF,KAAI;AACF,SAAO,UAAU,OAAO;AACxB,OAAK,MAAM,OAAO,OAAO,oBAAoB,CAC3C,KAAI,oBAAoB,IAAI,QAAQ;SAEhC;AAIV,QAAO"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Boolean when-expressions for extension UI contributions (Phase 2).
3
+ * Grammar: expression := term (('&&' | '||') term)*; term := '!' term | atom;
4
+ * atom := variable (('==' | '!=') literal)? | '(' expression ')'
5
+ */
6
+ export type WhenContext = Record<string, unknown>;
7
+ /**
8
+ * Evaluate a when-expression string against a flat context object.
9
+ * Unknown variables are treated as falsey for boolean tests; missing keys are undefined.
10
+ */
11
+ export declare function evaluateWhenExpression(expr: string, ctx: WhenContext): boolean;
@@ -0,0 +1,215 @@
1
+ //#region src/extensions/when-expression.ts
2
+ function tokenize(input) {
3
+ const out = [];
4
+ let i = 0;
5
+ const s = input.trim();
6
+ while (i < s.length) {
7
+ const c = s[i];
8
+ if (/\s/.test(c)) {
9
+ i++;
10
+ continue;
11
+ }
12
+ if (s.slice(i, i + 2) === "&&") {
13
+ out.push({ k: "AND" });
14
+ i += 2;
15
+ continue;
16
+ }
17
+ if (s.slice(i, i + 2) === "||") {
18
+ out.push({ k: "OR" });
19
+ i += 2;
20
+ continue;
21
+ }
22
+ if (s.slice(i, i + 2) === "==") {
23
+ out.push({ k: "EQ" });
24
+ i += 2;
25
+ continue;
26
+ }
27
+ if (s.slice(i, i + 2) === "!=") {
28
+ out.push({ k: "NE" });
29
+ i += 2;
30
+ continue;
31
+ }
32
+ if (c === "(") {
33
+ out.push({ k: "LP" });
34
+ i++;
35
+ continue;
36
+ }
37
+ if (c === ")") {
38
+ out.push({ k: "RP" });
39
+ i++;
40
+ continue;
41
+ }
42
+ if (c === "!") {
43
+ out.push({ k: "NOT" });
44
+ i++;
45
+ continue;
46
+ }
47
+ if (c === "'" || c === "\"") {
48
+ const q = c;
49
+ i++;
50
+ let buf = "";
51
+ while (i < s.length) {
52
+ const ch = s[i];
53
+ if (ch === "\\" && i + 1 < s.length) {
54
+ buf += s[i + 1];
55
+ i += 2;
56
+ continue;
57
+ }
58
+ if (ch === q) {
59
+ i++;
60
+ break;
61
+ }
62
+ buf += ch;
63
+ i++;
64
+ }
65
+ out.push({
66
+ k: "STR",
67
+ v: buf
68
+ });
69
+ continue;
70
+ }
71
+ if (/[0-9]/.test(c) || c === "-" && i + 1 < s.length && /[0-9]/.test(s[i + 1])) {
72
+ let j = i + 1;
73
+ while (j < s.length && /[0-9.]/.test(s[j])) j++;
74
+ const n = Number(s.slice(i, j));
75
+ out.push({
76
+ k: "NUM",
77
+ v: Number.isFinite(n) ? n : 0
78
+ });
79
+ i = j;
80
+ continue;
81
+ }
82
+ if (/[a-zA-Z_]/.test(c)) {
83
+ let j = i + 1;
84
+ while (j < s.length && /[a-zA-Z0-9_.]/.test(s[j])) j++;
85
+ const word = s.slice(i, j);
86
+ i = j;
87
+ if (word === "true") out.push({
88
+ k: "BOOL",
89
+ v: true
90
+ });
91
+ else if (word === "false") out.push({
92
+ k: "BOOL",
93
+ v: false
94
+ });
95
+ else out.push({
96
+ k: "IDENT",
97
+ v: word
98
+ });
99
+ continue;
100
+ }
101
+ throw new Error(`Unexpected character in when-expression at ${i}: ${c}`);
102
+ }
103
+ out.push({ k: "EOF" });
104
+ return out;
105
+ }
106
+ function ctxLookup(ctx, key) {
107
+ if (key in ctx) return ctx[key];
108
+ }
109
+ var Parser = class {
110
+ i = 0;
111
+ constructor(toks, ctx) {
112
+ this.toks = toks;
113
+ this.ctx = ctx;
114
+ }
115
+ parse() {
116
+ const v = this.parseOr();
117
+ const tail = this.cur();
118
+ if (tail.k !== "EOF") throw new Error(`Unexpected tokens after expression (at ${this.i}: ${tail.k}${"v" in tail ? ` ${tail.v}` : ""})`);
119
+ return v;
120
+ }
121
+ cur() {
122
+ return this.toks[this.i] ?? { k: "EOF" };
123
+ }
124
+ parseOr() {
125
+ let v = this.parseAnd();
126
+ while (this.cur().k === "OR") {
127
+ this.i++;
128
+ const rhs = this.parseAnd();
129
+ v = v || rhs;
130
+ }
131
+ return v;
132
+ }
133
+ parseAnd() {
134
+ let v = this.parseUnary();
135
+ while (this.cur().k === "AND") {
136
+ this.i++;
137
+ const rhs = this.parseUnary();
138
+ v = v && rhs;
139
+ }
140
+ return v;
141
+ }
142
+ parseUnary() {
143
+ if (this.cur().k === "NOT") {
144
+ this.i++;
145
+ return !this.parseUnary();
146
+ }
147
+ return this.parsePrimary();
148
+ }
149
+ parsePrimary() {
150
+ if (this.cur().k === "LP") {
151
+ this.i++;
152
+ const inner = this.parseOr();
153
+ if (this.cur().k !== "RP") throw new Error("Expected )");
154
+ this.i++;
155
+ return inner;
156
+ }
157
+ return this.parseAtom();
158
+ }
159
+ parseAtom() {
160
+ const left = this.readAtomValue();
161
+ const op = this.cur();
162
+ if (op.k === "EQ" || op.k === "NE") {
163
+ this.i++;
164
+ const eq = valuesLooselyEqual(left, this.readAtomValue());
165
+ return op.k === "EQ" ? eq : !eq;
166
+ }
167
+ return isTruthyWhen(left);
168
+ }
169
+ readAtomValue() {
170
+ const t = this.cur();
171
+ if (t.k === "IDENT") {
172
+ this.i++;
173
+ return ctxLookup(this.ctx, t.v);
174
+ }
175
+ if (t.k === "STR") {
176
+ this.i++;
177
+ return t.v;
178
+ }
179
+ if (t.k === "NUM") {
180
+ this.i++;
181
+ return t.v;
182
+ }
183
+ if (t.k === "BOOL") {
184
+ this.i++;
185
+ return t.v;
186
+ }
187
+ throw new Error("Expected value in when-expression");
188
+ }
189
+ };
190
+ function valuesLooselyEqual(a, b) {
191
+ if (a === b) return true;
192
+ if (a == null || b == null) return a === b;
193
+ if (typeof a === "boolean" || typeof b === "boolean") return Boolean(a) === Boolean(b);
194
+ if (typeof a === "number" && typeof b === "number") return a === b;
195
+ return String(a) === String(b);
196
+ }
197
+ function isTruthyWhen(v) {
198
+ if (typeof v === "boolean") return v;
199
+ if (typeof v === "number") return v !== 0;
200
+ if (typeof v === "string") return v.length > 0;
201
+ return v != null;
202
+ }
203
+ /**
204
+ * Evaluate a when-expression string against a flat context object.
205
+ * Unknown variables are treated as falsey for boolean tests; missing keys are undefined.
206
+ */
207
+ function evaluateWhenExpression(expr, ctx) {
208
+ const s = expr.trim();
209
+ if (!s) return true;
210
+ return new Parser(tokenize(s), ctx).parse();
211
+ }
212
+ //#endregion
213
+ export { evaluateWhenExpression };
214
+
215
+ //# sourceMappingURL=when-expression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"when-expression.js","names":[],"sources":["../../../src/extensions/when-expression.ts"],"sourcesContent":["/**\n * Boolean when-expressions for extension UI contributions (Phase 2).\n * Grammar: expression := term (('&&' | '||') term)*; term := '!' term | atom;\n * atom := variable (('==' | '!=') literal)? | '(' expression ')'\n */\n\nexport type WhenContext = Record<string, unknown>;\n\ntype Tok =\n | { k: 'AND' }\n | { k: 'OR' }\n | { k: 'NOT' }\n | { k: 'EQ' }\n | { k: 'NE' }\n | { k: 'LP' }\n | { k: 'RP' }\n | { k: 'IDENT'; v: string }\n | { k: 'STR'; v: string }\n | { k: 'NUM'; v: number }\n | { k: 'BOOL'; v: boolean }\n | { k: 'EOF' };\n\nfunction tokenize(input: string): Tok[] {\n const out: Tok[] = [];\n let i = 0;\n const s = input.trim();\n while (i < s.length) {\n const c = s[i]!;\n if (/\\s/.test(c)) {\n i++;\n continue;\n }\n if (s.slice(i, i + 2) === '&&') {\n out.push({ k: 'AND' });\n i += 2;\n continue;\n }\n if (s.slice(i, i + 2) === '||') {\n out.push({ k: 'OR' });\n i += 2;\n continue;\n }\n if (s.slice(i, i + 2) === '==') {\n out.push({ k: 'EQ' });\n i += 2;\n continue;\n }\n if (s.slice(i, i + 2) === '!=') {\n out.push({ k: 'NE' });\n i += 2;\n continue;\n }\n if (c === '(') {\n out.push({ k: 'LP' });\n i++;\n continue;\n }\n if (c === ')') {\n out.push({ k: 'RP' });\n i++;\n continue;\n }\n if (c === '!') {\n out.push({ k: 'NOT' });\n i++;\n continue;\n }\n if (c === \"'\" || c === '\"') {\n const q = c;\n i++;\n let buf = '';\n while (i < s.length) {\n const ch = s[i]!;\n if (ch === '\\\\' && i + 1 < s.length) {\n buf += s[i + 1]!;\n i += 2;\n continue;\n }\n if (ch === q) {\n i++;\n break;\n }\n buf += ch;\n i++;\n }\n out.push({ k: 'STR', v: buf });\n continue;\n }\n if (/[0-9]/.test(c) || (c === '-' && i + 1 < s.length && /[0-9]/.test(s[i + 1]!))) {\n let j = i + 1;\n while (j < s.length && /[0-9.]/.test(s[j]!)) j++;\n const n = Number(s.slice(i, j));\n out.push({ k: 'NUM', v: Number.isFinite(n) ? n : 0 });\n i = j;\n continue;\n }\n if (/[a-zA-Z_]/.test(c)) {\n let j = i + 1;\n while (j < s.length && /[a-zA-Z0-9_.]/.test(s[j]!)) j++;\n const word = s.slice(i, j);\n i = j;\n if (word === 'true') out.push({ k: 'BOOL', v: true });\n else if (word === 'false') out.push({ k: 'BOOL', v: false });\n else out.push({ k: 'IDENT', v: word });\n continue;\n }\n throw new Error(`Unexpected character in when-expression at ${i}: ${c}`);\n }\n out.push({ k: 'EOF' });\n return out;\n}\n\nfunction ctxLookup(ctx: WhenContext, key: string): unknown {\n if (key in ctx) return ctx[key];\n return undefined;\n}\n\nclass Parser {\n private i = 0;\n constructor(\n private readonly toks: Tok[],\n private readonly ctx: WhenContext,\n ) {}\n\n parse(): boolean {\n const v = this.parseOr();\n const tail = this.cur();\n if (tail.k !== 'EOF') {\n throw new Error(\n `Unexpected tokens after expression (at ${this.i}: ${tail.k}${'v' in tail ? ` ${(tail as { v: unknown }).v}` : ''})`,\n );\n }\n return v;\n }\n\n private cur(): Tok {\n return this.toks[this.i] ?? { k: 'EOF' };\n }\n\n private parseOr(): boolean {\n let v = this.parseAnd();\n while (this.cur().k === 'OR') {\n this.i++;\n const rhs = this.parseAnd();\n v = v || rhs;\n }\n return v;\n }\n\n private parseAnd(): boolean {\n let v = this.parseUnary();\n while (this.cur().k === 'AND') {\n this.i++;\n const rhs = this.parseUnary();\n v = v && rhs;\n }\n return v;\n }\n\n private parseUnary(): boolean {\n if (this.cur().k === 'NOT') {\n this.i++;\n return !this.parseUnary();\n }\n return this.parsePrimary();\n }\n\n private parsePrimary(): boolean {\n const t = this.cur();\n if (t.k === 'LP') {\n this.i++;\n const inner = this.parseOr();\n if (this.cur().k !== 'RP') throw new Error('Expected )');\n this.i++;\n return inner;\n }\n return this.parseAtom();\n }\n\n private parseAtom(): boolean {\n const left = this.readAtomValue();\n const op = this.cur();\n if (op.k === 'EQ' || op.k === 'NE') {\n this.i++;\n const right = this.readAtomValue();\n const eq = valuesLooselyEqual(left, right);\n return op.k === 'EQ' ? eq : !eq;\n }\n return isTruthyWhen(left);\n }\n\n private readAtomValue(): unknown {\n const t = this.cur();\n if (t.k === 'IDENT') {\n this.i++;\n return ctxLookup(this.ctx, t.v);\n }\n if (t.k === 'STR') {\n this.i++;\n return t.v;\n }\n if (t.k === 'NUM') {\n this.i++;\n return t.v;\n }\n if (t.k === 'BOOL') {\n this.i++;\n return t.v;\n }\n throw new Error('Expected value in when-expression');\n }\n}\n\nfunction valuesLooselyEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return a === b;\n if (typeof a === 'boolean' || typeof b === 'boolean') return Boolean(a) === Boolean(b);\n if (typeof a === 'number' && typeof b === 'number') return a === b;\n return String(a) === String(b);\n}\n\nfunction isTruthyWhen(v: unknown): boolean {\n if (typeof v === 'boolean') return v;\n if (typeof v === 'number') return v !== 0;\n if (typeof v === 'string') return v.length > 0;\n return v != null;\n}\n\n/**\n * Evaluate a when-expression string against a flat context object.\n * Unknown variables are treated as falsey for boolean tests; missing keys are undefined.\n */\nexport function evaluateWhenExpression(expr: string, ctx: WhenContext): boolean {\n const s = expr.trim();\n if (!s) return true;\n const toks = tokenize(s);\n return new Parser(toks, ctx).parse();\n}\n"],"mappings":"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"}
@@ -64,7 +64,7 @@ function createHonoApp(config) {
64
64
  c.header("X-Content-Type-Options", "nosniff");
65
65
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
66
66
  c.header("X-XSS-Protection", "1; mode=block");
67
- c.header("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
67
+ c.header("Permissions-Policy", "camera=(), microphone=(self), geolocation=()");
68
68
  c.header("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'");
69
69
  }));
70
70
  app.use("/api/skills/upload", bodyLimit({
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\n\nimport { createFixedWindowRateLimiter } from '../../infra/rate-limit.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { auth } from './middleware/auth.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\n\nconst log = createLogger('HonoApp');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n token?: string;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n const { service, token } = config;\n const app = new Hono();\n\n const gatewayPort = service.currentConfig.gateway.port ?? 18790;\n const configuredOrigins = service.currentConfig.gateway.corsOrigins;\n\n let corsOrigin: string | string[];\n if (configuredOrigins && configuredOrigins.length > 0) {\n corsOrigin = configuredOrigins;\n } else {\n corsOrigin = [\n `http://localhost:${gatewayPort}`,\n `http://127.0.0.1:${gatewayPort}`,\n 'http://localhost:3000',\n 'http://127.0.0.1:3000',\n ];\n }\n\n const CORS_OPTIONS = {\n origin: corsOrigin,\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n };\n\n app.use(logContextMiddleware());\n app.use(logger());\n app.use(cors(CORS_OPTIONS));\n\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n c.header('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');\n c.header(\n 'Content-Security-Policy',\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'\",\n );\n }));\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: 10 * 1024 * 1024,\n onError: (c) => {\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n app.use('/api/*', async (c, next) => {\n const maxSize = c.req.path === '/api/agent' ? WEBCHAT_AGENT_BODY_MAX : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) =>\n ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413),\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(\n auth({\n token,\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n }),\n );\n\n const strictRateLimiter = new Map<string, ReturnType<typeof createFixedWindowRateLimiter>>();\n\n const RATE_LIMIT_CLEANUP_INTERVAL = 5 * 60 * 1000;\n setInterval(() => {\n for (const [ip, limiter] of strictRateLimiter.entries()) {\n const result = limiter.consume();\n if (result.remaining === 9) {\n strictRateLimiter.delete(ip);\n }\n }\n }, RATE_LIMIT_CLEANUP_INTERVAL);\n\n const strictRateLimitMiddleware = createMiddleware(async (c, next) => {\n /*\n const clientIp = c.req.header('x-forwarded-for')?.split(',')[0]?.trim()\n ?? c.req.header('x-real-ip')\n ?? 'unknown';\n\n let limiter = strictRateLimiter.get(clientIp);\n if (!limiter) {\n limiter = createFixedWindowRateLimiter({ maxRequests: 10, windowMs: 60_000 });\n strictRateLimiter.set(clientIp, limiter);\n }\n\n const result = limiter.consume();\n if (!result.allowed) {\n c.header('Retry-After', String(Math.ceil(result.retryAfterMs / 1000)));\n return c.json({ error: 'Too many requests' }, 429);\n }\n\n c.header('X-RateLimit-Remaining', String(result.remaining));\n */\n await next();\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(authenticated, {\n service,\n strictRateLimitMiddleware,\n sseConfig,\n });\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error({ err }, 'Hono error');\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;aAMqD;AAUrD,MAAM,MAAM,aAAa,UAAU;;;;;;AAYnC,SAAgB,8BAA8B,MAAuB;AACnE,QAAO,sCAAsC,KAAK,KAAK;;AAGzD,SAAgB,cAAc,QAA6B;CACzD,MAAM,EAAE,SAAS,UAAU;CAC3B,MAAM,MAAM,IAAI,MAAM;CAEtB,MAAM,cAAc,QAAQ,cAAc,QAAQ,QAAQ;CAC1D,MAAM,oBAAoB,QAAQ,cAAc,QAAQ;CAExD,IAAI;AACJ,KAAI,qBAAqB,kBAAkB,SAAS,EAClD,cAAa;KAEb,cAAa;EACX,oBAAoB;EACpB,oBAAoB;EACpB;EACA;EACD;CAGH,MAAM,eAAe;EACnB,QAAQ;EACR,cAAc;GAAC;GAAO;GAAQ;GAAS;GAAU;GAAU;EAC3D,cAAc;GAAC;GAAgB;GAAiB;GAAU;GAAgB;GAAgB;EAC1F,aAAa;EACb,QAAQ;EACT;AAED,KAAI,IAAI,sBAAsB,CAAC;AAC/B,KAAI,IAAI,QAAQ,CAAC;AACjB,KAAI,IAAI,KAAK,aAAa,CAAC;AAE3B,KAAI,IAAI,iBAAiB,OAAO,GAAG,SAAS;AAC1C,QAAM,MAAM;AACZ,MAAI,8BAA8B,EAAE,IAAI,KAAK,CAC3C;AAEF,IAAE,OAAO,mBAAmB,OAAO;AACnC,IAAE,OAAO,0BAA0B,UAAU;AAC7C,IAAE,OAAO,mBAAmB,kCAAkC;AAC9D,IAAE,OAAO,oBAAoB,gBAAgB;AAC7C,IAAE,OAAO,sBAAsB,2CAA2C;AAC1E,IAAE,OACA,2BACA,0KACD;GACD,CAAC;AAEH,KAAI,IAAI,sBAAsB,UAAU;EACtC,SAAS,KAAK,OAAO;EACrB,UAAU,MAAM;AACd,UAAO,EAAE,KAAK;IAAE,OAAO;IAA2B,SAAS;IAAQ,EAAE,IAAI;;EAE5E,CAAC,CAAC;CAEH,MAAM,uBAAuB,IAAI,OAAO;CACxC,MAAM,yBAAyB,iCAAiC;AAEhE,KAAI,IAAI,UAAU,OAAO,GAAG,SAAS;EACnC,MAAM,UAAU,EAAE,IAAI,SAAS,eAAe,yBAAyB;EACvE,MAAM,YAAY,KAAK,KAAK,WAAW,OAAO,MAAM;AACpD,SAAO,UAAU;GACf;GACA,UAAU,QACR,IAAI,KAAK;IAAE,OAAO;IAA0B,SAAS,GAAG,UAAU;IAAK,EAAE,IAAI;GAChF,CAAC,CAAC,GAAG,KAAK;GACX;AAEF,6BAA4B,KAAK,QAAQ;AAKzC,oCAAmC,KAAK,QAAQ;CAEhD,MAAM,gBAAgB,IAAI,MAAM;AAChC,eAAc,IACZ,KAAK;EACH;EACA,sBAAsB,QAAQ,cAAc,SAAS;EACtD,CAAC,CACH;CAED,MAAM,oCAAoB,IAAI,KAA8D;AAG5F,mBAAkB;AAChB,OAAK,MAAM,CAAC,IAAI,YAAY,kBAAkB,SAAS,CAErD,KADe,QAAQ,SACb,CAAC,cAAc,EACvB,mBAAkB,OAAO,GAAG;IALE,MAAS,IAQd;AA8B/B,6BAA4B,eAAe;EACzC;EACA,2BA9BgC,iBAAiB,OAAO,GAAG,SAAS;AAoBpE,SAAM,MAAM;IAUa;EACzB,WAAA;GAPA;GACA,mBAAmB,QAAQ,cAAc,QAAQ;GAMxC;EACV,CAAC;AAEF,KAAI,MAAM,KAAK,cAAc;AAE7B,KAAI,UAAU,MAAM;AAClB,SAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;GAC1C;AAEF,KAAI,SAAS,KAAK,MAAM;AACtB,MAAI,MAAM,EAAE,KAAK,EAAE,aAAa;AAChC,SAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,EAAE,IAAI;GACtD;AAEF,QAAO"}
1
+ {"version":3,"file":"app.js","names":[],"sources":["../../../../src/gateway/hono/app.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { createMiddleware } from 'hono/factory';\nimport { bodyLimit } from 'hono/body-limit';\n\nimport { createFixedWindowRateLimiter } from '../../infra/rate-limit.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { GatewayService } from '../service.js';\nimport { maxWebchatAgentRequestBodyBytes } from '../chat-limits.js';\nimport { auth } from './middleware/auth.js';\nimport { logContextMiddleware } from './middleware/log-context.js';\nimport { logger } from './middleware/logger.js';\nimport { registerPublicExtensionAssetRoutes } from './routes/auth-registry-extensions.js';\nimport { registerAuthenticatedRoutes } from './routes/index.js';\nimport { registerPublicGatewayRoutes } from './routes/public-gateway.js';\n\nconst log = createLogger('HonoApp');\n\nexport interface HonoAppConfig {\n service: GatewayService;\n token?: string;\n}\n\n/**\n * Extension sandbox HTML under `/api/extensions/:id/assets/*` ships its own CSP\n * (`frame-ancestors 'self'`). The global gateway middleware must not overwrite it\n * with `frame-ancestors 'none'` / `X-Frame-Options: DENY`, or the console cannot embed iframes.\n */\nexport function isExtensionGatewayUiAssetPath(path: string): boolean {\n return /^\\/api\\/extensions\\/[^/]+\\/assets\\//.test(path);\n}\n\nexport function createHonoApp(config: HonoAppConfig): Hono {\n const { service, token } = config;\n const app = new Hono();\n\n const gatewayPort = service.currentConfig.gateway.port ?? 18790;\n const configuredOrigins = service.currentConfig.gateway.corsOrigins;\n\n let corsOrigin: string | string[];\n if (configuredOrigins && configuredOrigins.length > 0) {\n corsOrigin = configuredOrigins;\n } else {\n corsOrigin = [\n `http://localhost:${gatewayPort}`,\n `http://127.0.0.1:${gatewayPort}`,\n 'http://localhost:3000',\n 'http://127.0.0.1:3000',\n ];\n }\n\n const CORS_OPTIONS = {\n origin: corsOrigin,\n allowMethods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'X-Session-Id', 'Last-Event-ID'],\n credentials: true,\n maxAge: 86400,\n };\n\n app.use(logContextMiddleware());\n app.use(logger());\n app.use(cors(CORS_OPTIONS));\n\n app.use(createMiddleware(async (c, next) => {\n await next();\n if (isExtensionGatewayUiAssetPath(c.req.path)) {\n return;\n }\n c.header('X-Frame-Options', 'DENY');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'strict-origin-when-cross-origin');\n c.header('X-XSS-Protection', '1; mode=block');\n // microphone=(self): allow same-origin chat voice (composer). microphone=() breaks packaged Electron loading the gateway SPA.\n c.header('Permissions-Policy', 'camera=(), microphone=(self), geolocation=()');\n c.header(\n 'Content-Security-Policy',\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'\",\n );\n }));\n\n app.use('/api/skills/upload', bodyLimit({\n maxSize: 10 * 1024 * 1024,\n onError: (c) => {\n return c.json({ error: 'Skill package too large', maxSize: '10MB' }, 413);\n },\n }));\n\n const DEFAULT_API_BODY_MAX = 1 * 1024 * 1024;\n const WEBCHAT_AGENT_BODY_MAX = maxWebchatAgentRequestBodyBytes();\n\n app.use('/api/*', async (c, next) => {\n const maxSize = c.req.path === '/api/agent' ? WEBCHAT_AGENT_BODY_MAX : DEFAULT_API_BODY_MAX;\n const maxSizeMb = Math.ceil(maxSize / (1024 * 1024));\n return bodyLimit({\n maxSize,\n onError: (ctx) =>\n ctx.json({ error: 'Request body too large', maxSize: `${maxSizeMb}MB` }, 413),\n })(c, next);\n });\n\n registerPublicGatewayRoutes(app, service);\n\n // Extension UI assets are served without auth: sandboxed iframes (no allow-same-origin)\n // have an opaque origin of `null` and cannot forward the ?token= from the parent HTML URL.\n // Security is enforced by the strict CSP (frame-ancestors 'self') on every response.\n registerPublicExtensionAssetRoutes(app, service);\n\n const authenticated = new Hono();\n authenticated.use(\n auth({\n token,\n getGatewayAuth: () => service.currentConfig.gateway?.auth,\n }),\n );\n\n const strictRateLimiter = new Map<string, ReturnType<typeof createFixedWindowRateLimiter>>();\n\n const RATE_LIMIT_CLEANUP_INTERVAL = 5 * 60 * 1000;\n setInterval(() => {\n for (const [ip, limiter] of strictRateLimiter.entries()) {\n const result = limiter.consume();\n if (result.remaining === 9) {\n strictRateLimiter.delete(ip);\n }\n }\n }, RATE_LIMIT_CLEANUP_INTERVAL);\n\n const strictRateLimitMiddleware = createMiddleware(async (c, next) => {\n /*\n const clientIp = c.req.header('x-forwarded-for')?.split(',')[0]?.trim()\n ?? c.req.header('x-real-ip')\n ?? 'unknown';\n\n let limiter = strictRateLimiter.get(clientIp);\n if (!limiter) {\n limiter = createFixedWindowRateLimiter({ maxRequests: 10, windowMs: 60_000 });\n strictRateLimiter.set(clientIp, limiter);\n }\n\n const result = limiter.consume();\n if (!result.allowed) {\n c.header('Retry-After', String(Math.ceil(result.retryAfterMs / 1000)));\n return c.json({ error: 'Too many requests' }, 429);\n }\n\n c.header('X-RateLimit-Remaining', String(result.remaining));\n */\n await next();\n });\n\n const sseConfig = {\n service,\n maxSseConnections: service.currentConfig.gateway.maxSseConnections,\n };\n\n registerAuthenticatedRoutes(authenticated, {\n service,\n strictRateLimitMiddleware,\n sseConfig,\n });\n\n app.route('/', authenticated);\n\n app.notFound((c) => {\n return c.json({ error: 'Not found' }, 404);\n });\n\n app.onError((err, c) => {\n log.error({ err }, 'Hono error');\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;aAMqD;AAUrD,MAAM,MAAM,aAAa,UAAU;;;;;;AAYnC,SAAgB,8BAA8B,MAAuB;AACnE,QAAO,sCAAsC,KAAK,KAAK;;AAGzD,SAAgB,cAAc,QAA6B;CACzD,MAAM,EAAE,SAAS,UAAU;CAC3B,MAAM,MAAM,IAAI,MAAM;CAEtB,MAAM,cAAc,QAAQ,cAAc,QAAQ,QAAQ;CAC1D,MAAM,oBAAoB,QAAQ,cAAc,QAAQ;CAExD,IAAI;AACJ,KAAI,qBAAqB,kBAAkB,SAAS,EAClD,cAAa;KAEb,cAAa;EACX,oBAAoB;EACpB,oBAAoB;EACpB;EACA;EACD;CAGH,MAAM,eAAe;EACnB,QAAQ;EACR,cAAc;GAAC;GAAO;GAAQ;GAAS;GAAU;GAAU;EAC3D,cAAc;GAAC;GAAgB;GAAiB;GAAU;GAAgB;GAAgB;EAC1F,aAAa;EACb,QAAQ;EACT;AAED,KAAI,IAAI,sBAAsB,CAAC;AAC/B,KAAI,IAAI,QAAQ,CAAC;AACjB,KAAI,IAAI,KAAK,aAAa,CAAC;AAE3B,KAAI,IAAI,iBAAiB,OAAO,GAAG,SAAS;AAC1C,QAAM,MAAM;AACZ,MAAI,8BAA8B,EAAE,IAAI,KAAK,CAC3C;AAEF,IAAE,OAAO,mBAAmB,OAAO;AACnC,IAAE,OAAO,0BAA0B,UAAU;AAC7C,IAAE,OAAO,mBAAmB,kCAAkC;AAC9D,IAAE,OAAO,oBAAoB,gBAAgB;AAE7C,IAAE,OAAO,sBAAsB,+CAA+C;AAC9E,IAAE,OACA,2BACA,0KACD;GACD,CAAC;AAEH,KAAI,IAAI,sBAAsB,UAAU;EACtC,SAAS,KAAK,OAAO;EACrB,UAAU,MAAM;AACd,UAAO,EAAE,KAAK;IAAE,OAAO;IAA2B,SAAS;IAAQ,EAAE,IAAI;;EAE5E,CAAC,CAAC;CAEH,MAAM,uBAAuB,IAAI,OAAO;CACxC,MAAM,yBAAyB,iCAAiC;AAEhE,KAAI,IAAI,UAAU,OAAO,GAAG,SAAS;EACnC,MAAM,UAAU,EAAE,IAAI,SAAS,eAAe,yBAAyB;EACvE,MAAM,YAAY,KAAK,KAAK,WAAW,OAAO,MAAM;AACpD,SAAO,UAAU;GACf;GACA,UAAU,QACR,IAAI,KAAK;IAAE,OAAO;IAA0B,SAAS,GAAG,UAAU;IAAK,EAAE,IAAI;GAChF,CAAC,CAAC,GAAG,KAAK;GACX;AAEF,6BAA4B,KAAK,QAAQ;AAKzC,oCAAmC,KAAK,QAAQ;CAEhD,MAAM,gBAAgB,IAAI,MAAM;AAChC,eAAc,IACZ,KAAK;EACH;EACA,sBAAsB,QAAQ,cAAc,SAAS;EACtD,CAAC,CACH;CAED,MAAM,oCAAoB,IAAI,KAA8D;AAG5F,mBAAkB;AAChB,OAAK,MAAM,CAAC,IAAI,YAAY,kBAAkB,SAAS,CAErD,KADe,QAAQ,SACb,CAAC,cAAc,EACvB,mBAAkB,OAAO,GAAG;IALE,MAAS,IAQd;AA8B/B,6BAA4B,eAAe;EACzC;EACA,2BA9BgC,iBAAiB,OAAO,GAAG,SAAS;AAoBpE,SAAM,MAAM;IAUa;EACzB,WAAA;GAPA;GACA,mBAAmB,QAAQ,cAAc,QAAQ;GAMxC;EACV,CAAC;AAEF,KAAI,MAAM,KAAK,cAAc;AAE7B,KAAI,UAAU,MAAM;AAClB,SAAO,EAAE,KAAK,EAAE,OAAO,aAAa,EAAE,IAAI;GAC1C;AAEF,KAAI,SAAS,KAAK,MAAM;AACtB,MAAI,MAAM,EAAE,KAAK,EAAE,aAAa;AAChC,SAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,EAAE,IAAI;GACtD;AAEF,QAAO"}
@@ -55,6 +55,19 @@ export declare function buildSafeWebConfigPayload(service: GatewayService): Prom
55
55
  injectionFrequency?: "every-turn" | "first-turn";
56
56
  contextCadence?: number;
57
57
  dialecticCadence?: number;
58
+ dreaming?: {
59
+ enabled?: boolean;
60
+ frequency?: string;
61
+ timezone?: string;
62
+ phases?: {
63
+ deep?: {
64
+ enabled?: boolean;
65
+ minScore?: number;
66
+ minRecallCount?: number;
67
+ limit?: number;
68
+ };
69
+ };
70
+ };
58
71
  };
59
72
  sessionSearch: {
60
73
  summaryModel?: string;
@@ -1,6 +1,8 @@
1
1
  import { getAllModels, getAvailableModels, init_providers } from "../../../providers/index.js";
2
2
  import { ActivationPlanner } from "../../../extensions/activation-planner.js";
3
3
  import { mergeActivationContext } from "../../../extensions/activation-context.js";
4
+ import { buildWhenContextSnapshot } from "../../../extensions/when-context.js";
5
+ import { fetchRegistry, listExtensions, searchExtensions } from "../../../extensions/marketplace.js";
4
6
  import { createOAuthHandler } from "../oauth.js";
5
7
  import { createOAuthAsyncHandler } from "../oauth-async.js";
6
8
  import { extensionAssetMimeType } from "../lib/extension-assets.js";
@@ -159,6 +161,7 @@ function registerAuthRegistryExtensionsRoutes(authenticated, deps) {
159
161
  active: activeIds.has(ext.id),
160
162
  activationEligible: activationEligibleIds.has(ext.id),
161
163
  hasUi: Boolean(ext.manifest.ui),
164
+ hasConfigSchema: Boolean(ext.manifest.configSchema),
162
165
  ui: ext.manifest.ui ? {
163
166
  icon: ext.manifest.ui.icon,
164
167
  permissions: ext.manifest.ui.permissions,
@@ -254,6 +257,31 @@ function registerAuthRegistryExtensionsRoutes(authenticated, deps) {
254
257
  await saveExtensionStore(namespace, config);
255
258
  return c.json({ ok: true });
256
259
  });
260
+ authenticated.get("/api/context", (c) => {
261
+ const loader = service.getExtensionLoader();
262
+ const snapshot = buildWhenContextSnapshot(service.currentConfig, loader);
263
+ return c.json(snapshot);
264
+ });
265
+ authenticated.get("/api/marketplace", async (c) => {
266
+ const q = c.req.query("q");
267
+ const category = c.req.query("category");
268
+ try {
269
+ let extensions;
270
+ if (typeof q === "string" && q.trim()) extensions = await searchExtensions(q.trim());
271
+ else if (typeof category === "string" && category.trim()) extensions = await listExtensions(category.trim());
272
+ else extensions = (await fetchRegistry()).extensions;
273
+ return c.json({
274
+ ok: true,
275
+ extensions
276
+ });
277
+ } catch (err) {
278
+ return c.json({
279
+ ok: false,
280
+ extensions: [],
281
+ error: err instanceof Error ? err.message : "marketplace fetch failed"
282
+ }, 500);
283
+ }
284
+ });
257
285
  authenticated.post("/api/registry/reload", async (c) => {
258
286
  try {
259
287
  await service.reloadConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"auth-registry-extensions.js","names":[],"sources":["../../../../../src/gateway/hono/routes/auth-registry-extensions.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { relative, resolve } from 'node:path';\n\nimport type { Config as SurfaceConfig } from '../../../config/config-surface.js';\nimport type { GatewayService } from '../../service.js';\nimport { mergeActivationContext } from '../../../extensions/activation-context.js';\nimport { ActivationPlanner } from '../../../extensions/activation-planner.js';\nimport { getAllModels, getAvailableModels, type Model, type Api } from '../../../providers/index.js';\nimport { createOAuthHandler, loadOAuthCredentialsToCache } from '../oauth.js';\nimport { createOAuthAsyncHandler } from '../oauth-async.js';\nimport { extensionAssetMimeType } from '../lib/extension-assets.js';\nimport { loadExtensionStore, saveExtensionStore } from '../lib/extension-store.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst EXTENSION_ASSET_CSP =\n \"default-src 'self'; \" +\n \"script-src 'self' 'unsafe-inline'; \" +\n \"style-src 'self' 'unsafe-inline'; \" +\n \"img-src 'self' data: blob:; \" +\n \"connect-src 'none'; \" +\n \"frame-ancestors 'self'; \" +\n \"frame-src 'none'; \" +\n \"base-uri 'none'; \" +\n \"object-src 'none'; \" +\n \"form-action 'none'\";\n\nfunction rewriteExtensionAssetHtml(html: string, extensionId: string, assetPath: string): string {\n const assetDir = assetPath.includes('/') ? assetPath.slice(0, assetPath.lastIndexOf('/') + 1) : '';\n const assetBase = `/api/extensions/${extensionId}/assets/${assetDir}`;\n\n return html\n .replace(/(<script\\b[^>]*?\\ssrc=)([\"'])(\\.\\/)?([^\"']+)\\2/gi, (_match, tag, quote, _dot, file) => {\n const isAbsolute =\n file.startsWith('/') || file.startsWith('http://') || file.startsWith('https://');\n const resolvedSrc = isAbsolute ? file : `${assetBase}${file}`;\n return `${tag}${quote}${resolvedSrc}${quote} crossorigin=\"anonymous\"`;\n })\n .replace(/(<link\\b[^>]*?\\shref=)([\"'])(\\.\\/)?([^\"']+)\\2/gi, (_match, tag, quote, _dot, file) => {\n const isAbsolute =\n file.startsWith('/') || file.startsWith('http://') || file.startsWith('https://');\n const resolvedHref = isAbsolute ? file : `${assetBase}${file}`;\n return `${tag}${quote}${resolvedHref}${quote}`;\n });\n}\n\n/**\n * Register extension UI asset routes on the public (unauthenticated) app.\n *\n * Sandboxed iframes (`sandbox=\"allow-scripts …\"` without `allow-same-origin`) have an\n * opaque origin of `null`, so sub-resource requests from inside the iframe cannot\n * carry the `?token=` query parameter that was on the parent HTML URL. Putting these\n * routes behind the auth middleware therefore causes every JS/CSS asset to return 401.\n *\n * Security is maintained by the strict Content-Security-Policy returned with every\n * asset (`frame-ancestors 'self'`), which prevents any page other than the gateway\n * console itself from embedding the extension iframes.\n */\nexport function registerPublicExtensionAssetRoutes(app: Hono, service: GatewayService): void {\n app.get('/api/extensions/:id/assets/*', async (c) => {\n const extensionId = c.req.param('id');\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ error: 'Extensions unavailable' }, 503);\n }\n\n const discovered = loader.discoverExtensions();\n const ext = discovered.find((e) => e.id === extensionId);\n if (!ext || !ext.manifest.ui) {\n return c.json({ error: 'Extension not found or has no UI' }, 404);\n }\n\n const prefix = `/api/extensions/${extensionId}/assets/`;\n const assetPathEncoded = c.req.path.startsWith(prefix) ? c.req.path.slice(prefix.length) : '';\n let assetPath = assetPathEncoded;\n try {\n assetPath = decodeURIComponent(assetPathEncoded);\n } catch {\n return c.json({ error: 'Invalid asset path' }, 400);\n }\n\n if (!assetPath || assetPath.includes('..')) {\n return c.json({ error: 'Invalid asset path' }, 400);\n }\n\n const root = resolve(ext.path);\n const fullPath = resolve(root, assetPath);\n const rel = relative(root, fullPath);\n if (rel.startsWith('..') || rel === '') {\n return c.json({ error: 'Path traversal denied' }, 403);\n }\n\n if (!existsSync(fullPath) || !statSync(fullPath).isFile()) {\n return c.json({ error: 'Not found' }, 404);\n }\n\n const rawContent = readFileSync(fullPath);\n const mimeType = extensionAssetMimeType(assetPath);\n\n const body: string | Uint8Array = mimeType.startsWith('text/html')\n ? rewriteExtensionAssetHtml(rawContent.toString('utf-8'), extensionId, assetPath)\n : new Uint8Array(rawContent);\n\n return new Response(body, {\n status: 200,\n headers: {\n 'Content-Type': mimeType,\n 'Content-Security-Policy': EXTENSION_ASSET_CSP,\n 'Cache-Control': 'no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'Access-Control-Allow-Origin': 'null',\n },\n });\n });\n}\n\nexport function registerAuthRegistryExtensionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n // ========== Auth API (/api/auth) ==========\n\n // GET /api/auth/token - Get current gateway token\n authenticated.get('/api/auth/token', (c) => {\n const authToken = service.getAuthToken();\n return c.json({ \n ok: true, \n payload: { \n token: authToken,\n mode: service.getAuthMode(),\n } \n });\n });\n\n // POST /api/auth/token/refresh - Generate new gateway token\n authenticated.post('/api/auth/token/refresh', async (c) => {\n try {\n const newToken = await service.refreshAuthToken();\n return c.json({ \n ok: true, \n payload: { \n token: newToken,\n message: 'Token refreshed successfully. Please update your client configuration.'\n } \n });\n } catch (err) {\n return c.json({ \n ok: false, \n error: err instanceof Error ? err.message : 'Failed to refresh token' \n }, 500);\n }\n });\n\n // ========== OAuth API (/api/auth/oauth) ==========\n authenticated.route('/api/auth/oauth', createOAuthHandler(service));\n\n // ========== Async OAuth API (/api/auth/oauth-async) ==========\n authenticated.route('/api/auth/oauth-async', createOAuthAsyncHandler(service));\n\n // Load OAuth credentials from config into cache on startup\n loadOAuthCredentialsToCache(service);\n\n // ========== Registry API ==========\n \n // GET /api/registry - Full registry for frontend\n authenticated.get('/api/registry', async (c) => {\n const allModels = getAllModels();\n const availableModels = await getAvailableModels();\n const configured = new Set(availableModels.map(m => `${m.provider}/${m.id}`));\n \n // Group models by provider\n const providerMap = new Map<string, Model<Api>[]>();\n for (const model of allModels) {\n const list = providerMap.get(model.provider) ?? [];\n list.push(model);\n providerMap.set(model.provider, list);\n }\n \n return c.json({\n ok: true,\n payload: {\n version: 'pi-ai',\n providers: Array.from(providerMap.entries()).map(([id, models]) => ({\n id,\n name: id.charAt(0).toUpperCase() + id.slice(1),\n configured: models.some(m => configured.has(`${m.provider}/${m.id}`)),\n models: models.map(m => ({\n ref: `${m.provider}/${m.id}`,\n id: m.id,\n name: m.name,\n provider: m.provider,\n reasoning: m.reasoning ?? false,\n input: m.input ?? ['text'],\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n available: configured.has(`${m.provider}/${m.id}`),\n })),\n })),\n },\n });\n });\n\n // ========== Extension UI (manifest discovery + static assets) ==========\n\n authenticated.get('/api/extensions', async (c) => {\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ extensions: [] });\n }\n\n const registry = loader.getRegistry();\n const discovered = loader.discoverExtensions();\n const activeIds = new Set<string>();\n for (const [id] of registry.extensions) {\n activeIds.add(id);\n }\n\n loader.setConfig(service.currentConfig as unknown as SurfaceConfig);\n let activationEligibleIds: Set<string> = new Set();\n try {\n const planner = new ActivationPlanner(loader.buildManifestRegistry());\n activationEligibleIds = new Set(\n planner.getActivatedIds(mergeActivationContext(service.currentConfig as unknown as SurfaceConfig)),\n );\n } catch {\n activationEligibleIds = new Set();\n }\n\n const extensions = discovered.map((ext) => ({\n id: ext.manifest.id,\n name: ext.manifest.name,\n description: ext.manifest.description,\n version: ext.manifest.version,\n kind: ext.manifest.kind,\n source: ext.source,\n active: activeIds.has(ext.id),\n activationEligible: activationEligibleIds.has(ext.id),\n hasUi: Boolean(ext.manifest.ui),\n ui: ext.manifest.ui\n ? {\n icon: ext.manifest.ui.icon,\n permissions: ext.manifest.ui.permissions,\n contributions: ext.manifest.ui.contributions,\n }\n : undefined,\n }));\n return c.json({ extensions });\n });\n\n /**\n * Built-in (bundled) extension enable/disable — persists `extensions.enabled` / `extensions.disabled`.\n * Body: `{ extensionId: string, enabled: boolean }`\n */\n authenticated.post('/api/extensions/bundled/activation', strictRateLimitMiddleware, async (c) => {\n const body = (await c.req.json().catch(() => null)) as\n | { extensionId?: unknown; enabled?: unknown }\n | null;\n const extensionId =\n typeof body?.extensionId === 'string' ? body.extensionId.trim() : '';\n if (!extensionId) {\n return c.json({ ok: false, error: { message: 'extensionId is required' } }, 400);\n }\n if (typeof body?.enabled !== 'boolean') {\n return c.json({ ok: false, error: { message: 'enabled must be a boolean' } }, 400);\n }\n const result = await service.setBundledExtensionActivationTarget(extensionId, body.enabled);\n if (!result.ok) {\n return c.json({ ok: false, error: { message: result.error ?? 'Request failed' } }, 400);\n }\n return c.json({\n ok: true,\n payload: { requiresGatewayRestart: result.requiresGatewayRestart },\n });\n });\n\n authenticated.get('/api/extensions/:id', async (c) => {\n const extensionId = c.req.param('id');\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ error: 'Extensions unavailable' }, 503);\n }\n const discovered = loader.discoverExtensions();\n const ext = discovered.find((e) => e.id === extensionId);\n if (!ext) {\n return c.json({ error: 'Extension not found' }, 404);\n }\n const registry = loader.getRegistry();\n return c.json({\n id: ext.manifest.id,\n name: ext.manifest.name,\n description: ext.manifest.description,\n version: ext.manifest.version,\n kind: ext.manifest.kind,\n source: ext.source,\n path: ext.path,\n active: registry.extensions.has(ext.id),\n manifest: ext.manifest,\n });\n });\n\n authenticated.get('/api/extensions/:id/storage', async (c) => {\n const extensionId = c.req.param('id');\n const store = await loadExtensionStore(extensionId);\n return c.json({ keys: Object.keys(store) });\n });\n\n authenticated.get('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const store = await loadExtensionStore(extensionId);\n if (!(key in store)) {\n return c.json({ error: 'Key not found' }, 404);\n }\n return c.json({ value: store[key] });\n });\n\n authenticated.put('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const body = (await c.req.json().catch(() => null)) as { value?: unknown } | null;\n if (body === null || !('value' in body)) {\n return c.json({ error: 'Request body must contain a \"value\" field' }, 400);\n }\n const store = await loadExtensionStore(extensionId);\n store[key] = body.value;\n await saveExtensionStore(extensionId, store);\n return c.json({ ok: true });\n });\n\n authenticated.delete('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const store = await loadExtensionStore(extensionId);\n delete store[key];\n await saveExtensionStore(extensionId, store);\n return c.json({ ok: true });\n });\n\n authenticated.get('/api/extensions/:id/config', async (c) => {\n const extensionId = c.req.param('id');\n return c.json(await loadExtensionStore(`__config__${extensionId}`));\n });\n\n authenticated.patch('/api/extensions/:id/config', async (c) => {\n const extensionId = c.req.param('id');\n const patch = (await c.req.json().catch(() => null)) as Record<string, unknown> | null;\n if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {\n return c.json({ error: 'Request body must be a JSON object' }, 400);\n }\n const namespace = `__config__${extensionId}`;\n const config = await loadExtensionStore(namespace);\n Object.assign(config, patch);\n await saveExtensionStore(namespace, config);\n return c.json({ ok: true });\n });\n\n // POST /api/registry/reload — reload gateway config and refresh model list for clients\n authenticated.post('/api/registry/reload', async (c) => {\n try {\n // Reload config\n await service.reloadConfig();\n \n // Reload OAuth credentials from new config\n loadOAuthCredentialsToCache(service);\n \n const models = getAllModels();\n \n // Emit SSE event to all connected clients\n service.emit('registry.updated', { modelCount: models.length });\n \n return c.json({\n ok: true,\n payload: {\n message: 'Registry reloaded',\n modelCount: models.length,\n },\n });\n } catch (err) {\n return c.json({\n error: err instanceof Error ? err.message : 'Failed to reload registry',\n }, 500);\n }\n });\n\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"auth-registry-extensions.js","names":["extensionMarketplace.searchExtensions","extensionMarketplace.listExtensions","extensionMarketplace.fetchRegistry"],"sources":["../../../../../src/gateway/hono/routes/auth-registry-extensions.ts"],"sourcesContent":["import type { Hono } from 'hono';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { relative, resolve } from 'node:path';\n\nimport type { Config as SurfaceConfig } from '../../../config/config-surface.js';\nimport type { GatewayService } from '../../service.js';\nimport { buildWhenContextSnapshot } from '../../../extensions/when-context.js';\nimport * as extensionMarketplace from '../../../extensions/marketplace.js';\nimport { mergeActivationContext } from '../../../extensions/activation-context.js';\nimport { ActivationPlanner } from '../../../extensions/activation-planner.js';\nimport { getAllModels, getAvailableModels, type Model, type Api } from '../../../providers/index.js';\nimport { createOAuthHandler, loadOAuthCredentialsToCache } from '../oauth.js';\nimport { createOAuthAsyncHandler } from '../oauth-async.js';\nimport { extensionAssetMimeType } from '../lib/extension-assets.js';\nimport { loadExtensionStore, saveExtensionStore } from '../lib/extension-store.js';\nimport type { AuthenticatedRouteDeps } from './deps.js';\n\nconst EXTENSION_ASSET_CSP =\n \"default-src 'self'; \" +\n \"script-src 'self' 'unsafe-inline'; \" +\n \"style-src 'self' 'unsafe-inline'; \" +\n \"img-src 'self' data: blob:; \" +\n \"connect-src 'none'; \" +\n \"frame-ancestors 'self'; \" +\n \"frame-src 'none'; \" +\n \"base-uri 'none'; \" +\n \"object-src 'none'; \" +\n \"form-action 'none'\";\n\nfunction rewriteExtensionAssetHtml(html: string, extensionId: string, assetPath: string): string {\n const assetDir = assetPath.includes('/') ? assetPath.slice(0, assetPath.lastIndexOf('/') + 1) : '';\n const assetBase = `/api/extensions/${extensionId}/assets/${assetDir}`;\n\n return html\n .replace(/(<script\\b[^>]*?\\ssrc=)([\"'])(\\.\\/)?([^\"']+)\\2/gi, (_match, tag, quote, _dot, file) => {\n const isAbsolute =\n file.startsWith('/') || file.startsWith('http://') || file.startsWith('https://');\n const resolvedSrc = isAbsolute ? file : `${assetBase}${file}`;\n return `${tag}${quote}${resolvedSrc}${quote} crossorigin=\"anonymous\"`;\n })\n .replace(/(<link\\b[^>]*?\\shref=)([\"'])(\\.\\/)?([^\"']+)\\2/gi, (_match, tag, quote, _dot, file) => {\n const isAbsolute =\n file.startsWith('/') || file.startsWith('http://') || file.startsWith('https://');\n const resolvedHref = isAbsolute ? file : `${assetBase}${file}`;\n return `${tag}${quote}${resolvedHref}${quote}`;\n });\n}\n\n/**\n * Register extension UI asset routes on the public (unauthenticated) app.\n *\n * Sandboxed iframes (`sandbox=\"allow-scripts …\"` without `allow-same-origin`) have an\n * opaque origin of `null`, so sub-resource requests from inside the iframe cannot\n * carry the `?token=` query parameter that was on the parent HTML URL. Putting these\n * routes behind the auth middleware therefore causes every JS/CSS asset to return 401.\n *\n * Security is maintained by the strict Content-Security-Policy returned with every\n * asset (`frame-ancestors 'self'`), which prevents any page other than the gateway\n * console itself from embedding the extension iframes.\n */\nexport function registerPublicExtensionAssetRoutes(app: Hono, service: GatewayService): void {\n app.get('/api/extensions/:id/assets/*', async (c) => {\n const extensionId = c.req.param('id');\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ error: 'Extensions unavailable' }, 503);\n }\n\n const discovered = loader.discoverExtensions();\n const ext = discovered.find((e) => e.id === extensionId);\n if (!ext || !ext.manifest.ui) {\n return c.json({ error: 'Extension not found or has no UI' }, 404);\n }\n\n const prefix = `/api/extensions/${extensionId}/assets/`;\n const assetPathEncoded = c.req.path.startsWith(prefix) ? c.req.path.slice(prefix.length) : '';\n let assetPath = assetPathEncoded;\n try {\n assetPath = decodeURIComponent(assetPathEncoded);\n } catch {\n return c.json({ error: 'Invalid asset path' }, 400);\n }\n\n if (!assetPath || assetPath.includes('..')) {\n return c.json({ error: 'Invalid asset path' }, 400);\n }\n\n const root = resolve(ext.path);\n const fullPath = resolve(root, assetPath);\n const rel = relative(root, fullPath);\n if (rel.startsWith('..') || rel === '') {\n return c.json({ error: 'Path traversal denied' }, 403);\n }\n\n if (!existsSync(fullPath) || !statSync(fullPath).isFile()) {\n return c.json({ error: 'Not found' }, 404);\n }\n\n const rawContent = readFileSync(fullPath);\n const mimeType = extensionAssetMimeType(assetPath);\n\n const body: string | Uint8Array = mimeType.startsWith('text/html')\n ? rewriteExtensionAssetHtml(rawContent.toString('utf-8'), extensionId, assetPath)\n : new Uint8Array(rawContent);\n\n return new Response(body, {\n status: 200,\n headers: {\n 'Content-Type': mimeType,\n 'Content-Security-Policy': EXTENSION_ASSET_CSP,\n 'Cache-Control': 'no-store',\n 'X-Content-Type-Options': 'nosniff',\n 'Access-Control-Allow-Origin': 'null',\n },\n });\n });\n}\n\nexport function registerAuthRegistryExtensionsRoutes(authenticated: Hono, deps: AuthenticatedRouteDeps): void {\n const { service, strictRateLimitMiddleware } = deps;\n\n // ========== Auth API (/api/auth) ==========\n\n // GET /api/auth/token - Get current gateway token\n authenticated.get('/api/auth/token', (c) => {\n const authToken = service.getAuthToken();\n return c.json({ \n ok: true, \n payload: { \n token: authToken,\n mode: service.getAuthMode(),\n } \n });\n });\n\n // POST /api/auth/token/refresh - Generate new gateway token\n authenticated.post('/api/auth/token/refresh', async (c) => {\n try {\n const newToken = await service.refreshAuthToken();\n return c.json({ \n ok: true, \n payload: { \n token: newToken,\n message: 'Token refreshed successfully. Please update your client configuration.'\n } \n });\n } catch (err) {\n return c.json({ \n ok: false, \n error: err instanceof Error ? err.message : 'Failed to refresh token' \n }, 500);\n }\n });\n\n // ========== OAuth API (/api/auth/oauth) ==========\n authenticated.route('/api/auth/oauth', createOAuthHandler(service));\n\n // ========== Async OAuth API (/api/auth/oauth-async) ==========\n authenticated.route('/api/auth/oauth-async', createOAuthAsyncHandler(service));\n\n // Load OAuth credentials from config into cache on startup\n loadOAuthCredentialsToCache(service);\n\n // ========== Registry API ==========\n \n // GET /api/registry - Full registry for frontend\n authenticated.get('/api/registry', async (c) => {\n const allModels = getAllModels();\n const availableModels = await getAvailableModels();\n const configured = new Set(availableModels.map(m => `${m.provider}/${m.id}`));\n \n // Group models by provider\n const providerMap = new Map<string, Model<Api>[]>();\n for (const model of allModels) {\n const list = providerMap.get(model.provider) ?? [];\n list.push(model);\n providerMap.set(model.provider, list);\n }\n \n return c.json({\n ok: true,\n payload: {\n version: 'pi-ai',\n providers: Array.from(providerMap.entries()).map(([id, models]) => ({\n id,\n name: id.charAt(0).toUpperCase() + id.slice(1),\n configured: models.some(m => configured.has(`${m.provider}/${m.id}`)),\n models: models.map(m => ({\n ref: `${m.provider}/${m.id}`,\n id: m.id,\n name: m.name,\n provider: m.provider,\n reasoning: m.reasoning ?? false,\n input: m.input ?? ['text'],\n contextWindow: m.contextWindow ?? 128000,\n maxTokens: m.maxTokens ?? 4096,\n cost: {\n input: m.cost?.input ?? 0,\n output: m.cost?.output ?? 0,\n },\n available: configured.has(`${m.provider}/${m.id}`),\n })),\n })),\n },\n });\n });\n\n // ========== Extension UI (manifest discovery + static assets) ==========\n\n authenticated.get('/api/extensions', async (c) => {\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ extensions: [] });\n }\n\n const registry = loader.getRegistry();\n const discovered = loader.discoverExtensions();\n const activeIds = new Set<string>();\n for (const [id] of registry.extensions) {\n activeIds.add(id);\n }\n\n loader.setConfig(service.currentConfig as unknown as SurfaceConfig);\n let activationEligibleIds: Set<string> = new Set();\n try {\n const planner = new ActivationPlanner(loader.buildManifestRegistry());\n activationEligibleIds = new Set(\n planner.getActivatedIds(mergeActivationContext(service.currentConfig as unknown as SurfaceConfig)),\n );\n } catch {\n activationEligibleIds = new Set();\n }\n\n const extensions = discovered.map((ext) => ({\n id: ext.manifest.id,\n name: ext.manifest.name,\n description: ext.manifest.description,\n version: ext.manifest.version,\n kind: ext.manifest.kind,\n source: ext.source,\n active: activeIds.has(ext.id),\n activationEligible: activationEligibleIds.has(ext.id),\n hasUi: Boolean(ext.manifest.ui),\n hasConfigSchema: Boolean(ext.manifest.configSchema),\n ui: ext.manifest.ui\n ? {\n icon: ext.manifest.ui.icon,\n permissions: ext.manifest.ui.permissions,\n contributions: ext.manifest.ui.contributions,\n }\n : undefined,\n }));\n return c.json({ extensions });\n });\n\n /**\n * Built-in (bundled) extension enable/disable — persists `extensions.enabled` / `extensions.disabled`.\n * Body: `{ extensionId: string, enabled: boolean }`\n */\n authenticated.post('/api/extensions/bundled/activation', strictRateLimitMiddleware, async (c) => {\n const body = (await c.req.json().catch(() => null)) as\n | { extensionId?: unknown; enabled?: unknown }\n | null;\n const extensionId =\n typeof body?.extensionId === 'string' ? body.extensionId.trim() : '';\n if (!extensionId) {\n return c.json({ ok: false, error: { message: 'extensionId is required' } }, 400);\n }\n if (typeof body?.enabled !== 'boolean') {\n return c.json({ ok: false, error: { message: 'enabled must be a boolean' } }, 400);\n }\n const result = await service.setBundledExtensionActivationTarget(extensionId, body.enabled);\n if (!result.ok) {\n return c.json({ ok: false, error: { message: result.error ?? 'Request failed' } }, 400);\n }\n return c.json({\n ok: true,\n payload: { requiresGatewayRestart: result.requiresGatewayRestart },\n });\n });\n\n authenticated.get('/api/extensions/:id', async (c) => {\n const extensionId = c.req.param('id');\n const loader = service.getExtensionLoader();\n if (!loader) {\n return c.json({ error: 'Extensions unavailable' }, 503);\n }\n const discovered = loader.discoverExtensions();\n const ext = discovered.find((e) => e.id === extensionId);\n if (!ext) {\n return c.json({ error: 'Extension not found' }, 404);\n }\n const registry = loader.getRegistry();\n return c.json({\n id: ext.manifest.id,\n name: ext.manifest.name,\n description: ext.manifest.description,\n version: ext.manifest.version,\n kind: ext.manifest.kind,\n source: ext.source,\n path: ext.path,\n active: registry.extensions.has(ext.id),\n manifest: ext.manifest,\n });\n });\n\n authenticated.get('/api/extensions/:id/storage', async (c) => {\n const extensionId = c.req.param('id');\n const store = await loadExtensionStore(extensionId);\n return c.json({ keys: Object.keys(store) });\n });\n\n authenticated.get('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const store = await loadExtensionStore(extensionId);\n if (!(key in store)) {\n return c.json({ error: 'Key not found' }, 404);\n }\n return c.json({ value: store[key] });\n });\n\n authenticated.put('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const body = (await c.req.json().catch(() => null)) as { value?: unknown } | null;\n if (body === null || !('value' in body)) {\n return c.json({ error: 'Request body must contain a \"value\" field' }, 400);\n }\n const store = await loadExtensionStore(extensionId);\n store[key] = body.value;\n await saveExtensionStore(extensionId, store);\n return c.json({ ok: true });\n });\n\n authenticated.delete('/api/extensions/:id/storage/:key', async (c) => {\n const extensionId = c.req.param('id');\n const key = decodeURIComponent(c.req.param('key'));\n const store = await loadExtensionStore(extensionId);\n delete store[key];\n await saveExtensionStore(extensionId, store);\n return c.json({ ok: true });\n });\n\n authenticated.get('/api/extensions/:id/config', async (c) => {\n const extensionId = c.req.param('id');\n return c.json(await loadExtensionStore(`__config__${extensionId}`));\n });\n\n authenticated.patch('/api/extensions/:id/config', async (c) => {\n const extensionId = c.req.param('id');\n const patch = (await c.req.json().catch(() => null)) as Record<string, unknown> | null;\n if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {\n return c.json({ error: 'Request body must be a JSON object' }, 400);\n }\n const namespace = `__config__${extensionId}`;\n const config = await loadExtensionStore(namespace);\n Object.assign(config, patch);\n await saveExtensionStore(namespace, config);\n return c.json({ ok: true });\n });\n\n authenticated.get('/api/context', (c) => {\n const loader = service.getExtensionLoader();\n const snapshot = buildWhenContextSnapshot(\n service.currentConfig as unknown as SurfaceConfig,\n loader,\n );\n return c.json(snapshot);\n });\n\n authenticated.get('/api/marketplace', async (c) => {\n const q = c.req.query('q');\n const category = c.req.query('category');\n try {\n let extensions;\n if (typeof q === 'string' && q.trim()) {\n extensions = await extensionMarketplace.searchExtensions(q.trim());\n } else if (typeof category === 'string' && category.trim()) {\n extensions = await extensionMarketplace.listExtensions(category.trim());\n } else {\n const reg = await extensionMarketplace.fetchRegistry();\n extensions = reg.extensions;\n }\n return c.json({ ok: true, extensions });\n } catch (err) {\n return c.json(\n {\n ok: false,\n extensions: [],\n error: err instanceof Error ? err.message : 'marketplace fetch failed',\n },\n 500,\n );\n }\n });\n\n // POST /api/registry/reload — reload gateway config and refresh model list for clients\n authenticated.post('/api/registry/reload', async (c) => {\n try {\n // Reload config\n await service.reloadConfig();\n \n // Reload OAuth credentials from new config\n loadOAuthCredentialsToCache(service);\n \n const models = getAllModels();\n \n // Emit SSE event to all connected clients\n service.emit('registry.updated', { modelCount: models.length });\n \n return c.json({\n ok: true,\n payload: {\n message: 'Registry reloaded',\n modelCount: models.length,\n },\n });\n } catch (err) {\n return c.json({\n error: err instanceof Error ? err.message : 'Failed to reload registry',\n }, 500);\n }\n });\n\n}\n"],"mappings":"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"}
@@ -192,6 +192,54 @@ function registerConfigRoutes(authenticated, deps) {
192
192
  if (p.dialecticCadence === null) delete t.dialecticCadence;
193
193
  else if (typeof p.dialecticCadence === "number" && p.dialecticCadence >= 1) t.dialecticCadence = Math.floor(p.dialecticCadence);
194
194
  }
195
+ if (p.dreaming !== void 0) {
196
+ const d0 = p.dreaming;
197
+ if (d0 === null) delete t.dreaming;
198
+ else if (typeof d0 === "object" && d0 !== null && !Array.isArray(d0)) {
199
+ if (!("dreaming" in t) || typeof t.dreaming !== "object") t.dreaming = {};
200
+ const dt = t.dreaming;
201
+ const dp = d0;
202
+ if (dp.enabled !== void 0) if (dp.enabled === null) delete dt.enabled;
203
+ else dt.enabled = Boolean(dp.enabled);
204
+ if (dp.frequency !== void 0) {
205
+ const v = dp.frequency;
206
+ if (v === null || v === "") delete dt.frequency;
207
+ else if (typeof v === "string") dt.frequency = v;
208
+ }
209
+ if (dp.timezone !== void 0) {
210
+ const v = dp.timezone;
211
+ if (v === null || v === "") delete dt.timezone;
212
+ else if (typeof v === "string") dt.timezone = v;
213
+ }
214
+ if (dp.phases !== void 0 || dp.deep !== void 0) {
215
+ if (!("phases" in dt) || typeof dt.phases !== "object" || dt.phases === null) dt.phases = {};
216
+ const phases = dt.phases;
217
+ if (!("deep" in phases) || typeof phases.deep !== "object" || phases.deep === null) phases.deep = {};
218
+ const deep = phases.deep;
219
+ const deepPatchRaw = typeof dp.phases === "object" && dp.phases !== null && !Array.isArray(dp.phases) ? dp.phases.deep : dp.deep;
220
+ const deepPatch = typeof deepPatchRaw === "object" && deepPatchRaw !== null && !Array.isArray(deepPatchRaw) ? deepPatchRaw : null;
221
+ if (deepPatch) {
222
+ if (deepPatch.enabled !== void 0) if (deepPatch.enabled === null) delete deep.enabled;
223
+ else deep.enabled = Boolean(deepPatch.enabled);
224
+ if (deepPatch.minScore !== void 0) {
225
+ const v = deepPatch.minScore;
226
+ if (v === null) delete deep.minScore;
227
+ else if (typeof v === "number" && Number.isFinite(v)) deep.minScore = v;
228
+ }
229
+ if (deepPatch.minRecallCount !== void 0) {
230
+ const v = deepPatch.minRecallCount;
231
+ if (v === null) delete deep.minRecallCount;
232
+ else if (typeof v === "number" && Number.isFinite(v)) deep.minRecallCount = Math.floor(v);
233
+ }
234
+ if (deepPatch.limit !== void 0) {
235
+ const v = deepPatch.limit;
236
+ if (v === null) delete deep.limit;
237
+ else if (typeof v === "number" && Number.isFinite(v)) deep.limit = Math.floor(v);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
195
243
  }
196
244
  }
197
245
  if (dPatch.sessionSearch !== void 0) {