@agimon-ai/doompi-domain 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/adapters/applyDomains.cjs +2 -0
- package/dist/adapters/applyDomains.cjs.map +1 -0
- package/dist/adapters/applyDomains.d.cts +24 -0
- package/dist/adapters/applyDomains.d.cts.map +1 -0
- package/dist/adapters/applyDomains.d.mts +24 -0
- package/dist/adapters/applyDomains.d.mts.map +1 -0
- package/dist/adapters/applyDomains.mjs +2 -0
- package/dist/adapters/applyDomains.mjs.map +1 -0
- package/dist/adapters/domainCatalog.cjs +2 -0
- package/dist/adapters/domainCatalog.cjs.map +1 -0
- package/dist/adapters/domainCatalog.d.cts +20 -0
- package/dist/adapters/domainCatalog.d.cts.map +1 -0
- package/dist/adapters/domainCatalog.d.mts +20 -0
- package/dist/adapters/domainCatalog.d.mts.map +1 -0
- package/dist/adapters/domainCatalog.mjs +2 -0
- package/dist/adapters/domainCatalog.mjs.map +1 -0
- package/dist/adapters/domainSwitchHandoff.cjs +2 -0
- package/dist/adapters/domainSwitchHandoff.cjs.map +1 -0
- package/dist/adapters/domainSwitchHandoff.d.cts +10 -0
- package/dist/adapters/domainSwitchHandoff.d.cts.map +1 -0
- package/dist/adapters/domainSwitchHandoff.d.mts +10 -0
- package/dist/adapters/domainSwitchHandoff.d.mts.map +1 -0
- package/dist/adapters/domainSwitchHandoff.mjs +2 -0
- package/dist/adapters/domainSwitchHandoff.mjs.map +1 -0
- package/dist/adapters/mcpFilter.cjs +3 -0
- package/dist/adapters/mcpFilter.cjs.map +1 -0
- package/dist/adapters/mcpFilter.d.cts +39 -0
- package/dist/adapters/mcpFilter.d.cts.map +1 -0
- package/dist/adapters/mcpFilter.d.mts +39 -0
- package/dist/adapters/mcpFilter.d.mts.map +1 -0
- package/dist/adapters/mcpFilter.mjs +3 -0
- package/dist/adapters/mcpFilter.mjs.map +1 -0
- package/dist/adapters/mcpSessionEnvironment.cjs +2 -0
- package/dist/adapters/mcpSessionEnvironment.cjs.map +1 -0
- package/dist/adapters/mcpSessionEnvironment.d.cts +14 -0
- package/dist/adapters/mcpSessionEnvironment.d.cts.map +1 -0
- package/dist/adapters/mcpSessionEnvironment.d.mts +14 -0
- package/dist/adapters/mcpSessionEnvironment.d.mts.map +1 -0
- package/dist/adapters/mcpSessionEnvironment.mjs +2 -0
- package/dist/adapters/mcpSessionEnvironment.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +2 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/adapters/pi/extension.d.cts +13 -0
- package/dist/adapters/pi/extension.d.cts.map +1 -0
- package/dist/adapters/pi/extension.d.mts +13 -0
- package/dist/adapters/pi/extension.d.mts.map +1 -0
- package/dist/adapters/pi/extension.mjs +2 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pi/voiceTool.cjs +2 -0
- package/dist/adapters/pi/voiceTool.cjs.map +1 -0
- package/dist/adapters/pi/voiceTool.d.cts +16 -0
- package/dist/adapters/pi/voiceTool.d.cts.map +1 -0
- package/dist/adapters/pi/voiceTool.d.mts +16 -0
- package/dist/adapters/pi/voiceTool.d.mts.map +1 -0
- package/dist/adapters/pi/voiceTool.mjs +2 -0
- package/dist/adapters/pi/voiceTool.mjs.map +1 -0
- package/dist/adapters/pluginMaterializer.cjs +2 -0
- package/dist/adapters/pluginMaterializer.cjs.map +1 -0
- package/dist/adapters/pluginMaterializer.d.cts +14 -0
- package/dist/adapters/pluginMaterializer.d.cts.map +1 -0
- package/dist/adapters/pluginMaterializer.d.mts +14 -0
- package/dist/adapters/pluginMaterializer.d.mts.map +1 -0
- package/dist/adapters/pluginMaterializer.mjs +2 -0
- package/dist/adapters/pluginMaterializer.mjs.map +1 -0
- package/dist/adapters/resourceCollector.cjs +3 -0
- package/dist/adapters/resourceCollector.cjs.map +1 -0
- package/dist/adapters/resourceCollector.d.cts +25 -0
- package/dist/adapters/resourceCollector.d.cts.map +1 -0
- package/dist/adapters/resourceCollector.d.mts +25 -0
- package/dist/adapters/resourceCollector.d.mts.map +1 -0
- package/dist/adapters/resourceCollector.mjs +3 -0
- package/dist/adapters/resourceCollector.mjs.map +1 -0
- package/dist/adapters/skillCacheLocation.cjs +2 -0
- package/dist/adapters/skillCacheLocation.cjs.map +1 -0
- package/dist/adapters/skillCacheLocation.d.cts +16 -0
- package/dist/adapters/skillCacheLocation.d.cts.map +1 -0
- package/dist/adapters/skillCacheLocation.d.mts +16 -0
- package/dist/adapters/skillCacheLocation.d.mts.map +1 -0
- package/dist/adapters/skillCacheLocation.mjs +2 -0
- package/dist/adapters/skillCacheLocation.mjs.map +1 -0
- package/dist/adapters/telemetry/logSinkTelemetry.cjs +2 -0
- package/dist/adapters/telemetry/logSinkTelemetry.cjs.map +1 -0
- package/dist/adapters/telemetry/logSinkTelemetry.d.cts +14 -0
- package/dist/adapters/telemetry/logSinkTelemetry.d.cts.map +1 -0
- package/dist/adapters/telemetry/logSinkTelemetry.d.mts +14 -0
- package/dist/adapters/telemetry/logSinkTelemetry.d.mts.map +1 -0
- package/dist/adapters/telemetry/logSinkTelemetry.mjs +2 -0
- package/dist/adapters/telemetry/logSinkTelemetry.mjs.map +1 -0
- package/dist/apply.cjs +1 -0
- package/dist/apply.d.cts +2 -0
- package/dist/apply.d.mts +2 -0
- package/dist/apply.mjs +1 -0
- package/dist/commands/domainsCommand.cjs +2 -0
- package/dist/commands/domainsCommand.cjs.map +1 -0
- package/dist/commands/domainsCommand.d.cts +24 -0
- package/dist/commands/domainsCommand.d.cts.map +1 -0
- package/dist/commands/domainsCommand.d.mts +24 -0
- package/dist/commands/domainsCommand.d.mts.map +1 -0
- package/dist/commands/domainsCommand.mjs +2 -0
- package/dist/commands/domainsCommand.mjs.map +1 -0
- package/dist/extensions/pi.cjs +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.mjs +1 -0
- package/dist/mcp.cjs +1 -0
- package/dist/mcp.d.cts +3 -0
- package/dist/mcp.d.mts +3 -0
- package/dist/mcp.mjs +1 -0
- package/dist/plugins.cjs +1 -0
- package/dist/plugins.d.cts +2 -0
- package/dist/plugins.d.mts +2 -0
- package/dist/plugins.mjs +1 -0
- package/dist/resources.cjs +1 -0
- package/dist/resources.d.cts +4 -0
- package/dist/resources.d.mts +4 -0
- package/dist/resources.mjs +1 -0
- package/dist/schemas/domainVoiceTools.cjs +2 -0
- package/dist/schemas/domainVoiceTools.cjs.map +1 -0
- package/dist/schemas/domainVoiceTools.d.cts +22 -0
- package/dist/schemas/domainVoiceTools.d.cts.map +1 -0
- package/dist/schemas/domainVoiceTools.d.mts +22 -0
- package/dist/schemas/domainVoiceTools.d.mts.map +1 -0
- package/dist/schemas/domainVoiceTools.mjs +2 -0
- package/dist/schemas/domainVoiceTools.mjs.map +1 -0
- package/dist/services/domainText.cjs +2 -0
- package/dist/services/domainText.cjs.map +1 -0
- package/dist/services/domainText.d.cts +30 -0
- package/dist/services/domainText.d.cts.map +1 -0
- package/dist/services/domainText.d.mts +30 -0
- package/dist/services/domainText.d.mts.map +1 -0
- package/dist/services/domainText.mjs +2 -0
- package/dist/services/domainText.mjs.map +1 -0
- package/dist/services/toolNames.cjs +2 -0
- package/dist/services/toolNames.cjs.map +1 -0
- package/dist/services/toolNames.d.cts +21 -0
- package/dist/services/toolNames.d.cts.map +1 -0
- package/dist/services/toolNames.d.mts +21 -0
- package/dist/services/toolNames.d.mts.map +1 -0
- package/dist/services/toolNames.mjs +2 -0
- package/dist/services/toolNames.mjs.map +1 -0
- package/dist/types/domains.cjs +2 -0
- package/dist/types/domains.cjs.map +1 -0
- package/dist/types/domains.d.cts +33 -0
- package/dist/types/domains.d.cts.map +1 -0
- package/dist/types/domains.d.mts +33 -0
- package/dist/types/domains.d.mts.map +1 -0
- package/dist/types/domains.mjs +2 -0
- package/dist/types/domains.mjs.map +1 -0
- package/dist/types/handoff.d.cts +35 -0
- package/dist/types/handoff.d.cts.map +1 -0
- package/dist/types/handoff.d.mts +35 -0
- package/dist/types/handoff.d.mts.map +1 -0
- package/dist/types/resources.d.cts +46 -0
- package/dist/types/resources.d.cts.map +1 -0
- package/dist/types/resources.d.mts +46 -0
- package/dist/types/resources.d.mts.map +1 -0
- package/dist/types/telemetry.cjs +2 -0
- package/dist/types/telemetry.cjs.map +1 -0
- package/dist/types/telemetry.d.cts +15 -0
- package/dist/types/telemetry.d.cts.map +1 -0
- package/dist/types/telemetry.d.mts +15 -0
- package/dist/types/telemetry.d.mts.map +1 -0
- package/dist/types/telemetry.mjs +2 -0
- package/dist/types/telemetry.mjs.map +1 -0
- package/llms.txt +7 -0
- package/package.json +117 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @agimon-ai/doompi-domain
|
|
2
|
+
|
|
3
|
+
Domain selection for [DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi) sessions.
|
|
4
|
+
|
|
5
|
+
**Domains** are one of the four axes of a DoomPi selection, alongside the major mode, the profile
|
|
6
|
+
and the minor modes. Unlike a major mode, domains compose: a session selects a set of them, and the
|
|
7
|
+
union of what they contribute is what the model sees.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
# .doom/domains.yaml
|
|
11
|
+
plugins:
|
|
12
|
+
entries:
|
|
13
|
+
frontend: plugins/frontend
|
|
14
|
+
infra: plugins/infra
|
|
15
|
+
domains:
|
|
16
|
+
development:
|
|
17
|
+
description: Application code, tests and review tooling.
|
|
18
|
+
plugins: [frontend]
|
|
19
|
+
mcp:
|
|
20
|
+
servers: [code-intel]
|
|
21
|
+
platform:
|
|
22
|
+
description: Deployment, infrastructure and incident tooling.
|
|
23
|
+
plugins: [infra]
|
|
24
|
+
aliases:
|
|
25
|
+
everything: [development, platform]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What it registers
|
|
29
|
+
|
|
30
|
+
| Surface | Behaviour |
|
|
31
|
+
| --------------------------- | ------------------------------------------------------------- |
|
|
32
|
+
| `/domains` | prints the selection, or switches to `name[,name...]` |
|
|
33
|
+
| `/domains` with no argument | opens a multi-select picker in the TUI |
|
|
34
|
+
| `list_domains` voice tool | returns the active, effective and available domains |
|
|
35
|
+
| `switch_domains` voice tool | validates a selection and queues the switch |
|
|
36
|
+
| autocomplete | comma-triggered completion of domain names inside `/domains` |
|
|
37
|
+
| `resources_discover` | re-answers with the current selection's skills after a reload |
|
|
38
|
+
|
|
39
|
+
## What a switch stages
|
|
40
|
+
|
|
41
|
+
A domain switch is not just a config write, which is why it does not live in
|
|
42
|
+
`@agimon-ai/doompi-config/selectionSwitch` with the major-mode and profile switches. Applying a
|
|
43
|
+
selection:
|
|
44
|
+
|
|
45
|
+
- materializes any remote (git or npm) plugins the selected domains name, into a content-addressed
|
|
46
|
+
cache guarded by an install marker;
|
|
47
|
+
- discovers the exact `SKILL.md` files behind the selection, through a manifest cache keyed by
|
|
48
|
+
worktree so a second session does not rescan the tree;
|
|
49
|
+
- adapts each plugin's Claude-authored agent definitions into the form Pi loads, and stages them
|
|
50
|
+
privately (`0600`) beside a dispatcher agent;
|
|
51
|
+
- merges the repository and plugin `.mcp.json` layers, applies the union of the selected domains'
|
|
52
|
+
MCP allowlists, and rewrites the proxy's upstream list into a per-run config;
|
|
53
|
+
- projects the result into the session environment and the harness state, then reloads Pi.
|
|
54
|
+
|
|
55
|
+
An allowlist only takes effect once **every** selected domain declares one. Mixing a scoped domain
|
|
56
|
+
with a domain that has not opted in leaves the session unfiltered, so an unmigrated domain cannot
|
|
57
|
+
silently lose its tools.
|
|
58
|
+
|
|
59
|
+
## Switching from voice
|
|
60
|
+
|
|
61
|
+
The voice tool never applies a switch itself. It plans the transition, parks the validated selection
|
|
62
|
+
in a TTL-bounded store and sends `/domains --voice-switch-token=…` back as a follow-up, so the reload
|
|
63
|
+
happens inside a command handler where it can be the terminal action. The selection travels in the
|
|
64
|
+
store rather than in the follow-up text, and the token is bound to the session that minted it.
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
DoomPi depends on this package and activates it as fixed host core, so a DoomPi install already has
|
|
69
|
+
it. It is not selectable from `.doom/domains.yaml` — it is what reads that file.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./mcpFilter.cjs"),n=require("./mcpSessionEnvironment.cjs"),r=require("./pluginMaterializer.cjs"),i=require("./resourceCollector.cjs");let a=require("node:path");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/domains"),s=require("@agimon-ai/doompi-config/harnessStore"),c=require("@agimon-ai/doompi-mcp");async function l(e,l){let{root:u,temporaryDirectory:d}=(0,s.requireHarnessPaths)(l),f=await r.materializePluginEntries((0,o.resolvePluginEntries)(u,e,[...l.pluginDirectories])),p=t.resolveMcpAllowlist(u,e),m=await i.collectResources(u,f,{agents:l.agents,mcp:l.mcp,temporaryDirectory:d,sharedSkills:(0,o.resolveSharedSkills)(u,e),mcpAllowlist:p});l.mcp?Object.assign(process.env,n.mcpSessionEnvironment({repoRoot:u,stagingDirectory:m.temporaryDirectory,generatedConfigPath:m.mcpConfigPath,pluginConfigPaths:m.pluginMcpConfigPaths,allowlist:p})):delete process.env[c.SESSION_ENV_VAR];let h=(0,s.updateHarnessState)({domains:e,skillDirectories:m.skillDirectories,pluginHooks:m.pluginHooks,mcpConfigPath:m.mcpConfigPath});return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=m.agentDirectories.join(a.default.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=m.skillDirectories.join(a.default.delimiter),h}exports.applyDomains=l;
|
|
2
|
+
//# sourceMappingURL=applyDomains.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyDomains.cjs","names":["requireHarnessPaths","materializePluginEntries","resolvePluginEntries","resolveMcpAllowlist","collectResources","resolveSharedSkills","mcpSessionEnvironment","SESSION_ENV_VAR","updateHarnessState","path"],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { SESSION_ENV_VAR } from '@agimon-ai/doompi-mcp';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { mcpSessionEnvironment } from './mcpSessionEnvironment.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules, which drops their module-level caches and\n * makes them re-read the environment.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n const resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n\n if (state.mcp) {\n Object.assign(\n process.env,\n mcpSessionEnvironment({\n repoRoot: root,\n stagingDirectory: resources.temporaryDirectory,\n generatedConfigPath: resources.mcpConfigPath,\n pluginConfigPaths: resources.pluginMcpConfigPaths,\n allowlist: mcpAllowlist,\n }),\n );\n } else {\n delete process.env[SESSION_ENV_VAR];\n }\n const updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n });\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"0XA4BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,uBAAA,EAAuBA,EAAAA,oBAAAA,CAAoB,CAAK,EAExD,EAAgB,MAAMC,EAAAA,0BAAAA,EAC1BC,EAAAA,qBAAAA,CAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAeC,EAAAA,oBAAoB,EAAM,CAAW,EACpD,EAAY,MAAMC,EAAAA,iBAAiB,EAAM,EAAe,CAC5D,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,qBACA,cAAA,EAAcC,EAAAA,oBAAAA,CAAoB,EAAM,CAAW,EACnD,cACF,CAAC,EAEG,EAAM,IACR,OAAO,OACL,QAAQ,IACRC,EAAAA,sBAAsB,CACpB,SAAU,EACV,iBAAkB,EAAU,mBAC5B,oBAAqB,EAAU,cAC/B,kBAAmB,EAAU,qBAC7B,UAAW,CACb,CAAC,CACH,EAEA,OAAO,QAAQ,IAAIC,EAAAA,iBAErB,IAAM,GAAA,EAAUC,EAAAA,mBAAAA,CAAmB,CACjC,QAAS,EACT,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,aAC3B,CAAC,EAGD,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKC,EAAAA,QAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAKA,EAAAA,QAAK,SAAS,EAClF,CACT"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DoomHarnessContext, HarnessState } from "@agimon-ai/doompi-config/types";
|
|
2
|
+
//#region src/adapters/applyDomains.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Live domain switching: skills, plugin hooks, MCP config and subagent directories.
|
|
5
|
+
*
|
|
6
|
+
* Writes through the harness state module and relies on Pi's reload
|
|
7
|
+
* re-executing extension modules, which drops their module-level caches and
|
|
8
|
+
* makes them re-read the environment.
|
|
9
|
+
*
|
|
10
|
+
* What reload cannot do is change the SET of loaded extensions: Pi freezes the
|
|
11
|
+
* CLI --extension list at construction and re-resolves that same array on every
|
|
12
|
+
* reload. Callers report those parts as needing a relaunch instead of pretending
|
|
13
|
+
* they applied.
|
|
14
|
+
*
|
|
15
|
+
* The major-mode and profile switches live in
|
|
16
|
+
* @agimon-ai/doompi-config/selectionSwitch: they are config writes with no
|
|
17
|
+
* resource staging, and the synced startup path applies them before any command
|
|
18
|
+
* exists to run. Domains stays here because it stages plugins, skills and MCP
|
|
19
|
+
* config.
|
|
20
|
+
*/
|
|
21
|
+
declare function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { applyDomains };
|
|
24
|
+
//# sourceMappingURL=applyDomains.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyDomains.d.cts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA4BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DoomHarnessContext, HarnessState } from "@agimon-ai/doompi-config/types";
|
|
2
|
+
//#region src/adapters/applyDomains.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Live domain switching: skills, plugin hooks, MCP config and subagent directories.
|
|
5
|
+
*
|
|
6
|
+
* Writes through the harness state module and relies on Pi's reload
|
|
7
|
+
* re-executing extension modules, which drops their module-level caches and
|
|
8
|
+
* makes them re-read the environment.
|
|
9
|
+
*
|
|
10
|
+
* What reload cannot do is change the SET of loaded extensions: Pi freezes the
|
|
11
|
+
* CLI --extension list at construction and re-resolves that same array on every
|
|
12
|
+
* reload. Callers report those parts as needing a relaunch instead of pretending
|
|
13
|
+
* they applied.
|
|
14
|
+
*
|
|
15
|
+
* The major-mode and profile switches live in
|
|
16
|
+
* @agimon-ai/doompi-config/selectionSwitch: they are config writes with no
|
|
17
|
+
* resource staging, and the synced startup path applies them before any command
|
|
18
|
+
* exists to run. Domains stays here because it stages plugins, skills and MCP
|
|
19
|
+
* config.
|
|
20
|
+
*/
|
|
21
|
+
declare function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { applyDomains };
|
|
24
|
+
//# sourceMappingURL=applyDomains.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyDomains.d.mts","names":[],"sources":["../../src/adapters/applyDomains.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA4BsB,aAAa,uBAAuB,OAAO,qBAAqB,QAAQ"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{resolveMcpAllowlist as e}from"./mcpFilter.mjs";import{mcpSessionEnvironment as t}from"./mcpSessionEnvironment.mjs";import{materializePluginEntries as n}from"./pluginMaterializer.mjs";import{collectResources as r}from"./resourceCollector.mjs";import i from"node:path";import{resolvePluginEntries as a,resolveSharedSkills as o}from"@agimon-ai/doompi-config/domains";import{requireHarnessPaths as s,updateHarnessState as c}from"@agimon-ai/doompi-config/harnessStore";import{SESSION_ENV_VAR as l}from"@agimon-ai/doompi-mcp";async function u(u,d){let{root:f,temporaryDirectory:p}=s(d),m=await n(a(f,u,[...d.pluginDirectories])),h=e(f,u),g=await r(f,m,{agents:d.agents,mcp:d.mcp,temporaryDirectory:p,sharedSkills:o(f,u),mcpAllowlist:h});d.mcp?Object.assign(process.env,t({repoRoot:f,stagingDirectory:g.temporaryDirectory,generatedConfigPath:g.mcpConfigPath,pluginConfigPaths:g.pluginMcpConfigPaths,allowlist:h})):delete process.env[l];let _=c({domains:u,skillDirectories:g.skillDirectories,pluginHooks:g.pluginHooks,mcpConfigPath:g.mcpConfigPath});return process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS=g.agentDirectories.join(i.delimiter),process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS=g.skillDirectories.join(i.delimiter),_}export{u as applyDomains};
|
|
2
|
+
//# sourceMappingURL=applyDomains.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyDomains.mjs","names":[],"sources":["../../src/adapters/applyDomains.ts"],"sourcesContent":["import path from 'node:path';\nimport { resolvePluginEntries, resolveSharedSkills } from '@agimon-ai/doompi-config/domains';\nimport { requireHarnessPaths, updateHarnessState } from '@agimon-ai/doompi-config/harnessStore';\nimport type { DoomHarnessContext, HarnessState } from '@agimon-ai/doompi-config/types';\nimport { SESSION_ENV_VAR } from '@agimon-ai/doompi-mcp';\nimport { resolveMcpAllowlist } from './mcpFilter.ts';\nimport { mcpSessionEnvironment } from './mcpSessionEnvironment.ts';\nimport { materializePluginEntries } from './pluginMaterializer.ts';\nimport { collectResources } from './resourceCollector.ts';\n\n/**\n * Live domain switching: skills, plugin hooks, MCP config and subagent directories.\n *\n * Writes through the harness state module and relies on Pi's reload\n * re-executing extension modules, which drops their module-level caches and\n * makes them re-read the environment.\n *\n * What reload cannot do is change the SET of loaded extensions: Pi freezes the\n * CLI --extension list at construction and re-resolves that same array on every\n * reload. Callers report those parts as needing a relaunch instead of pretending\n * they applied.\n *\n * The major-mode and profile switches live in\n * @agimon-ai/doompi-config/selectionSwitch: they are config writes with no\n * resource staging, and the synced startup path applies them before any command\n * exists to run. Domains stays here because it stages plugins, skills and MCP\n * config.\n */\nexport async function applyDomains(domainNames: string[], state: DoomHarnessContext): Promise<HarnessState> {\n const { root, temporaryDirectory } = requireHarnessPaths(state);\n\n const pluginEntries = await materializePluginEntries(\n resolvePluginEntries(root, domainNames, [...state.pluginDirectories]),\n );\n const mcpAllowlist = resolveMcpAllowlist(root, domainNames);\n const resources = await collectResources(root, pluginEntries, {\n agents: state.agents,\n mcp: state.mcp,\n temporaryDirectory,\n sharedSkills: resolveSharedSkills(root, domainNames),\n mcpAllowlist,\n });\n\n if (state.mcp) {\n Object.assign(\n process.env,\n mcpSessionEnvironment({\n repoRoot: root,\n stagingDirectory: resources.temporaryDirectory,\n generatedConfigPath: resources.mcpConfigPath,\n pluginConfigPaths: resources.pluginMcpConfigPaths,\n allowlist: mcpAllowlist,\n }),\n );\n } else {\n delete process.env[SESSION_ENV_VAR];\n }\n const updated = updateHarnessState({\n domains: domainNames,\n skillDirectories: resources.skillDirectories,\n pluginHooks: resources.pluginHooks,\n mcpConfigPath: resources.mcpConfigPath,\n });\n process.env.PI_SUBAGENT_EXTRA_AGENT_DIRS = resources.agentDirectories.join(path.delimiter);\n process.env.PI_SUBAGENT_EXTRA_SKILL_DIRS = resources.skillDirectories.join(path.delimiter);\n return updated;\n}\n"],"mappings":"+gBA4BA,eAAsB,EAAa,EAAuB,EAAkD,CAC1G,GAAM,CAAE,OAAM,sBAAuB,EAAoB,CAAK,EAExD,EAAgB,MAAM,EAC1B,EAAqB,EAAM,EAAa,CAAC,GAAG,EAAM,iBAAiB,CAAC,CACtE,EACM,EAAe,EAAoB,EAAM,CAAW,EACpD,EAAY,MAAM,EAAiB,EAAM,EAAe,CAC5D,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,qBACA,aAAc,EAAoB,EAAM,CAAW,EACnD,cACF,CAAC,EAEG,EAAM,IACR,OAAO,OACL,QAAQ,IACR,EAAsB,CACpB,SAAU,EACV,iBAAkB,EAAU,mBAC5B,oBAAqB,EAAU,cAC/B,kBAAmB,EAAU,qBAC7B,UAAW,CACb,CAAC,CACH,EAEA,OAAO,QAAQ,IAAI,GAErB,IAAM,EAAU,EAAmB,CACjC,QAAS,EACT,iBAAkB,EAAU,iBAC5B,YAAa,EAAU,YACvB,cAAe,EAAU,aAC3B,CAAC,EAGD,MAFA,SAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EACzF,QAAQ,IAAI,6BAA+B,EAAU,iBAAiB,KAAK,EAAK,SAAS,EAClF,CACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../services/domainText.cjs");let t=require("@agimon-ai/doompi-config/harnessStore"),n=require("@agimon-ai/doompi-config/piContext");function r(){let r,i=()=>r??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let r=(0,n.requireDoomConfigContext)(e).harness,a=(0,t.requireHarnessRoot)(r),{defaultDomainsForMajorMode:o,listDomainNames:s,loadDomains:c}=await i(),l=[...new Set(s(a))].sort(),u=c(a),d=(0,n.requireDoomConfigContext)(e).pendingSelection?void 0:(0,n.readDoomConfigSelection)(e),f=d?[...d.domains]:[...r.domains],p=o(r.majorMode,process.env,u.defaultDomains);return{active:f,effective:d?[...d.domains]:r.domains.length>0?[...r.domains]:[...p],available:l}},validate:async(r,a)=>{let o=e.normalizeDomainNames(a),s=(0,n.requireDoomConfigContext)(r).harness,c=(0,t.requireHarnessRoot)(s),{listDomainNames:l,resolvePluginEntries:u}=await i(),d=new Set(l(c)),f=o.filter(e=>!d.has(e));if(f.length>0)throw Error(`Unknown domain: ${f.join(`, `)}`);return u(c,o,[...s.pluginDirectories]),o},describe:async e=>{let t=(0,n.requireDoomConfigContext)(e).harness.root;if(!t)return{};let{loadDomains:r}=await i();return Object.fromEntries(Object.entries(r(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:a}=await i(),o=r(t);if(o===void 0)return;let s=n(a(e),o);return s.length===0?void 0:{prefix:o,items:s}}}}exports.createDomainCatalog=r;
|
|
2
|
+
//# sourceMappingURL=domainCatalog.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainCatalog.cjs","names":["requireDoomConfigContext","requireHarnessRoot","readDoomConfigSelection","normalizeDomainNames"],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog() {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = requireDoomConfigContext(ctx).pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(ctx).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"qJAaA,SAAgB,GAAsB,CACpC,IAAI,EACE,MAAc,IAAW,OAAO,oCAoDtC,MAAO,CAAE,UAlDW,IAAkD,CACpE,IAAM,GAAA,EAAQA,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QACtC,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,GAAA,EAAYD,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,iBAAmB,IAAA,IAAA,EAAYE,EAAAA,wBAAAA,CAAwB,CAAG,EACpG,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAuB,IAAiD,CAC9F,IAAM,EAAWC,EAAAA,qBAAqB,CAAM,EACtC,GAAA,EAAQH,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QACtC,GAAA,EAAWC,EAAAA,mBAAAA,CAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAuE,CAC7F,IAAM,GAAA,EAAOD,EAAAA,yBAAAA,CAAyB,CAAG,CAAC,CAAC,QAAQ,KACnD,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DomainCompletion, DomainListing } from "../types/domains.cjs";
|
|
2
|
+
import { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
//#region src/adapters/domainCatalog.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The manifest reader every surface of this package shares.
|
|
6
|
+
*
|
|
7
|
+
* Every session registers `/domains`, but most never run it, so the plugin
|
|
8
|
+
* catalogue and the domain manifest stay behind a dynamic import: only a session
|
|
9
|
+
* that actually lists or switches domains pays to read them.
|
|
10
|
+
*/
|
|
11
|
+
declare function createDomainCatalog(): {
|
|
12
|
+
list: (ctx: ExtensionContext) => Promise<DomainListing>;
|
|
13
|
+
validate: (ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
|
|
14
|
+
describe: (ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
|
|
15
|
+
completions: (root: string, textBeforeCursor: string) => Promise<DomainCompletion | undefined>;
|
|
16
|
+
};
|
|
17
|
+
type DomainCatalog = ReturnType<typeof createDomainCatalog>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { DomainCatalog, createDomainCatalog };
|
|
20
|
+
//# sourceMappingURL=domainCatalog.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainCatalog.d.cts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;iBAagB;EAIW,OAAA,KAAA,qBAAmB,QAAQ;EAmBvB,WAAA,KAAA,kBAAgB,8BAA8B;EAY9C,WAAA,KAAA,qBAAmB,QAAQ;EASvB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DomainCompletion, DomainListing } from "../types/domains.mjs";
|
|
2
|
+
import { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
//#region src/adapters/domainCatalog.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The manifest reader every surface of this package shares.
|
|
6
|
+
*
|
|
7
|
+
* Every session registers `/domains`, but most never run it, so the plugin
|
|
8
|
+
* catalogue and the domain manifest stay behind a dynamic import: only a session
|
|
9
|
+
* that actually lists or switches domains pays to read them.
|
|
10
|
+
*/
|
|
11
|
+
declare function createDomainCatalog(): {
|
|
12
|
+
list: (ctx: ExtensionContext) => Promise<DomainListing>;
|
|
13
|
+
validate: (ctx: ExtensionContext, values: readonly string[]) => Promise<string[]>;
|
|
14
|
+
describe: (ctx: ExtensionContext) => Promise<Record<string, string | undefined>>;
|
|
15
|
+
completions: (root: string, textBeforeCursor: string) => Promise<DomainCompletion | undefined>;
|
|
16
|
+
};
|
|
17
|
+
type DomainCatalog = ReturnType<typeof createDomainCatalog>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { DomainCatalog, createDomainCatalog };
|
|
20
|
+
//# sourceMappingURL=domainCatalog.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainCatalog.d.mts","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"mappings":";;;;;;;;;;iBAagB;EAIW,OAAA,KAAA,qBAAmB,QAAQ;EAmBvB,WAAA,KAAA,kBAAgB,8BAA8B;EAY9C,WAAA,KAAA,qBAAmB,QAAQ;EASvB,cAAA,cAAM,6BAA6B,QAAQ;;KAalE,gBAAgB,kBAAkB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{normalizeDomainNames as e}from"../services/domainText.mjs";import{requireHarnessRoot as t}from"@agimon-ai/doompi-config/harnessStore";import{readDoomConfigSelection as n,requireDoomConfigContext as r}from"@agimon-ai/doompi-config/piContext";function i(){let i,a=()=>i??=import(`@agimon-ai/doompi-config/domains`);return{list:async e=>{let i=r(e).harness,o=t(i),{defaultDomainsForMajorMode:s,listDomainNames:c,loadDomains:l}=await a(),u=[...new Set(c(o))].sort(),d=l(o),f=r(e).pendingSelection?void 0:n(e),p=f?[...f.domains]:[...i.domains],m=s(i.majorMode,process.env,d.defaultDomains);return{active:p,effective:f?[...f.domains]:i.domains.length>0?[...i.domains]:[...m],available:u}},validate:async(n,i)=>{let o=e(i),s=r(n).harness,c=t(s),{listDomainNames:l,resolvePluginEntries:u}=await a(),d=new Set(l(c)),f=o.filter(e=>!d.has(e));if(f.length>0)throw Error(`Unknown domain: ${f.join(`, `)}`);return u(c,o,[...s.pluginDirectories]),o},describe:async e=>{let t=r(e).harness.root;if(!t)return{};let{loadDomains:n}=await a();return Object.fromEntries(Object.entries(n(t).domains).map(([e,t])=>[e,t.description]))},completions:async(e,t)=>{let{domainCompletionItems:n,domainCompletionPrefix:r,listDomainNames:i}=await a(),o=r(t);if(o===void 0)return;let s=n(i(e),o);return s.length===0?void 0:{prefix:o,items:s}}}}export{i as createDomainCatalog};
|
|
2
|
+
//# sourceMappingURL=domainCatalog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainCatalog.mjs","names":[],"sources":["../../src/adapters/domainCatalog.ts"],"sourcesContent":["import { requireHarnessRoot } from '@agimon-ai/doompi-config/harnessStore';\nimport { readDoomConfigSelection, requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { normalizeDomainNames } from '../services/domainText.ts';\nimport type { DomainCompletion, DomainListing } from '../types/domains.ts';\n\n/**\n * The manifest reader every surface of this package shares.\n *\n * Every session registers `/domains`, but most never run it, so the plugin\n * catalogue and the domain manifest stay behind a dynamic import: only a session\n * that actually lists or switches domains pays to read them.\n */\nexport function createDomainCatalog() {\n let config: Promise<typeof import('@agimon-ai/doompi-config/domains')> | undefined;\n const load = () => (config ??= import('@agimon-ai/doompi-config/domains'));\n\n const list = async (ctx: ExtensionContext): Promise<DomainListing> => {\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { defaultDomainsForMajorMode, listDomainNames, loadDomains } = await load();\n const available = [...new Set(listDomainNames(repoRoot))].sort();\n const manifest = loadDomains(repoRoot);\n // A journalled selection outranks the environment, but only once the switch\n // that produced it has settled: a pending one has not been applied yet.\n const selection = requireDoomConfigContext(ctx).pendingSelection ? undefined : readDoomConfigSelection(ctx);\n const active = selection ? [...selection.domains] : [...state.domains];\n const fallback = defaultDomainsForMajorMode(state.majorMode, process.env, manifest.defaultDomains);\n const effective = selection\n ? [...selection.domains]\n : state.domains.length > 0\n ? [...state.domains]\n : [...fallback];\n return { active, effective, available };\n };\n\n const validate = async (ctx: ExtensionContext, values: readonly string[]): Promise<string[]> => {\n const selected = normalizeDomainNames(values);\n const state = requireDoomConfigContext(ctx).harness;\n const repoRoot = requireHarnessRoot(state);\n const { listDomainNames, resolvePluginEntries } = await load();\n const available = new Set(listDomainNames(repoRoot));\n const unknown = selected.filter((name) => !available.has(name));\n if (unknown.length > 0) throw new Error(`Unknown domain: ${unknown.join(', ')}`);\n resolvePluginEntries(repoRoot, selected, [...state.pluginDirectories]);\n return selected;\n };\n\n const describe = async (ctx: ExtensionContext): Promise<Record<string, string | undefined>> => {\n const root = requireDoomConfigContext(ctx).harness.root;\n if (!root) return {};\n const { loadDomains } = await load();\n return Object.fromEntries(\n Object.entries(loadDomains(root).domains).map(([name, domain]) => [name, domain.description]),\n );\n };\n\n const completions = async (root: string, textBeforeCursor: string): Promise<DomainCompletion | undefined> => {\n const { domainCompletionItems, domainCompletionPrefix, listDomainNames } = await load();\n const prefix = domainCompletionPrefix(textBeforeCursor);\n if (prefix === undefined) return undefined;\n // Re-read on every keystroke: a domain added while the session is running\n // has to be completable without a reload.\n const items = domainCompletionItems(listDomainNames(root), prefix);\n return items.length === 0 ? undefined : { prefix, items };\n };\n\n return { list, validate, describe, completions };\n}\n\nexport type DomainCatalog = ReturnType<typeof createDomainCatalog>;\n"],"mappings":"wPAaA,SAAgB,GAAsB,CACpC,IAAI,EACE,MAAc,IAAW,OAAO,oCAoDtC,MAAO,CAAE,UAlDW,IAAkD,CACpE,IAAM,EAAQ,EAAyB,CAAG,CAAC,CAAC,QACtC,EAAW,EAAmB,CAAK,EACnC,CAAE,6BAA4B,kBAAiB,eAAgB,MAAM,EAAK,EAC1E,EAAY,CAAC,GAAG,IAAI,IAAI,EAAgB,CAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EACzD,EAAW,EAAY,CAAQ,EAG/B,EAAY,EAAyB,CAAG,CAAC,CAAC,iBAAmB,IAAA,GAAY,EAAwB,CAAG,EACpG,EAAS,EAAY,CAAC,GAAG,EAAU,OAAO,EAAI,CAAC,GAAG,EAAM,OAAO,EAC/D,EAAW,EAA2B,EAAM,UAAW,QAAQ,IAAK,EAAS,cAAc,EAMjG,MAAO,CAAE,SAAQ,UALC,EACd,CAAC,GAAG,EAAU,OAAO,EACrB,EAAM,QAAQ,OAAS,EACrB,CAAC,GAAG,EAAM,OAAO,EACjB,CAAC,GAAG,CAAQ,EACU,WAAU,CACxC,EAiCe,eA/BS,EAAuB,IAAiD,CAC9F,IAAM,EAAW,EAAqB,CAAM,EACtC,EAAQ,EAAyB,CAAG,CAAC,CAAC,QACtC,EAAW,EAAmB,CAAK,EACnC,CAAE,kBAAiB,wBAAyB,MAAM,EAAK,EACvD,EAAY,IAAI,IAAI,EAAgB,CAAQ,CAAC,EAC7C,EAAU,EAAS,OAAQ,GAAS,CAAC,EAAU,IAAI,CAAI,CAAC,EAC9D,GAAI,EAAQ,OAAS,EAAG,MAAU,MAAM,mBAAmB,EAAQ,KAAK,IAAI,GAAG,EAE/E,OADA,EAAqB,EAAU,EAAU,CAAC,GAAG,EAAM,iBAAiB,CAAC,EAC9D,CACT,EAqByB,cAnBD,IAAuE,CAC7F,IAAM,EAAO,EAAyB,CAAG,CAAC,CAAC,QAAQ,KACnD,GAAI,CAAC,EAAM,MAAO,CAAC,EACnB,GAAM,CAAE,eAAgB,MAAM,EAAK,EACnC,OAAO,OAAO,YACZ,OAAO,QAAQ,EAAY,CAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,EAAM,KAAY,CAAC,EAAM,EAAO,WAAW,CAAC,CAC9F,CACF,EAYmC,kBAVR,EAAc,IAAoE,CAC3G,GAAM,CAAE,wBAAuB,yBAAwB,mBAAoB,MAAM,EAAK,EAChF,EAAS,EAAuB,CAAgB,EACtD,GAAI,IAAW,IAAA,GAAW,OAG1B,IAAM,EAAQ,EAAsB,EAAgB,CAAI,EAAG,CAAM,EACjE,OAAO,EAAM,SAAW,EAAI,IAAA,GAAY,CAAE,SAAQ,OAAM,CAC1D,CAE+C,CACjD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../types/domains.cjs"),t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4;function r(){let e=globalThis;return e[t]??={records:new Map}}function i(e){return Error(`Invalid domain switch handoff ${e}.`)}function a(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function o(e,t,n,r){let o=e.trim();if(!o||o.length>n||a(o)||r&&!r.test(o))throw i(t);return o}function s(e){return o(e,`session id`,256)}function c(e){return o(e,`host generation`,256)}function l(t){return o(t,`operation id`,128,e.SAFE_DOMAIN_NAME)}function u(t){return o(t,`reload handoff token`,256,e.SAFE_DOMAIN_NAME)}function d(t){return o(t,`token`,256,e.SAFE_DOMAIN_NAME)}function f(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=o(i,`domain name`,128,e.SAFE_DOMAIN_NAME);r.has(t)||(r.add(t),n.push(t))}return n}function p(e=Date.now()){for(let[t,n]of r().records)n.expiresAt<=e&&r().records.delete(t)}function m(e,t){return e.sessionId===s(t.sessionId)?t.hostGeneration===void 0||e.hostGeneration===c(t.hostGeneration):!1}function h(e){return{...e,domains:[...e.domains]}}function g(e,t){p();let i=s(t.sessionId),a=c(t.hostGeneration),o=l(t.operationId),d=f(t.domains),m=u(t.reloadHandoffToken),g=Date.now(),_={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:i,hostGeneration:a,operationId:o,domains:d,reloadHandoffToken:m,createdAt:g,expiresAt:g+n,ownerGeneration:e};return r().records.set(_.token,_),h(_)}function _(e,t){p();let n=d(t),i=r().records.get(n);if(!(!i||!m(i,e))&&(r().records.delete(n),!(i.expiresAt<=Date.now())))return h(i)}function v(e,t){p();let n=d(t),i=r().records.get(n);return!i||!m(i,e)?!1:r().records.delete(n)}function y(e,t){p();let n=s(t),i=0;for(let[t,a]of r().records)a.ownerGeneration===e&&a.sessionId===n&&(r().records.delete(t),i+=1);return i}function b(e){let t=0;for(let[n,i]of r().records)i.ownerGeneration===e&&(r().records.delete(n),t+=1);return t}function x(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return g(e,n)},consume(e,n){if(!t)return _(n,e)},discard(e,n){return!t&&v(n,e)},clearSession(n){return t?0:y(e,n)},dispose(){return t?0:(t=!0,b(e))}}}exports.DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS=32,exports.DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH=256,exports.DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH=128,exports.DOMAIN_SWITCH_HANDOFF_TTL_MS=n,exports.createDomainSwitchHandoffStore=x;
|
|
2
|
+
//# sourceMappingURL=domainSwitchHandoff.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainSwitchHandoff.cjs","names":["SAFE_DOMAIN_NAME"],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: string, label: string, maximum: number, pattern?: RegExp): string {\n const text = value.trim();\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return identity.hostGeneration === undefined || record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DomainSwitchHandoffStore } from "../types/handoff.cjs";
|
|
2
|
+
//#region src/adapters/domainSwitchHandoff.d.ts
|
|
3
|
+
declare const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30000;
|
|
4
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;
|
|
5
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;
|
|
6
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;
|
|
7
|
+
declare function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS, DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, DOMAIN_SWITCH_HANDOFF_TTL_MS, createDomainSwitchHandoffStore };
|
|
10
|
+
//# sourceMappingURL=domainSwitchHandoff.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainSwitchHandoff.d.cts","names":[],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"mappings":";;cAYa;cACA;cACA;cACA;iBA6JG,kCAAkC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DomainSwitchHandoffStore } from "../types/handoff.mjs";
|
|
2
|
+
//#region src/adapters/domainSwitchHandoff.d.ts
|
|
3
|
+
declare const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30000;
|
|
4
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;
|
|
5
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;
|
|
6
|
+
declare const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;
|
|
7
|
+
declare function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS, DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, DOMAIN_SWITCH_HANDOFF_TTL_MS, createDomainSwitchHandoffStore };
|
|
10
|
+
//# sourceMappingURL=domainSwitchHandoff.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainSwitchHandoff.d.mts","names":[],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"mappings":";;cAYa;cACA;cACA;cACA;iBA6JG,kCAAkC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{SAFE_DOMAIN_NAME as e}from"../types/domains.mjs";const t=Symbol.for(`@agimon-ai/doompi-domain.switch-handoff.v1`),n=3e4,r=32,i=256,a=128;function o(){let e=globalThis;return e[t]??={records:new Map}}function s(e){return Error(`Invalid domain switch handoff ${e}.`)}function c(e){for(let t of e){let e=t.codePointAt(0);if(e!==void 0&&(e<=31||e===127))return!0}return!1}function l(e,t,n,r){let i=e.trim();if(!i||i.length>n||c(i)||r&&!r.test(i))throw s(t);return i}function u(e){return l(e,`session id`,256)}function d(e){return l(e,`host generation`,256)}function f(t){return l(t,`operation id`,128,e)}function p(t){return l(t,`reload handoff token`,256,e)}function m(t){return l(t,`token`,256,e)}function h(t){if(t.length>32)throw Error(`A maximum of 32 domains may be selected.`);let n=[],r=new Set;for(let i of t){let t=l(i,`domain name`,128,e);r.has(t)||(r.add(t),n.push(t))}return n}function g(e=Date.now()){for(let[t,n]of o().records)n.expiresAt<=e&&o().records.delete(t)}function _(e,t){return e.sessionId===u(t.sessionId)?t.hostGeneration===void 0||e.hostGeneration===d(t.hostGeneration):!1}function v(e){return{...e,domains:[...e.domains]}}function y(e,t){g();let r=u(t.sessionId),i=d(t.hostGeneration),a=f(t.operationId),s=h(t.domains),c=p(t.reloadHandoffToken),l=Date.now(),m={token:`doom-domain-switch:${crypto.randomUUID()}`,sessionId:r,hostGeneration:i,operationId:a,domains:s,reloadHandoffToken:c,createdAt:l,expiresAt:l+n,ownerGeneration:e};return o().records.set(m.token,m),v(m)}function b(e,t){g();let n=m(t),r=o().records.get(n);if(!(!r||!_(r,e))&&(o().records.delete(n),!(r.expiresAt<=Date.now())))return v(r)}function x(e,t){g();let n=m(t),r=o().records.get(n);return!r||!_(r,e)?!1:o().records.delete(n)}function S(e,t){g();let n=u(t),r=0;for(let[t,i]of o().records)i.ownerGeneration===e&&i.sessionId===n&&(o().records.delete(t),r+=1);return r}function C(e){let t=0;for(let[n,r]of o().records)r.ownerGeneration===e&&(o().records.delete(n),t+=1);return t}function w(){let e=crypto.randomUUID(),t=!1;return{ownerGeneration:e,issue(n){if(t)throw Error(`Domain switch handoff store is disposed.`);return y(e,n)},consume(e,n){if(!t)return b(n,e)},discard(e,n){return!t&&x(n,e)},clearSession(n){return t?0:S(e,n)},dispose(){return t?0:(t=!0,C(e))}}}export{r as DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS,i as DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH,a as DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH,n as DOMAIN_SWITCH_HANDOFF_TTL_MS,w as createDomainSwitchHandoffStore};
|
|
2
|
+
//# sourceMappingURL=domainSwitchHandoff.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domainSwitchHandoff.mjs","names":[],"sources":["../../src/adapters/domainSwitchHandoff.ts"],"sourcesContent":["import type {\n DomainSwitchHandoff,\n DomainSwitchHandoffIdentity,\n DomainSwitchHandoffRequest,\n DomainSwitchHandoffStore,\n} from '../types/handoff.ts';\nimport { SAFE_DOMAIN_NAME } from '../types/domains.ts';\n\nconst DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY = '@agimon-ai/doompi-domain.switch-handoff.v1';\nconst DOMAIN_SWITCH_HANDOFF_SYMBOL = Symbol.for(DOMAIN_SWITCH_HANDOFF_REGISTRY_KEY);\nconst DOMAIN_SWITCH_TOKEN_PREFIX = 'doom-domain-switch:';\n\nexport const DOMAIN_SWITCH_HANDOFF_TTL_MS = 30_000;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS = 32;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH = 256;\nexport const DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH = 128;\n\ninterface StoredDomainSwitchHandoff extends DomainSwitchHandoff {\n readonly ownerGeneration: string;\n}\n\ninterface HandoffRegistry {\n readonly records: Map<string, StoredDomainSwitchHandoff>;\n}\n\ntype HandoffGlobal = typeof globalThis & { [DOMAIN_SWITCH_HANDOFF_SYMBOL]?: HandoffRegistry };\n\n/**\n * One registry per process, keyed by a well-known symbol.\n *\n * Pi can reload this module in place, and a reload that lost the parked handoffs\n * would strand the follow-up command the voice tool already sent. Ownership is\n * tracked per store instead, so a disposed store takes only its own records.\n */\nfunction registry(): HandoffRegistry {\n const root = globalThis as HandoffGlobal;\n return (root[DOMAIN_SWITCH_HANDOFF_SYMBOL] ??= { records: new Map() });\n}\n\nfunction invalid(label: string): Error {\n return new Error(`Invalid domain switch handoff ${label}.`);\n}\n\nfunction hasControlCharacter(value: string): boolean {\n for (const character of value) {\n const code = character.codePointAt(0);\n if (code !== undefined && (code <= 0x1f || code === 0x7f)) return true;\n }\n return false;\n}\n\nfunction normalized(value: string, label: string, maximum: number, pattern?: RegExp): string {\n const text = value.trim();\n if (!text || text.length > maximum || hasControlCharacter(text) || (pattern && !pattern.test(text))) {\n throw invalid(label);\n }\n return text;\n}\n\nfunction sessionId(value: string): string {\n return normalized(value, 'session id', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction hostGeneration(value: string): string {\n return normalized(value, 'host generation', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH);\n}\n\nfunction operationId(value: string): string {\n return normalized(value, 'operation id', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction reloadToken(value: string): string {\n return normalized(value, 'reload handoff token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction token(value: string): string {\n return normalized(value, 'token', DOMAIN_SWITCH_HANDOFF_MAX_IDENTIFIER_LENGTH, SAFE_DOMAIN_NAME);\n}\n\nfunction domains(values: readonly string[]): string[] {\n if (values.length > DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS) {\n throw new Error(`A maximum of ${DOMAIN_SWITCH_HANDOFF_MAX_DOMAINS} domains may be selected.`);\n }\n const result: string[] = [];\n const seen = new Set<string>();\n for (const value of values) {\n const name = normalized(value, 'domain name', DOMAIN_SWITCH_HANDOFF_MAX_OPERATION_LENGTH, SAFE_DOMAIN_NAME);\n if (seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\nfunction prune(now = Date.now()): void {\n for (const [key, record] of registry().records) {\n if (record.expiresAt <= now) registry().records.delete(key);\n }\n}\n\nfunction identityMatches(record: DomainSwitchHandoff, identity: DomainSwitchHandoffIdentity): boolean {\n if (record.sessionId !== sessionId(identity.sessionId)) return false;\n return identity.hostGeneration === undefined || record.hostGeneration === hostGeneration(identity.hostGeneration);\n}\n\nfunction copy(record: DomainSwitchHandoff): DomainSwitchHandoff {\n return { ...record, domains: [...record.domains] };\n}\n\nfunction issue(ownerGeneration: string, request: DomainSwitchHandoffRequest): DomainSwitchHandoff {\n prune();\n const normalizedSessionId = sessionId(request.sessionId);\n const normalizedHostGeneration = hostGeneration(request.hostGeneration);\n const normalizedOperationId = operationId(request.operationId);\n const normalizedDomains = domains(request.domains);\n const normalizedReloadToken = reloadToken(request.reloadHandoffToken);\n const createdAt = Date.now();\n const record: StoredDomainSwitchHandoff = {\n token: `${DOMAIN_SWITCH_TOKEN_PREFIX}${crypto.randomUUID()}`,\n sessionId: normalizedSessionId,\n hostGeneration: normalizedHostGeneration,\n operationId: normalizedOperationId,\n domains: normalizedDomains,\n reloadHandoffToken: normalizedReloadToken,\n createdAt,\n expiresAt: createdAt + DOMAIN_SWITCH_HANDOFF_TTL_MS,\n ownerGeneration,\n };\n registry().records.set(record.token, record);\n return copy(record);\n}\n\nfunction consume(identity: DomainSwitchHandoffIdentity, handoffToken: string): DomainSwitchHandoff | undefined {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return undefined;\n registry().records.delete(normalizedToken);\n if (record.expiresAt <= Date.now()) return undefined;\n return copy(record);\n}\n\nfunction discard(identity: DomainSwitchHandoffIdentity, handoffToken: string): boolean {\n prune();\n const normalizedToken = token(handoffToken);\n const record = registry().records.get(normalizedToken);\n if (!record || !identityMatches(record, identity)) return false;\n return registry().records.delete(normalizedToken);\n}\n\nfunction clearOwnedSession(ownerGeneration: string, requestedSessionId: string): number {\n prune();\n const normalizedSessionId = sessionId(requestedSessionId);\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration || record.sessionId !== normalizedSessionId) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nfunction disposeOwner(ownerGeneration: string): number {\n let removed = 0;\n for (const [key, record] of registry().records) {\n if (record.ownerGeneration !== ownerGeneration) continue;\n registry().records.delete(key);\n removed += 1;\n }\n return removed;\n}\n\nexport function createDomainSwitchHandoffStore(): DomainSwitchHandoffStore {\n const ownerGeneration = crypto.randomUUID();\n let disposed = false;\n return {\n ownerGeneration,\n issue(request) {\n if (disposed) throw new Error('Domain switch handoff store is disposed.');\n return issue(ownerGeneration, request);\n },\n consume(handoffToken, identity) {\n if (disposed) return undefined;\n return consume(identity, handoffToken);\n },\n discard(handoffToken, identity) {\n if (disposed) return false;\n return discard(identity, handoffToken);\n },\n clearSession(requestedSessionId) {\n if (disposed) return 0;\n return clearOwnedSession(ownerGeneration, requestedSessionId);\n },\n dispose() {\n if (disposed) return 0;\n disposed = true;\n return disposeOwner(ownerGeneration);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("@agimon-ai/doompi-config/domains"),r=require("node:fs");r=e.__toESM(r,1);let i=require("yaml");const a=`agiflow-proxy`,o=`mcp-config.yaml`,s=`--config`;function c(e,t){let r=(0,n.loadDomains)(e),i=(0,n.expandDomainNames)(r,t);if(i.length===0)return;let a=new Set,o=new Set;for(let e of i){let t=r.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])a.add(e);for(let e of t.proxy??[])o.add(e)}return{servers:[...a],proxy:[...o]}}function l(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function u(e,t){if(!t||t.length===0)return e;let n=(0,i.parse)(e)??{},r=n.mcpServers??{},a={},o=[];for(let[e,n]of Object.entries(r))t.includes(e)?a[e]=n:o.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(a).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${o.join(`, `)||`(none)`}`,``].join(`
|
|
2
|
+
`)}${(0,i.stringify)({...n,mcpServers:a})}`}async function d(e,t){try{return await r.default.promises.copyFile(e,t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function f(e,n,i){await r.default.promises.mkdir(i,{recursive:!0});let c=JSON.parse(await r.default.promises.readFile(e,`utf8`)),l=c.mcpServers??{},u=l[a],f=t.default.join(n,o);if(u){let e=[...u.args??[]],n=e.indexOf(s);if(n!==-1){let r=t.default.join(i,o);await d(f,r)&&(e[n+1]=r,l[a]={...u,args:e})}}let p=t.default.join(i,`mcp.json`);return await r.default.promises.writeFile(p,`${JSON.stringify({...c,mcpServers:l},null,2)}\n`,{mode:384}),p}async function p(e,n,i,c){if(!n)return e;let d=l(e,n.servers),f=d.mcpServers??{},p=f[a];if(!p||!n.proxy||n.proxy.length===0)return d;let m=[...p.args??[]],h=m.indexOf(s);if(h===-1||!m[h+1])return d;let g=t.default.resolve(i,m[h+1]),_;try{_=await r.default.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return d;throw e}let v=t.default.join(c,o);return await r.default.promises.writeFile(v,u(_,n.proxy),{mode:384}),m[h+1]=v,{...d,mcpServers:{...f,[a]:{...p,args:m}}}}exports.PROXY_SERVER_NAME=a,exports.applyMcpAllowlist=p,exports.filterMcpServers=l,exports.filterProxyConfig=u,exports.persistMcpConfig=f,exports.resolveMcpAllowlist=c;
|
|
3
|
+
//# sourceMappingURL=mcpFilter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpFilter.cjs","names":["loadDomains","expandDomainNames","parseYaml","stringifyYaml","fs","path"],"sources":["../../src/adapters/mcpFilter.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { type DomainMcpAllowlist, expandDomainNames, loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { JsonObject } from '../types/resources.ts';\n\n/** Server name in .mcp.json whose upstreams are declared in mcp-config.yaml. */\nexport const PROXY_SERVER_NAME = 'agiflow-proxy';\nconst PROXY_CONFIG_FILE = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\n/**\n * Unions the mcp allowlists of the selected domains.\n *\n * Returns undefined when any selected domain omits `mcp`, which means \"give me\n * everything\". Filtering only kicks in once every selected domain has opted in,\n * so a domain that has not been migrated yet cannot silently lose its tools.\n */\nexport function resolveMcpAllowlist(repoRoot: string, domainNames: string[]): DomainMcpAllowlist | undefined {\n const manifest = loadDomains(repoRoot);\n const expanded = expandDomainNames(manifest, domainNames);\n if (expanded.length === 0) return undefined;\n\n const servers = new Set<string>();\n const proxy = new Set<string>();\n for (const name of expanded) {\n const allowlist = manifest.domains[name]?.mcp;\n if (!allowlist) return undefined;\n for (const server of allowlist.servers ?? []) servers.add(server);\n for (const upstream of allowlist.proxy ?? []) proxy.add(upstream);\n }\n return { servers: [...servers], proxy: [...proxy] };\n}\n\n/** Drops servers that are not on the allowlist. An empty list means keep all. */\nexport function filterMcpServers(config: JsonObject, allowed?: string[]): JsonObject {\n if (!allowed || allowed.length === 0) return config;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n for (const [name, definition] of Object.entries(servers)) {\n if (allowed.includes(name)) kept[name] = definition;\n }\n return { ...config, mcpServers: kept };\n}\n\n/**\n * Rewrites the proxy's upstream list, keeping only allowed servers.\n *\n * Entries are removed rather than marked disabled so a filtered upstream cannot\n * be reached even if the proxy ignores the disabled flag. Comments in the\n * source file are not preserved; the result is a per-run temporary file.\n */\nexport function filterProxyConfig(yamlText: string, allowed?: string[]): string {\n if (!allowed || allowed.length === 0) return yamlText;\n const config = (parseYaml(yamlText) ?? {}) as JsonObject;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n for (const [name, definition] of Object.entries(servers)) {\n if (allowed.includes(name)) kept[name] = definition;\n else dropped.push(name);\n }\n const header = [\n '# Generated by doompi for this run. Do not edit.',\n `# Upstreams kept: ${Object.keys(kept).join(', ') || '(none)'}`,\n `# Upstreams removed by the domain allowlist: ${dropped.join(', ') || '(none)'}`,\n '',\n ].join('\\n');\n return `${header}${stringifyYaml({ ...config, mcpServers: kept })}`;\n}\n\nasync function copyFileIfPresent(source: string, target: string): Promise<boolean> {\n try {\n await fs.promises.copyFile(source, target);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n}\n\n/**\n * Copies a generated MCP config out of the run directory so another frontend\n * can use it, keeping the proxy's --config pointing at the copy.\n *\n * The run directory is deleted when the harness exits, so emitting for\n * claude.sh or codex.sh has to relocate both files together.\n */\nexport async function persistMcpConfig(\n sourceConfigPath: string,\n temporaryDirectory: string,\n targetDirectory: string,\n): Promise<string> {\n await fs.promises.mkdir(targetDirectory, { recursive: true });\n const config = JSON.parse(await fs.promises.readFile(sourceConfigPath, 'utf8')) as JsonObject;\n const servers = (config.mcpServers ?? {}) as Record<string, JsonObject>;\n const proxy = servers[PROXY_SERVER_NAME];\n const generatedProxyConfig = path.join(temporaryDirectory, PROXY_CONFIG_FILE);\n\n if (proxy) {\n const args = [...((proxy.args ?? []) as string[])];\n const configIndex = args.indexOf(CONFIG_ARGUMENT);\n if (configIndex !== -1) {\n const targetProxyConfig = path.join(targetDirectory, PROXY_CONFIG_FILE);\n if (await copyFileIfPresent(generatedProxyConfig, targetProxyConfig)) {\n args[configIndex + 1] = targetProxyConfig;\n servers[PROXY_SERVER_NAME] = { ...proxy, args };\n }\n }\n }\n\n const targetPath = path.join(targetDirectory, 'mcp.json');\n await fs.promises.writeFile(targetPath, `${JSON.stringify({ ...config, mcpServers: servers }, null, 2)}\\n`, {\n mode: 0o600,\n });\n return targetPath;\n}\n\n/**\n * Applies the allowlist to a merged MCP config, writing a filtered proxy config\n * into the run directory and repointing the proxy at it when needed.\n */\nexport async function applyMcpAllowlist(\n config: JsonObject,\n allowlist: DomainMcpAllowlist | undefined,\n repoRoot: string,\n temporaryDirectory: string,\n): Promise<JsonObject> {\n if (!allowlist) return config;\n const filtered = filterMcpServers(config, allowlist.servers);\n const servers = (filtered.mcpServers ?? {}) as Record<string, JsonObject>;\n const proxy = servers[PROXY_SERVER_NAME];\n if (!proxy || !allowlist.proxy || allowlist.proxy.length === 0) return filtered;\n\n const args = [...((proxy.args ?? []) as string[])];\n const configIndex = args.indexOf(CONFIG_ARGUMENT);\n if (configIndex === -1 || !args[configIndex + 1]) return filtered;\n\n const sourcePath = path.resolve(repoRoot, args[configIndex + 1]);\n let sourceConfig: string;\n try {\n sourceConfig = await fs.promises.readFile(sourcePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return filtered;\n throw error;\n }\n const targetPath = path.join(temporaryDirectory, PROXY_CONFIG_FILE);\n await fs.promises.writeFile(targetPath, filterProxyConfig(sourceConfig, allowlist.proxy), { mode: 0o600 });\n args[configIndex + 1] = targetPath;\n return { ...filtered, mcpServers: { ...servers, [PROXY_SERVER_NAME]: { ...proxy, args } } };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { JsonObject } from "../types/resources.cjs";
|
|
2
|
+
import { DomainMcpAllowlist } from "@agimon-ai/doompi-config/domains";
|
|
3
|
+
//#region src/adapters/mcpFilter.d.ts
|
|
4
|
+
/** Server name in .mcp.json whose upstreams are declared in mcp-config.yaml. */
|
|
5
|
+
declare const PROXY_SERVER_NAME = "agiflow-proxy";
|
|
6
|
+
/**
|
|
7
|
+
* Unions the mcp allowlists of the selected domains.
|
|
8
|
+
*
|
|
9
|
+
* Returns undefined when any selected domain omits `mcp`, which means "give me
|
|
10
|
+
* everything". Filtering only kicks in once every selected domain has opted in,
|
|
11
|
+
* so a domain that has not been migrated yet cannot silently lose its tools.
|
|
12
|
+
*/
|
|
13
|
+
declare function resolveMcpAllowlist(repoRoot: string, domainNames: string[]): DomainMcpAllowlist | undefined;
|
|
14
|
+
/** Drops servers that are not on the allowlist. An empty list means keep all. */
|
|
15
|
+
declare function filterMcpServers(config: JsonObject, allowed?: string[]): JsonObject;
|
|
16
|
+
/**
|
|
17
|
+
* Rewrites the proxy's upstream list, keeping only allowed servers.
|
|
18
|
+
*
|
|
19
|
+
* Entries are removed rather than marked disabled so a filtered upstream cannot
|
|
20
|
+
* be reached even if the proxy ignores the disabled flag. Comments in the
|
|
21
|
+
* source file are not preserved; the result is a per-run temporary file.
|
|
22
|
+
*/
|
|
23
|
+
declare function filterProxyConfig(yamlText: string, allowed?: string[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Copies a generated MCP config out of the run directory so another frontend
|
|
26
|
+
* can use it, keeping the proxy's --config pointing at the copy.
|
|
27
|
+
*
|
|
28
|
+
* The run directory is deleted when the harness exits, so emitting for
|
|
29
|
+
* claude.sh or codex.sh has to relocate both files together.
|
|
30
|
+
*/
|
|
31
|
+
declare function persistMcpConfig(sourceConfigPath: string, temporaryDirectory: string, targetDirectory: string): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Applies the allowlist to a merged MCP config, writing a filtered proxy config
|
|
34
|
+
* into the run directory and repointing the proxy at it when needed.
|
|
35
|
+
*/
|
|
36
|
+
declare function applyMcpAllowlist(config: JsonObject, allowlist: DomainMcpAllowlist | undefined, repoRoot: string, temporaryDirectory: string): Promise<JsonObject>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { PROXY_SERVER_NAME, applyMcpAllowlist, filterMcpServers, filterProxyConfig, persistMcpConfig, resolveMcpAllowlist };
|
|
39
|
+
//# sourceMappingURL=mcpFilter.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpFilter.d.cts","names":[],"sources":["../../src/adapters/mcpFilter.ts"],"mappings":";;;;cAOa;;;;;;;;iBAWG,oBAAoB,kBAAkB,wBAAwB;;iBAiB9D,iBAAiB,QAAQ,YAAY,qBAAqB;;;;;;;;iBAiB1D,kBAAkB,kBAAkB;;;;;;;;iBAoC9B,iBACpB,0BACA,4BACA,0BACC;;;;;iBA8BmB,kBACpB,QAAQ,YACR,WAAW,gCACX,kBACA,6BACC,QAAQ"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { JsonObject } from "../types/resources.mjs";
|
|
2
|
+
import { DomainMcpAllowlist } from "@agimon-ai/doompi-config/domains";
|
|
3
|
+
//#region src/adapters/mcpFilter.d.ts
|
|
4
|
+
/** Server name in .mcp.json whose upstreams are declared in mcp-config.yaml. */
|
|
5
|
+
declare const PROXY_SERVER_NAME = "agiflow-proxy";
|
|
6
|
+
/**
|
|
7
|
+
* Unions the mcp allowlists of the selected domains.
|
|
8
|
+
*
|
|
9
|
+
* Returns undefined when any selected domain omits `mcp`, which means "give me
|
|
10
|
+
* everything". Filtering only kicks in once every selected domain has opted in,
|
|
11
|
+
* so a domain that has not been migrated yet cannot silently lose its tools.
|
|
12
|
+
*/
|
|
13
|
+
declare function resolveMcpAllowlist(repoRoot: string, domainNames: string[]): DomainMcpAllowlist | undefined;
|
|
14
|
+
/** Drops servers that are not on the allowlist. An empty list means keep all. */
|
|
15
|
+
declare function filterMcpServers(config: JsonObject, allowed?: string[]): JsonObject;
|
|
16
|
+
/**
|
|
17
|
+
* Rewrites the proxy's upstream list, keeping only allowed servers.
|
|
18
|
+
*
|
|
19
|
+
* Entries are removed rather than marked disabled so a filtered upstream cannot
|
|
20
|
+
* be reached even if the proxy ignores the disabled flag. Comments in the
|
|
21
|
+
* source file are not preserved; the result is a per-run temporary file.
|
|
22
|
+
*/
|
|
23
|
+
declare function filterProxyConfig(yamlText: string, allowed?: string[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Copies a generated MCP config out of the run directory so another frontend
|
|
26
|
+
* can use it, keeping the proxy's --config pointing at the copy.
|
|
27
|
+
*
|
|
28
|
+
* The run directory is deleted when the harness exits, so emitting for
|
|
29
|
+
* claude.sh or codex.sh has to relocate both files together.
|
|
30
|
+
*/
|
|
31
|
+
declare function persistMcpConfig(sourceConfigPath: string, temporaryDirectory: string, targetDirectory: string): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Applies the allowlist to a merged MCP config, writing a filtered proxy config
|
|
34
|
+
* into the run directory and repointing the proxy at it when needed.
|
|
35
|
+
*/
|
|
36
|
+
declare function applyMcpAllowlist(config: JsonObject, allowlist: DomainMcpAllowlist | undefined, repoRoot: string, temporaryDirectory: string): Promise<JsonObject>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { PROXY_SERVER_NAME, applyMcpAllowlist, filterMcpServers, filterProxyConfig, persistMcpConfig, resolveMcpAllowlist };
|
|
39
|
+
//# sourceMappingURL=mcpFilter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpFilter.d.mts","names":[],"sources":["../../src/adapters/mcpFilter.ts"],"mappings":";;;;cAOa;;;;;;;;iBAWG,oBAAoB,kBAAkB,wBAAwB;;iBAiB9D,iBAAiB,QAAQ,YAAY,qBAAqB;;;;;;;;iBAiB1D,kBAAkB,kBAAkB;;;;;;;;iBAoC9B,iBACpB,0BACA,4BACA,0BACC;;;;;iBA8BmB,kBACpB,QAAQ,YACR,WAAW,gCACX,kBACA,6BACC,QAAQ"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import e from"node:path";import{expandDomainNames as t,loadDomains as n}from"@agimon-ai/doompi-config/domains";import r from"node:fs";import{parse as i,stringify as a}from"yaml";const o=`agiflow-proxy`,s=`mcp-config.yaml`,c=`--config`;function l(e,r){let i=n(e),a=t(i,r);if(a.length===0)return;let o=new Set,s=new Set;for(let e of a){let t=i.domains[e]?.mcp;if(!t)return;for(let e of t.servers??[])o.add(e);for(let e of t.proxy??[])s.add(e)}return{servers:[...o],proxy:[...s]}}function u(e,t){if(!t||t.length===0)return e;let n=e.mcpServers??{},r={};for(let[e,i]of Object.entries(n))t.includes(e)&&(r[e]=i);return{...e,mcpServers:r}}function d(e,t){if(!t||t.length===0)return e;let n=i(e)??{},r=n.mcpServers??{},o={},s=[];for(let[e,n]of Object.entries(r))t.includes(e)?o[e]=n:s.push(e);return`${[`# Generated by doompi for this run. Do not edit.`,`# Upstreams kept: ${Object.keys(o).join(`, `)||`(none)`}`,`# Upstreams removed by the domain allowlist: ${s.join(`, `)||`(none)`}`,``].join(`
|
|
2
|
+
`)}${a({...n,mcpServers:o})}`}async function f(e,t){try{return await r.promises.copyFile(e,t),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async function p(t,n,i){await r.promises.mkdir(i,{recursive:!0});let a=JSON.parse(await r.promises.readFile(t,`utf8`)),l=a.mcpServers??{},u=l[o],d=e.join(n,s);if(u){let t=[...u.args??[]],n=t.indexOf(c);if(n!==-1){let r=e.join(i,s);await f(d,r)&&(t[n+1]=r,l[o]={...u,args:t})}}let p=e.join(i,`mcp.json`);return await r.promises.writeFile(p,`${JSON.stringify({...a,mcpServers:l},null,2)}\n`,{mode:384}),p}async function m(t,n,i,a){if(!n)return t;let l=u(t,n.servers),f=l.mcpServers??{},p=f[o];if(!p||!n.proxy||n.proxy.length===0)return l;let m=[...p.args??[]],h=m.indexOf(c);if(h===-1||!m[h+1])return l;let g=e.resolve(i,m[h+1]),_;try{_=await r.promises.readFile(g,`utf8`)}catch(e){if(e.code===`ENOENT`)return l;throw e}let v=e.join(a,s);return await r.promises.writeFile(v,d(_,n.proxy),{mode:384}),m[h+1]=v,{...l,mcpServers:{...f,[o]:{...p,args:m}}}}export{o as PROXY_SERVER_NAME,m as applyMcpAllowlist,u as filterMcpServers,d as filterProxyConfig,p as persistMcpConfig,l as resolveMcpAllowlist};
|
|
3
|
+
//# sourceMappingURL=mcpFilter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpFilter.mjs","names":["parseYaml","stringifyYaml"],"sources":["../../src/adapters/mcpFilter.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { type DomainMcpAllowlist, expandDomainNames, loadDomains } from '@agimon-ai/doompi-config/domains';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { JsonObject } from '../types/resources.ts';\n\n/** Server name in .mcp.json whose upstreams are declared in mcp-config.yaml. */\nexport const PROXY_SERVER_NAME = 'agiflow-proxy';\nconst PROXY_CONFIG_FILE = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\n/**\n * Unions the mcp allowlists of the selected domains.\n *\n * Returns undefined when any selected domain omits `mcp`, which means \"give me\n * everything\". Filtering only kicks in once every selected domain has opted in,\n * so a domain that has not been migrated yet cannot silently lose its tools.\n */\nexport function resolveMcpAllowlist(repoRoot: string, domainNames: string[]): DomainMcpAllowlist | undefined {\n const manifest = loadDomains(repoRoot);\n const expanded = expandDomainNames(manifest, domainNames);\n if (expanded.length === 0) return undefined;\n\n const servers = new Set<string>();\n const proxy = new Set<string>();\n for (const name of expanded) {\n const allowlist = manifest.domains[name]?.mcp;\n if (!allowlist) return undefined;\n for (const server of allowlist.servers ?? []) servers.add(server);\n for (const upstream of allowlist.proxy ?? []) proxy.add(upstream);\n }\n return { servers: [...servers], proxy: [...proxy] };\n}\n\n/** Drops servers that are not on the allowlist. An empty list means keep all. */\nexport function filterMcpServers(config: JsonObject, allowed?: string[]): JsonObject {\n if (!allowed || allowed.length === 0) return config;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n for (const [name, definition] of Object.entries(servers)) {\n if (allowed.includes(name)) kept[name] = definition;\n }\n return { ...config, mcpServers: kept };\n}\n\n/**\n * Rewrites the proxy's upstream list, keeping only allowed servers.\n *\n * Entries are removed rather than marked disabled so a filtered upstream cannot\n * be reached even if the proxy ignores the disabled flag. Comments in the\n * source file are not preserved; the result is a per-run temporary file.\n */\nexport function filterProxyConfig(yamlText: string, allowed?: string[]): string {\n if (!allowed || allowed.length === 0) return yamlText;\n const config = (parseYaml(yamlText) ?? {}) as JsonObject;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n for (const [name, definition] of Object.entries(servers)) {\n if (allowed.includes(name)) kept[name] = definition;\n else dropped.push(name);\n }\n const header = [\n '# Generated by doompi for this run. Do not edit.',\n `# Upstreams kept: ${Object.keys(kept).join(', ') || '(none)'}`,\n `# Upstreams removed by the domain allowlist: ${dropped.join(', ') || '(none)'}`,\n '',\n ].join('\\n');\n return `${header}${stringifyYaml({ ...config, mcpServers: kept })}`;\n}\n\nasync function copyFileIfPresent(source: string, target: string): Promise<boolean> {\n try {\n await fs.promises.copyFile(source, target);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n}\n\n/**\n * Copies a generated MCP config out of the run directory so another frontend\n * can use it, keeping the proxy's --config pointing at the copy.\n *\n * The run directory is deleted when the harness exits, so emitting for\n * claude.sh or codex.sh has to relocate both files together.\n */\nexport async function persistMcpConfig(\n sourceConfigPath: string,\n temporaryDirectory: string,\n targetDirectory: string,\n): Promise<string> {\n await fs.promises.mkdir(targetDirectory, { recursive: true });\n const config = JSON.parse(await fs.promises.readFile(sourceConfigPath, 'utf8')) as JsonObject;\n const servers = (config.mcpServers ?? {}) as Record<string, JsonObject>;\n const proxy = servers[PROXY_SERVER_NAME];\n const generatedProxyConfig = path.join(temporaryDirectory, PROXY_CONFIG_FILE);\n\n if (proxy) {\n const args = [...((proxy.args ?? []) as string[])];\n const configIndex = args.indexOf(CONFIG_ARGUMENT);\n if (configIndex !== -1) {\n const targetProxyConfig = path.join(targetDirectory, PROXY_CONFIG_FILE);\n if (await copyFileIfPresent(generatedProxyConfig, targetProxyConfig)) {\n args[configIndex + 1] = targetProxyConfig;\n servers[PROXY_SERVER_NAME] = { ...proxy, args };\n }\n }\n }\n\n const targetPath = path.join(targetDirectory, 'mcp.json');\n await fs.promises.writeFile(targetPath, `${JSON.stringify({ ...config, mcpServers: servers }, null, 2)}\\n`, {\n mode: 0o600,\n });\n return targetPath;\n}\n\n/**\n * Applies the allowlist to a merged MCP config, writing a filtered proxy config\n * into the run directory and repointing the proxy at it when needed.\n */\nexport async function applyMcpAllowlist(\n config: JsonObject,\n allowlist: DomainMcpAllowlist | undefined,\n repoRoot: string,\n temporaryDirectory: string,\n): Promise<JsonObject> {\n if (!allowlist) return config;\n const filtered = filterMcpServers(config, allowlist.servers);\n const servers = (filtered.mcpServers ?? {}) as Record<string, JsonObject>;\n const proxy = servers[PROXY_SERVER_NAME];\n if (!proxy || !allowlist.proxy || allowlist.proxy.length === 0) return filtered;\n\n const args = [...((proxy.args ?? []) as string[])];\n const configIndex = args.indexOf(CONFIG_ARGUMENT);\n if (configIndex === -1 || !args[configIndex + 1]) return filtered;\n\n const sourcePath = path.resolve(repoRoot, args[configIndex + 1]);\n let sourceConfig: string;\n try {\n sourceConfig = await fs.promises.readFile(sourcePath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return filtered;\n throw error;\n }\n const targetPath = path.join(temporaryDirectory, PROXY_CONFIG_FILE);\n await fs.promises.writeFile(targetPath, filterProxyConfig(sourceConfig, allowlist.proxy), { mode: 0o600 });\n args[configIndex + 1] = targetPath;\n return { ...filtered, mcpServers: { ...servers, [PROXY_SERVER_NAME]: { ...proxy, args } } };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("@agimon-ai/doompi-mcp");function t(t){let n={repoRoot:t.repoRoot,stagingDirectory:t.stagingDirectory,...t.generatedConfigPath?{generatedConfigPath:t.generatedConfigPath}:{},...t.pluginConfigPaths?.length?{pluginConfigPaths:[...t.pluginConfigPaths]}:{},...t.allowlist?{allowlist:structuredClone(t.allowlist)}:{}};return(0,e.sessionConfigEnvironment)(n)}exports.mcpSessionEnvironment=t;
|
|
2
|
+
//# sourceMappingURL=mcpSessionEnvironment.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpSessionEnvironment.cjs","names":["sessionConfigEnvironment"],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"sourcesContent":["import { type McpAllowlist, type McpSessionConfig, sessionConfigEnvironment } from '@agimon-ai/doompi-mcp';\n\nexport interface McpSessionEnvironmentInput {\n readonly repoRoot: string;\n readonly stagingDirectory: string;\n readonly generatedConfigPath?: string;\n readonly pluginConfigPaths?: readonly string[];\n readonly allowlist?: McpAllowlist;\n}\n\n/** Projects the complete domain-selected MCP picture into the Pi session environment. */\nexport function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string> {\n const configuration: McpSessionConfig = {\n repoRoot: input.repoRoot,\n stagingDirectory: input.stagingDirectory,\n ...(input.generatedConfigPath ? { generatedConfigPath: input.generatedConfigPath } : {}),\n ...(input.pluginConfigPaths?.length ? { pluginConfigPaths: [...input.pluginConfigPaths] } : {}),\n ...(input.allowlist ? { allowlist: structuredClone(input.allowlist) } : {}),\n };\n return sessionConfigEnvironment(configuration);\n}\n"],"mappings":"uCAWA,SAAgB,EAAsB,EAA2D,CAC/F,IAAM,EAAkC,CACtC,SAAU,EAAM,SAChB,iBAAkB,EAAM,iBACxB,GAAI,EAAM,oBAAsB,CAAE,oBAAqB,EAAM,mBAAoB,EAAI,CAAC,EACtF,GAAI,EAAM,mBAAmB,OAAS,CAAE,kBAAmB,CAAC,GAAG,EAAM,iBAAiB,CAAE,EAAI,CAAC,EAC7F,GAAI,EAAM,UAAY,CAAE,UAAW,gBAAgB,EAAM,SAAS,CAAE,EAAI,CAAC,CAC3E,EACA,OAAA,EAAOA,EAAAA,yBAAAA,CAAyB,CAAa,CAC/C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { McpAllowlist } from "@agimon-ai/doompi-mcp";
|
|
2
|
+
//#region src/adapters/mcpSessionEnvironment.d.ts
|
|
3
|
+
interface McpSessionEnvironmentInput {
|
|
4
|
+
readonly repoRoot: string;
|
|
5
|
+
readonly stagingDirectory: string;
|
|
6
|
+
readonly generatedConfigPath?: string;
|
|
7
|
+
readonly pluginConfigPaths?: readonly string[];
|
|
8
|
+
readonly allowlist?: McpAllowlist;
|
|
9
|
+
}
|
|
10
|
+
/** Projects the complete domain-selected MCP picture into the Pi session environment. */
|
|
11
|
+
declare function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { McpSessionEnvironmentInput, mcpSessionEnvironment };
|
|
14
|
+
//# sourceMappingURL=mcpSessionEnvironment.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpSessionEnvironment.d.cts","names":[],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"mappings":";;UAEiB;WACN;WACA;WACA;WACA;WACA,YAAY;;;iBAIP,sBAAsB,OAAO,6BAA6B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { McpAllowlist } from "@agimon-ai/doompi-mcp";
|
|
2
|
+
//#region src/adapters/mcpSessionEnvironment.d.ts
|
|
3
|
+
interface McpSessionEnvironmentInput {
|
|
4
|
+
readonly repoRoot: string;
|
|
5
|
+
readonly stagingDirectory: string;
|
|
6
|
+
readonly generatedConfigPath?: string;
|
|
7
|
+
readonly pluginConfigPaths?: readonly string[];
|
|
8
|
+
readonly allowlist?: McpAllowlist;
|
|
9
|
+
}
|
|
10
|
+
/** Projects the complete domain-selected MCP picture into the Pi session environment. */
|
|
11
|
+
declare function mcpSessionEnvironment(input: McpSessionEnvironmentInput): Record<string, string>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { McpSessionEnvironmentInput, mcpSessionEnvironment };
|
|
14
|
+
//# sourceMappingURL=mcpSessionEnvironment.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpSessionEnvironment.d.mts","names":[],"sources":["../../src/adapters/mcpSessionEnvironment.ts"],"mappings":";;UAEiB;WACN;WACA;WACA;WACA;WACA,YAAY;;;iBAIP,sBAAsB,OAAO,6BAA6B"}
|