patchwork-os 1.2.0-beta.2.canary.781 → 1.2.0-beta.2.canary.783
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activityLog.js +6 -1
- package/dist/activityLog.js.map +1 -1
- package/dist/approvalQueue.js +15 -0
- package/dist/approvalQueue.js.map +1 -1
- package/dist/bridge.js +32 -1
- package/dist/bridge.js.map +1 -1
- package/dist/claudeDriver.d.ts +10 -0
- package/dist/claudeDriver.js +27 -4
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +5 -0
- package/dist/claudeOrchestrator.js +94 -6
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/patchworkInit.js +10 -0
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/policyExplain.d.ts +50 -0
- package/dist/commands/policyExplain.js +221 -0
- package/dist/commands/policyExplain.js.map +1 -0
- package/dist/commands/profile.d.ts +17 -0
- package/dist/commands/profile.js +85 -0
- package/dist/commands/profile.js.map +1 -0
- package/dist/commands/recipe.d.ts +8 -0
- package/dist/commands/recipe.js +64 -0
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.js +19 -0
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/connectors/baseConnector.js +5 -0
- package/dist/connectors/baseConnector.js.map +1 -1
- package/dist/connectors/secrets.js +10 -2
- package/dist/connectors/secrets.js.map +1 -1
- package/dist/connectors/tokenStorage.d.ts +9 -0
- package/dist/connectors/tokenStorage.js +27 -0
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/decisionTraceLog.js +4 -3
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/envSanitizer.d.ts +39 -0
- package/dist/drivers/claude/envSanitizer.js +114 -0
- package/dist/drivers/claude/envSanitizer.js.map +1 -1
- package/dist/drivers/claude/subprocess.d.ts +12 -0
- package/dist/drivers/claude/subprocess.js +114 -25
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/claude/subprocessSettings.d.ts +4 -1
- package/dist/drivers/claude/subprocessSettings.js +24 -7
- package/dist/drivers/claude/subprocessSettings.js.map +1 -1
- package/dist/drivers/codex/subprocess.d.ts +19 -0
- package/dist/drivers/codex/subprocess.js +40 -8
- package/dist/drivers/codex/subprocess.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +24 -0
- package/dist/drivers/gemini/index.js +76 -13
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/types.d.ts +14 -0
- package/dist/drivers/types.js.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/governance/doctorReport.d.ts +45 -0
- package/dist/governance/doctorReport.js +251 -0
- package/dist/governance/doctorReport.js.map +1 -0
- package/dist/governance/effectivePolicy.d.ts +134 -0
- package/dist/governance/effectivePolicy.js +322 -0
- package/dist/governance/effectivePolicy.js.map +1 -0
- package/dist/governance/killSwitchPolicy.d.ts +30 -0
- package/dist/governance/killSwitchPolicy.js +52 -0
- package/dist/governance/killSwitchPolicy.js.map +1 -0
- package/dist/governance/pluginPolicy.d.ts +111 -0
- package/dist/governance/pluginPolicy.js +247 -0
- package/dist/governance/pluginPolicy.js.map +1 -0
- package/dist/governance/profile.d.ts +153 -0
- package/dist/governance/profile.js +181 -0
- package/dist/governance/profile.js.map +1 -0
- package/dist/governance/secretValues.d.ts +89 -0
- package/dist/governance/secretValues.js +286 -0
- package/dist/governance/secretValues.js.map +1 -0
- package/dist/governance/toolFacts.d.ts +11 -0
- package/dist/governance/toolFacts.js +46 -0
- package/dist/governance/toolFacts.js.map +1 -0
- package/dist/governance/untrustedContent.d.ts +43 -0
- package/dist/governance/untrustedContent.js +81 -0
- package/dist/governance/untrustedContent.js.map +1 -0
- package/dist/index.js +73 -5
- package/dist/index.js.map +1 -1
- package/dist/logger.js +7 -6
- package/dist/logger.js.map +1 -1
- package/dist/patchworkConfig.d.ts +22 -0
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.d.ts +10 -2
- package/dist/pluginLoader.js +12 -8
- package/dist/pluginLoader.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +0 -6
- package/dist/recipeOrchestration.js +80 -23
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.js +52 -0
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +43 -3
- package/dist/recipes/agentExecutor.js +55 -7
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/approvalRequest.d.ts +8 -0
- package/dist/recipes/approvalRequest.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +18 -5
- package/dist/recipes/chainedRunner.js +107 -12
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/haltCategory.d.ts +4 -0
- package/dist/recipes/haltCategory.js +4 -0
- package/dist/recipes/haltCategory.js.map +1 -1
- package/dist/recipes/schema.d.ts +5 -1
- package/dist/recipes/schemaGenerator.js +12 -1
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/stepObservation.d.ts +15 -0
- package/dist/recipes/stepObservation.js +27 -22
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/templateEngine.d.ts +11 -1
- package/dist/recipes/templateEngine.js +12 -2
- package/dist/recipes/templateEngine.js.map +1 -1
- package/dist/recipes/toolRegistry.d.ts +3 -1
- package/dist/recipes/toolRegistry.js +9 -3
- package/dist/recipes/toolRegistry.js.map +1 -1
- package/dist/recipes/tools/http.d.ts +5 -2
- package/dist/recipes/tools/http.js +29 -10
- package/dist/recipes/tools/http.js.map +1 -1
- package/dist/recipes/validation.d.ts +10 -1
- package/dist/recipes/validation.js +51 -1
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +46 -5
- package/dist/recipes/yamlRunner.js +173 -21
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/schemas/recipe.v1.json +60 -3
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -1
- package/dist/ssrfGuard.d.ts +89 -5
- package/dist/ssrfGuard.js +250 -5
- package/dist/ssrfGuard.js.map +1 -1
- package/dist/tools/httpClient.js +23 -165
- package/dist/tools/httpClient.js.map +1 -1
- package/dist/transport.js +41 -0
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `patchwork policy explain <recipe> [tool]` — what can this recipe / worker /
|
|
3
|
+
* tool actually do right now, and why?
|
|
4
|
+
*
|
|
5
|
+
* Uses the SAME functions the runtime enforces with:
|
|
6
|
+
* - `resolveProfile` (config → profile) profile.ts
|
|
7
|
+
* - `toolFactsFor` (registry → tool facts) toolFacts.ts
|
|
8
|
+
* - `resolveAgentContainment` + `stepSandboxRequest` (agent steps)
|
|
9
|
+
* - `decideWorkerAction` + `resolveGateOutcome` (worker gate)
|
|
10
|
+
* - `parseDataPolicy` + `resolveDestination` + `decideBoundary` (privacy)
|
|
11
|
+
* - `readKillSwitch`
|
|
12
|
+
* - `computeEffectivePolicy` (composition) effectivePolicy.ts
|
|
13
|
+
*
|
|
14
|
+
* It never re-implements a rule. `effectivePolicy.test.ts` pins the runner
|
|
15
|
+
* to the same calculation, and this command is the human-readable face of it.
|
|
16
|
+
*
|
|
17
|
+
* Reads config and ledgers; writes nothing.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import { parse as parseYaml } from "yaml";
|
|
21
|
+
import { computeEffectivePolicy, formatEffectivePolicy, } from "../governance/effectivePolicy.js";
|
|
22
|
+
import { readKillSwitch } from "../governance/killSwitchPolicy.js";
|
|
23
|
+
import { explainPluginPolicy, pluginSpecsOf, policyInputFromConfig, } from "../governance/pluginPolicy.js";
|
|
24
|
+
import { resolveAgentContainment, resolveProfile, } from "../governance/profile.js";
|
|
25
|
+
import { toolFactsFor } from "../governance/toolFacts.js";
|
|
26
|
+
import { loadConfig } from "../patchworkConfig.js";
|
|
27
|
+
import { patchworkPath } from "../patchworkHome.js";
|
|
28
|
+
import { stepSandboxRequest } from "../recipes/agentExecutor.js";
|
|
29
|
+
import { findYamlRecipePath } from "../recipesHttp.js";
|
|
30
|
+
import "../recipes/tools/index.js";
|
|
31
|
+
function resolveRecipeFile(ref, recipesDir) {
|
|
32
|
+
if (existsSync(ref) && /\.(ya?ml|json)$/.test(ref))
|
|
33
|
+
return ref;
|
|
34
|
+
return findYamlRecipePath(recipesDir, ref);
|
|
35
|
+
}
|
|
36
|
+
export async function explainRecipePolicy(ref, opts = {}) {
|
|
37
|
+
const recipesDir = opts.recipesDir ?? patchworkPath("recipes");
|
|
38
|
+
const file = resolveRecipeFile(ref, recipesDir);
|
|
39
|
+
if (!file) {
|
|
40
|
+
throw new Error(`recipe not found: ${ref} (looked in ${recipesDir})`);
|
|
41
|
+
}
|
|
42
|
+
const raw = parseYaml(readFileSync(file, "utf-8"));
|
|
43
|
+
const name = typeof raw.name === "string" ? raw.name : ref;
|
|
44
|
+
const trigger = (raw.trigger?.type ??
|
|
45
|
+
"manual");
|
|
46
|
+
let cfg = opts.config;
|
|
47
|
+
if (!cfg) {
|
|
48
|
+
try {
|
|
49
|
+
cfg = loadConfig();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
cfg = undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const profile = resolveProfile(cfg ? { profile: cfg.profile, approvalGate: cfg.approvalGate } : undefined);
|
|
56
|
+
// Worker facts — the same loader + decision the gate uses. Only when the
|
|
57
|
+
// profile (or the live flag) makes the worker gate active; otherwise the
|
|
58
|
+
// manifest is inert and the report says so by omitting the worker.
|
|
59
|
+
let workerId;
|
|
60
|
+
let workerCtx;
|
|
61
|
+
{
|
|
62
|
+
const { resolveWorkerIdForRecipe } = await import("../recipeOrchestration.js");
|
|
63
|
+
workerId = await resolveWorkerIdForRecipe(name, opts.workersDir);
|
|
64
|
+
const { isEnabled, FLAG_WORKER_AUTONOMY } = await import("../featureFlags.js");
|
|
65
|
+
const gateActive = profile.workerAuthority || isEnabled(FLAG_WORKER_AUTONOMY);
|
|
66
|
+
if (workerId && gateActive) {
|
|
67
|
+
const { loadWorkerTrustForRecipe } = await import("../workers/runWorkerShadow.js");
|
|
68
|
+
const trust = loadWorkerTrustForRecipe(name, {
|
|
69
|
+
...(opts.patchworkDir && { patchworkDir: opts.patchworkDir }),
|
|
70
|
+
...(opts.workersDir && { workersDir: opts.workersDir }),
|
|
71
|
+
});
|
|
72
|
+
if (trust) {
|
|
73
|
+
const { parseForbidRules } = await import("../workers/forbidPolicy.js");
|
|
74
|
+
workerCtx = {
|
|
75
|
+
worker: trust.worker,
|
|
76
|
+
store: trust.store,
|
|
77
|
+
forbidRules: parseForbidRules(trust.worker.forbids).rules,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Privacy registry — parsed the way executeAgent parses it.
|
|
83
|
+
const { parseRegistry, resolveDestination } = await import("../privacy/destinationRegistry.js");
|
|
84
|
+
const { decideBoundary, parseDataPolicy, DEFAULT_CLASSIFICATION } = await import("../privacy/dataPolicy.js");
|
|
85
|
+
const registry = parseRegistry(cfg?.privacy);
|
|
86
|
+
const { decideWorkerAction, resolveGateOutcome } = await import("../workers/workerGate.js");
|
|
87
|
+
const plugins = explainPluginPolicy(pluginSpecsOf(raw), policyInputFromConfig(profile, cfg)).map((v) => ({ spec: v.spec, allowed: v.allowed, reason: v.reason }));
|
|
88
|
+
const steps = [];
|
|
89
|
+
const rawSteps = Array.isArray(raw.steps)
|
|
90
|
+
? raw.steps
|
|
91
|
+
: [];
|
|
92
|
+
let n = 0;
|
|
93
|
+
for (const step of rawSteps) {
|
|
94
|
+
const agent = step.agent;
|
|
95
|
+
const toolId = agent
|
|
96
|
+
? "agent"
|
|
97
|
+
: typeof step.tool === "string"
|
|
98
|
+
? step.tool
|
|
99
|
+
: undefined;
|
|
100
|
+
const stepId = (typeof step.id === "string" && step.id) ||
|
|
101
|
+
(typeof step.into === "string" && step.into) ||
|
|
102
|
+
(agent && typeof agent.into === "string" && agent.into) ||
|
|
103
|
+
`step_${n}`;
|
|
104
|
+
n++;
|
|
105
|
+
if (!toolId)
|
|
106
|
+
continue; // compound / nested steps are governed by their children
|
|
107
|
+
if (opts.tool && opts.tool !== toolId)
|
|
108
|
+
continue;
|
|
109
|
+
const containment = agent
|
|
110
|
+
? resolveAgentContainment(profile, stepSandboxRequest({
|
|
111
|
+
...(agent.sandbox !== undefined && {
|
|
112
|
+
sandbox: agent.sandbox,
|
|
113
|
+
}),
|
|
114
|
+
...(Array.isArray(agent.tools) && {
|
|
115
|
+
allowedTools: agent.tools,
|
|
116
|
+
}),
|
|
117
|
+
...(Array.isArray(agent.disallowedTools) && {
|
|
118
|
+
disallowedTools: agent.disallowedTools,
|
|
119
|
+
}),
|
|
120
|
+
...(typeof agent.mcpAccess === "boolean" && {
|
|
121
|
+
mcpAccess: agent.mcpAccess,
|
|
122
|
+
}),
|
|
123
|
+
}))
|
|
124
|
+
: undefined;
|
|
125
|
+
const tool = toolFactsFor(toolId, containment ? { containment } : undefined);
|
|
126
|
+
let worker;
|
|
127
|
+
if (workerCtx && workerId) {
|
|
128
|
+
const decision = decideWorkerAction(workerCtx.worker, toolId, undefined, workerCtx.store, workerCtx.forbidRules.length > 0
|
|
129
|
+
? { forbidRules: workerCtx.forbidRules }
|
|
130
|
+
: undefined);
|
|
131
|
+
const resolved = resolveGateOutcome(decision, undefined);
|
|
132
|
+
worker = {
|
|
133
|
+
id: workerId,
|
|
134
|
+
action: decision.action,
|
|
135
|
+
...(decision.ruleId !== undefined && {
|
|
136
|
+
ruleId: String(decision.ruleId),
|
|
137
|
+
}),
|
|
138
|
+
...(resolved.standingPermissionId !== undefined && {
|
|
139
|
+
standingPermissionId: resolved.standingPermissionId,
|
|
140
|
+
}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
let privacy;
|
|
144
|
+
if (agent && registry.destinations.length > 0) {
|
|
145
|
+
const declared = agent.data_policy;
|
|
146
|
+
const policy = declared === undefined
|
|
147
|
+
? { classification: DEFAULT_CLASSIFICATION }
|
|
148
|
+
: parseDataPolicy(declared);
|
|
149
|
+
const driver = typeof agent.driver === "string" ? agent.driver : cfg?.driver;
|
|
150
|
+
const dest = resolveDestination(registry, driver, policy?.classification ?? DEFAULT_CLASSIFICATION, cfg?.localEndpoint ? { endpoint: cfg.localEndpoint } : {});
|
|
151
|
+
if (dest) {
|
|
152
|
+
const outcome = policy
|
|
153
|
+
? decideBoundary(policy, dest.destination, {
|
|
154
|
+
localDestinationAccepts: dest.localDestinationAccepts,
|
|
155
|
+
})
|
|
156
|
+
: {
|
|
157
|
+
decision: "DENY",
|
|
158
|
+
reason: "unrecognised classification",
|
|
159
|
+
};
|
|
160
|
+
privacy = {
|
|
161
|
+
classification: policy?.classification ?? "invalid",
|
|
162
|
+
destination: dest.destination.id,
|
|
163
|
+
decision: outcome.decision,
|
|
164
|
+
...(outcome.reason !== undefined && { reason: outcome.reason }),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const input = {
|
|
169
|
+
profile,
|
|
170
|
+
recipe: {
|
|
171
|
+
name,
|
|
172
|
+
...(typeof raw.requireApproval === "boolean" && {
|
|
173
|
+
requireApproval: raw.requireApproval,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
trigger,
|
|
177
|
+
tool,
|
|
178
|
+
...(worker && { worker }),
|
|
179
|
+
...(privacy && { privacy }),
|
|
180
|
+
killSwitch: readKillSwitch(profile),
|
|
181
|
+
};
|
|
182
|
+
steps.push({
|
|
183
|
+
stepId,
|
|
184
|
+
toolId,
|
|
185
|
+
input,
|
|
186
|
+
result: computeEffectivePolicy(input),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
recipe: name,
|
|
191
|
+
file,
|
|
192
|
+
trigger,
|
|
193
|
+
profileMode: profile.mode,
|
|
194
|
+
...(workerId && { workerId }),
|
|
195
|
+
plugins,
|
|
196
|
+
steps,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export function formatExplainReport(r) {
|
|
200
|
+
const out = [];
|
|
201
|
+
if (r.plugins.length > 0) {
|
|
202
|
+
out.push(`PLUGINS (servers:)`);
|
|
203
|
+
for (const p of r.plugins) {
|
|
204
|
+
out.push(` ${p.allowed ? "ALLOWED" : "REFUSED"} ${p.spec} — ${p.reason}`);
|
|
205
|
+
}
|
|
206
|
+
out.push("");
|
|
207
|
+
}
|
|
208
|
+
if (r.steps.length === 0) {
|
|
209
|
+
out.push(`RECIPE: ${r.recipe}`);
|
|
210
|
+
out.push(`PROFILE: ${r.profileMode.toUpperCase()}`);
|
|
211
|
+
out.push("No tool or agent steps to explain.");
|
|
212
|
+
return out.join("\n");
|
|
213
|
+
}
|
|
214
|
+
for (const s of r.steps) {
|
|
215
|
+
out.push(`STEP: ${s.stepId}`);
|
|
216
|
+
out.push(formatEffectivePolicy(s.input, s.result));
|
|
217
|
+
out.push("");
|
|
218
|
+
}
|
|
219
|
+
return out.join("\n").trimEnd();
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=policyExplain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyExplain.js","sourceRoot":"","sources":["../../src/commands/policyExplain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `patchwork profile [show|governed|compat]` — read or set the governance
|
|
3
|
+
* profile in config.json.
|
|
4
|
+
*
|
|
5
|
+
* Setting writes ONE key. Everything the profile implies is resolved at
|
|
6
|
+
* bridge startup from that key (src/governance/profile.ts), so there is no
|
|
7
|
+
* second place the posture can be recorded and drift. A running bridge must
|
|
8
|
+
* be restarted for the change to take effect; the command says so.
|
|
9
|
+
*/
|
|
10
|
+
export interface ProfileCommandResult {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
text: string;
|
|
13
|
+
exitCode: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function runProfileCommand(args: string[], opts?: {
|
|
16
|
+
configPath?: string;
|
|
17
|
+
}): ProfileCommandResult;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `patchwork profile [show|governed|compat]` — read or set the governance
|
|
3
|
+
* profile in config.json.
|
|
4
|
+
*
|
|
5
|
+
* Setting writes ONE key. Everything the profile implies is resolved at
|
|
6
|
+
* bridge startup from that key (src/governance/profile.ts), so there is no
|
|
7
|
+
* second place the posture can be recorded and drift. A running bridge must
|
|
8
|
+
* be restarted for the change to take effect; the command says so.
|
|
9
|
+
*/
|
|
10
|
+
import { formatGovernanceReport, governanceReport, } from "../governance/doctorReport.js";
|
|
11
|
+
import { PROFILE_MODES, resolveProfile, } from "../governance/profile.js";
|
|
12
|
+
import { loadConfig, saveConfig } from "../patchworkConfig.js";
|
|
13
|
+
export function runProfileCommand(args, opts = {}) {
|
|
14
|
+
const verb = args[0] ?? "show";
|
|
15
|
+
if (verb === "--help" || verb === "-h") {
|
|
16
|
+
return {
|
|
17
|
+
ok: true,
|
|
18
|
+
exitCode: 0,
|
|
19
|
+
text: "Usage: patchwork profile [show|governed|compat] [--json]\n\n" +
|
|
20
|
+
" show print the resolved governance posture (same lines as `patchwork doctor`)\n" +
|
|
21
|
+
" governed set profile: governed in config.json — approval gate ≥ high, automated\n" +
|
|
22
|
+
" triggers gated, worker authority + policy matrix enforced, agent steps\n" +
|
|
23
|
+
" contained, recipe plugins allowlisted, kill switch fails closed\n" +
|
|
24
|
+
" compat set profile: compat — byte-identical to pre-profile behaviour\n\n" +
|
|
25
|
+
"A running bridge must be restarted (launchctl kickstart …) to pick up a change.\n",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (verb === "show") {
|
|
29
|
+
const json = args.includes("--json");
|
|
30
|
+
let cfg;
|
|
31
|
+
try {
|
|
32
|
+
cfg = loadConfig(opts.configPath);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
cfg = undefined;
|
|
36
|
+
}
|
|
37
|
+
const report = governanceReport(cfg ? { config: cfg } : {});
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
exitCode: report.governed ? 0 : 1,
|
|
41
|
+
text: json
|
|
42
|
+
? JSON.stringify({
|
|
43
|
+
profile: report.profile.mode,
|
|
44
|
+
governed: report.governed,
|
|
45
|
+
lines: report.lines,
|
|
46
|
+
reasons: report.reasons,
|
|
47
|
+
}, null, 2)
|
|
48
|
+
: formatGovernanceReport(report),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (!PROFILE_MODES.includes(verb)) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
exitCode: 2,
|
|
55
|
+
text: `[profile] unknown verb "${verb}"; expected show | governed | compat\n`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const mode = verb;
|
|
59
|
+
let cfg;
|
|
60
|
+
try {
|
|
61
|
+
cfg = loadConfig(opts.configPath);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
exitCode: 1,
|
|
67
|
+
text: `[profile] cannot read config.json: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const before = cfg.profile;
|
|
71
|
+
saveConfig({ ...cfg, profile: mode }, opts.configPath);
|
|
72
|
+
const resolved = resolveProfile({
|
|
73
|
+
profile: mode,
|
|
74
|
+
approvalGate: cfg.approvalGate,
|
|
75
|
+
});
|
|
76
|
+
const lines = [
|
|
77
|
+
`[profile] ${before === undefined ? "(unset)" : before} → ${mode}`,
|
|
78
|
+
mode === "governed"
|
|
79
|
+
? `[profile] governed: approval gate ${resolved.approvalGate}, automated triggers gated, worker authority + policy matrix enforced, agent steps contained, plugins allowlisted, kill switch fails closed`
|
|
80
|
+
: "[profile] compat: behaviour is byte-identical to a pre-profile install",
|
|
81
|
+
"[profile] restart the bridge for this to take effect; `patchwork doctor` shows the live posture",
|
|
82
|
+
];
|
|
83
|
+
return { ok: true, exitCode: 0, text: `${lines.join("\n")}\n` };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/commands/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -141,6 +141,14 @@ export interface WatchedRecipeRunResult {
|
|
|
141
141
|
run?: RunRecipeResult;
|
|
142
142
|
summary?: RecipeExecutionSummary;
|
|
143
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolve the governance deps for a bridge-less (`--local`) run. Exported for
|
|
146
|
+
* tests. Returns `{}` under compat or when the caller already supplied them.
|
|
147
|
+
*/
|
|
148
|
+
export declare function resolveLocalGovernance(provided: Partial<RunnerDeps> | undefined, io?: {
|
|
149
|
+
isTTY?: boolean;
|
|
150
|
+
ask?: (question: string) => Promise<string>;
|
|
151
|
+
}): Promise<Partial<RunnerDeps>>;
|
|
144
152
|
export declare function runRecipe(recipeRef: string, options?: RunRecipeOptions): Promise<RunRecipeResult>;
|
|
145
153
|
export declare function summarizeRecipeExecution(result: RunRecipeResult["result"]): RecipeExecutionSummary;
|
|
146
154
|
/**
|
package/dist/commands/recipe.js
CHANGED
|
@@ -818,6 +818,62 @@ function extractSchemaHeader(content) {
|
|
|
818
818
|
}
|
|
819
819
|
return {};
|
|
820
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Resolve the governance deps for a bridge-less (`--local`) run. Exported for
|
|
823
|
+
* tests. Returns `{}` under compat or when the caller already supplied them.
|
|
824
|
+
*/
|
|
825
|
+
export async function resolveLocalGovernance(provided, io = {}) {
|
|
826
|
+
if (provided?.governance !== undefined)
|
|
827
|
+
return {};
|
|
828
|
+
const { loadConfig } = await import("../patchworkConfig.js");
|
|
829
|
+
const { resolveProfile } = await import("../governance/profile.js");
|
|
830
|
+
let cfg = {};
|
|
831
|
+
try {
|
|
832
|
+
cfg = loadConfig();
|
|
833
|
+
}
|
|
834
|
+
catch {
|
|
835
|
+
cfg = {};
|
|
836
|
+
}
|
|
837
|
+
const profile = resolveProfile(cfg);
|
|
838
|
+
// Publish it process-wide too: executeAgent (containment) and
|
|
839
|
+
// loadRecipeServers (plugin allowlist) read `activeProfile()`, not the
|
|
840
|
+
// runner deps. A local run that set only `deps.governance` would gate
|
|
841
|
+
// approvals as governed while containing agents as compat.
|
|
842
|
+
const { setActiveProfile } = await import("../governance/profile.js");
|
|
843
|
+
setActiveProfile(profile);
|
|
844
|
+
if (profile.mode !== "governed")
|
|
845
|
+
return {};
|
|
846
|
+
if (provided?.requireApprovalFn !== undefined)
|
|
847
|
+
return { governance: profile };
|
|
848
|
+
const isTTY = io.isTTY ?? (process.stdin.isTTY === true && process.stdout.isTTY === true);
|
|
849
|
+
const ask = io.ask ??
|
|
850
|
+
(async (question) => {
|
|
851
|
+
const readline = await import("node:readline/promises");
|
|
852
|
+
const rl = readline.createInterface({
|
|
853
|
+
input: process.stdin,
|
|
854
|
+
output: process.stdout,
|
|
855
|
+
});
|
|
856
|
+
try {
|
|
857
|
+
return await rl.question(question);
|
|
858
|
+
}
|
|
859
|
+
finally {
|
|
860
|
+
rl.close();
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
const requireApprovalFn = async (input) => {
|
|
864
|
+
if (input.effective === "ALLOW")
|
|
865
|
+
return true;
|
|
866
|
+
if (!isTTY) {
|
|
867
|
+
process.stderr.write(`[recipe run] step "${input.summary ?? input.toolId}" needs human approval under the governed profile and there is no terminal to ask on — refused. Run from a terminal, or run through the bridge so the approval reaches the dashboard.\n`);
|
|
868
|
+
return { approved: false, refusal: "rejected" };
|
|
869
|
+
}
|
|
870
|
+
const answer = await ask(`[recipe run] approve ${input.summary ?? input.toolId} (${input.tier} tier)? [y/N] `);
|
|
871
|
+
return /^y(es)?$/i.test(answer.trim())
|
|
872
|
+
? true
|
|
873
|
+
: { approved: false, refusal: "rejected" };
|
|
874
|
+
};
|
|
875
|
+
return { governance: profile, requireApprovalFn };
|
|
876
|
+
}
|
|
821
877
|
export async function runRecipe(recipeRef, options = {}) {
|
|
822
878
|
const recipePath = resolveRecipePath(recipeRef);
|
|
823
879
|
const recipe = loadYamlRecipe(recipePath);
|
|
@@ -831,11 +887,19 @@ export async function runRecipe(recipeRef, options = {}) {
|
|
|
831
887
|
const recipeToRun = selection
|
|
832
888
|
? { ...recipe, steps: [selection.step] }
|
|
833
889
|
: recipe;
|
|
890
|
+
// Phase 0: a local run is governed by the SAME profile as a bridge run.
|
|
891
|
+
// Under governed, a step the effective policy says needs a human is put to
|
|
892
|
+
// the operator on the terminal; with no TTY it is refused (fail closed)
|
|
893
|
+
// rather than executed. Under compat nothing is injected — byte-identical
|
|
894
|
+
// to before. Tests that pass their own `deps.requireApprovalFn` /
|
|
895
|
+
// `deps.governance` keep them.
|
|
896
|
+
const localGovernance = await resolveLocalGovernance(options.deps);
|
|
834
897
|
const runnerDeps = {
|
|
835
898
|
...options.deps,
|
|
836
899
|
workdir: options.workdir ?? options.deps?.workdir ?? process.cwd(),
|
|
837
900
|
...(options.manualRunId && { manualRunId: options.manualRunId }),
|
|
838
901
|
...(options.ledgerDir && { ledgerDir: options.ledgerDir }),
|
|
902
|
+
...localGovernance,
|
|
839
903
|
};
|
|
840
904
|
if (options.dryRun) {
|
|
841
905
|
throw new Error("runRecipeDryPlan must be used for dry-run execution");
|