claude-opencode-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +514 -0
  4. package/SECURITY.md +80 -0
  5. package/agents/deepseek-coder.md +32 -0
  6. package/agents/deepseek-researcher.md +36 -0
  7. package/agents/deepseek-reviewer.md +31 -0
  8. package/agents/deepseek-tester.md +31 -0
  9. package/dist/cli/init.d.ts +6 -0
  10. package/dist/cli/init.js +208 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config/config.d.ts +109 -0
  13. package/dist/config/config.js +163 -0
  14. package/dist/config/config.js.map +1 -0
  15. package/dist/errors.d.ts +29 -0
  16. package/dist/errors.js +68 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +116 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mcp/execute.d.ts +30 -0
  22. package/dist/mcp/execute.js +128 -0
  23. package/dist/mcp/execute.js.map +1 -0
  24. package/dist/mcp/server.d.ts +19 -0
  25. package/dist/mcp/server.js +38 -0
  26. package/dist/mcp/server.js.map +1 -0
  27. package/dist/mcp/session-utils.d.ts +5 -0
  28. package/dist/mcp/session-utils.js +55 -0
  29. package/dist/mcp/session-utils.js.map +1 -0
  30. package/dist/mcp/tool-utils.d.ts +11 -0
  31. package/dist/mcp/tool-utils.js +44 -0
  32. package/dist/mcp/tool-utils.js.map +1 -0
  33. package/dist/mcp/tools/abort-session.d.ts +3 -0
  34. package/dist/mcp/tools/abort-session.js +48 -0
  35. package/dist/mcp/tools/abort-session.js.map +1 -0
  36. package/dist/mcp/tools/create-session.d.ts +3 -0
  37. package/dist/mcp/tools/create-session.js +53 -0
  38. package/dist/mcp/tools/create-session.js.map +1 -0
  39. package/dist/mcp/tools/delegate-task.d.ts +4 -0
  40. package/dist/mcp/tools/delegate-task.js +98 -0
  41. package/dist/mcp/tools/delegate-task.js.map +1 -0
  42. package/dist/mcp/tools/get-diff.d.ts +3 -0
  43. package/dist/mcp/tools/get-diff.js +97 -0
  44. package/dist/mcp/tools/get-diff.js.map +1 -0
  45. package/dist/mcp/tools/get-session.d.ts +3 -0
  46. package/dist/mcp/tools/get-session.js +79 -0
  47. package/dist/mcp/tools/get-session.js.map +1 -0
  48. package/dist/mcp/tools/list-agents.d.ts +3 -0
  49. package/dist/mcp/tools/list-agents.js +72 -0
  50. package/dist/mcp/tools/list-agents.js.map +1 -0
  51. package/dist/mcp/tools/send-message.d.ts +3 -0
  52. package/dist/mcp/tools/send-message.js +59 -0
  53. package/dist/mcp/tools/send-message.js.map +1 -0
  54. package/dist/opencode/agents.d.ts +46 -0
  55. package/dist/opencode/agents.js +239 -0
  56. package/dist/opencode/agents.js.map +1 -0
  57. package/dist/opencode/binary.d.ts +19 -0
  58. package/dist/opencode/binary.js +129 -0
  59. package/dist/opencode/binary.js.map +1 -0
  60. package/dist/opencode/client.d.ts +32 -0
  61. package/dist/opencode/client.js +55 -0
  62. package/dist/opencode/client.js.map +1 -0
  63. package/dist/opencode/manager.d.ts +66 -0
  64. package/dist/opencode/manager.js +492 -0
  65. package/dist/opencode/manager.js.map +1 -0
  66. package/dist/opencode/permission-watcher.d.ts +26 -0
  67. package/dist/opencode/permission-watcher.js +144 -0
  68. package/dist/opencode/permission-watcher.js.map +1 -0
  69. package/dist/opencode/run.d.ts +39 -0
  70. package/dist/opencode/run.js +318 -0
  71. package/dist/opencode/run.js.map +1 -0
  72. package/dist/opencode/sessions.d.ts +26 -0
  73. package/dist/opencode/sessions.js +105 -0
  74. package/dist/opencode/sessions.js.map +1 -0
  75. package/dist/opencode/setup.d.ts +38 -0
  76. package/dist/opencode/setup.js +109 -0
  77. package/dist/opencode/setup.js.map +1 -0
  78. package/dist/security/paths.d.ts +20 -0
  79. package/dist/security/paths.js +93 -0
  80. package/dist/security/paths.js.map +1 -0
  81. package/dist/security/policy.d.ts +17 -0
  82. package/dist/security/policy.js +86 -0
  83. package/dist/security/policy.js.map +1 -0
  84. package/dist/types/index.d.ts +131 -0
  85. package/dist/types/index.js +2 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/util/fs.d.ts +12 -0
  88. package/dist/util/fs.js +73 -0
  89. package/dist/util/fs.js.map +1 -0
  90. package/dist/util/git.d.ts +54 -0
  91. package/dist/util/git.js +202 -0
  92. package/dist/util/git.js.map +1 -0
  93. package/dist/util/logger.d.ts +21 -0
  94. package/dist/util/logger.js +129 -0
  95. package/dist/util/logger.js.map +1 -0
  96. package/dist/util/text.d.ts +11 -0
  97. package/dist/util/text.js +126 -0
  98. package/dist/util/text.js.map +1 -0
  99. package/dist/workspace/context.d.ts +12 -0
  100. package/dist/workspace/context.js +67 -0
  101. package/dist/workspace/context.js.map +1 -0
  102. package/dist/workspace/resolver.d.ts +35 -0
  103. package/dist/workspace/resolver.js +112 -0
  104. package/dist/workspace/resolver.js.map +1 -0
  105. package/dist/workspace/validator.d.ts +13 -0
  106. package/dist/workspace/validator.js +102 -0
  107. package/dist/workspace/validator.js.map +1 -0
  108. package/docs/agent-configuration.md +100 -0
  109. package/docs/architecture.md +115 -0
  110. package/docs/claude-code-setup.md +116 -0
  111. package/docs/configuration.md +184 -0
  112. package/docs/contributing.md +71 -0
  113. package/docs/deepseek-setup.md +84 -0
  114. package/docs/development.md +146 -0
  115. package/docs/installation.md +88 -0
  116. package/docs/opencode-setup.md +97 -0
  117. package/docs/security-audit.md +80 -0
  118. package/docs/security.md +128 -0
  119. package/docs/troubleshooting.md +126 -0
  120. package/docs/workspace-model.md +126 -0
  121. package/package.json +79 -0
