@co0ontty/wand 1.1.1 → 1.1.2

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/dist/config.js CHANGED
@@ -14,6 +14,7 @@ export const defaultConfig = () => ({
14
14
  defaultCwd: process.cwd(),
15
15
  startupCommands: [],
16
16
  allowedCommandPrefixes: [],
17
+ shortcutLogMaxBytes: 10 * 1024 * 1024,
17
18
  commandPresets: [
18
19
  {
19
20
  label: "Claude",
@@ -87,6 +88,9 @@ function mergeWithDefaults(input) {
87
88
  defaultCwd: typeof input.defaultCwd === "string" && input.defaultCwd.trim()
88
89
  ? input.defaultCwd
89
90
  : defaults.defaultCwd,
91
+ shortcutLogMaxBytes: typeof input.shortcutLogMaxBytes === "number" && input.shortcutLogMaxBytes >= 0
92
+ ? input.shortcutLogMaxBytes
93
+ : defaults.shortcutLogMaxBytes,
90
94
  startupCommands: Array.isArray(input.startupCommands) ? input.startupCommands : defaults.startupCommands,
91
95
  allowedCommandPrefixes: Array.isArray(input.allowedCommandPrefixes)
92
96
  ? input.allowedCommandPrefixes
@@ -731,7 +731,7 @@ export class ProcessManager extends EventEmitter {
731
731
  super();
732
732
  this.config = config;
733
733
  this.storage = storage;
734
- this.logger = new SessionLogger(configDir || path.join(process.env.HOME || process.cwd(), ".wand"));
734
+ this.logger = new SessionLogger(configDir || path.join(process.env.HOME || process.cwd(), ".wand"), config.shortcutLogMaxBytes);
735
735
  // Initialize lifecycle manager
736
736
  this.lifecycleManager = new SessionLifecycleManager({
737
737
  onStateChange: (sessionId, oldState, newState) => {
@@ -1179,11 +1179,17 @@ export class ProcessManager extends EventEmitter {
1179
1179
  inputLength: input.length,
1180
1180
  view: view ?? "chat"
1181
1181
  });
1182
- // Log shortcut key interactions in managed/full-access modes for auto-confirm analysis
1183
- if (shortcutKey && record.autoApprovePermissions) {
1182
+ // Log shortcut key interactions for auto-confirm and mode analysis
1183
+ if (shortcutKey) {
1184
1184
  const outputLines = record.output.split("\n");
1185
1185
  const tailLines = outputLines.slice(-15).join("\n");
1186
- this.logger.appendShortcutLog(id, shortcutKey, tailLines);
1186
+ const ctx = {
1187
+ mode: record.mode,
1188
+ autoApprove: record.autoApprovePermissions,
1189
+ permissionBlocked: record.ptyPermissionBlocked || !!record.pendingEscalation,
1190
+ input,
1191
+ };
1192
+ this.logger.appendShortcutLog(id, shortcutKey, tailLines, ctx);
1187
1193
  }
1188
1194
  // Track user input via bridge for Chat mode
1189
1195
  if (record.ptyBridge) {
@@ -1,4 +1,15 @@
1
- import type { ConversationTurn } from "./types.js";
1
+ import type { ConversationTurn, ExecutionMode } from "./types.js";
2
+ /** Context passed alongside a shortcut key interaction for richer logging */
3
+ export interface ShortcutLogContext {
4
+ /** Execution mode the session is running in (e.g. "managed", "full-access") */
5
+ mode: ExecutionMode;
6
+ /** Whether auto-approve is active for this session */
7
+ autoApprove: boolean;
8
+ /** Whether a permission prompt was blocking at the time of the keypress */
9
+ permissionBlocked: boolean;
10
+ /** The actual input string sent to PTY */
11
+ input: string;
12
+ }
2
13
  /**
3
14
  * SessionLogger saves raw session content to local files for debugging and analysis.
4
15
  *
@@ -11,7 +22,8 @@ import type { ConversationTurn } from "./types.js";
11
22
  export declare class SessionLogger {
12
23
  private readonly baseDir;
13
24
  private readonly dirs;
14
- constructor(configDir: string);
25
+ private readonly shortcutLogMaxBytes;
26
+ constructor(configDir: string, shortcutLogMaxBytes?: number);
15
27
  private ensureDir;
16
28
  /**
17
29
  * Rotate PTY log files if the current one exceeds the size limit.
@@ -31,5 +43,7 @@ export declare class SessionLogger {
31
43
  /** Delete all log files for a session */
32
44
  deleteSession(sessionId: string): void;
33
45
  /** Append a shortcut key interaction log entry (for analyzing auto-confirm gaps) */
34
- appendShortcutLog(sessionId: string, shortcutKey: string, tailLines: string): void;
46
+ appendShortcutLog(sessionId: string, shortcutKey: string, tailLines: string, ctx?: ShortcutLogContext): void;
47
+ /** Truncate shortcut log by keeping only the most recent half of entries */
48
+ private truncateShortcutLog;
35
49
  }
@@ -1,4 +1,4 @@
1
- import { mkdirSync, rmSync, appendFileSync, writeFileSync, existsSync, statSync, renameSync, unlinkSync } from "node:fs";
1
+ import { mkdirSync, rmSync, appendFileSync, writeFileSync, readFileSync, existsSync, statSync, renameSync, unlinkSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
  // ── Constants ──
@@ -6,6 +6,8 @@ import process from "node:process";
6
6
  const PTY_LOG_MAX_SIZE = 50 * 1024 * 1024;
7
7
  /** Maximum number of rotated log files to keep */
8
8
  const PTY_LOG_MAX_ROTATIONS = 3;
9
+ /** Default max size for shortcut interaction logs per session (10 MB) */
10
+ const DEFAULT_SHORTCUT_LOG_MAX_BYTES = 10 * 1024 * 1024;
9
11
  /**
10
12
  * SessionLogger saves raw session content to local files for debugging and analysis.
11
13
  *
@@ -18,8 +20,10 @@ const PTY_LOG_MAX_ROTATIONS = 3;
18
20
  export class SessionLogger {
19
21
  baseDir;
20
22
  dirs = new Map();
21
- constructor(configDir) {
23
+ shortcutLogMaxBytes;
24
+ constructor(configDir, shortcutLogMaxBytes) {
22
25
  this.baseDir = path.join(configDir, "sessions");
26
+ this.shortcutLogMaxBytes = shortcutLogMaxBytes ?? DEFAULT_SHORTCUT_LOG_MAX_BYTES;
23
27
  try {
24
28
  mkdirSync(this.baseDir, { recursive: true });
25
29
  }
@@ -127,18 +131,50 @@ export class SessionLogger {
127
131
  this.dirs.delete(sessionId);
128
132
  }
129
133
  /** Append a shortcut key interaction log entry (for analyzing auto-confirm gaps) */
130
- appendShortcutLog(sessionId, shortcutKey, tailLines) {
134
+ appendShortcutLog(sessionId, shortcutKey, tailLines, ctx) {
135
+ if (this.shortcutLogMaxBytes <= 0)
136
+ return;
131
137
  try {
132
138
  const dir = this.ensureDir(sessionId);
139
+ const logPath = path.join(dir, "shortcut-interactions.jsonl");
133
140
  const entry = JSON.stringify({
134
141
  ts: new Date().toISOString(),
135
142
  key: shortcutKey,
143
+ mode: ctx?.mode,
144
+ autoApprove: ctx?.autoApprove,
145
+ permissionBlocked: ctx?.permissionBlocked,
146
+ input: ctx?.input,
136
147
  tail: tailLines,
137
148
  }) + "\n";
138
- appendFileSync(path.join(dir, "shortcut-interactions.jsonl"), entry);
149
+ // Check size and truncate if needed
150
+ if (existsSync(logPath)) {
151
+ const size = statSync(logPath).size;
152
+ if (size + entry.length > this.shortcutLogMaxBytes) {
153
+ this.truncateShortcutLog(logPath);
154
+ }
155
+ }
156
+ appendFileSync(logPath, entry);
139
157
  }
140
158
  catch {
141
159
  // Non-critical
142
160
  }
143
161
  }
162
+ /** Truncate shortcut log by keeping only the most recent half of entries */
163
+ truncateShortcutLog(logPath) {
164
+ try {
165
+ const content = readFileSync(logPath, "utf8");
166
+ const lines = content.split("\n").filter(Boolean);
167
+ // Keep the latter half
168
+ const keepFrom = Math.floor(lines.length / 2);
169
+ const trimmed = lines.slice(keepFrom).join("\n") + "\n";
170
+ writeFileSync(logPath, trimmed);
171
+ }
172
+ catch {
173
+ // If truncation fails, delete the file to prevent unbounded growth
174
+ try {
175
+ unlinkSync(logPath);
176
+ }
177
+ catch { /* ignore */ }
178
+ }
179
+ }
144
180
  }
package/dist/types.d.ts CHANGED
@@ -40,6 +40,8 @@ export interface WandConfig {
40
40
  startupCommands: string[];
41
41
  allowedCommandPrefixes: string[];
42
42
  commandPresets: CommandPreset[];
43
+ /** Max total size (bytes) for shortcut interaction logs per session (default: 10 MB). Set 0 to disable logging. */
44
+ shortcutLogMaxBytes?: number;
43
45
  }
44
46
  export interface CommandRequest {
45
47
  command: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {