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,196 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+
6
+ /**
7
+ * Encrypted data blob with IV, auth tag, and ciphertext (all hex-encoded)
8
+ */
9
+ export interface EncryptedBlob {
10
+ iv: string; // hex
11
+ tag: string; // hex
12
+ ciphertext: string; // hex
13
+ }
14
+
15
+ const ALGORITHM = "aes-256-gcm";
16
+ const IV_BYTES = 12;
17
+ const KEY_BYTES = 32;
18
+ const HOOK_SECRET_BYTES = 32;
19
+
20
+ interface SecretsFile {
21
+ encryptionKey?: string;
22
+ hookSecret?: string;
23
+ [key: string]: string | undefined;
24
+ }
25
+
26
+ function getCodepiperDir(): string {
27
+ return path.join(os.homedir(), ".codepiper");
28
+ }
29
+
30
+ function getSecretsPath(): string {
31
+ return path.join(getCodepiperDir(), "secrets.json");
32
+ }
33
+
34
+ function ensureCodepiperDir(): void {
35
+ const dir = getCodepiperDir();
36
+ if (!fs.existsSync(dir)) {
37
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
38
+ }
39
+ try {
40
+ fs.chmodSync(dir, 0o700);
41
+ } catch {
42
+ // best-effort on non-POSIX filesystems
43
+ }
44
+ }
45
+
46
+ function hardenSecretsFilePermissions(): void {
47
+ const secretsPath = getSecretsPath();
48
+ if (!fs.existsSync(secretsPath)) {
49
+ return;
50
+ }
51
+ try {
52
+ fs.chmodSync(secretsPath, 0o600);
53
+ } catch {
54
+ // best-effort on non-POSIX filesystems
55
+ }
56
+ }
57
+
58
+ function readSecretsFile(): SecretsFile {
59
+ ensureCodepiperDir();
60
+ const secretsPath = getSecretsPath();
61
+ if (!fs.existsSync(secretsPath)) {
62
+ return {};
63
+ }
64
+ hardenSecretsFilePermissions();
65
+
66
+ const raw = fs.readFileSync(secretsPath, "utf-8");
67
+ const parsed: unknown = JSON.parse(raw);
68
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
69
+ throw new Error(`Invalid secrets file format: ${secretsPath}`);
70
+ }
71
+ return parsed as SecretsFile;
72
+ }
73
+
74
+ function writeSecretsFile(secrets: SecretsFile): void {
75
+ ensureCodepiperDir();
76
+ const secretsPath = getSecretsPath();
77
+
78
+ // Write via temp file + rename to avoid partial writes.
79
+ const tempPath = `${secretsPath}.tmp-${process.pid}-${Date.now()}`;
80
+ const fd = fs.openSync(tempPath, "w", 0o600);
81
+ try {
82
+ fs.writeSync(fd, JSON.stringify(secrets, null, 2));
83
+ } finally {
84
+ fs.closeSync(fd);
85
+ }
86
+
87
+ fs.renameSync(tempPath, secretsPath);
88
+ try {
89
+ fs.chmodSync(secretsPath, 0o600);
90
+ } catch {
91
+ // best-effort on filesystems that do not honor POSIX permissions
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Get or create the encryption key from ~/.codepiper/secrets.json.
97
+ * If the file or key doesn't exist, generates a new 32-byte random key,
98
+ * writes it back, and sets file permissions to 600.
99
+ */
100
+ export function getOrCreateEncryptionKey(): Buffer {
101
+ const secrets = readSecretsFile();
102
+
103
+ if (secrets.encryptionKey) {
104
+ return Buffer.from(secrets.encryptionKey, "hex");
105
+ }
106
+
107
+ // Generate a new key
108
+ const key = crypto.randomBytes(KEY_BYTES);
109
+ secrets.encryptionKey = key.toString("hex");
110
+ writeSecretsFile(secrets);
111
+
112
+ return key;
113
+ }
114
+
115
+ /**
116
+ * Get or create a persistent hook authentication secret.
117
+ * The secret is shared across daemon restarts so preserved sessions keep working.
118
+ */
119
+ export function getOrCreateHookSecret(): string {
120
+ const secrets = readSecretsFile();
121
+ const existing = secrets.hookSecret;
122
+ if (existing && /^[a-f0-9]{64}$/.test(existing)) {
123
+ return existing;
124
+ }
125
+
126
+ const secret = crypto.randomBytes(HOOK_SECRET_BYTES).toString("hex");
127
+ secrets.hookSecret = secret;
128
+ writeSecretsFile(secrets);
129
+ return secret;
130
+ }
131
+
132
+ /**
133
+ * Encrypt plaintext using AES-256-GCM.
134
+ * Returns an EncryptedBlob with hex-encoded iv, tag, and ciphertext.
135
+ */
136
+ export function encrypt(plaintext: string, key: Buffer): EncryptedBlob {
137
+ const iv = crypto.randomBytes(IV_BYTES);
138
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
139
+
140
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
141
+
142
+ const tag = cipher.getAuthTag();
143
+
144
+ return {
145
+ iv: iv.toString("hex"),
146
+ tag: tag.toString("hex"),
147
+ ciphertext: encrypted.toString("hex"),
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Decrypt an EncryptedBlob back to plaintext using AES-256-GCM.
153
+ * Throws if the ciphertext or auth tag has been tampered with.
154
+ */
155
+ export function decrypt(blob: EncryptedBlob, key: Buffer): string {
156
+ const iv = Buffer.from(blob.iv, "hex");
157
+ const tag = Buffer.from(blob.tag, "hex");
158
+ const ciphertext = Buffer.from(blob.ciphertext, "hex");
159
+
160
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
161
+ decipher.setAuthTag(tag);
162
+
163
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
164
+
165
+ return decrypted.toString("utf-8");
166
+ }
167
+
168
+ /**
169
+ * Mask a value for display: shows "****" + last 4 chars.
170
+ * For values shorter than 5 chars, returns "****".
171
+ */
172
+ export function maskValue(value: string): string {
173
+ if (value.length < 5) {
174
+ return "****";
175
+ }
176
+ return `****${value.slice(-4)}`;
177
+ }
178
+
179
+ /**
180
+ * Encrypt a vars object (Record<string, string>) as a single JSON blob.
181
+ * Returns the encrypted JSON as a string (JSON-serialized EncryptedBlob).
182
+ */
183
+ export function encryptVars(vars: Record<string, string>, key: Buffer): string {
184
+ const plaintext = JSON.stringify(vars);
185
+ const blob = encrypt(plaintext, key);
186
+ return JSON.stringify(blob);
187
+ }
188
+
189
+ /**
190
+ * Decrypt an encrypted vars JSON string back to a Record<string, string>.
191
+ */
192
+ export function decryptVars(encryptedJson: string, key: Buffer): Record<string, string> {
193
+ const blob: EncryptedBlob = JSON.parse(encryptedJson);
194
+ const plaintext = decrypt(blob, key);
195
+ return JSON.parse(plaintext);
196
+ }