agent-dag 3.8.0 → 3.8.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/web/index.html
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
document.documentElement.setAttribute("data-theme", stored === "light" ? "light" : "dark");
|
|
41
41
|
})();
|
|
42
42
|
</script>
|
|
43
|
-
<script type="module" crossorigin src="/assets/index-
|
|
43
|
+
<script type="module" crossorigin src="/assets/index-D4ZAGkpT.js"></script>
|
|
44
44
|
<link rel="stylesheet" crossorigin href="/assets/index-ByAgTqB8.css">
|
|
45
45
|
</head>
|
|
46
46
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-dag",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.2",
|
|
4
4
|
"description": "Live deck of Claude Code and Codex agents — watch tool calls, token spend and every Claude Code subagent on one calm canvas. Run it with npx ccdeck.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/release-notes.json
CHANGED
|
@@ -31,6 +31,18 @@
|
|
|
31
31
|
"about a defect in the type, and the suite refuses both it and no space at",
|
|
32
32
|
"all."
|
|
33
33
|
],
|
|
34
|
+
"3.8.2": [
|
|
35
|
+
{
|
|
36
|
+
"title": "\u2328\ufe0f A closed-behind-your-back dialog no longer kills every keyboard shortcut",
|
|
37
|
+
"body": "Open the context ring on a session's card and leave it open. The session finishes, and a couple of minutes later the canvas evicts it \u2014 or you press Clear. The dialog vanished, and from that moment every shortcut in the deck stopped working: Space, C, R, F, L, H, U, A, J, K, T, M, and the trash button too. Only reloading the page brought them back.\n\nThe deck was still counting the dialog as open, because it remembered which session you had opened it for rather than whether anything was on screen. It now closes the dialog when its session leaves the board, which is what should have happened in the first place. The end-of-session recap had the same problem and is fixed with it."
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"3.8.1": [
|
|
41
|
+
{
|
|
42
|
+
"title": "\ud83d\udd12 Security: update if the deck runs on a Mac",
|
|
43
|
+
"body": "A local process able to reach the deck's port \u2014 no token, no browser, no tab \u2014 could get a string of its choosing compiled as AppleScript when the deck raised a desktop notification with no page open. On macOS that is code running as you.\n\nThe strings were already passed as arguments rather than pasted into the script, which is normally the whole defence. It is not enough for `osascript`: its option parser reads any argument starting with a dash as an option, so a name beginning with `-e` was taken as more script instead of as text. Every place the deck hands `osascript` or `notify-send` something from outside now carries an explicit end-of-options marker, so nothing from outside can be read as an instruction again.\n\nNothing to change on your side beyond updating. If you would rather keep the deck off the desktop entirely, the Notifications switch in the sound menu does that, and `AGENTS_DECK_NO_NOTIFY=1` does it at launch \u2014 but the fix is the thing to have."
|
|
44
|
+
}
|
|
45
|
+
],
|
|
34
46
|
"3.8.0": [
|
|
35
47
|
{
|
|
36
48
|
"title": "\ud83d\udd15 Notifications now have an off switch",
|
|
@@ -102,10 +102,26 @@ export async function notify(title, body, platform = process.platform, deps = {}
|
|
|
102
102
|
// shell tool this descends from built its notification by interpolation and
|
|
103
103
|
// that is the one place it had left the pattern it had banned everywhere
|
|
104
104
|
// else.
|
|
105
|
+
//
|
|
106
|
+
// AND `--` BEFORE THE OPERANDS, WHICH IS THE HALF THAT WAS MISSING. Passing
|
|
107
|
+
// a string as argv is not the same as it being treated as data: osascript's
|
|
108
|
+
// option parser reads any LEADING-DASH operand as an option, so a title
|
|
109
|
+
// beginning with `-e` was taken as a second script chunk and COMPILED. The
|
|
110
|
+
// note above asserted the argv form made an attacker-chosen host name safe;
|
|
111
|
+
// it did not.
|
|
112
|
+
//
|
|
113
|
+
// The reach was the whole of it. `title` is `basename(cwd) — ccdeck` from
|
|
114
|
+
// block-notify.mjs, `cwd` arrives in the body of `POST /api/event`, and
|
|
115
|
+
// that route is in OPEN_MUTATIONS — no token, no browser identity, which is
|
|
116
|
+
// exactly the sandboxed-subprocess-with-loopback-egress case the gate
|
|
117
|
+
// comments name. Verified on Darwin 25.5 with no payload at all: without
|
|
118
|
+
// `--`, a leading `-e` operand answers "The run handler is specified more
|
|
119
|
+
// than once", which is the compiler reporting on the attacker's string;
|
|
120
|
+
// with `--`, the same string arrives as `item 1 of argv`.
|
|
105
121
|
const r = await exec("osascript", [
|
|
106
122
|
"-e",
|
|
107
123
|
'on run argv\ndisplay notification (item 2 of argv) with title (item 1 of argv)\nend run',
|
|
108
|
-
title, body,
|
|
124
|
+
"--", title, body,
|
|
109
125
|
]).catch(() => null);
|
|
110
126
|
return r?.ok === true;
|
|
111
127
|
}
|
|
@@ -158,7 +174,14 @@ export async function notify(title, body, platform = process.platform, deps = {}
|
|
|
158
174
|
], { env: { ...process.env, CCDECK_TOAST_TITLE: title, CCDECK_TOAST_BODY: body } }).catch(() => null);
|
|
159
175
|
return r?.ok === true;
|
|
160
176
|
}
|
|
161
|
-
|
|
177
|
+
// `--` for the same reason as the darwin branch above, and it is worth having
|
|
178
|
+
// even though the consequence here is smaller: notify-send parses a leading
|
|
179
|
+
// dash as an option too, so an attacker-chosen title could suppress or
|
|
180
|
+
// misdirect the notification. There is no script compiler behind it, so this
|
|
181
|
+
// is a broken notification rather than an execution — fixed anyway, because
|
|
182
|
+
// the rule is "never let a user string be read as an option" and a rule with
|
|
183
|
+
// an exception is a rule somebody will apply to the wrong call next time.
|
|
184
|
+
const r = await exec("notify-send", ["--", title, body]).catch(() => null);
|
|
162
185
|
return r?.ok === true;
|
|
163
186
|
}
|
|
164
187
|
|
|
@@ -172,7 +195,10 @@ export async function closeTab(browserKey, url, platform = process.platform, dep
|
|
|
172
195
|
// and says so in its own contract, so `osascript -` would read an empty
|
|
173
196
|
// script and report success having done nothing. The URL still travels as
|
|
174
197
|
// argv, which is the part that matters.
|
|
175
|
-
|
|
198
|
+
// `--` here too. The url comes from a real http(s) history row so it cannot
|
|
199
|
+
// begin with a dash today, but that is a property of the caller rather than
|
|
200
|
+
// of this line, and the next caller will not know it.
|
|
201
|
+
const r = await exec("osascript", ["-e", closeTabScript(app), "--", url]).catch(() => null);
|
|
176
202
|
if (!r?.ok) return { ok: false, reason: "script_failed" };
|
|
177
203
|
const said = String(r.stdout ?? "").trim();
|
|
178
204
|
return { ok: said === "closed", reason: said || "unknown" };
|
|
@@ -185,7 +211,10 @@ export async function quitBrowser(browserKey, platform = process.platform, deps
|
|
|
185
211
|
const exec = deps.run ?? run;
|
|
186
212
|
if (platform === "darwin") {
|
|
187
213
|
const r = await exec("osascript", [
|
|
188
|
-
|
|
214
|
+
// `--`, as everywhere else that hands osascript an operand. `app` comes
|
|
215
|
+
// from a fixed table so it is safe by construction; the separator costs
|
|
216
|
+
// nothing and means no reader has to go and check that.
|
|
217
|
+
"-e", 'on run argv\ntell application (item 1 of argv) to quit\nend run', "--", app,
|
|
189
218
|
]).catch(() => null);
|
|
190
219
|
return { ok: r?.ok === true, reason: r?.ok ? "quit" : "script_failed" };
|
|
191
220
|
}
|