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.
Files changed (149) hide show
  1. package/.env.example +28 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LEGAL_NOTICE.md +39 -0
  4. package/LICENSE +21 -0
  5. package/README.md +524 -0
  6. package/package.json +90 -0
  7. package/packages/cli/package.json +13 -0
  8. package/packages/cli/src/commands/analytics.ts +157 -0
  9. package/packages/cli/src/commands/attach.ts +299 -0
  10. package/packages/cli/src/commands/audit.ts +50 -0
  11. package/packages/cli/src/commands/auth.ts +261 -0
  12. package/packages/cli/src/commands/daemon.ts +162 -0
  13. package/packages/cli/src/commands/doctor.ts +303 -0
  14. package/packages/cli/src/commands/env-set.ts +162 -0
  15. package/packages/cli/src/commands/hook-forward.ts +268 -0
  16. package/packages/cli/src/commands/keys.ts +77 -0
  17. package/packages/cli/src/commands/kill.ts +19 -0
  18. package/packages/cli/src/commands/logs.ts +419 -0
  19. package/packages/cli/src/commands/model.ts +172 -0
  20. package/packages/cli/src/commands/policy-set.ts +185 -0
  21. package/packages/cli/src/commands/policy.ts +227 -0
  22. package/packages/cli/src/commands/providers.ts +114 -0
  23. package/packages/cli/src/commands/resize.ts +34 -0
  24. package/packages/cli/src/commands/send.ts +184 -0
  25. package/packages/cli/src/commands/sessions.ts +202 -0
  26. package/packages/cli/src/commands/slash.ts +92 -0
  27. package/packages/cli/src/commands/start.ts +243 -0
  28. package/packages/cli/src/commands/stop.ts +19 -0
  29. package/packages/cli/src/commands/tail.ts +137 -0
  30. package/packages/cli/src/commands/workflow.ts +786 -0
  31. package/packages/cli/src/commands/workspace.ts +127 -0
  32. package/packages/cli/src/lib/api.ts +78 -0
  33. package/packages/cli/src/lib/args.ts +72 -0
  34. package/packages/cli/src/lib/format.ts +93 -0
  35. package/packages/cli/src/main.ts +563 -0
  36. package/packages/core/package.json +7 -0
  37. package/packages/core/src/config.ts +30 -0
  38. package/packages/core/src/errors.ts +38 -0
  39. package/packages/core/src/eventBus.ts +56 -0
  40. package/packages/core/src/eventBusAdapter.ts +143 -0
  41. package/packages/core/src/index.ts +10 -0
  42. package/packages/core/src/sqliteEventBus.ts +336 -0
  43. package/packages/core/src/types.ts +63 -0
  44. package/packages/daemon/package.json +11 -0
  45. package/packages/daemon/src/api/analyticsRoutes.ts +343 -0
  46. package/packages/daemon/src/api/authRoutes.ts +344 -0
  47. package/packages/daemon/src/api/bodyLimit.ts +133 -0
  48. package/packages/daemon/src/api/envSetRoutes.ts +170 -0
  49. package/packages/daemon/src/api/gitRoutes.ts +409 -0
  50. package/packages/daemon/src/api/hooks.ts +588 -0
  51. package/packages/daemon/src/api/inputPolicy.ts +249 -0
  52. package/packages/daemon/src/api/notificationRoutes.ts +532 -0
  53. package/packages/daemon/src/api/policyRoutes.ts +234 -0
  54. package/packages/daemon/src/api/policySetRoutes.ts +445 -0
  55. package/packages/daemon/src/api/routeUtils.ts +28 -0
  56. package/packages/daemon/src/api/routes.ts +1004 -0
  57. package/packages/daemon/src/api/server.ts +1388 -0
  58. package/packages/daemon/src/api/settingsRoutes.ts +367 -0
  59. package/packages/daemon/src/api/sqliteErrors.ts +47 -0
  60. package/packages/daemon/src/api/stt.ts +143 -0
  61. package/packages/daemon/src/api/terminalRoutes.ts +200 -0
  62. package/packages/daemon/src/api/validation.ts +287 -0
  63. package/packages/daemon/src/api/validationRoutes.ts +174 -0
  64. package/packages/daemon/src/api/workflowRoutes.ts +567 -0
  65. package/packages/daemon/src/api/workspaceRoutes.ts +151 -0
  66. package/packages/daemon/src/api/ws.ts +1588 -0
  67. package/packages/daemon/src/auth/apiRateLimiter.ts +73 -0
  68. package/packages/daemon/src/auth/authMiddleware.ts +305 -0
  69. package/packages/daemon/src/auth/authService.ts +496 -0
  70. package/packages/daemon/src/auth/rateLimiter.ts +137 -0
  71. package/packages/daemon/src/config/pricing.ts +79 -0
  72. package/packages/daemon/src/crypto/encryption.ts +196 -0
  73. package/packages/daemon/src/db/db.ts +2745 -0
  74. package/packages/daemon/src/db/index.ts +16 -0
  75. package/packages/daemon/src/db/migrations.ts +182 -0
  76. package/packages/daemon/src/db/policyDb.ts +349 -0
  77. package/packages/daemon/src/db/schema.sql +408 -0
  78. package/packages/daemon/src/db/workflowDb.ts +464 -0
  79. package/packages/daemon/src/git/gitUtils.ts +544 -0
  80. package/packages/daemon/src/index.ts +6 -0
  81. package/packages/daemon/src/main.ts +525 -0
  82. package/packages/daemon/src/notifications/pushNotifier.ts +369 -0
  83. package/packages/daemon/src/providers/codexAppServerScaffold.ts +49 -0
  84. package/packages/daemon/src/providers/registry.ts +111 -0
  85. package/packages/daemon/src/providers/types.ts +82 -0
  86. package/packages/daemon/src/sessions/auditLogger.ts +103 -0
  87. package/packages/daemon/src/sessions/policyEngine.ts +165 -0
  88. package/packages/daemon/src/sessions/policyMatcher.ts +114 -0
  89. package/packages/daemon/src/sessions/policyTypes.ts +94 -0
  90. package/packages/daemon/src/sessions/ptyProcess.ts +141 -0
  91. package/packages/daemon/src/sessions/sessionManager.ts +1770 -0
  92. package/packages/daemon/src/sessions/tmuxSession.ts +1073 -0
  93. package/packages/daemon/src/sessions/transcriptManager.ts +110 -0
  94. package/packages/daemon/src/sessions/transcriptParser.ts +149 -0
  95. package/packages/daemon/src/sessions/transcriptTailer.ts +214 -0
  96. package/packages/daemon/src/tracking/tokenTracker.ts +168 -0
  97. package/packages/daemon/src/workflows/contextManager.ts +83 -0
  98. package/packages/daemon/src/workflows/index.ts +31 -0
  99. package/packages/daemon/src/workflows/resultExtractor.ts +118 -0
  100. package/packages/daemon/src/workflows/waitConditionPoller.ts +131 -0
  101. package/packages/daemon/src/workflows/workflowParser.ts +217 -0
  102. package/packages/daemon/src/workflows/workflowRunner.ts +969 -0
  103. package/packages/daemon/src/workflows/workflowTypes.ts +188 -0
  104. package/packages/daemon/src/workflows/workflowValidator.ts +533 -0
  105. package/packages/providers/claude-code/package.json +11 -0
  106. package/packages/providers/claude-code/src/index.ts +7 -0
  107. package/packages/providers/claude-code/src/overlaySettings.ts +198 -0
  108. package/packages/providers/claude-code/src/provider.ts +311 -0
  109. package/packages/web/dist/android-chrome-192x192.png +0 -0
  110. package/packages/web/dist/android-chrome-512x512.png +0 -0
  111. package/packages/web/dist/apple-touch-icon.png +0 -0
  112. package/packages/web/dist/assets/AnalyticsPage-BIopKWRf.js +17 -0
  113. package/packages/web/dist/assets/PoliciesPage-CjdLN3dl.js +11 -0
  114. package/packages/web/dist/assets/SessionDetailPage-BtSA0V0M.js +179 -0
  115. package/packages/web/dist/assets/SettingsPage-Dbbz4Ca5.js +37 -0
  116. package/packages/web/dist/assets/WorkflowsPage-Dv6f3GgU.js +1 -0
  117. package/packages/web/dist/assets/chart-vendor-DlOHLaCG.js +49 -0
  118. package/packages/web/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  119. package/packages/web/dist/assets/css.worker-BvV5MPou.js +93 -0
  120. package/packages/web/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
  121. package/packages/web/dist/assets/html.worker-BLJhxQJQ.js +470 -0
  122. package/packages/web/dist/assets/index-BbdhRfr2.css +1 -0
  123. package/packages/web/dist/assets/index-hgphORiw.js +204 -0
  124. package/packages/web/dist/assets/json.worker-usMZ-FED.js +58 -0
  125. package/packages/web/dist/assets/monaco-core-B_19GPAS.css +1 -0
  126. package/packages/web/dist/assets/monaco-core-DQ5Mk8AK.js +1234 -0
  127. package/packages/web/dist/assets/monaco-react-DfZNWvtW.js +11 -0
  128. package/packages/web/dist/assets/monacoSetup-DvBj52bT.js +1 -0
  129. package/packages/web/dist/assets/pencil-Dbczxz59.js +11 -0
  130. package/packages/web/dist/assets/react-vendor-B5MgMUHH.js +136 -0
  131. package/packages/web/dist/assets/refresh-cw-B0MGsYPL.js +6 -0
  132. package/packages/web/dist/assets/tabs-C8LsWiR5.js +1 -0
  133. package/packages/web/dist/assets/terminal-vendor-Cs8KPbV3.js +9 -0
  134. package/packages/web/dist/assets/terminal-vendor-LcAfv9l9.css +32 -0
  135. package/packages/web/dist/assets/trash-2-Btlg0d4l.js +6 -0
  136. package/packages/web/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
  137. package/packages/web/dist/favicon.ico +0 -0
  138. package/packages/web/dist/icon.svg +1 -0
  139. package/packages/web/dist/index.html +29 -0
  140. package/packages/web/dist/manifest.json +29 -0
  141. package/packages/web/dist/og-image.png +0 -0
  142. package/packages/web/dist/originals/android-chrome-192x192.png +0 -0
  143. package/packages/web/dist/originals/android-chrome-512x512.png +0 -0
  144. package/packages/web/dist/originals/apple-touch-icon.png +0 -0
  145. package/packages/web/dist/originals/favicon.ico +0 -0
  146. package/packages/web/dist/piper.svg +1 -0
  147. package/packages/web/dist/sounds/codepiper-soft-chime.wav +0 -0
  148. package/packages/web/dist/sw.js +257 -0
  149. 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
+ }