f2f1cd9b6c
- Today's spend in the status bar and the Claude Code statusline is the
caller's own (`GET /api/pricing/cost?mine=1`, scope.ownSessionWhere), not
every user's sum for an admin or analyst.
- A screenshot pasted into a local claude pane is sent as Claude Code's own
image-paste key: Alt+V on Windows (Ctrl+V did nothing there), ^V elsewhere,
and only when the machine can read its clipboard (wl-paste/xclip + a
display on Linux), else it is uploaded (GET /api/run/native-paste {key}).
- A file manager's files pasted or dropped on a local browser go in by their
own paths, read from the OS clipboard or the file manager's selection
(server/lib/file-manager.js, POST /api/run/clipboard-files and
/selected-files): no upload, no size limit. A copied image file is pasted
by path, text that comes with a picture of it (spreadsheet cells) is
pasted as text, Windows paths go in as `C:/…` mentions.
- Tab titles for Snapshot, Artifacts, Files and Administration.
- An npm tool AgentHub installed under ~/.local is updated there, not by its
own updater into npm's global prefix.
86 lines
3.1 KiB
JavaScript
86 lines
3.1 KiB
JavaScript
/**
|
|
* @file desktop-env.js
|
|
* @description The graphical session's own environment variables, for panes
|
|
* the dashboard spawns. The server usually runs as a systemd *user* service,
|
|
* which has no `DISPLAY`/`WAYLAND_DISPLAY` of its own — so neither does any
|
|
* tmux pane it starts, and a `claude` running in one cannot reach the X or
|
|
* Wayland clipboard: pasting an image answers "no image found in clipboard"
|
|
* while the same CLI in a normal terminal pastes fine. `systemctl --user
|
|
* show-environment` is the canonical local source for those values (the
|
|
* desktop session imports them there at login), with this process's own
|
|
* environment preferred when it already has them — a checkout started from a
|
|
* terminal inherits the real ones.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const { execFileSync } = require("node:child_process");
|
|
|
|
/** The only names forwarded. A pane gets a clipboard, not the whole session. */
|
|
const FORWARDED = ["DISPLAY", "WAYLAND_DISPLAY", "XAUTHORITY", "XDG_RUNTIME_DIR"];
|
|
|
|
let execImpl = (args) =>
|
|
execFileSync("systemctl", args, {
|
|
encoding: "utf8",
|
|
timeout: 2000,
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
});
|
|
|
|
/** Test seam — same pattern as tmux.js. */
|
|
function __setExecImpl(fn) {
|
|
execImpl = fn;
|
|
}
|
|
|
|
/** `KEY=value` lines from `systemctl --user show-environment`, as a map. */
|
|
function systemdUserEnv() {
|
|
let out;
|
|
try {
|
|
out = execImpl(["--user", "show-environment"]);
|
|
} catch {
|
|
// No systemd user manager (macOS, a container, a plain login shell) — the
|
|
// process environment below is the only source then.
|
|
return {};
|
|
}
|
|
const env = {};
|
|
for (const line of String(out).split("\n")) {
|
|
const eq = line.indexOf("=");
|
|
if (eq > 0) env[line.slice(0, eq)] = line.slice(eq + 1);
|
|
}
|
|
return env;
|
|
}
|
|
|
|
/**
|
|
* The forwarded variables that actually have a usable value right now.
|
|
* A value carrying a newline is dropped rather than passed on: these end up in
|
|
* a `tmux -e NAME=value` argv, and a line break there is not a value tmux can
|
|
* represent.
|
|
*/
|
|
function desktopEnv() {
|
|
const fromSystemd = systemdUserEnv();
|
|
const out = {};
|
|
for (const name of FORWARDED) {
|
|
const value = process.env[name] || fromSystemd[name];
|
|
if (typeof value === "string" && value && !/[\r\n\0]/.test(value)) out[name] = value;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Whether a CLI in a pane started now can read this desktop's clipboard — what
|
|
* a native image paste (`^V`, the terminal's `lib/nativePaste.ts`) relies on.
|
|
* On Linux Claude Code shells out to `wl-paste` (Wayland) or `xclip` (X11);
|
|
* neither ships with a stock Ubuntu, and with neither — or with no display to
|
|
* reach — the `^V` pastes nothing at all, so the browser must upload instead.
|
|
* macOS and Windows read their clipboard with built-in tools.
|
|
*/
|
|
function canReadClipboard({
|
|
platform = process.platform,
|
|
env = null,
|
|
which = (name) => require("./ai-tools").which(name),
|
|
} = {}) {
|
|
if (platform !== "linux") return true;
|
|
const e = env || desktopEnv();
|
|
return !!((e.WAYLAND_DISPLAY && which("wl-paste")) || (e.DISPLAY && which("xclip")));
|
|
}
|
|
|
|
module.exports = { desktopEnv, canReadClipboard, FORWARDED, __setExecImpl };
|