codekin 0.4.1 → 0.5.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/README.md +12 -15
- package/bin/codekin.mjs +52 -32
- package/dist/assets/index-BwKZeT4V.css +1 -0
- package/dist/assets/index-CfBnNU24.js +186 -0
- package/dist/index.html +2 -2
- package/package.json +2 -7
- package/server/dist/approval-manager.d.ts +7 -2
- package/server/dist/approval-manager.js +44 -78
- package/server/dist/approval-manager.js.map +1 -1
- package/server/dist/claude-process.d.ts +23 -3
- package/server/dist/claude-process.js +120 -27
- package/server/dist/claude-process.js.map +1 -1
- package/server/dist/commit-event-handler.js.map +1 -1
- package/server/dist/config.d.ts +4 -0
- package/server/dist/config.js +17 -0
- package/server/dist/config.js.map +1 -1
- package/server/dist/diff-manager.d.ts +41 -0
- package/server/dist/diff-manager.js +303 -0
- package/server/dist/diff-manager.js.map +1 -0
- package/server/dist/error-page.d.ts +5 -0
- package/server/dist/error-page.js +144 -0
- package/server/dist/error-page.js.map +1 -0
- package/server/dist/native-permissions.d.ts +44 -0
- package/server/dist/native-permissions.js +163 -0
- package/server/dist/native-permissions.js.map +1 -0
- package/server/dist/orchestrator-children.d.ts +74 -0
- package/server/dist/orchestrator-children.js +281 -0
- package/server/dist/orchestrator-children.js.map +1 -0
- package/server/dist/orchestrator-learning.d.ts +134 -0
- package/server/dist/orchestrator-learning.js +567 -0
- package/server/dist/orchestrator-learning.js.map +1 -0
- package/server/dist/orchestrator-manager.d.ts +25 -0
- package/server/dist/orchestrator-manager.js +353 -0
- package/server/dist/orchestrator-manager.js.map +1 -0
- package/server/dist/orchestrator-memory.d.ts +77 -0
- package/server/dist/orchestrator-memory.js +288 -0
- package/server/dist/orchestrator-memory.js.map +1 -0
- package/server/dist/orchestrator-monitor.d.ts +59 -0
- package/server/dist/orchestrator-monitor.js +238 -0
- package/server/dist/orchestrator-monitor.js.map +1 -0
- package/server/dist/orchestrator-reports.d.ts +45 -0
- package/server/dist/orchestrator-reports.js +124 -0
- package/server/dist/orchestrator-reports.js.map +1 -0
- package/server/dist/orchestrator-routes.d.ts +17 -0
- package/server/dist/orchestrator-routes.js +526 -0
- package/server/dist/orchestrator-routes.js.map +1 -0
- package/server/dist/session-archive.js +9 -2
- package/server/dist/session-archive.js.map +1 -1
- package/server/dist/session-manager.d.ts +99 -39
- package/server/dist/session-manager.js +565 -394
- package/server/dist/session-manager.js.map +1 -1
- package/server/dist/session-naming.d.ts +6 -10
- package/server/dist/session-naming.js +60 -62
- package/server/dist/session-naming.js.map +1 -1
- package/server/dist/session-persistence.d.ts +6 -1
- package/server/dist/session-persistence.js +6 -0
- package/server/dist/session-persistence.js.map +1 -1
- package/server/dist/session-restart-scheduler.d.ts +30 -0
- package/server/dist/session-restart-scheduler.js +41 -0
- package/server/dist/session-restart-scheduler.js.map +1 -0
- package/server/dist/session-routes.js +122 -61
- package/server/dist/session-routes.js.map +1 -1
- package/server/dist/stepflow-types.d.ts +1 -1
- package/server/dist/tsconfig.tsbuildinfo +1 -1
- package/server/dist/types.d.ts +34 -2
- package/server/dist/types.js +8 -1
- package/server/dist/types.js.map +1 -1
- package/server/dist/upload-routes.js +7 -1
- package/server/dist/upload-routes.js.map +1 -1
- package/server/dist/version-check.d.ts +17 -0
- package/server/dist/version-check.js +89 -0
- package/server/dist/version-check.js.map +1 -0
- package/server/dist/workflow-engine.d.ts +74 -1
- package/server/dist/workflow-engine.js +20 -1
- package/server/dist/workflow-engine.js.map +1 -1
- package/server/dist/ws-message-handler.js +115 -9
- package/server/dist/ws-message-handler.js.map +1 -1
- package/server/dist/ws-server.js +90 -15
- package/server/dist/ws-server.js.map +1 -1
- package/dist/assets/index-BAdQqYEY.js +0 -182
- package/dist/assets/index-CeZYNLWt.css +0 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiffManager — encapsulates stateless git-diff operations.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from SessionManager to reduce its complexity. All methods operate
|
|
5
|
+
* on a working directory path and have no dependency on session state, making
|
|
6
|
+
* them independently testable.
|
|
7
|
+
*
|
|
8
|
+
* The class is instantiated once by SessionManager and delegates are called
|
|
9
|
+
* with the relevant session's workingDir.
|
|
10
|
+
*/
|
|
11
|
+
import { execFile } from 'child_process';
|
|
12
|
+
import { promises as fs } from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { promisify } from 'util';
|
|
15
|
+
import { parseDiff, createUntrackedFileDiff } from './diff-parser.js';
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
/** Max stdout for git commands (2 MB). */
|
|
18
|
+
const GIT_MAX_BUFFER = 2 * 1024 * 1024;
|
|
19
|
+
/** Timeout for git commands (10 seconds). */
|
|
20
|
+
const GIT_TIMEOUT_MS = 10_000;
|
|
21
|
+
/** Max paths per git command to stay under ARG_MAX (~128 KB on Linux). */
|
|
22
|
+
const GIT_PATH_CHUNK_SIZE = 200;
|
|
23
|
+
/**
|
|
24
|
+
* Return a copy of process.env with GIT_* vars removed that can interfere
|
|
25
|
+
* with child git processes (e.g. GIT_INDEX_FILE, GIT_DIR, GIT_PREFIX).
|
|
26
|
+
* The server may inherit these from the shell that launched pm2/node.
|
|
27
|
+
*/
|
|
28
|
+
export function cleanGitEnv() {
|
|
29
|
+
return Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('GIT_') || key === 'GIT_EDITOR'));
|
|
30
|
+
}
|
|
31
|
+
/** Run a git command as a fixed argv array (no shell interpolation). */
|
|
32
|
+
export async function execGit(args, cwd) {
|
|
33
|
+
const { stdout } = await execFileAsync('git', args, {
|
|
34
|
+
cwd,
|
|
35
|
+
env: cleanGitEnv(),
|
|
36
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
37
|
+
timeout: GIT_TIMEOUT_MS,
|
|
38
|
+
});
|
|
39
|
+
return stdout;
|
|
40
|
+
}
|
|
41
|
+
/** Run a git command with paths chunked to avoid E2BIG. Concatenates stdout. */
|
|
42
|
+
export async function execGitChunked(baseArgs, paths, cwd) {
|
|
43
|
+
let result = '';
|
|
44
|
+
for (let i = 0; i < paths.length; i += GIT_PATH_CHUNK_SIZE) {
|
|
45
|
+
const chunk = paths.slice(i, i + GIT_PATH_CHUNK_SIZE);
|
|
46
|
+
result += await execGit([...baseArgs, '--', ...chunk], cwd);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
/** Get file statuses from `git status --porcelain` for given paths (or all). */
|
|
51
|
+
export async function getFileStatuses(cwd, paths) {
|
|
52
|
+
const args = ['status', '--porcelain', '-z'];
|
|
53
|
+
if (paths)
|
|
54
|
+
args.push('--', ...paths);
|
|
55
|
+
const raw = await execGit(args, cwd);
|
|
56
|
+
const result = {};
|
|
57
|
+
// git status --porcelain=v1 -z format: XY NUL path NUL
|
|
58
|
+
const parts = raw.split('\0');
|
|
59
|
+
let i = 0;
|
|
60
|
+
while (i < parts.length) {
|
|
61
|
+
const entry = parts[i];
|
|
62
|
+
if (entry.length < 3) {
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const x = entry[0];
|
|
67
|
+
const y = entry[1];
|
|
68
|
+
const filePath = entry.slice(3);
|
|
69
|
+
if (x === 'R' || x === 'C') {
|
|
70
|
+
const newPath = parts[i + 1] ?? filePath;
|
|
71
|
+
result[newPath] = 'renamed';
|
|
72
|
+
i += 2;
|
|
73
|
+
}
|
|
74
|
+
else if (x === 'D' || y === 'D') {
|
|
75
|
+
result[filePath] = 'deleted';
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
else if (x === '?' && y === '?') {
|
|
79
|
+
result[filePath] = 'added';
|
|
80
|
+
i++;
|
|
81
|
+
}
|
|
82
|
+
else if (x === 'A') {
|
|
83
|
+
result[filePath] = 'added';
|
|
84
|
+
i++;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
result[filePath] = 'modified';
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Encapsulates git-diff and discard operations for a working directory.
|
|
95
|
+
*
|
|
96
|
+
* Stateless — all methods take a `cwd` parameter and don't hold any mutable
|
|
97
|
+
* state, so a single instance can safely serve all sessions.
|
|
98
|
+
*/
|
|
99
|
+
export class DiffManager {
|
|
100
|
+
/**
|
|
101
|
+
* Run git diff in a working directory and return structured results.
|
|
102
|
+
* Includes untracked file discovery for 'unstaged' and 'all' scopes.
|
|
103
|
+
*/
|
|
104
|
+
async getDiff(cwd, scope = 'all') {
|
|
105
|
+
try {
|
|
106
|
+
// Get branch name
|
|
107
|
+
let branch;
|
|
108
|
+
try {
|
|
109
|
+
const branchResult = await execGit(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
|
|
110
|
+
branch = branchResult.trim();
|
|
111
|
+
if (branch === 'HEAD') {
|
|
112
|
+
const shaResult = await execGit(['rev-parse', '--short', 'HEAD'], cwd);
|
|
113
|
+
branch = `detached at ${shaResult.trim()}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
branch = 'unknown';
|
|
118
|
+
}
|
|
119
|
+
// Build diff command based on scope
|
|
120
|
+
const diffArgs = ['diff', '--find-renames', '--no-color', '--unified=3'];
|
|
121
|
+
if (scope === 'staged') {
|
|
122
|
+
diffArgs.push('--cached');
|
|
123
|
+
}
|
|
124
|
+
else if (scope === 'all') {
|
|
125
|
+
diffArgs.push('HEAD');
|
|
126
|
+
}
|
|
127
|
+
let rawDiff;
|
|
128
|
+
try {
|
|
129
|
+
rawDiff = await execGit(diffArgs, cwd);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
if (scope === 'all') {
|
|
133
|
+
const [staged, unstaged] = await Promise.all([
|
|
134
|
+
execGit(['diff', '--cached', '--find-renames', '--no-color', '--unified=3'], cwd).catch(() => ''),
|
|
135
|
+
execGit(['diff', '--find-renames', '--no-color', '--unified=3'], cwd).catch(() => ''),
|
|
136
|
+
]);
|
|
137
|
+
rawDiff = staged + unstaged;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
rawDiff = '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const { files, truncated, truncationReason } = parseDiff(rawDiff);
|
|
144
|
+
// Discover untracked files for 'unstaged' and 'all' scopes
|
|
145
|
+
if (scope !== 'staged') {
|
|
146
|
+
try {
|
|
147
|
+
const untrackedRaw = await execGit(['ls-files', '--others', '--exclude-standard'], cwd);
|
|
148
|
+
const untrackedPaths = untrackedRaw.trim().split('\n').filter(Boolean);
|
|
149
|
+
const MAX_UNTRACKED_FILE_SIZE = 1024 * 1024; // 1 MB
|
|
150
|
+
for (const relPath of untrackedPaths) {
|
|
151
|
+
try {
|
|
152
|
+
const fullPath = path.join(cwd, relPath);
|
|
153
|
+
const stat = await fs.stat(fullPath);
|
|
154
|
+
if (stat.size > MAX_UNTRACKED_FILE_SIZE) {
|
|
155
|
+
console.log(`[diff] skipping large untracked file: ${relPath} (${(stat.size / 1024 / 1024).toFixed(1)} MB, limit ${MAX_UNTRACKED_FILE_SIZE / 1024 / 1024} MB)`);
|
|
156
|
+
files.push({
|
|
157
|
+
path: relPath,
|
|
158
|
+
status: 'added',
|
|
159
|
+
isBinary: true,
|
|
160
|
+
additions: 0,
|
|
161
|
+
deletions: 0,
|
|
162
|
+
hunks: [],
|
|
163
|
+
});
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
167
|
+
files.push(createUntrackedFileDiff(relPath, content));
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
files.push({
|
|
171
|
+
path: relPath,
|
|
172
|
+
status: 'added',
|
|
173
|
+
isBinary: true,
|
|
174
|
+
additions: 0,
|
|
175
|
+
deletions: 0,
|
|
176
|
+
hunks: [],
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// ls-files failed — skip untracked
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const summary = {
|
|
186
|
+
filesChanged: files.length,
|
|
187
|
+
insertions: files.reduce((sum, f) => sum + f.additions, 0),
|
|
188
|
+
deletions: files.reduce((sum, f) => sum + f.deletions, 0),
|
|
189
|
+
truncated,
|
|
190
|
+
truncationReason,
|
|
191
|
+
};
|
|
192
|
+
return { type: 'diff_result', files, summary, branch, scope };
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const message = err instanceof Error ? err.message : 'Failed to get diff';
|
|
196
|
+
return { type: 'diff_error', message };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Discard changes in a working directory per the given scope and paths.
|
|
201
|
+
* Returns a fresh diff_result after discarding.
|
|
202
|
+
*/
|
|
203
|
+
async discardChanges(cwd, scope, paths, statuses) {
|
|
204
|
+
try {
|
|
205
|
+
// Validate paths
|
|
206
|
+
if (paths) {
|
|
207
|
+
const root = path.join(path.resolve(cwd), path.sep);
|
|
208
|
+
for (const p of paths) {
|
|
209
|
+
if (p.includes('..') || path.isAbsolute(p)) {
|
|
210
|
+
return { type: 'diff_error', message: `Invalid path: ${p}` };
|
|
211
|
+
}
|
|
212
|
+
const resolved = path.resolve(cwd, p);
|
|
213
|
+
if (resolved !== path.resolve(cwd) && !resolved.startsWith(root)) {
|
|
214
|
+
return { type: 'diff_error', message: `Path escapes working directory: ${p}` };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Determine file statuses if not provided
|
|
219
|
+
let fileStatuses = statuses ?? {};
|
|
220
|
+
if (!statuses && paths) {
|
|
221
|
+
fileStatuses = await getFileStatuses(cwd, paths);
|
|
222
|
+
}
|
|
223
|
+
else if (!statuses && !paths) {
|
|
224
|
+
fileStatuses = await getFileStatuses(cwd);
|
|
225
|
+
}
|
|
226
|
+
const targetPaths = paths ?? Object.keys(fileStatuses);
|
|
227
|
+
// Separate files by status for different handling
|
|
228
|
+
const trackedPaths = [];
|
|
229
|
+
const untrackedPaths = [];
|
|
230
|
+
const stagedNewPaths = [];
|
|
231
|
+
for (const p of targetPaths) {
|
|
232
|
+
const status = fileStatuses[p];
|
|
233
|
+
if (status === 'added') {
|
|
234
|
+
try {
|
|
235
|
+
const indexEntry = (await execGit(['ls-files', '--stage', '--', p], cwd)).trim();
|
|
236
|
+
if (indexEntry) {
|
|
237
|
+
stagedNewPaths.push(p);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
untrackedPaths.push(p);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
untrackedPaths.push(p);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
trackedPaths.push(p);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Handle tracked files (modified, deleted, renamed) with git restore
|
|
252
|
+
if (trackedPaths.length > 0) {
|
|
253
|
+
const restoreArgs = ['restore'];
|
|
254
|
+
if (scope === 'staged') {
|
|
255
|
+
restoreArgs.push('--staged');
|
|
256
|
+
}
|
|
257
|
+
else if (scope === 'all') {
|
|
258
|
+
restoreArgs.push('--staged', '--worktree');
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
restoreArgs.push('--worktree');
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
await execGitChunked(restoreArgs, trackedPaths, cwd);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
console.warn('[discard] git restore failed, trying fallback:', err);
|
|
268
|
+
if (scope === 'staged' || scope === 'all') {
|
|
269
|
+
await execGitChunked(['reset', 'HEAD'], trackedPaths, cwd);
|
|
270
|
+
}
|
|
271
|
+
if (scope === 'unstaged' || scope === 'all') {
|
|
272
|
+
await execGitChunked(['checkout'], trackedPaths, cwd);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Handle staged new files
|
|
277
|
+
if (stagedNewPaths.length > 0) {
|
|
278
|
+
if (scope === 'staged') {
|
|
279
|
+
await execGitChunked(['rm', '--cached'], stagedNewPaths, cwd);
|
|
280
|
+
}
|
|
281
|
+
else if (scope === 'all') {
|
|
282
|
+
await execGitChunked(['rm', '--cached'], stagedNewPaths, cwd);
|
|
283
|
+
for (const p of stagedNewPaths) {
|
|
284
|
+
await fs.unlink(path.join(cwd, p)).catch(() => { });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Handle untracked files (delete from disk)
|
|
289
|
+
if (untrackedPaths.length > 0 && scope !== 'staged') {
|
|
290
|
+
for (const p of untrackedPaths) {
|
|
291
|
+
await fs.unlink(path.join(cwd, p)).catch(() => { });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Return fresh diff
|
|
295
|
+
return await this.getDiff(cwd, scope);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
const message = err instanceof Error ? err.message : 'Failed to discard changes';
|
|
299
|
+
return { type: 'diff_error', message };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=diff-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-manager.js","sourceRoot":"","sources":["../diff-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a styled 404 error page matching the Codekin design theme.
|
|
3
|
+
*/
|
|
4
|
+
export function generate404Page() {
|
|
5
|
+
return `<!DOCTYPE html>
|
|
6
|
+
<html lang="en" class="dark">
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8" />
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
11
|
+
<title>404 — Codekin</title>
|
|
12
|
+
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;600&family=Lato:wght@400;700&display=swap" rel="stylesheet" />
|
|
13
|
+
<style>
|
|
14
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
15
|
+
body {
|
|
16
|
+
background: #090e0f;
|
|
17
|
+
color: #c6d4d5;
|
|
18
|
+
font-family: 'Lato', sans-serif;
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
}
|
|
24
|
+
.container {
|
|
25
|
+
text-align: center;
|
|
26
|
+
max-width: 480px;
|
|
27
|
+
padding: 2rem;
|
|
28
|
+
}
|
|
29
|
+
.code {
|
|
30
|
+
font-family: 'Inconsolata', monospace;
|
|
31
|
+
font-size: 6rem;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
color: #e4ae42;
|
|
34
|
+
line-height: 1;
|
|
35
|
+
margin-bottom: 0.5rem;
|
|
36
|
+
}
|
|
37
|
+
h1 {
|
|
38
|
+
font-family: 'Lato', sans-serif;
|
|
39
|
+
font-size: 1.5rem;
|
|
40
|
+
font-weight: 700;
|
|
41
|
+
color: #dce4e5;
|
|
42
|
+
margin-bottom: 1rem;
|
|
43
|
+
}
|
|
44
|
+
p {
|
|
45
|
+
color: #94aeb1;
|
|
46
|
+
font-size: 1rem;
|
|
47
|
+
line-height: 1.6;
|
|
48
|
+
margin-bottom: 2rem;
|
|
49
|
+
}
|
|
50
|
+
a {
|
|
51
|
+
display: inline-block;
|
|
52
|
+
padding: 0.65rem 1.75rem;
|
|
53
|
+
background: #e4ae42;
|
|
54
|
+
color: #1e170a;
|
|
55
|
+
font-weight: 700;
|
|
56
|
+
font-size: 0.9rem;
|
|
57
|
+
border-radius: 6px;
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
transition: background 0.15s;
|
|
60
|
+
}
|
|
61
|
+
a:hover { background: #cf9a2d; }
|
|
62
|
+
</style>
|
|
63
|
+
</head>
|
|
64
|
+
<body>
|
|
65
|
+
<div class="container">
|
|
66
|
+
<div class="code">404</div>
|
|
67
|
+
<h1>Page not found</h1>
|
|
68
|
+
<p>The page you're looking for doesn't exist or the session may have been removed.</p>
|
|
69
|
+
<a href="/">Go to Home</a>
|
|
70
|
+
</div>
|
|
71
|
+
</body>
|
|
72
|
+
</html>`;
|
|
73
|
+
}
|
|
74
|
+
export function generate500Page() {
|
|
75
|
+
return `<!DOCTYPE html>
|
|
76
|
+
<html lang="en" class="dark">
|
|
77
|
+
<head>
|
|
78
|
+
<meta charset="UTF-8" />
|
|
79
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
80
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
81
|
+
<title>Error — Codekin</title>
|
|
82
|
+
<link href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;600&family=Lato:wght@400;700&display=swap" rel="stylesheet" />
|
|
83
|
+
<style>
|
|
84
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
85
|
+
body {
|
|
86
|
+
background: #090e0f;
|
|
87
|
+
color: #c6d4d5;
|
|
88
|
+
font-family: 'Lato', sans-serif;
|
|
89
|
+
min-height: 100vh;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
}
|
|
94
|
+
.container {
|
|
95
|
+
text-align: center;
|
|
96
|
+
max-width: 480px;
|
|
97
|
+
padding: 2rem;
|
|
98
|
+
}
|
|
99
|
+
.code {
|
|
100
|
+
font-family: 'Inconsolata', monospace;
|
|
101
|
+
font-size: 6rem;
|
|
102
|
+
font-weight: 600;
|
|
103
|
+
color: #d94444;
|
|
104
|
+
line-height: 1;
|
|
105
|
+
margin-bottom: 0.5rem;
|
|
106
|
+
}
|
|
107
|
+
h1 {
|
|
108
|
+
font-family: 'Lato', sans-serif;
|
|
109
|
+
font-size: 1.5rem;
|
|
110
|
+
font-weight: 700;
|
|
111
|
+
color: #dce4e5;
|
|
112
|
+
margin-bottom: 1rem;
|
|
113
|
+
}
|
|
114
|
+
p {
|
|
115
|
+
color: #94aeb1;
|
|
116
|
+
font-size: 1rem;
|
|
117
|
+
line-height: 1.6;
|
|
118
|
+
margin-bottom: 2rem;
|
|
119
|
+
}
|
|
120
|
+
a {
|
|
121
|
+
display: inline-block;
|
|
122
|
+
padding: 0.65rem 1.75rem;
|
|
123
|
+
background: #e4ae42;
|
|
124
|
+
color: #1e170a;
|
|
125
|
+
font-weight: 700;
|
|
126
|
+
font-size: 0.9rem;
|
|
127
|
+
border-radius: 6px;
|
|
128
|
+
text-decoration: none;
|
|
129
|
+
transition: background 0.15s;
|
|
130
|
+
}
|
|
131
|
+
a:hover { background: #cf9a2d; }
|
|
132
|
+
</style>
|
|
133
|
+
</head>
|
|
134
|
+
<body>
|
|
135
|
+
<div class="container">
|
|
136
|
+
<div class="code">500</div>
|
|
137
|
+
<h1>Something went wrong</h1>
|
|
138
|
+
<p>An unexpected error occurred. Please try again or return to the home page.</p>
|
|
139
|
+
<a href="/">Go to Home</a>
|
|
140
|
+
</div>
|
|
141
|
+
</body>
|
|
142
|
+
</html>`;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=error-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-page.js","sourceRoot":"","sources":["../error-page.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAmED,CAAA;AACR,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAmED,CAAA;AACR,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native permissions utility for Codekin.
|
|
3
|
+
*
|
|
4
|
+
* Reads/writes `.claude/settings.local.json` in the project directory so that
|
|
5
|
+
* Claude Code's native permission system remembers approvals across sessions
|
|
6
|
+
* without re-prompting.
|
|
7
|
+
*
|
|
8
|
+
* Also converts Codekin's approval registry into `--allowedTools` patterns
|
|
9
|
+
* for passing at spawn time.
|
|
10
|
+
*/
|
|
11
|
+
/** Read existing native permissions from `.claude/settings.local.json`. */
|
|
12
|
+
export declare function readNativePermissions(repoDir: string): string[];
|
|
13
|
+
/** Add a native permission to `.claude/settings.local.json` (append-only, atomic write). */
|
|
14
|
+
export declare function addNativePermission(repoDir: string, permission: string): Promise<void>;
|
|
15
|
+
/** Remove a native permission from `.claude/settings.local.json`. */
|
|
16
|
+
export declare function removeNativePermission(repoDir: string, permission: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Convert a tool invocation to Claude Code's native permission format.
|
|
19
|
+
*
|
|
20
|
+
* Examples:
|
|
21
|
+
* - Bash + `{ command: "npm run build" }` → `"Bash(npm run build)"`
|
|
22
|
+
* - WebFetch (any input) → `"WebFetch"`
|
|
23
|
+
* - Read (file tool, pre-approved) → `null`
|
|
24
|
+
*/
|
|
25
|
+
export declare function toNativePermission(toolName: string, toolInput: Record<string, unknown>): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Convert Codekin's approval registry into `--allowedTools` patterns.
|
|
28
|
+
*
|
|
29
|
+
* Only includes tool names and wildcard patterns — NOT exact commands.
|
|
30
|
+
* Exact commands accumulate over time (hundreds of multi-line scripts)
|
|
31
|
+
* and when included in `--allowedTools` they create an argument so large
|
|
32
|
+
* and complex that it breaks the CLI's parser (manifests as "A.split is
|
|
33
|
+
* not a function" errors on unrelated tools like Agent and TodoWrite).
|
|
34
|
+
* Exact commands are still auto-approved at runtime via the approval hook.
|
|
35
|
+
*
|
|
36
|
+
* Translations:
|
|
37
|
+
* - Pattern `"git diff *"` → `"Bash(git diff:*)"`
|
|
38
|
+
* - Tool `"WebFetch"` → `"WebFetch"`
|
|
39
|
+
*/
|
|
40
|
+
export declare function toAllowedToolsPatterns(approvals: {
|
|
41
|
+
tools: string[];
|
|
42
|
+
commands: string[];
|
|
43
|
+
patterns: string[];
|
|
44
|
+
}): string[];
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native permissions utility for Codekin.
|
|
3
|
+
*
|
|
4
|
+
* Reads/writes `.claude/settings.local.json` in the project directory so that
|
|
5
|
+
* Claude Code's native permission system remembers approvals across sessions
|
|
6
|
+
* without re-prompting.
|
|
7
|
+
*
|
|
8
|
+
* Also converts Codekin's approval registry into `--allowedTools` patterns
|
|
9
|
+
* for passing at spawn time.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
/** In-process mutex map keyed by repo path to prevent concurrent write races. */
|
|
14
|
+
const writeLocks = new Map();
|
|
15
|
+
/** Acquire a per-repo write lock. All sessions share one Node process. */
|
|
16
|
+
function withLock(repoDir, fn) {
|
|
17
|
+
const prev = writeLocks.get(repoDir) ?? Promise.resolve();
|
|
18
|
+
const next = prev.then(fn, fn);
|
|
19
|
+
writeLocks.set(repoDir, next);
|
|
20
|
+
// Clean up the reference once done so we don't leak memory
|
|
21
|
+
void next.then(() => {
|
|
22
|
+
if (writeLocks.get(repoDir) === next)
|
|
23
|
+
writeLocks.delete(repoDir);
|
|
24
|
+
});
|
|
25
|
+
return next;
|
|
26
|
+
}
|
|
27
|
+
function settingsPath(repoDir) {
|
|
28
|
+
return join(repoDir, '.claude', 'settings.local.json');
|
|
29
|
+
}
|
|
30
|
+
/** Read existing native permissions from `.claude/settings.local.json`. */
|
|
31
|
+
export function readNativePermissions(repoDir) {
|
|
32
|
+
const filePath = settingsPath(repoDir);
|
|
33
|
+
if (!existsSync(filePath))
|
|
34
|
+
return [];
|
|
35
|
+
try {
|
|
36
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
37
|
+
return data.permissions?.allow ?? [];
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Add a native permission to `.claude/settings.local.json` (append-only, atomic write). */
|
|
44
|
+
export async function addNativePermission(repoDir, permission) {
|
|
45
|
+
await withLock(repoDir, async () => {
|
|
46
|
+
const filePath = settingsPath(repoDir);
|
|
47
|
+
const dir = join(repoDir, '.claude');
|
|
48
|
+
let settings = {};
|
|
49
|
+
if (existsSync(filePath)) {
|
|
50
|
+
try {
|
|
51
|
+
settings = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Corrupted file — start fresh but preserve structure
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!settings.permissions)
|
|
58
|
+
settings.permissions = {};
|
|
59
|
+
if (!settings.permissions.allow)
|
|
60
|
+
settings.permissions.allow = [];
|
|
61
|
+
// Don't add duplicates
|
|
62
|
+
if (settings.permissions.allow.includes(permission))
|
|
63
|
+
return;
|
|
64
|
+
settings.permissions.allow.push(permission);
|
|
65
|
+
mkdirSync(dir, { recursive: true });
|
|
66
|
+
const tmp = filePath + '.tmp';
|
|
67
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
|
|
68
|
+
renameSync(tmp, filePath);
|
|
69
|
+
console.log(`[native-permissions] added "${permission}" to ${filePath}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Remove a native permission from `.claude/settings.local.json`. */
|
|
73
|
+
export async function removeNativePermission(repoDir, permission) {
|
|
74
|
+
await withLock(repoDir, async () => {
|
|
75
|
+
const filePath = settingsPath(repoDir);
|
|
76
|
+
if (!existsSync(filePath))
|
|
77
|
+
return;
|
|
78
|
+
let settings = {};
|
|
79
|
+
try {
|
|
80
|
+
settings = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const allow = settings.permissions?.allow;
|
|
86
|
+
if (!allow)
|
|
87
|
+
return;
|
|
88
|
+
const idx = allow.indexOf(permission);
|
|
89
|
+
if (idx === -1)
|
|
90
|
+
return;
|
|
91
|
+
allow.splice(idx, 1);
|
|
92
|
+
const tmp = filePath + '.tmp';
|
|
93
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', { mode: 0o600 });
|
|
94
|
+
renameSync(tmp, filePath);
|
|
95
|
+
console.log(`[native-permissions] removed "${permission}" from ${filePath}`);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Convert a tool invocation to Claude Code's native permission format.
|
|
100
|
+
*
|
|
101
|
+
* Examples:
|
|
102
|
+
* - Bash + `{ command: "npm run build" }` → `"Bash(npm run build)"`
|
|
103
|
+
* - WebFetch (any input) → `"WebFetch"`
|
|
104
|
+
* - Read (file tool, pre-approved) → `null`
|
|
105
|
+
*/
|
|
106
|
+
export function toNativePermission(toolName, toolInput) {
|
|
107
|
+
// File tools are pre-approved by permission mode — no need to persist
|
|
108
|
+
const preApproved = new Set(['Read', 'Write', 'Edit', 'Glob', 'Grep', 'NotebookEdit']);
|
|
109
|
+
if (preApproved.has(toolName))
|
|
110
|
+
return null;
|
|
111
|
+
if (toolName === 'Bash') {
|
|
112
|
+
const cmd = (typeof toolInput.command === 'string' ? toolInput.command : '').trim();
|
|
113
|
+
if (!cmd)
|
|
114
|
+
return null;
|
|
115
|
+
return `Bash(${cmd})`;
|
|
116
|
+
}
|
|
117
|
+
return toolName;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Escape parentheses and backslashes in a string for use inside a
|
|
121
|
+
* `Bash(...)` allowedTools pattern. The Claude CLI's `--allowedTools`
|
|
122
|
+
* parser splits on commas/spaces but tracks a single level of `(…)`.
|
|
123
|
+
* Unescaped `)` inside the command prematurely closes the pattern,
|
|
124
|
+
* corrupting the entire allowedTools list and breaking tool resolution
|
|
125
|
+
* (manifests as "A.split is not a function" errors on unrelated tools).
|
|
126
|
+
*/
|
|
127
|
+
function escapeForAllowedTools(s) {
|
|
128
|
+
return s.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convert Codekin's approval registry into `--allowedTools` patterns.
|
|
132
|
+
*
|
|
133
|
+
* Only includes tool names and wildcard patterns — NOT exact commands.
|
|
134
|
+
* Exact commands accumulate over time (hundreds of multi-line scripts)
|
|
135
|
+
* and when included in `--allowedTools` they create an argument so large
|
|
136
|
+
* and complex that it breaks the CLI's parser (manifests as "A.split is
|
|
137
|
+
* not a function" errors on unrelated tools like Agent and TodoWrite).
|
|
138
|
+
* Exact commands are still auto-approved at runtime via the approval hook.
|
|
139
|
+
*
|
|
140
|
+
* Translations:
|
|
141
|
+
* - Pattern `"git diff *"` → `"Bash(git diff:*)"`
|
|
142
|
+
* - Tool `"WebFetch"` → `"WebFetch"`
|
|
143
|
+
*/
|
|
144
|
+
export function toAllowedToolsPatterns(approvals) {
|
|
145
|
+
const result = [];
|
|
146
|
+
for (const tool of approvals.tools) {
|
|
147
|
+
result.push(tool);
|
|
148
|
+
}
|
|
149
|
+
// Exact commands are intentionally excluded from --allowedTools.
|
|
150
|
+
// They are handled by the auto-approval hook at prompt time instead.
|
|
151
|
+
for (const pattern of approvals.patterns) {
|
|
152
|
+
// Convert "prefix *" → "Bash(prefix:*)"
|
|
153
|
+
if (pattern.endsWith(' *')) {
|
|
154
|
+
const prefix = pattern.slice(0, -2);
|
|
155
|
+
result.push(`Bash(${escapeForAllowedTools(prefix)}:*)`);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
result.push(`Bash(${escapeForAllowedTools(pattern)})`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=native-permissions.js.map
|