@toddzheng024/dscode-bundle 0.7.8 → 0.7.9
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/package.json +1 -1
- package/plugins/auto-review/audit.mjs +5 -1
- package/plugins/auto-review/policy.mjs +6 -2
- package/plugins/clipboard-image/index.mjs +3 -3
- package/plugins/code-review/baseline.mjs +1 -1
- package/plugins/code-review/git.mjs +15 -7
- package/plugins/email/gmail.mjs +1 -1
- package/plugins/email/inbox.mjs +1 -1
- package/plugins/email/store.mjs +2 -2
- package/plugins/session-bridge/communication.mjs +13 -4
- package/plugins/session-bridge/index.mjs +1 -1
- package/vendor/tui/dscode-clipboard-image/index.mjs +3 -3
- package/vendor/tui/dscode-email/gmail.mjs +1 -1
- package/vendor/tui/dscode-email/inbox.mjs +1 -1
- package/vendor/tui/dscode-email/store.mjs +2 -2
- package/vendor/tui/index.mjs +2 -2
package/package.json
CHANGED
|
@@ -10,7 +10,11 @@ export function auditStore(directory) {
|
|
|
10
10
|
read(id) {
|
|
11
11
|
const path = pathFor(id);
|
|
12
12
|
if (!existsSync(path)) return [];
|
|
13
|
-
|
|
13
|
+
// A torn or corrupt line is skipped, never allowed to break every later read.
|
|
14
|
+
// Audit rows are a telemetry sidecar: silent skips are accepted and the usable trail stays readable.
|
|
15
|
+
return readFileSync(path, 'utf8').split('\n').filter(Boolean).flatMap(line => {
|
|
16
|
+
try { return [JSON.parse(line)]; } catch { return []; }
|
|
17
|
+
});
|
|
14
18
|
},
|
|
15
19
|
append(id, record) {
|
|
16
20
|
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
@@ -25,7 +25,7 @@ export function redact(text) {
|
|
|
25
25
|
return String(text)
|
|
26
26
|
.replace(/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g, '[REDACTED]')
|
|
27
27
|
.replace(/\b(?:sk-[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16})\b/g, '[REDACTED]')
|
|
28
|
-
.replace(/\b(Bearer\s+)[A-Za-z0-9._
|
|
28
|
+
.replace(/\b(Bearer\s+)[A-Za-z0-9._~+/-]{8,}=*/gi, '$1[REDACTED]')
|
|
29
29
|
.replace(/((?:api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret|authorization|cookie)["']?\s*[:=]\s*["']?)([^\s"',;}]+)/gi, '$1[REDACTED]')
|
|
30
30
|
.replace(/(https?:\/\/)[^\s/@:]+:[^\s/@]+@/g, '$1[REDACTED]@');
|
|
31
31
|
}
|
|
@@ -65,7 +65,7 @@ export function parseDecision(text) {
|
|
|
65
65
|
const ESCALATION_DIAGNOSTICS = new Set(['ps', 'lsof', 'pgrep', 'sw_vers', 'uname', 'id', 'date', 'hostname', 'pwd', 'sysctl']);
|
|
66
66
|
// Quotes, substitution, redirects and chaining all mean the command can do more
|
|
67
67
|
// than the diagnostic whose name it starts with.
|
|
68
|
-
const SHELL_META = /[;&|<>`$()
|
|
68
|
+
const SHELL_META = /[;&|<>`$()[\]\n\\'"]/;
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Match one pending escalation against the read-only diagnostic allowlist.
|
|
@@ -80,5 +80,9 @@ export function escalationDiagnosticGrant(toolName, args) {
|
|
|
80
80
|
if (command.length === 0 || command.length > 200 || SHELL_META.test(command)) return undefined;
|
|
81
81
|
const argv = command.split(/\s+/);
|
|
82
82
|
if (!ESCALATION_DIAGNOSTICS.has(argv[0])) return undefined;
|
|
83
|
+
// A diagnostic is only safe read-only: `sysctl -w` and assignment-like sysctl keys write
|
|
84
|
+
// kernel state, and arguments can turn hostname/date into a system change when privileged.
|
|
85
|
+
if (argv[0] === 'sysctl' && argv.some(token => token === '-w' || token.includes('='))) return undefined;
|
|
86
|
+
if ((argv[0] === 'hostname' || argv[0] === 'date') && argv.length > 1) return undefined;
|
|
83
87
|
return { command, argv };
|
|
84
88
|
}
|
|
@@ -51,9 +51,9 @@ export async function readClipboardImage() {
|
|
|
51
51
|
await execFile(binary, [path], { timeout: 10_000, maxBuffer: 1024 });
|
|
52
52
|
} catch (error) {
|
|
53
53
|
await rm(directory, { recursive: true, force: true });
|
|
54
|
-
if (error?.code === 2) throw Error('Clipboard has no image');
|
|
55
|
-
if (error?.code === 4) throw Error('Clipboard image is too large');
|
|
56
|
-
throw Error('Could not read clipboard image');
|
|
54
|
+
if (error?.code === 2) throw Error('Clipboard has no image', { cause: error });
|
|
55
|
+
if (error?.code === 4) throw Error('Clipboard image is too large', { cause: error });
|
|
56
|
+
throw Error('Could not read clipboard image', { cause: error });
|
|
57
57
|
}
|
|
58
58
|
clipboardDirs.add(directory);
|
|
59
59
|
if (!cleanupRegistered) {
|
|
@@ -39,7 +39,7 @@ async function git(dir, cwd, args, { index, signal, env = {} } = {}) {
|
|
|
39
39
|
});
|
|
40
40
|
return stdout;
|
|
41
41
|
} catch (error) {
|
|
42
|
-
if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') throw Error('Review diff exceeds 160 KiB. Use --path to review a smaller part.');
|
|
42
|
+
if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') throw Error('Review diff exceeds 160 KiB. Use --path to review a smaller part.', { cause: error });
|
|
43
43
|
throw error;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -39,7 +39,7 @@ const matchesPath = (file, path) => !path || file === path || file.startsWith(`$
|
|
|
39
39
|
const safeLabel = value => JSON.stringify(value);
|
|
40
40
|
const sensitiveFile = file => /^(?:\.env(?:\..*)?|\.npmrc|\.pypirc|id_(?:rsa|ed25519))$|\.(?:pem|p12|pfx|key)$/i.test(posix.basename(file));
|
|
41
41
|
|
|
42
|
-
async function untracked(cwd, path, signal) {
|
|
42
|
+
export async function untracked(cwd, path, signal) {
|
|
43
43
|
const args = ['ls-files', '--others', '--exclude-standard', '-z', '--', ...(path ? [path] : [])];
|
|
44
44
|
const names = (await git(cwd, args, signal)).split('\0').filter(Boolean).filter(file => matchesPath(file, path));
|
|
45
45
|
const chunks = [], omitted = [];
|
|
@@ -51,13 +51,21 @@ async function untracked(cwd, path, signal) {
|
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
53
|
const full = join(cwd, file);
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
let data;
|
|
55
|
+
try {
|
|
56
|
+
const info = await lstat(full);
|
|
57
|
+
if (!info.isFile() || info.size > 128 * 1024) {
|
|
58
|
+
omitted.push(file);
|
|
59
|
+
chunks.push(`Untracked file omitted from review: ${safeLabel(file)} (not a small regular file)\n`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
data = await readFile(full);
|
|
63
|
+
} catch {
|
|
64
|
+
// A file that vanishes or turns unreadable between listing and reading is omitted, never fatal.
|
|
56
65
|
omitted.push(file);
|
|
57
|
-
chunks.push(`Untracked file omitted from review: ${safeLabel(file)} (
|
|
66
|
+
chunks.push(`Untracked file omitted from review: ${safeLabel(file)} (unreadable or vanished during collection)\n`);
|
|
58
67
|
continue;
|
|
59
68
|
}
|
|
60
|
-
const data = await readFile(full);
|
|
61
69
|
if (data.includes(0)) {
|
|
62
70
|
omitted.push(file);
|
|
63
71
|
chunks.push(`Untracked binary file omitted from review: ${safeLabel(file)}\n`);
|
|
@@ -88,7 +96,7 @@ export function isGitWorkspaceSync(cwd, run = execFileSync, now = Date.now()) {
|
|
|
88
96
|
// Any failure (no repository, missing directory, git unavailable) means the review tool cannot work here.
|
|
89
97
|
let value = false;
|
|
90
98
|
try { run('git', ['rev-parse', '--is-inside-work-tree'], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }); value = true; }
|
|
91
|
-
catch {
|
|
99
|
+
catch { /* any failure means the review tool cannot work here */ }
|
|
92
100
|
gitWorkspaceCache.set(cwd, { at: now, value });
|
|
93
101
|
return value;
|
|
94
102
|
}
|
|
@@ -99,7 +107,7 @@ export function isGitAvailableSync(run = execFileSync) {
|
|
|
99
107
|
if (run === execFileSync && gitAvailable !== undefined) return gitAvailable;
|
|
100
108
|
let value = false;
|
|
101
109
|
try { run('git', ['--version'], { stdio: 'ignore', timeout: 3000 }); value = true; }
|
|
102
|
-
catch {
|
|
110
|
+
catch { /* git is unavailable */ }
|
|
103
111
|
if (run === execFileSync) gitAvailable = value;
|
|
104
112
|
return value;
|
|
105
113
|
}
|
package/plugins/email/gmail.mjs
CHANGED
|
@@ -186,7 +186,7 @@ export function createGmailConnector({ inbox = createEmailInbox(), directory = j
|
|
|
186
186
|
store.write('state.json', next); return { accepted: next.accepted, skipped: next.skipped };
|
|
187
187
|
} catch (error) {
|
|
188
188
|
const safe = error.status ? error.message : /^(Google|Gmail|Grant|Set DSCODE_|Cannot read|Invalid Gmail)/.test(error.message) ? error.message : 'Gmail sync interrupted. Retry sync.';
|
|
189
|
-
store.write('state.json', { ...state, error: safe }); throw Error(safe);
|
|
189
|
+
store.write('state.json', { ...state, error: safe }); throw Error(safe, { cause: error });
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
},
|
package/plugins/email/inbox.mjs
CHANGED
|
@@ -52,7 +52,7 @@ export function createEmailInbox({ directory = process.env.DSCODE_EMAIL_DIR || j
|
|
|
52
52
|
try {
|
|
53
53
|
writeFileSync(temp, json, { flag: 'wx', mode: 0o600 });
|
|
54
54
|
renameSync(temp, target);
|
|
55
|
-
} finally { try { unlinkSync(temp); } catch
|
|
55
|
+
} finally { try { unlinkSync(temp); } catch { /* best-effort cleanup: never mask the write's own outcome */ } }
|
|
56
56
|
return mail;
|
|
57
57
|
},
|
|
58
58
|
list() {
|
package/plugins/email/store.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { tryLockExclusive } from '@deepseek-ai/node-addon-system/flock';
|
|
|
6
6
|
export function emailStore(directory, label = 'email') {
|
|
7
7
|
const read = name => {
|
|
8
8
|
try { return JSON.parse(readFileSync(join(directory, name), 'utf8')); }
|
|
9
|
-
catch (error) { if (error.code === 'ENOENT') return null; throw Error('Cannot read local ' + label + ' configuration.'); }
|
|
9
|
+
catch (error) { if (error.code === 'ENOENT') return null; throw Error('Cannot read local ' + label + ' configuration.', { cause: error }); }
|
|
10
10
|
};
|
|
11
11
|
return {
|
|
12
12
|
directory, read,
|
|
@@ -17,7 +17,7 @@ export function emailStore(directory, label = 'email') {
|
|
|
17
17
|
try {
|
|
18
18
|
writeFileSync(temp, JSON.stringify(value), { flag: 'wx', mode: 0o600 });
|
|
19
19
|
renameSync(temp, join(directory, name));
|
|
20
|
-
} finally { try { unlinkSync(temp); } catch
|
|
20
|
+
} finally { try { unlinkSync(temp); } catch { /* best-effort cleanup: never mask the write's own outcome */ } }
|
|
21
21
|
},
|
|
22
22
|
async locked(action) {
|
|
23
23
|
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
@@ -57,7 +57,7 @@ export class CommunicationService {
|
|
|
57
57
|
};
|
|
58
58
|
agent.cancel = state.cancelWrapper;
|
|
59
59
|
this.states.set(agent.id, state);
|
|
60
|
-
|
|
60
|
+
this.ensureReady(state);
|
|
61
61
|
}
|
|
62
62
|
state(agent) {
|
|
63
63
|
const state = this.states.get(agent.id);
|
|
@@ -65,6 +65,15 @@ export class CommunicationService {
|
|
|
65
65
|
this.store.authenticate(state.auth);
|
|
66
66
|
return state;
|
|
67
67
|
}
|
|
68
|
+
/** Recovery runs once per agent; a failed attempt fails closed for the step that awaited
|
|
69
|
+
* it and is retried by the next step, instead of failing every later step forever. */
|
|
70
|
+
ensureReady(state) {
|
|
71
|
+
if (!state.ready || state.readyFailed) {
|
|
72
|
+
state.readyFailed = false;
|
|
73
|
+
state.ready = this.background(this.recover(state).catch(error => { state.readyFailed = true; throw error; }));
|
|
74
|
+
}
|
|
75
|
+
return state.ready;
|
|
76
|
+
}
|
|
68
77
|
async remove(agent) {
|
|
69
78
|
const state = this.states.get(agent.id);
|
|
70
79
|
if (!state || state.agent !== agent) return;
|
|
@@ -161,7 +170,7 @@ export class CommunicationService {
|
|
|
161
170
|
for (const m of messages) if (communicationId(m)) {
|
|
162
171
|
batch.add(communicationId(m)); this.store.transition(state.auth, communicationId(m), 'admitted', `${state.auth.generation}:${turn}`);
|
|
163
172
|
}
|
|
164
|
-
await state
|
|
173
|
+
await this.ensureReady(state);
|
|
165
174
|
await this.confirm(state);
|
|
166
175
|
signal.throwIfAborted();
|
|
167
176
|
if (step === 1 && !state.cutoffs.has(turn)) fail('missing_cutoff', 'Missing turn-start mailbox cutoff');
|
|
@@ -193,7 +202,7 @@ export class CommunicationService {
|
|
|
193
202
|
}
|
|
194
203
|
async receive(agent, payload) {
|
|
195
204
|
const state = this.state(agent);
|
|
196
|
-
await state
|
|
205
|
+
await this.ensureReady(state);
|
|
197
206
|
this.store.authenticate(state.auth);
|
|
198
207
|
// Reply routing is checked both here and by the shared admission transaction.
|
|
199
208
|
let admission;
|
|
@@ -210,7 +219,7 @@ export class CommunicationService {
|
|
|
210
219
|
}
|
|
211
220
|
async send(agent, args, reply = false) {
|
|
212
221
|
if (agent.session.header.origin === 'subagent') fail('root_session_required', 'Cross-session requests and replies belong to the root session; report this to your parent agent.');
|
|
213
|
-
const state = this.state(agent); await state
|
|
222
|
+
const state = this.state(agent); await this.ensureReady(state);
|
|
214
223
|
let destination = args.session_id, kind = args.kind, inReplyTo = args.in_reply_to;
|
|
215
224
|
if (reply) {
|
|
216
225
|
const original = this.store.get(args.request_message_id);
|
|
@@ -39,7 +39,7 @@ export async function apply(ctx) {
|
|
|
39
39
|
}, async ({ project, workspace, cursor = 0, limit = 20 }) => {
|
|
40
40
|
if (!Number.isSafeInteger(cursor) || cursor < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw Error('Invalid pagination');
|
|
41
41
|
const all = (await discover(home)).filter(s => (!project || s.card?.project?.id === project) && (!workspace || s.cwd === workspace)).sort((a, b) => a.id.localeCompare(b.id));
|
|
42
|
-
return { sessions: all.slice(cursor, cursor + limit).map(({ socket, ...s }) => s), nextCursor: cursor + limit < all.length ? cursor + limit : null };
|
|
42
|
+
return { sessions: all.slice(cursor, cursor + limit).map(({ socket: _socket, ...s }) => s), nextCursor: cursor + limit < all.length ? cursor + limit : null };
|
|
43
43
|
});
|
|
44
44
|
register('read_session', 'Read a session event page without waking it or collecting deferred notes.', {
|
|
45
45
|
session_id: field('Complete active session ID', true), after: field('Last seen session event sequence', false, 'number'), limit: field('1..100', false, 'number'),
|
|
@@ -51,9 +51,9 @@ export async function readClipboardImage() {
|
|
|
51
51
|
await execFile(binary, [path], { timeout: 10_000, maxBuffer: 1024 });
|
|
52
52
|
} catch (error) {
|
|
53
53
|
await rm(directory, { recursive: true, force: true });
|
|
54
|
-
if (error?.code === 2) throw Error('Clipboard has no image');
|
|
55
|
-
if (error?.code === 4) throw Error('Clipboard image is too large');
|
|
56
|
-
throw Error('Could not read clipboard image');
|
|
54
|
+
if (error?.code === 2) throw Error('Clipboard has no image', { cause: error });
|
|
55
|
+
if (error?.code === 4) throw Error('Clipboard image is too large', { cause: error });
|
|
56
|
+
throw Error('Could not read clipboard image', { cause: error });
|
|
57
57
|
}
|
|
58
58
|
clipboardDirs.add(directory);
|
|
59
59
|
if (!cleanupRegistered) {
|
|
@@ -186,7 +186,7 @@ export function createGmailConnector({ inbox = createEmailInbox(), directory = j
|
|
|
186
186
|
store.write('state.json', next); return { accepted: next.accepted, skipped: next.skipped };
|
|
187
187
|
} catch (error) {
|
|
188
188
|
const safe = error.status ? error.message : /^(Google|Gmail|Grant|Set DSCODE_|Cannot read|Invalid Gmail)/.test(error.message) ? error.message : 'Gmail sync interrupted. Retry sync.';
|
|
189
|
-
store.write('state.json', { ...state, error: safe }); throw Error(safe);
|
|
189
|
+
store.write('state.json', { ...state, error: safe }); throw Error(safe, { cause: error });
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
},
|
|
@@ -52,7 +52,7 @@ export function createEmailInbox({ directory = process.env.DSCODE_EMAIL_DIR || j
|
|
|
52
52
|
try {
|
|
53
53
|
writeFileSync(temp, json, { flag: 'wx', mode: 0o600 });
|
|
54
54
|
renameSync(temp, target);
|
|
55
|
-
} finally { try { unlinkSync(temp); } catch
|
|
55
|
+
} finally { try { unlinkSync(temp); } catch { /* best-effort cleanup: never mask the write's own outcome */ } }
|
|
56
56
|
return mail;
|
|
57
57
|
},
|
|
58
58
|
list() {
|
|
@@ -6,7 +6,7 @@ import { tryLockExclusive } from '@deepseek-ai/node-addon-system/flock';
|
|
|
6
6
|
export function emailStore(directory, label = 'email') {
|
|
7
7
|
const read = name => {
|
|
8
8
|
try { return JSON.parse(readFileSync(join(directory, name), 'utf8')); }
|
|
9
|
-
catch (error) { if (error.code === 'ENOENT') return null; throw Error('Cannot read local ' + label + ' configuration.'); }
|
|
9
|
+
catch (error) { if (error.code === 'ENOENT') return null; throw Error('Cannot read local ' + label + ' configuration.', { cause: error }); }
|
|
10
10
|
};
|
|
11
11
|
return {
|
|
12
12
|
directory, read,
|
|
@@ -17,7 +17,7 @@ export function emailStore(directory, label = 'email') {
|
|
|
17
17
|
try {
|
|
18
18
|
writeFileSync(temp, JSON.stringify(value), { flag: 'wx', mode: 0o600 });
|
|
19
19
|
renameSync(temp, join(directory, name));
|
|
20
|
-
} finally { try { unlinkSync(temp); } catch
|
|
20
|
+
} finally { try { unlinkSync(temp); } catch { /* best-effort cleanup: never mask the write's own outcome */ } }
|
|
21
21
|
},
|
|
22
22
|
async locked(action) {
|
|
23
23
|
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
package/vendor/tui/index.mjs
CHANGED
|
@@ -32390,7 +32390,7 @@ function Header({ cwd = "", model = "", effort = "", animated = false }) {
|
|
|
32390
32390
|
if (!full) return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1 },
|
|
32391
32391
|
(0, import_react.createElement)(Text, { wrap: "truncate-end" },
|
|
32392
32392
|
(0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "❄ DSCODE"),
|
|
32393
|
-
(0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.7.
|
|
32393
|
+
(0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.7.9")),
|
|
32394
32394
|
(0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(modelName + " · " + effortName, width)),
|
|
32395
32395
|
(0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" }, welcomePath(cwd, width)));
|
|
32396
32396
|
return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1 },
|
|
@@ -32401,7 +32401,7 @@ function Header({ cwd = "", model = "", effort = "", animated = false }) {
|
|
|
32401
32401
|
(0, import_react.createElement)(Box, { flexDirection: "column", width: detailsWidth, marginTop: 2 },
|
|
32402
32402
|
(0, import_react.createElement)(Text, { color: inkColor(getPalette().text), bold: true }, "DSCODE"),
|
|
32403
32403
|
(0, import_react.createElement)(Text, { color: inkColor(getPalette().brandDeep) }, "────────────"),
|
|
32404
|
-
(0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.7.
|
|
32404
|
+
(0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.7.9"),
|
|
32405
32405
|
(0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.model"), 9) + modelName, detailsWidth)),
|
|
32406
32406
|
(0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(dscodePadEnd(dscodeT("welcome.effort"), 9) + effortName, detailsWidth)),
|
|
32407
32407
|
(0, import_react.createElement)(Text, null, " "),
|