codepiper 0.1.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.
- package/.env.example +28 -0
- package/CHANGELOG.md +10 -0
- package/LEGAL_NOTICE.md +39 -0
- package/LICENSE +21 -0
- package/README.md +524 -0
- package/package.json +90 -0
- package/packages/cli/package.json +13 -0
- package/packages/cli/src/commands/analytics.ts +157 -0
- package/packages/cli/src/commands/attach.ts +299 -0
- package/packages/cli/src/commands/audit.ts +50 -0
- package/packages/cli/src/commands/auth.ts +261 -0
- package/packages/cli/src/commands/daemon.ts +162 -0
- package/packages/cli/src/commands/doctor.ts +303 -0
- package/packages/cli/src/commands/env-set.ts +162 -0
- package/packages/cli/src/commands/hook-forward.ts +268 -0
- package/packages/cli/src/commands/keys.ts +77 -0
- package/packages/cli/src/commands/kill.ts +19 -0
- package/packages/cli/src/commands/logs.ts +419 -0
- package/packages/cli/src/commands/model.ts +172 -0
- package/packages/cli/src/commands/policy-set.ts +185 -0
- package/packages/cli/src/commands/policy.ts +227 -0
- package/packages/cli/src/commands/providers.ts +114 -0
- package/packages/cli/src/commands/resize.ts +34 -0
- package/packages/cli/src/commands/send.ts +184 -0
- package/packages/cli/src/commands/sessions.ts +202 -0
- package/packages/cli/src/commands/slash.ts +92 -0
- package/packages/cli/src/commands/start.ts +243 -0
- package/packages/cli/src/commands/stop.ts +19 -0
- package/packages/cli/src/commands/tail.ts +137 -0
- package/packages/cli/src/commands/workflow.ts +786 -0
- package/packages/cli/src/commands/workspace.ts +127 -0
- package/packages/cli/src/lib/api.ts +78 -0
- package/packages/cli/src/lib/args.ts +72 -0
- package/packages/cli/src/lib/format.ts +93 -0
- package/packages/cli/src/main.ts +563 -0
- package/packages/core/package.json +7 -0
- package/packages/core/src/config.ts +30 -0
- package/packages/core/src/errors.ts +38 -0
- package/packages/core/src/eventBus.ts +56 -0
- package/packages/core/src/eventBusAdapter.ts +143 -0
- package/packages/core/src/index.ts +10 -0
- package/packages/core/src/sqliteEventBus.ts +336 -0
- package/packages/core/src/types.ts +63 -0
- package/packages/daemon/package.json +11 -0
- package/packages/daemon/src/api/analyticsRoutes.ts +343 -0
- package/packages/daemon/src/api/authRoutes.ts +344 -0
- package/packages/daemon/src/api/bodyLimit.ts +133 -0
- package/packages/daemon/src/api/envSetRoutes.ts +170 -0
- package/packages/daemon/src/api/gitRoutes.ts +409 -0
- package/packages/daemon/src/api/hooks.ts +588 -0
- package/packages/daemon/src/api/inputPolicy.ts +249 -0
- package/packages/daemon/src/api/notificationRoutes.ts +532 -0
- package/packages/daemon/src/api/policyRoutes.ts +234 -0
- package/packages/daemon/src/api/policySetRoutes.ts +445 -0
- package/packages/daemon/src/api/routeUtils.ts +28 -0
- package/packages/daemon/src/api/routes.ts +1004 -0
- package/packages/daemon/src/api/server.ts +1388 -0
- package/packages/daemon/src/api/settingsRoutes.ts +367 -0
- package/packages/daemon/src/api/sqliteErrors.ts +47 -0
- package/packages/daemon/src/api/stt.ts +143 -0
- package/packages/daemon/src/api/terminalRoutes.ts +200 -0
- package/packages/daemon/src/api/validation.ts +287 -0
- package/packages/daemon/src/api/validationRoutes.ts +174 -0
- package/packages/daemon/src/api/workflowRoutes.ts +567 -0
- package/packages/daemon/src/api/workspaceRoutes.ts +151 -0
- package/packages/daemon/src/api/ws.ts +1588 -0
- package/packages/daemon/src/auth/apiRateLimiter.ts +73 -0
- package/packages/daemon/src/auth/authMiddleware.ts +305 -0
- package/packages/daemon/src/auth/authService.ts +496 -0
- package/packages/daemon/src/auth/rateLimiter.ts +137 -0
- package/packages/daemon/src/config/pricing.ts +79 -0
- package/packages/daemon/src/crypto/encryption.ts +196 -0
- package/packages/daemon/src/db/db.ts +2745 -0
- package/packages/daemon/src/db/index.ts +16 -0
- package/packages/daemon/src/db/migrations.ts +182 -0
- package/packages/daemon/src/db/policyDb.ts +349 -0
- package/packages/daemon/src/db/schema.sql +408 -0
- package/packages/daemon/src/db/workflowDb.ts +464 -0
- package/packages/daemon/src/git/gitUtils.ts +544 -0
- package/packages/daemon/src/index.ts +6 -0
- package/packages/daemon/src/main.ts +525 -0
- package/packages/daemon/src/notifications/pushNotifier.ts +369 -0
- package/packages/daemon/src/providers/codexAppServerScaffold.ts +49 -0
- package/packages/daemon/src/providers/registry.ts +111 -0
- package/packages/daemon/src/providers/types.ts +82 -0
- package/packages/daemon/src/sessions/auditLogger.ts +103 -0
- package/packages/daemon/src/sessions/policyEngine.ts +165 -0
- package/packages/daemon/src/sessions/policyMatcher.ts +114 -0
- package/packages/daemon/src/sessions/policyTypes.ts +94 -0
- package/packages/daemon/src/sessions/ptyProcess.ts +141 -0
- package/packages/daemon/src/sessions/sessionManager.ts +1770 -0
- package/packages/daemon/src/sessions/tmuxSession.ts +1073 -0
- package/packages/daemon/src/sessions/transcriptManager.ts +110 -0
- package/packages/daemon/src/sessions/transcriptParser.ts +149 -0
- package/packages/daemon/src/sessions/transcriptTailer.ts +214 -0
- package/packages/daemon/src/tracking/tokenTracker.ts +168 -0
- package/packages/daemon/src/workflows/contextManager.ts +83 -0
- package/packages/daemon/src/workflows/index.ts +31 -0
- package/packages/daemon/src/workflows/resultExtractor.ts +118 -0
- package/packages/daemon/src/workflows/waitConditionPoller.ts +131 -0
- package/packages/daemon/src/workflows/workflowParser.ts +217 -0
- package/packages/daemon/src/workflows/workflowRunner.ts +969 -0
- package/packages/daemon/src/workflows/workflowTypes.ts +188 -0
- package/packages/daemon/src/workflows/workflowValidator.ts +533 -0
- package/packages/providers/claude-code/package.json +11 -0
- package/packages/providers/claude-code/src/index.ts +7 -0
- package/packages/providers/claude-code/src/overlaySettings.ts +198 -0
- package/packages/providers/claude-code/src/provider.ts +311 -0
- package/packages/web/dist/android-chrome-192x192.png +0 -0
- package/packages/web/dist/android-chrome-512x512.png +0 -0
- package/packages/web/dist/apple-touch-icon.png +0 -0
- package/packages/web/dist/assets/AnalyticsPage-BIopKWRf.js +17 -0
- package/packages/web/dist/assets/PoliciesPage-CjdLN3dl.js +11 -0
- package/packages/web/dist/assets/SessionDetailPage-BtSA0V0M.js +179 -0
- package/packages/web/dist/assets/SettingsPage-Dbbz4Ca5.js +37 -0
- package/packages/web/dist/assets/WorkflowsPage-Dv6f3GgU.js +1 -0
- package/packages/web/dist/assets/chart-vendor-DlOHLaCG.js +49 -0
- package/packages/web/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/packages/web/dist/assets/css.worker-BvV5MPou.js +93 -0
- package/packages/web/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
- package/packages/web/dist/assets/html.worker-BLJhxQJQ.js +470 -0
- package/packages/web/dist/assets/index-BbdhRfr2.css +1 -0
- package/packages/web/dist/assets/index-hgphORiw.js +204 -0
- package/packages/web/dist/assets/json.worker-usMZ-FED.js +58 -0
- package/packages/web/dist/assets/monaco-core-B_19GPAS.css +1 -0
- package/packages/web/dist/assets/monaco-core-DQ5Mk8AK.js +1234 -0
- package/packages/web/dist/assets/monaco-react-DfZNWvtW.js +11 -0
- package/packages/web/dist/assets/monacoSetup-DvBj52bT.js +1 -0
- package/packages/web/dist/assets/pencil-Dbczxz59.js +11 -0
- package/packages/web/dist/assets/react-vendor-B5MgMUHH.js +136 -0
- package/packages/web/dist/assets/refresh-cw-B0MGsYPL.js +6 -0
- package/packages/web/dist/assets/tabs-C8LsWiR5.js +1 -0
- package/packages/web/dist/assets/terminal-vendor-Cs8KPbV3.js +9 -0
- package/packages/web/dist/assets/terminal-vendor-LcAfv9l9.css +32 -0
- package/packages/web/dist/assets/trash-2-Btlg0d4l.js +6 -0
- package/packages/web/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
- package/packages/web/dist/favicon.ico +0 -0
- package/packages/web/dist/icon.svg +1 -0
- package/packages/web/dist/index.html +29 -0
- package/packages/web/dist/manifest.json +29 -0
- package/packages/web/dist/og-image.png +0 -0
- package/packages/web/dist/originals/android-chrome-192x192.png +0 -0
- package/packages/web/dist/originals/android-chrome-512x512.png +0 -0
- package/packages/web/dist/originals/apple-touch-icon.png +0 -0
- package/packages/web/dist/originals/favicon.ico +0 -0
- package/packages/web/dist/piper.svg +1 -0
- package/packages/web/dist/sounds/codepiper-soft-chime.wav +0 -0
- package/packages/web/dist/sw.js +257 -0
- package/scripts/postinstall-link-workspaces.mjs +58 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import type { EventBus, SessionHandle } from "@codepiper/core";
|
|
3
|
+
import type { Database } from "../db/db";
|
|
4
|
+
import { getProviderDefinition } from "../providers/registry";
|
|
5
|
+
import type { AuditLogger } from "../sessions/auditLogger";
|
|
6
|
+
import type { PolicyEngine } from "../sessions/policyEngine";
|
|
7
|
+
import type { PermissionRequest } from "../sessions/policyTypes";
|
|
8
|
+
import { isDangerousModeMetadata, type SessionManager } from "../sessions/sessionManager";
|
|
9
|
+
|
|
10
|
+
interface InputPolicyContext {
|
|
11
|
+
sessionManager: Pick<SessionManager, "getSession">;
|
|
12
|
+
db: Pick<Database, "getSession" | "getEffectivePolicies" | "insertEvent">;
|
|
13
|
+
eventBus: Pick<EventBus, "emit">;
|
|
14
|
+
policyEngine: Pick<PolicyEngine, "evaluate" | "getDefaultAction">;
|
|
15
|
+
auditLogger: Pick<AuditLogger, "logDecision">;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type InputPolicyRequest =
|
|
19
|
+
| {
|
|
20
|
+
kind: "text";
|
|
21
|
+
input: string;
|
|
22
|
+
newline: boolean;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
kind: "keys";
|
|
26
|
+
keys: string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface InputPolicyResult {
|
|
30
|
+
allowed: boolean;
|
|
31
|
+
sessionExists: boolean;
|
|
32
|
+
provider?: SessionHandle["provider"];
|
|
33
|
+
status?: number;
|
|
34
|
+
error?: string;
|
|
35
|
+
policyAction?: "allow" | "deny" | "ask";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class InputPolicyBlockedError extends Error {
|
|
39
|
+
readonly code = "policy_blocked";
|
|
40
|
+
readonly status: number;
|
|
41
|
+
readonly policyAction: "allow" | "deny" | "ask";
|
|
42
|
+
readonly provider?: SessionHandle["provider"];
|
|
43
|
+
|
|
44
|
+
constructor(result: InputPolicyResult) {
|
|
45
|
+
const message = result.error || "Input blocked by policy";
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "InputPolicyBlockedError";
|
|
48
|
+
this.status = result.status ?? 403;
|
|
49
|
+
this.policyAction = result.policyAction ?? "deny";
|
|
50
|
+
this.provider = result.provider;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function assertInputPolicyAllowed(result: InputPolicyResult): void {
|
|
55
|
+
if (result.allowed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
throw new InputPolicyBlockedError(result);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function resolveSessionForPolicy(
|
|
62
|
+
ctx: InputPolicyContext,
|
|
63
|
+
sessionId: string
|
|
64
|
+
): SessionHandle | undefined {
|
|
65
|
+
return ctx.sessionManager.getSession(sessionId) ?? ctx.db.getSession(sessionId);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hashString(value: string): string {
|
|
69
|
+
return crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildPolicyRequest(
|
|
73
|
+
session: SessionHandle,
|
|
74
|
+
sessionId: string,
|
|
75
|
+
request: InputPolicyRequest
|
|
76
|
+
): PermissionRequest {
|
|
77
|
+
if (request.kind === "text") {
|
|
78
|
+
return {
|
|
79
|
+
sessionId,
|
|
80
|
+
tool: "terminal_input",
|
|
81
|
+
args: {
|
|
82
|
+
input: request.input,
|
|
83
|
+
newline: request.newline,
|
|
84
|
+
provider: session.provider,
|
|
85
|
+
},
|
|
86
|
+
cwd: session.cwd,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
sessionId,
|
|
92
|
+
tool: "terminal_keys",
|
|
93
|
+
args: {
|
|
94
|
+
keys: request.keys,
|
|
95
|
+
provider: session.provider,
|
|
96
|
+
},
|
|
97
|
+
cwd: session.cwd,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function buildPayload(
|
|
102
|
+
session: SessionHandle,
|
|
103
|
+
request: InputPolicyRequest,
|
|
104
|
+
decision: {
|
|
105
|
+
policyAction: "allow" | "deny" | "ask";
|
|
106
|
+
effectiveAction: "allow" | "deny";
|
|
107
|
+
policyId?: string;
|
|
108
|
+
ruleId?: string;
|
|
109
|
+
reason: string;
|
|
110
|
+
}
|
|
111
|
+
): Record<string, unknown> {
|
|
112
|
+
const basePayload: Record<string, unknown> = {
|
|
113
|
+
provider: session.provider,
|
|
114
|
+
tool: request.kind === "text" ? "terminal_input" : "terminal_keys",
|
|
115
|
+
policyAction: decision.policyAction,
|
|
116
|
+
effectiveAction: decision.effectiveAction,
|
|
117
|
+
policyId: decision.policyId,
|
|
118
|
+
ruleId: decision.ruleId,
|
|
119
|
+
reason: decision.reason,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (request.kind === "text") {
|
|
123
|
+
basePayload.newline = request.newline;
|
|
124
|
+
basePayload.inputBytes = Buffer.byteLength(request.input, "utf8");
|
|
125
|
+
basePayload.inputHash = hashString(request.input);
|
|
126
|
+
return basePayload;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
basePayload.keysCount = request.keys.length;
|
|
130
|
+
basePayload.keysHash = hashString(JSON.stringify(request.keys));
|
|
131
|
+
return basePayload;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildAuditArgs(
|
|
135
|
+
session: SessionHandle,
|
|
136
|
+
request: InputPolicyRequest
|
|
137
|
+
): Record<string, unknown> {
|
|
138
|
+
if (request.kind === "text") {
|
|
139
|
+
return {
|
|
140
|
+
provider: session.provider,
|
|
141
|
+
newline: request.newline,
|
|
142
|
+
inputBytes: Buffer.byteLength(request.input, "utf8"),
|
|
143
|
+
inputHash: hashString(request.input),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
provider: session.provider,
|
|
149
|
+
keysCount: request.keys.length,
|
|
150
|
+
keysHash: hashString(JSON.stringify(request.keys)),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function enforceInputPolicyPreflight(
|
|
155
|
+
ctx: InputPolicyContext,
|
|
156
|
+
sessionId: string,
|
|
157
|
+
request: InputPolicyRequest
|
|
158
|
+
): Promise<InputPolicyResult> {
|
|
159
|
+
const session = resolveSessionForPolicy(ctx, sessionId);
|
|
160
|
+
if (!session) {
|
|
161
|
+
return { allowed: true, sessionExists: false };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const providerDefinition = getProviderDefinition(session.provider);
|
|
165
|
+
if (providerDefinition.capabilities.policyChannel !== "input-preflight") {
|
|
166
|
+
return { allowed: true, sessionExists: true, provider: session.provider };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (isDangerousModeMetadata(session.metadata)) {
|
|
170
|
+
return { allowed: true, sessionExists: true, provider: session.provider };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const policyRequest = buildPolicyRequest(session, sessionId, request);
|
|
174
|
+
const policies = ctx.db.getEffectivePolicies(sessionId);
|
|
175
|
+
const policyDecision = await ctx.policyEngine.evaluate(policyRequest, policies);
|
|
176
|
+
|
|
177
|
+
let effectiveAction: "allow" | "deny";
|
|
178
|
+
let responseStatus = 403;
|
|
179
|
+
let responseMessage = policyDecision.reason || "Input blocked by policy";
|
|
180
|
+
|
|
181
|
+
if (policyDecision.action === "allow") {
|
|
182
|
+
effectiveAction = "allow";
|
|
183
|
+
} else if (
|
|
184
|
+
policyDecision.action === "ask" &&
|
|
185
|
+
!policyDecision.policyId &&
|
|
186
|
+
ctx.policyEngine.getDefaultAction() === "ask"
|
|
187
|
+
) {
|
|
188
|
+
effectiveAction = "allow";
|
|
189
|
+
responseMessage = "No matching policy rule found (default ask fallback for no-hook provider)";
|
|
190
|
+
} else {
|
|
191
|
+
effectiveAction = "deny";
|
|
192
|
+
if (policyDecision.action === "ask") {
|
|
193
|
+
responseStatus = 409;
|
|
194
|
+
responseMessage =
|
|
195
|
+
policyDecision.reason ||
|
|
196
|
+
"Policy returned ask, but this provider does not support interactive policy approvals";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const payload = buildPayload(session, request, {
|
|
201
|
+
policyAction: policyDecision.action,
|
|
202
|
+
effectiveAction,
|
|
203
|
+
policyId: policyDecision.policyId,
|
|
204
|
+
ruleId: policyDecision.ruleId,
|
|
205
|
+
reason: responseMessage,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const eventId = ctx.db.insertEvent({
|
|
209
|
+
sessionId,
|
|
210
|
+
source: "pty",
|
|
211
|
+
type: "InputPolicyDecision",
|
|
212
|
+
payload,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
ctx.eventBus.emit("session:event", {
|
|
216
|
+
id: eventId,
|
|
217
|
+
sessionId,
|
|
218
|
+
type: "InputPolicyDecision",
|
|
219
|
+
source: "pty",
|
|
220
|
+
timestamp: new Date(),
|
|
221
|
+
payload,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
ctx.auditLogger.logDecision({
|
|
225
|
+
sessionId,
|
|
226
|
+
eventId,
|
|
227
|
+
toolName: policyRequest.tool,
|
|
228
|
+
args: buildAuditArgs(session, request),
|
|
229
|
+
decision: policyDecision,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (effectiveAction === "deny") {
|
|
233
|
+
return {
|
|
234
|
+
allowed: false,
|
|
235
|
+
sessionExists: true,
|
|
236
|
+
provider: session.provider,
|
|
237
|
+
status: responseStatus,
|
|
238
|
+
error: responseMessage,
|
|
239
|
+
policyAction: policyDecision.action,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
allowed: true,
|
|
245
|
+
sessionExists: true,
|
|
246
|
+
provider: session.provider,
|
|
247
|
+
policyAction: policyDecision.action,
|
|
248
|
+
};
|
|
249
|
+
}
|