@@ -0,0 +1,38 @@
1
+ import type { OpencodeClient } from "@opencode-ai/sdk";
2
+ import type { LoadedConfig, ResolvedConfig } from "../config/config.js";
3
+ import type { ProfileCapabilities } from "../security/policy.js";
4
+ import type { AgentDefinition, AgentWorkspace } from "../types/index.js";
5
+ import type { Logger } from "../util/logger.js";
6
+ import type { OpenCodeManager } from "./manager.js";
7
+ import { type ModelRef } from "./run.js";
8
+ export interface PrepareRunInput {
9
+ cwd?: string | undefined;
10
+ agent?: string | undefined;
11
+ model?: string | undefined;
12
+ }
13
+ export interface PreparedRun {
14
+ workspace: AgentWorkspace;
15
+ config: ResolvedConfig;
16
+ definitions: AgentDefinition[];
17
+ definition: AgentDefinition;
18
+ capabilities: ProfileCapabilities;
19
+ client: OpencodeClient;
20
+ serverKey: string;
21
+ model: ModelRef | null;
22
+ modelString: string | null;
23
+ }
24
+ export interface PrepareDeps {
25
+ manager: OpenCodeManager;
26
+ logger: Logger;
27
+ env: NodeJS.ProcessEnv;
28
+ globalConfig: ResolvedConfig;
29
+ /** Override per-workspace config loading (tests/embedding). */
30
+ loadProjectConfig?: (cwd: string) => Promise<LoadedConfig>;
31
+ }
32
+ export declare function getBuiltinDefinitions(): Promise<AgentDefinition[]>;
33
+ export declare function clearBuiltinCache(): void;
34
+ export declare function prepareRun(deps: PrepareDeps, input: PrepareRunInput, workspaceOverride?: AgentWorkspace): Promise<PreparedRun>;
35
+ export declare function getClientForWorkspace(deps: PrepareDeps, workspace: AgentWorkspace, definitions: AgentDefinition[], config: ResolvedConfig): Promise<{
36
+ client: OpencodeClient;
37
+ serverKey: string;
38
+ }>;
@@ -0,0 +1,109 @@
1
+ import fs from "node:fs";
2
+ import { BridgeError } from "../errors.js";
3
+ import { resolveWorkspace } from "../workspace/resolver.js";
4
+ import { agentCapabilities, buildAgentDefinitions, findAgentDefinition, loadBuiltinAgentDefinitions, loadProjectAgentDefinitions, toOpenCodeAgentConfig, } from "./agents.js";
5
+ import { formatModelRef, resolveModel } from "./run.js";
6
+ let builtinCache = null;
7
+ const verifiedClients = new WeakSet();
8
+ /**
9
+ * Ask the OpenCode instance which directory it is serving and refuse to
10
+ * delegate when it is not the requested workspace. This is the last line of
11
+ * defence against a misrouted request reaching the wrong repository.
12
+ */
13
+ async function assertWorkspaceRouting(client, workspace, logger) {
14
+ if (verifiedClients.has(client))
15
+ return;
16
+ let served;
17
+ try {
18
+ const result = await client.path.get();
19
+ if (result.error) {
20
+ logger.debug("workspace routing probe returned an error", {
21
+ error: JSON.stringify(result.error),
22
+ });
23
+ return;
24
+ }
25
+ served = result.data?.directory;
26
+ }
27
+ catch (error) {
28
+ logger.debug("workspace routing probe failed; continuing without it", {
29
+ error: error instanceof Error ? error.message : String(error),
30
+ });
31
+ return;
32
+ }
33
+ if (!served)
34
+ return;
35
+ let servedCanonical = served;
36
+ try {
37
+ servedCanonical = await fs.promises.realpath(served);
38
+ }
39
+ catch {
40
+ // keep the server-reported path
41
+ }
42
+ if (servedCanonical !== workspace.cwd) {
43
+ throw new BridgeError("WORKSPACE_MISMATCH", `OpenCode is serving ${servedCanonical} but the workspace is ${workspace.cwd}.`, { details: { requested: workspace.cwd, served: servedCanonical } });
44
+ }
45
+ verifiedClients.add(client);
46
+ }
47
+ export async function getBuiltinDefinitions() {
48
+ if (!builtinCache)
49
+ builtinCache = await loadBuiltinAgentDefinitions();
50
+ return builtinCache;
51
+ }
52
+ export function clearBuiltinCache() {
53
+ builtinCache = null;
54
+ }
55
+ export async function prepareRun(deps, input, workspaceOverride) {
56
+ const resolution = workspaceOverride
57
+ ? null
58
+ : await resolveWorkspace({
59
+ cwd: input.cwd,
60
+ env: deps.env,
61
+ globalConfig: deps.globalConfig,
62
+ ...(deps.loadProjectConfig ? { loadProjectConfig: deps.loadProjectConfig } : {}),
63
+ });
64
+ const workspace = workspaceOverride ?? resolution.workspace;
65
+ const config = resolution?.config ?? deps.globalConfig;
66
+ const [builtin, project] = await Promise.all([
67
+ getBuiltinDefinitions(),
68
+ loadProjectAgentDefinitions(workspace.cwd),
69
+ ]);
70
+ const definitions = buildAgentDefinitions({ builtin, project, config });
71
+ const agentName = (input.agent ?? config.defaults.agent).trim();
72
+ const definition = findAgentDefinition(definitions, agentName);
73
+ if (!definition) {
74
+ throw new BridgeError("AGENT_NOT_FOUND", `Unknown agent: ${agentName}`, {
75
+ details: {
76
+ requested: agentName,
77
+ available: definitions.map((entry) => entry.name),
78
+ },
79
+ });
80
+ }
81
+ const agentConfig = toOpenCodeAgentConfig(definitions, config);
82
+ const serverKey = await deps.manager.ensure({
83
+ cwd: workspace.cwd,
84
+ config,
85
+ agentConfig,
86
+ });
87
+ const client = await deps.manager.clientFor({ cwd: workspace.cwd, config, agentConfig });
88
+ await assertWorkspaceRouting(client, workspace, deps.logger);
89
+ const resolved = await resolveModel(client, config, input.model ?? definition.model);
90
+ return {
91
+ workspace: { ...workspace, readOnly: agentCapabilities(definition).readOnly },
92
+ config,
93
+ definitions,
94
+ definition,
95
+ capabilities: agentCapabilities(definition),
96
+ client,
97
+ serverKey,
98
+ model: resolved,
99
+ modelString: formatModelRef(resolved),
100
+ };
101
+ }
102
+ export async function getClientForWorkspace(deps, workspace, definitions, config) {
103
+ const agentConfig = toOpenCodeAgentConfig(definitions, config);
104
+ const serverKey = await deps.manager.ensure({ cwd: workspace.cwd, config, agentConfig });
105
+ const client = await deps.manager.clientFor({ cwd: workspace.cwd, config, agentConfig });
106
+ await assertWorkspaceRouting(client, workspace, deps.logger);
107
+ return { client, serverKey };
108
+ }
109
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/opencode/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAA8B,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAiB,YAAY,EAAE,MAAM,UAAU,CAAC;AA6BvE,IAAI,YAAY,GAA6B,IAAI,CAAC;AAElD,MAAM,eAAe,GAAG,IAAI,OAAO,EAAkB,CAAC;AAEtD;;;;GAIG;AACH,KAAK,UAAU,sBAAsB,CACnC,MAAsB,EACtB,SAAyB,EACzB,MAAc;IAEd,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO;IAExC,IAAI,MAA0B,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACvC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE;gBACxD,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;aACpC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,uDAAuD,EAAE;YACpE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,IAAI,eAAe,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC;QACH,eAAe,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;IACD,IAAI,eAAe,KAAK,SAAS,CAAC,GAAG,EAAE,CAAC;QACtC,MAAM,IAAI,WAAW,CACnB,oBAAoB,EACpB,uBAAuB,eAAe,yBAAyB,SAAS,CAAC,GAAG,GAAG,EAC/E,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,YAAY;QAAE,YAAY,GAAG,MAAM,2BAA2B,EAAE,CAAC;IACtE,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAiB,EACjB,KAAsB,EACtB,iBAAkC;IAElC,MAAM,UAAU,GAAG,iBAAiB;QAClC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM,gBAAgB,CAAC;YACrB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CAAC,CAAC;IAEhC,MAAM,SAAS,GAAG,iBAAiB,IAAI,UAAW,CAAC,SAAS,CAAC;IAC7D,MAAM,MAAM,GAAG,UAAU,EAAE,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC;IAEvD,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC3C,qBAAqB,EAAE;QACvB,2BAA2B,CAAC,SAAS,CAAC,GAAG,CAAC;KAC3C,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,qBAAqB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAExE,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAChE,MAAM,UAAU,GAAG,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,iBAAiB,EAAE,kBAAkB,SAAS,EAAE,EAAE;YACtE,OAAO,EAAE;gBACP,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAClD;SACF,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAC1C,GAAG,EAAE,SAAS,CAAC,GAAG;QAClB,MAAM;QACN,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACzF,MAAM,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAErF,OAAO;QACL,SAAS,EAAE,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE;QAC7E,MAAM;QACN,WAAW;QACX,UAAU;QACV,YAAY,EAAE,iBAAiB,CAAC,UAAU,CAAC;QAC3C,MAAM;QACN,SAAS;QACT,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,cAAc,CAAC,QAAQ,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAiB,EACjB,SAAyB,EACzB,WAA8B,EAC9B,MAAsB;IAEtB,MAAM,WAAW,GAAG,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACzF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACzF,MAAM,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,20 @@
1
+ /** Convert an OpenCode-style wildcard pattern (`*`, `?`) to a RegExp. */
2
+ export declare function wildcardToRegExp(pattern: string): RegExp;
3
+ export declare function matchesPattern(target: string, pattern: string): boolean;
4
+ export declare function matchesAnyPattern(target: string, patterns: string[]): boolean;
5
+ /** Patterns for sensitive files that should not be read or written by agents. */
6
+ export declare const DEFAULT_SENSITIVE_PATTERNS: readonly ["*.env", "*.env.*", "*.pem", "*.key", "*.p12", "*.pfx", "id_rsa*", "id_ed25519*", "id_ecdsa*", "credentials", "credentials.*", ".ssh/*", "*.npmrc"];
7
+ /** Patterns that stay readable even when sensitive-file protection is on. */
8
+ export declare const SENSITIVE_ALLOWLIST: readonly ["*.env.example", "*.env.sample", "*.env.template"];
9
+ export declare function isSensitivePath(target: string, extraPatterns?: string[]): boolean;
10
+ /** Canonicalize a path to its real path. Throws INVALID_PATH when it does not exist. */
11
+ export declare function canonicalize(target: string): Promise<string>;
12
+ /** True when `child` is the same as, or inside, `parent` (both canonical). */
13
+ export declare function isPathInside(parent: string, child: string): boolean;
14
+ export declare function assertNoNullBytes(value: string, label: string): void;
15
+ /**
16
+ * Resolve a task path hint relative to the workspace and make sure it stays
17
+ * inside the workspace. Hints are advisory for exploration, but they must not
18
+ * point outside the delegated workspace.
19
+ */
20
+ export declare function resolvePathHint(cwd: string, hint: string): string;
@@ -0,0 +1,93 @@
1
+ import path from "node:path";
2
+ import { BridgeError } from "../errors.js";
3
+ /** Convert an OpenCode-style wildcard pattern (`*`, `?`) to a RegExp. */
4
+ export function wildcardToRegExp(pattern) {
5
+ let output = "^";
6
+ for (const char of pattern) {
7
+ if (char === "*")
8
+ output += ".*";
9
+ else if (char === "?")
10
+ output += ".";
11
+ else
12
+ output += char.replace(/[.+^${}()|[\]\\]/g, "\\$&");
13
+ }
14
+ output += "$";
15
+ return new RegExp(output);
16
+ }
17
+ export function matchesPattern(target, pattern) {
18
+ if (pattern === "*")
19
+ return true;
20
+ const regex = wildcardToRegExp(pattern);
21
+ if (regex.test(target))
22
+ return true;
23
+ return regex.test(path.basename(target));
24
+ }
25
+ export function matchesAnyPattern(target, patterns) {
26
+ return patterns.some((pattern) => matchesPattern(target, pattern));
27
+ }
28
+ /** Patterns for sensitive files that should not be read or written by agents. */
29
+ export const DEFAULT_SENSITIVE_PATTERNS = [
30
+ "*.env",
31
+ "*.env.*",
32
+ "*.pem",
33
+ "*.key",
34
+ "*.p12",
35
+ "*.pfx",
36
+ "id_rsa*",
37
+ "id_ed25519*",
38
+ "id_ecdsa*",
39
+ "credentials",
40
+ "credentials.*",
41
+ ".ssh/*",
42
+ "*.npmrc",
43
+ ];
44
+ /** Patterns that stay readable even when sensitive-file protection is on. */
45
+ export const SENSITIVE_ALLOWLIST = ["*.env.example", "*.env.sample", "*.env.template"];
46
+ export function isSensitivePath(target, extraPatterns = []) {
47
+ if (matchesAnyPattern(target, [...SENSITIVE_ALLOWLIST]))
48
+ return false;
49
+ return matchesAnyPattern(target, [...DEFAULT_SENSITIVE_PATTERNS, ...extraPatterns]);
50
+ }
51
+ /** Canonicalize a path to its real path. Throws INVALID_PATH when it does not exist. */
52
+ export async function canonicalize(target) {
53
+ const fs = await import("node:fs");
54
+ try {
55
+ return await fs.promises.realpath(target);
56
+ }
57
+ catch (error) {
58
+ throw new BridgeError("INVALID_PATH", `Path does not exist or cannot be resolved: ${target}`, {
59
+ details: { path: target },
60
+ cause: error,
61
+ });
62
+ }
63
+ }
64
+ /** True when `child` is the same as, or inside, `parent` (both canonical). */
65
+ export function isPathInside(parent, child) {
66
+ const relative = path.relative(parent, child);
67
+ if (relative === "")
68
+ return true;
69
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
70
+ }
71
+ export function assertNoNullBytes(value, label) {
72
+ if (value.includes("\u0000")) {
73
+ throw new BridgeError("INVALID_PATH", `${label} contains a null byte`, {
74
+ details: { [label]: value },
75
+ });
76
+ }
77
+ }
78
+ /**
79
+ * Resolve a task path hint relative to the workspace and make sure it stays
80
+ * inside the workspace. Hints are advisory for exploration, but they must not
81
+ * point outside the delegated workspace.
82
+ */
83
+ export function resolvePathHint(cwd, hint) {
84
+ assertNoNullBytes(hint, "path hint");
85
+ const absolute = path.isAbsolute(hint) ? path.resolve(hint) : path.resolve(cwd, hint);
86
+ if (!isPathInside(cwd, absolute)) {
87
+ throw new BridgeError("INVALID_PATH", `Path hint escapes the workspace: ${hint}`, {
88
+ details: { hint, cwd, resolved: absolute },
89
+ });
90
+ }
91
+ return absolute;
92
+ }
93
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/security/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,KAAK,GAAG;YAAE,MAAM,IAAI,IAAI,CAAC;aAC5B,IAAI,IAAI,KAAK,GAAG;YAAE,MAAM,IAAI,GAAG,CAAC;;YAChC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,IAAI,GAAG,CAAC;IACd,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,OAAe;IAC5D,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,QAAkB;IAClE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,OAAO;IACP,SAAS;IACT,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,SAAS;IACT,aAAa;IACb,WAAW;IACX,aAAa;IACb,eAAe;IACf,QAAQ;IACR,SAAS;CACD,CAAC;AAEX,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,gBAAgB,CAAU,CAAC;AAEhG,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,gBAA0B,EAAE;IAC1E,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,iBAAiB,CAAC,MAAM,EAAE,CAAC,GAAG,0BAA0B,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc;IAC/C,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,WAAW,CAAC,cAAc,EAAE,8CAA8C,MAAM,EAAE,EAAE;YAC5F,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;YACzB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,KAAa;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,KAAa;IAC5D,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,WAAW,CAAC,cAAc,EAAE,GAAG,KAAK,uBAAuB,EAAE;YACrE,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE;SAC5B,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,IAAY;IACvD,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtF,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,WAAW,CAAC,cAAc,EAAE,oCAAoC,IAAI,EAAE,EAAE;YAChF,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;SAC3C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { SecurityConfig } from "../config/config.js";
2
+ import type { PermissionConfig } from "../types/index.js";
3
+ export type AgentProfile = "read" | "review" | "code" | "test";
4
+ export interface ProfileCapabilities {
5
+ readOnly: boolean;
6
+ canEdit: boolean;
7
+ canRunBash: boolean;
8
+ }
9
+ export declare const PROFILE_CAPABILITIES: Record<AgentProfile, ProfileCapabilities>;
10
+ /**
11
+ * Build the OpenCode permission config for an agent profile.
12
+ *
13
+ * `ask` is deliberately never emitted: the bridge is headless and an `ask`
14
+ * rule would leave a delegated agent waiting for a human forever.
15
+ */
16
+ export declare function buildPermissions(profile: AgentProfile, security: SecurityConfig): PermissionConfig;
17
+ export declare function profileForAgent(name: string): AgentProfile | null;
@@ -0,0 +1,86 @@
1
+ import { DEFAULT_SENSITIVE_PATTERNS, SENSITIVE_ALLOWLIST } from "./paths.js";
2
+ export const PROFILE_CAPABILITIES = {
3
+ read: { readOnly: true, canEdit: false, canRunBash: false },
4
+ review: { readOnly: true, canEdit: false, canRunBash: false },
5
+ code: { readOnly: false, canEdit: true, canRunBash: true },
6
+ test: { readOnly: true, canEdit: false, canRunBash: true },
7
+ };
8
+ function sensitiveFileRules(security) {
9
+ if (!security.protectEnvFiles)
10
+ return "allow";
11
+ const rules = { "*": "allow" };
12
+ for (const pattern of DEFAULT_SENSITIVE_PATTERNS)
13
+ rules[pattern] = "deny";
14
+ for (const pattern of SENSITIVE_ALLOWLIST)
15
+ rules[pattern] = "allow";
16
+ for (const pattern of security.extraProtectedPatterns ?? [])
17
+ rules[pattern] = "deny";
18
+ return rules;
19
+ }
20
+ function gitSafetyBashRules(security) {
21
+ const rules = { "*": "allow" };
22
+ if (security.denyGitPush) {
23
+ rules["git push*"] = "deny";
24
+ rules["git -C * push*"] = "deny";
25
+ rules["git -c * push*"] = "deny";
26
+ }
27
+ if (security.denyGitCommit) {
28
+ rules["git commit*"] = "deny";
29
+ rules["git -C * commit*"] = "deny";
30
+ rules["git -c * commit*"] = "deny";
31
+ }
32
+ return rules;
33
+ }
34
+ /**
35
+ * Build the OpenCode permission config for an agent profile.
36
+ *
37
+ * `ask` is deliberately never emitted: the bridge is headless and an `ask`
38
+ * rule would leave a delegated agent waiting for a human forever.
39
+ */
40
+ export function buildPermissions(profile, security) {
41
+ const capabilities = PROFILE_CAPABILITIES[profile];
42
+ const externalDirectory = security.externalDirectory ?? "deny";
43
+ const base = {
44
+ read: sensitiveFileRules(security),
45
+ glob: "allow",
46
+ grep: "allow",
47
+ list: "allow",
48
+ todowrite: "allow",
49
+ webfetch: "allow",
50
+ websearch: "allow",
51
+ lsp: "allow",
52
+ skill: "allow",
53
+ task: "deny",
54
+ question: "deny",
55
+ doom_loop: "deny",
56
+ external_directory: externalDirectory,
57
+ };
58
+ if (capabilities.canEdit) {
59
+ base.edit = sensitiveFileRules(security);
60
+ }
61
+ else {
62
+ base.edit = "deny";
63
+ }
64
+ if (capabilities.canRunBash) {
65
+ base.bash = gitSafetyBashRules(security);
66
+ }
67
+ else {
68
+ base.bash = "deny";
69
+ }
70
+ return base;
71
+ }
72
+ export function profileForAgent(name) {
73
+ switch (name) {
74
+ case "deepseek-researcher":
75
+ return "read";
76
+ case "deepseek-reviewer":
77
+ return "review";
78
+ case "deepseek-coder":
79
+ return "code";
80
+ case "deepseek-tester":
81
+ return "test";
82
+ default:
83
+ return null;
84
+ }
85
+ }
86
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/security/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import type { FileDiff } from "@opencode-ai/sdk";
2
+ /** The resolved, validated workspace a delegated agent operates in. */
3
+ export interface AgentWorkspace {
4
+ /** Canonical (realpath) absolute directory the agent works in. */
5
+ cwd: string;
6
+ /** The path the caller asked for, before canonicalization. */
7
+ requestedPath: string;
8
+ /** Optional soft hints supplied with the task. Never a hard restriction. */
9
+ allowedPaths?: string[];
10
+ /** Paths that are protected by policy (informational). */
11
+ deniedPaths?: string[];
12
+ /** Canonical git repository root, when the workspace is inside one. */
13
+ gitRoot?: string;
14
+ /** Whether the selected agent is restricted to read-only operation. */
15
+ readOnly: boolean;
16
+ /** OpenCode project identifier for the workspace. */
17
+ projectID?: string;
18
+ }
19
+ export type SessionStatus = "pending" | "running" | "completed" | "failed" | "aborted" | "timeout";
20
+ /** A delegated task/agent session tracked by the bridge. */
21
+ export interface AgentSession {
22
+ id: string;
23
+ cwd: string;
24
+ agent: string;
25
+ provider: string | null;
26
+ model: string | null;
27
+ title: string | null;
28
+ status: SessionStatus;
29
+ createdAt: string;
30
+ updatedAt: string;
31
+ /** OpenCode message id of the last assistant response, when known. */
32
+ lastMessageID?: string;
33
+ /** Bridge-level result summary of the last completed run. */
34
+ lastSummary?: string;
35
+ lastError?: {
36
+ code: string;
37
+ message: string;
38
+ };
39
+ durationMs?: number;
40
+ /** Git baseline captured when the session was created (for diff isolation). */
41
+ baseline?: {
42
+ head: string | null;
43
+ stashRef: string | null;
44
+ untracked: string[];
45
+ unborn: boolean;
46
+ };
47
+ }
48
+ export interface Finding {
49
+ severity?: string;
50
+ title: string;
51
+ detail?: string;
52
+ file?: string;
53
+ line?: number;
54
+ }
55
+ export interface DelegateResult {
56
+ status: "completed" | "failed" | "aborted" | "timeout";
57
+ session_id: string;
58
+ agent: string;
59
+ cwd: string;
60
+ model: string | null;
61
+ summary: string;
62
+ findings: Finding[];
63
+ files_changed: number;
64
+ duration_ms: number;
65
+ truncated: boolean;
66
+ error?: {
67
+ code: string;
68
+ message: string;
69
+ details?: Record<string, unknown>;
70
+ };
71
+ }
72
+ export interface AgentInfo {
73
+ name: string;
74
+ description: string;
75
+ mode: "primary" | "subagent" | "all";
76
+ readOnly: boolean;
77
+ canEdit: boolean;
78
+ canRunBash: boolean;
79
+ model: string | null;
80
+ source: "builtin" | "project" | "opencode";
81
+ }
82
+ export type PermissionAction = "allow" | "ask" | "deny";
83
+ export type PermissionRule = PermissionAction | Record<string, PermissionAction>;
84
+ export type PermissionConfig = Record<string, PermissionRule>;
85
+ export interface AgentDefinition {
86
+ name: string;
87
+ description: string;
88
+ mode: "primary" | "subagent" | "all";
89
+ prompt: string;
90
+ permission: PermissionConfig;
91
+ model: string | null;
92
+ temperature?: number;
93
+ /** Permission profile override (only honoured for project/configured agents). */
94
+ profile?: import("../security/policy.js").AgentProfile;
95
+ source: "builtin" | "project";
96
+ }
97
+ export interface RunProgress {
98
+ progress: number;
99
+ total?: number;
100
+ message: string;
101
+ }
102
+ export interface RunResult {
103
+ status: "completed" | "failed" | "timeout";
104
+ summary: string;
105
+ findings: Finding[];
106
+ error?: {
107
+ code: string;
108
+ message: string;
109
+ details?: Record<string, unknown>;
110
+ };
111
+ durationMs: number;
112
+ messageID?: string;
113
+ truncated: boolean;
114
+ }
115
+ export interface DiffFileSummary {
116
+ file: string;
117
+ additions: number;
118
+ deletions: number;
119
+ status: "modified" | "added" | "deleted" | "renamed" | "unknown";
120
+ }
121
+ export interface DiffResult {
122
+ session_id: string;
123
+ cwd: string;
124
+ source: "opencode" | "git";
125
+ files: DiffFileSummary[];
126
+ diff: string;
127
+ truncated: boolean;
128
+ note?: string;
129
+ /** Raw OpenCode file diffs, when available. */
130
+ opencodeDiff?: FileDiff[];
131
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ export declare function pathExists(target: string): Promise<boolean>;
2
+ export declare function ensureDir(dir: string): Promise<void>;
3
+ export declare function ensureDirSync(dir: string, mode?: number): void;
4
+ export declare function readJsonFile<T>(file: string): Promise<T | null>;
5
+ export declare function readJsonFileSync<T>(file: string): T | null;
6
+ /** Write a file atomically (write temp file in the same dir, then rename). */
7
+ export declare function writeFileAtomic(file: string, contents: string, options?: {
8
+ mode?: number;
9
+ }): Promise<void>;
10
+ export declare function writeFileAtomicSync(file: string, contents: string, options?: {
11
+ mode?: number;
12
+ }): void;
@@ -0,0 +1,73 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export async function pathExists(target) {
4
+ try {
5
+ await fs.promises.access(target);
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ export async function ensureDir(dir) {
13
+ await fs.promises.mkdir(dir, { recursive: true });
14
+ }
15
+ export function ensureDirSync(dir, mode) {
16
+ fs.mkdirSync(dir, { recursive: true, ...(mode !== undefined ? { mode } : {}) });
17
+ }
18
+ export async function readJsonFile(file) {
19
+ try {
20
+ const raw = await fs.promises.readFile(file, "utf8");
21
+ return JSON.parse(raw);
22
+ }
23
+ catch (error) {
24
+ if (error.code === "ENOENT")
25
+ return null;
26
+ throw error;
27
+ }
28
+ }
29
+ export function readJsonFileSync(file) {
30
+ try {
31
+ const raw = fs.readFileSync(file, "utf8");
32
+ return JSON.parse(raw);
33
+ }
34
+ catch (error) {
35
+ if (error.code === "ENOENT")
36
+ return null;
37
+ throw error;
38
+ }
39
+ }
40
+ /** Write a file atomically (write temp file in the same dir, then rename). */
41
+ export async function writeFileAtomic(file, contents, options = {}) {
42
+ const dir = path.dirname(file);
43
+ await ensureDir(dir);
44
+ const temp = path.join(dir, `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
45
+ await fs.promises.writeFile(temp, contents, {
46
+ encoding: "utf8",
47
+ ...(options.mode !== undefined ? { mode: options.mode } : {}),
48
+ });
49
+ await fs.promises.rename(temp, file);
50
+ if (options.mode !== undefined) {
51
+ await fs.promises.chmod(file, options.mode).catch(() => undefined);
52
+ }
53
+ }
54
+ export function writeFileAtomicSync(file, contents, options = {}) {
55
+ const dir = path.dirname(file);
56
+ // Session/log state lives in a directory only the owner can access.
57
+ ensureDirSync(dir, options.mode !== undefined ? 0o700 : undefined);
58
+ const temp = path.join(dir, `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
59
+ fs.writeFileSync(temp, contents, {
60
+ encoding: "utf8",
61
+ ...(options.mode !== undefined ? { mode: options.mode } : {}),
62
+ });
63
+ fs.renameSync(temp, file);
64
+ if (options.mode !== undefined) {
65
+ try {
66
+ fs.chmodSync(file, options.mode);
67
+ }
68
+ catch {
69
+ // best effort; the file is still written
70
+ }
71
+ }
72
+ }
73
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"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"}