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,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook forwarding command for Claude Code hooks
|
|
3
|
+
*
|
|
4
|
+
* This command is called by Claude Code hooks to forward events to the daemon.
|
|
5
|
+
* It reads JSON from stdin, POSTs to the daemon's hooks endpoint, and handles
|
|
6
|
+
* PermissionRequest responses.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* - CODEPIPER_UNIX_SOCK: Path to daemon socket
|
|
10
|
+
* - CODEPIPER_SESSION: Session ID for metadata
|
|
11
|
+
* - CODEPIPER_SECRET: Authentication token
|
|
12
|
+
*
|
|
13
|
+
* Exit codes:
|
|
14
|
+
* - 0: Success
|
|
15
|
+
* - 2: Block action (for PermissionRequest deny)
|
|
16
|
+
* - 1: Error
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readErrorJson, readJson, responseErrorMessage } from "../lib/api";
|
|
20
|
+
|
|
21
|
+
interface HookEvent {
|
|
22
|
+
event?: string; // Legacy format
|
|
23
|
+
hook_event_name?: string; // Claude Code 2.1.42+ format
|
|
24
|
+
session_id?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface PermissionResponse {
|
|
29
|
+
decision?: "allow" | "deny" | "ask";
|
|
30
|
+
allow?: boolean; // Backward compatibility with older daemon responses
|
|
31
|
+
denialMessage?: string;
|
|
32
|
+
message?: string;
|
|
33
|
+
updatedInput?: unknown;
|
|
34
|
+
updatedPermissions?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ClaudePermissionDecision {
|
|
38
|
+
decision: "allow" | "deny";
|
|
39
|
+
denialMessage?: string;
|
|
40
|
+
updatedInput?: unknown;
|
|
41
|
+
updatedPermissions?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read all data from stdin
|
|
46
|
+
*/
|
|
47
|
+
async function readStdin(): Promise<string> {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const chunks: Buffer[] = [];
|
|
50
|
+
|
|
51
|
+
process.stdin.on("data", (chunk) => {
|
|
52
|
+
chunks.push(Buffer.from(chunk));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
process.stdin.on("end", () => {
|
|
56
|
+
const data = Buffer.concat(chunks).toString("utf-8");
|
|
57
|
+
resolve(data);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
process.stdin.on("error", (error) => {
|
|
61
|
+
reject(error);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Resume stdin to start reading
|
|
65
|
+
process.stdin.resume();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate required environment variables
|
|
71
|
+
*/
|
|
72
|
+
function validateEnvironment(): {
|
|
73
|
+
socketPath: string;
|
|
74
|
+
sessionId: string;
|
|
75
|
+
secret: string;
|
|
76
|
+
} {
|
|
77
|
+
const socketPath = process.env.CODEPIPER_UNIX_SOCK;
|
|
78
|
+
const sessionId = process.env.CODEPIPER_SESSION;
|
|
79
|
+
const secret = process.env.CODEPIPER_SECRET;
|
|
80
|
+
|
|
81
|
+
if (!socketPath) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"CODEPIPER_UNIX_SOCK environment variable is required. " +
|
|
84
|
+
"This should be set by the daemon when spawning the session."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!sessionId) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"CODEPIPER_SESSION environment variable is required. " +
|
|
91
|
+
"This should be set by the daemon when spawning the session."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!secret) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
"CODEPIPER_SECRET environment variable is required. " +
|
|
98
|
+
"This should be set by the daemon when spawning the session."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { socketPath, sessionId, secret };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resolveEnvironment(options?: {
|
|
106
|
+
socketPath?: string;
|
|
107
|
+
sessionId?: string;
|
|
108
|
+
secret?: string;
|
|
109
|
+
}): {
|
|
110
|
+
socketPath: string;
|
|
111
|
+
sessionId: string;
|
|
112
|
+
secret: string;
|
|
113
|
+
} {
|
|
114
|
+
if (!options) {
|
|
115
|
+
return validateEnvironment();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const socketPath = options.socketPath ?? process.env.CODEPIPER_UNIX_SOCK;
|
|
119
|
+
const sessionId = options.sessionId ?? process.env.CODEPIPER_SESSION;
|
|
120
|
+
const secret = options.secret ?? process.env.CODEPIPER_SECRET;
|
|
121
|
+
|
|
122
|
+
if (!(socketPath && sessionId && secret)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"Missing hook-forward environment values. " +
|
|
125
|
+
"Required: socketPath, sessionId, secret (or CODEPIPER_* env vars)."
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { socketPath, sessionId, secret };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface ForwardHookResult {
|
|
133
|
+
exitCode: number;
|
|
134
|
+
output?: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Forward hook event to daemon
|
|
139
|
+
*/
|
|
140
|
+
export async function forwardHook(
|
|
141
|
+
input?: string,
|
|
142
|
+
options?: {
|
|
143
|
+
socketPath?: string;
|
|
144
|
+
sessionId?: string;
|
|
145
|
+
secret?: string;
|
|
146
|
+
}
|
|
147
|
+
): Promise<ForwardHookResult> {
|
|
148
|
+
// Validate/resolve environment from options + process env.
|
|
149
|
+
const { socketPath, sessionId, secret } = resolveEnvironment(options);
|
|
150
|
+
|
|
151
|
+
// Read hook event from stdin or use provided input
|
|
152
|
+
const eventInput = input ?? (await readStdin());
|
|
153
|
+
|
|
154
|
+
if (!eventInput || eventInput.trim() === "") {
|
|
155
|
+
throw new Error("No input received from stdin");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Parse JSON
|
|
159
|
+
let hookEvent: HookEvent;
|
|
160
|
+
try {
|
|
161
|
+
hookEvent = JSON.parse(eventInput);
|
|
162
|
+
} catch (error: any) {
|
|
163
|
+
throw new Error(`Invalid JSON input: ${error.message}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Build request payload in format expected by daemon
|
|
167
|
+
// Daemon expects: { sessionId, event, data }
|
|
168
|
+
// Claude Code sends either 'event' (legacy) or 'hook_event_name' (new format)
|
|
169
|
+
const eventName = hookEvent.event || hookEvent.hook_event_name;
|
|
170
|
+
|
|
171
|
+
if (!eventName) {
|
|
172
|
+
throw new Error("Missing required field: event or hook_event_name");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const payload = {
|
|
176
|
+
sessionId,
|
|
177
|
+
event: eventName,
|
|
178
|
+
data: hookEvent,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Send to daemon
|
|
182
|
+
let response: Response;
|
|
183
|
+
try {
|
|
184
|
+
response = await fetch("http://localhost/hooks/claude", {
|
|
185
|
+
unix: socketPath,
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
"X-CodePiper-Secret": secret,
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify(payload),
|
|
192
|
+
});
|
|
193
|
+
} catch (error: any) {
|
|
194
|
+
if (error.code === "ENOENT" || error.message?.includes("ENOENT")) {
|
|
195
|
+
throw new Error(`Failed to connect to daemon at ${socketPath}. Is the daemon running?`);
|
|
196
|
+
}
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Handle response
|
|
201
|
+
if (!response.ok) {
|
|
202
|
+
const errorData = await readErrorJson(response);
|
|
203
|
+
throw new Error(responseErrorMessage(response, errorData));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Parse response
|
|
207
|
+
const responseData = await readJson<PermissionResponse>(response);
|
|
208
|
+
|
|
209
|
+
// Handle PermissionRequest response
|
|
210
|
+
const isPermissionRequest = eventName === "PermissionRequest";
|
|
211
|
+
if (isPermissionRequest) {
|
|
212
|
+
const decision = responseData as PermissionResponse;
|
|
213
|
+
|
|
214
|
+
// Normalize decision across daemon versions.
|
|
215
|
+
const normalizedDecision =
|
|
216
|
+
decision.decision ??
|
|
217
|
+
(decision.allow === false ? "deny" : decision.allow === true ? "allow" : "ask");
|
|
218
|
+
|
|
219
|
+
// For "ask", emit no output and let Claude Code show interactive prompt.
|
|
220
|
+
if (normalizedDecision === "ask") {
|
|
221
|
+
return { exitCode: 0 };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const outputPayload: ClaudePermissionDecision = {
|
|
225
|
+
decision: normalizedDecision,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
if (decision.updatedInput !== undefined) {
|
|
229
|
+
outputPayload.updatedInput = decision.updatedInput;
|
|
230
|
+
}
|
|
231
|
+
if (decision.updatedPermissions !== undefined) {
|
|
232
|
+
outputPayload.updatedPermissions = decision.updatedPermissions;
|
|
233
|
+
}
|
|
234
|
+
if (normalizedDecision === "deny") {
|
|
235
|
+
const denialMessage = decision.denialMessage ?? decision.message;
|
|
236
|
+
if (typeof denialMessage === "string") {
|
|
237
|
+
outputPayload.denialMessage = denialMessage;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Output decision JSON to stdout for Claude Code to read
|
|
242
|
+
const output = JSON.stringify(outputPayload);
|
|
243
|
+
|
|
244
|
+
// Exit with appropriate code
|
|
245
|
+
const exitCode = normalizedDecision === "deny" ? 2 : 0;
|
|
246
|
+
|
|
247
|
+
return { exitCode, output };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// For other events, exit successfully without output
|
|
251
|
+
// (Per CLAUDE.md: hook stdout can inject context, so we output nothing)
|
|
252
|
+
return { exitCode: 0 };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Run hook-forward command
|
|
257
|
+
*/
|
|
258
|
+
export async function runHookForwardCommand(): Promise<void> {
|
|
259
|
+
const result = await forwardHook();
|
|
260
|
+
|
|
261
|
+
// Output to stdout if needed (PermissionRequest)
|
|
262
|
+
if (result.output) {
|
|
263
|
+
console.log(result.output);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Exit with appropriate code
|
|
267
|
+
process.exit(result.exitCode);
|
|
268
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readErrorJson, responseErrorMessage } from "../lib/api";
|
|
2
|
+
import { getRequiredValue } from "../lib/args";
|
|
3
|
+
|
|
4
|
+
export interface KeysOptions {
|
|
5
|
+
sessionId: string;
|
|
6
|
+
keys: string[];
|
|
7
|
+
socket: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function parseKeysOptions(args: string[]): KeysOptions {
|
|
11
|
+
let sessionId: string | undefined;
|
|
12
|
+
let socket = "/tmp/codepiper.sock";
|
|
13
|
+
const keys: string[] = [];
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < args.length; i++) {
|
|
16
|
+
const arg = args[i];
|
|
17
|
+
if (arg === undefined) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (arg === "--socket" || arg === "-s") {
|
|
22
|
+
socket = getRequiredValue(args, i, arg);
|
|
23
|
+
i++;
|
|
24
|
+
} else if (!arg.startsWith("-")) {
|
|
25
|
+
if (!sessionId) {
|
|
26
|
+
sessionId = arg;
|
|
27
|
+
} else {
|
|
28
|
+
keys.push(arg);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!sessionId) {
|
|
34
|
+
throw new Error("session-id is required");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (keys.length === 0) {
|
|
38
|
+
throw new Error("at least one key is required");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { sessionId, keys, socket };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function sendKeys(options: KeysOptions): Promise<void> {
|
|
45
|
+
const payload = {
|
|
46
|
+
keys: options.keys,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch(`http://localhost/sessions/${options.sessionId}/keys`, {
|
|
51
|
+
unix: options.socket,
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify(payload),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorData = await readErrorJson(response);
|
|
61
|
+
throw new Error(responseErrorMessage(response, errorData));
|
|
62
|
+
}
|
|
63
|
+
} catch (error: any) {
|
|
64
|
+
if (error.code === "ENOENT" || error.message?.includes("ENOENT")) {
|
|
65
|
+
throw new Error(`Failed to connect to daemon at ${options.socket}. Is the daemon running?`);
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function runKeysCommand(args: string[]): Promise<void> {
|
|
72
|
+
const options = parseKeysOptions(args);
|
|
73
|
+
|
|
74
|
+
await sendKeys(options);
|
|
75
|
+
|
|
76
|
+
console.log(`Keys sent to session ${options.sessionId}: ${options.keys.join(", ")}`);
|
|
77
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { daemonFetch } from "../lib/api";
|
|
2
|
+
import { getSocket } from "../lib/args";
|
|
3
|
+
import { colors, success } from "../lib/format";
|
|
4
|
+
|
|
5
|
+
export async function runKillCommand(args: string[]): Promise<void> {
|
|
6
|
+
const sessionId = args.find((a) => !a.startsWith("-"));
|
|
7
|
+
if (!sessionId) {
|
|
8
|
+
throw new Error("session-id is required");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const socket = getSocket(args);
|
|
12
|
+
|
|
13
|
+
await daemonFetch(`/sessions/${sessionId}/kill`, {
|
|
14
|
+
method: "POST",
|
|
15
|
+
socket,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
success(`Session ${colors.cyan}${sessionId.slice(0, 8)}...${colors.reset} killed`);
|
|
19
|
+
}
|