ledung 1.1.0 → 1.3.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/CHANGELOG.md +1 -0
- package/dist/main.js +41 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,7 @@ that changes `src/`, `web/` (not `web/dist/`) or `scripts/` without one (a commi
|
|
|
10
10
|
is nothing user-facing in it).
|
|
11
11
|
|
|
12
12
|
## 2026-11-02
|
|
13
|
+
- Started at sign-in on macOS or Linux, or from Ledung.app, Ledung now gets the same PATH as in your terminal, so Update Ledung, Ledung's GitHub features and the tools your sessions use work after a login too; a shell profile that hangs delays the start by about ten seconds instead of stopping it. Switch Start Ledung when I sign in off and on in Settings to get it (#464)
|
|
13
14
|
- Settings → About has an update channel, Public or Dev: on Dev an npm-installed Ledung gets dev builds before they go to everyone, and Update & restart now checks npm and updates an npm install with one click (#461)
|
|
14
15
|
|
|
15
16
|
## 2026-11-01
|
package/dist/main.js
CHANGED
|
@@ -4015,15 +4015,15 @@ var Shortcuts = class {
|
|
|
4015
4015
|
await this.writeLinks([file], ["--no-open"], null, "Start Ledung in the background");
|
|
4016
4016
|
} else if (kind === "mac") {
|
|
4017
4017
|
const run8 = [...this.nodeOptions(), script, ...startsInBackground(this.opts.launch) ? ["--foreground"] : [], "--no-open"].map(shQuote).join(" ");
|
|
4018
|
-
|
|
4019
|
-
fs13.writeFileSync(file, AGENT_PLIST(["/bin/sh", "-c", [...findNode(node), `[ -x "$NODE" ] || { ${missing}; }`, `exec "$NODE" ${run8}`].join("\n")], this.logFile()));
|
|
4018
|
+
fs13.writeFileSync(file, AGENT_PLIST(["/bin/sh", "-c", signInScript(node, run8, "/bin/zsh").join("\n")], this.logFile()));
|
|
4020
4019
|
} else {
|
|
4020
|
+
const run8 = [...this.nodeOptions(), script, "--no-open"].map(shQuote).join(" ");
|
|
4021
4021
|
fs13.writeFileSync(file, [
|
|
4022
4022
|
"[Desktop Entry]",
|
|
4023
4023
|
"Type=Application",
|
|
4024
4024
|
"Name=Ledung",
|
|
4025
4025
|
"Comment=Start Ledung in the background. Made by Ledung's Settings (issue #456).",
|
|
4026
|
-
`Exec=${[
|
|
4026
|
+
`Exec=${["/bin/sh", "-c", signInScript(node, run8, "/bin/sh").join("; ")].map(desktopArg).join(" ")}`,
|
|
4027
4027
|
"Terminal=false",
|
|
4028
4028
|
"X-GNOME-Autostart-enabled=true",
|
|
4029
4029
|
""
|
|
@@ -4054,6 +4054,9 @@ var Shortcuts = class {
|
|
|
4054
4054
|
...findNode(node),
|
|
4055
4055
|
`[ -x "$NODE" ] || { osascript -e 'display alert "Ledung cannot find Node.js" message "Install Node.js 24, or add the shortcut again in Settings in Ledung."'; exit 1; }`,
|
|
4056
4056
|
`"$NODE" ${run8} --open-only && exit 0`,
|
|
4057
|
+
// Only for a start: opening a running Ledung's page doesn't wait for the login shell.
|
|
4058
|
+
...loginPath("/bin/zsh"),
|
|
4059
|
+
...nodeOnPath(),
|
|
4057
4060
|
startsInBackground(this.opts.launch) ? `exec "$NODE" ${run8}` : `${LOG_FILE_SETTING}=${shQuote(this.logFile())} nohup "$NODE" ${run8} >> ${shQuote(this.logFile())} 2>&1 &`,
|
|
4058
4061
|
""
|
|
4059
4062
|
].join("\n"), { mode: 493 });
|
|
@@ -4071,6 +4074,23 @@ var Shortcuts = class {
|
|
|
4071
4074
|
function findNode(node) {
|
|
4072
4075
|
return [`NODE=${shQuote(node)}`, `[ -x "$NODE" ] || NODE=$("\${SHELL:-/bin/zsh}" -lic 'command -v node' 2>/dev/null | tail -n 1)`];
|
|
4073
4076
|
}
|
|
4077
|
+
var LOGIN_PATH_TICKS = 40;
|
|
4078
|
+
var PATH_MARKER = "LEDUNG_PATH=";
|
|
4079
|
+
function loginPath(fallbackShell) {
|
|
4080
|
+
const ask = `"\${SHELL:-${fallbackShell}}" -lic 'printf "\\n${PATH_MARKER}%s\\n" "$PATH"' </dev/null >"$T" 2>/dev/null & P=$!`;
|
|
4081
|
+
return [
|
|
4082
|
+
'L=; T=$(mktemp "${TMPDIR:-/tmp}/ledung-path.XXXXXX" 2>/dev/null) || T=',
|
|
4083
|
+
`[ -z "$T" ] || { ${ask}; I=0; while kill -0 "$P" 2>/dev/null && [ "$I" -lt ${LOGIN_PATH_TICKS} ]; do sleep 0.2; I=$((I + 1)); done; kill -KILL "$P" 2>/dev/null; L=$(sed -n 's/^${PATH_MARKER}//p' "$T" | tail -n 1); rm -f "$T"; } 2>/dev/null`,
|
|
4084
|
+
'if [ -n "$L" ]; then PATH=$L; LEDUNG_START_PATH=login; else LEDUNG_START_PATH=fallback; fi'
|
|
4085
|
+
];
|
|
4086
|
+
}
|
|
4087
|
+
function nodeOnPath() {
|
|
4088
|
+
return ['case ":$PATH:" in *":${NODE%/*}:"*) ;; *) PATH="$PATH:${NODE%/*}" ;; esac', "export PATH LEDUNG_START_PATH"];
|
|
4089
|
+
}
|
|
4090
|
+
function signInScript(node, run8, fallbackShell) {
|
|
4091
|
+
const missing = 'echo "Ledung cannot find Node.js. Install Node.js 24, or switch Start at sign-in off and on in Settings in Ledung." >&2; exit 1';
|
|
4092
|
+
return [...loginPath(fallbackShell), ...findNode(node), `[ -x "$NODE" ] || { ${missing}; }`, ...nodeOnPath(), `exec "$NODE" ${run8}`];
|
|
4093
|
+
}
|
|
4074
4094
|
function isOurApp(app) {
|
|
4075
4095
|
try {
|
|
4076
4096
|
return fs13.readFileSync(path16.join(app, "Contents", "Info.plist"), "utf8").includes(`<string>${BUNDLE_ID}</string>`);
|
|
@@ -15298,6 +15318,7 @@ var LINK_PAIRINGS_KEPT = 20;
|
|
|
15298
15318
|
var LINK_PAIR_TIMES_KEY = "link-pair-times";
|
|
15299
15319
|
var WAITING_POLL_MS = 3e4;
|
|
15300
15320
|
var WAITING_PUSH_MS = 1e3;
|
|
15321
|
+
var exercised463 = false;
|
|
15301
15322
|
var LINKED_HERE = "Computers are linked and unlinked";
|
|
15302
15323
|
var MAX_BODY2 = 1024 * 1024;
|
|
15303
15324
|
var NOT_PAIRED = "This device is not paired with Ledung. Pair it from Settings \u2192 Phones in Ledung on the computer.";
|
|
@@ -15677,6 +15698,7 @@ var LedungServer = class {
|
|
|
15677
15698
|
if (this.remote) this.setRemote(this.remote);
|
|
15678
15699
|
}
|
|
15679
15700
|
close() {
|
|
15701
|
+
this.closed = true;
|
|
15680
15702
|
if (this.waitingPoll) clearInterval(this.waitingPoll);
|
|
15681
15703
|
if (this.waitingPush) clearTimeout(this.waitingPush);
|
|
15682
15704
|
for (const ws of this.clients) ws.close(1001, "Ledung is shutting down.");
|
|
@@ -16031,6 +16053,11 @@ var LedungServer = class {
|
|
|
16031
16053
|
waitingThere = /* @__PURE__ */ new Map();
|
|
16032
16054
|
waitingPoll = null;
|
|
16033
16055
|
waitingPush = null;
|
|
16056
|
+
/**
|
|
16057
|
+
* Set by close: an exchange still on its way then changes nothing and starts no new one, as the database may be
|
|
16058
|
+
* closed by the time its answer comes (#463: it threw "database is not open" after a test's end).
|
|
16059
|
+
*/
|
|
16060
|
+
closed = false;
|
|
16034
16061
|
/** The count last sent to the linked computers, so an unchanged one isn't sent again. */
|
|
16035
16062
|
waitingSent = null;
|
|
16036
16063
|
/** What waits for the user here, as this computer's page counts it (App: questions, prompts, cards). */
|
|
@@ -16039,7 +16066,7 @@ var LedungServer = class {
|
|
|
16039
16066
|
}
|
|
16040
16067
|
/** Something that may change the count happened: sent to the linked computers shortly, if it did change. */
|
|
16041
16068
|
waitingChanged() {
|
|
16042
|
-
if (this.waitingPush || !this.linked || !this.servers.length) return;
|
|
16069
|
+
if (this.closed || this.waitingPush || !this.linked || !this.servers.length) return;
|
|
16043
16070
|
this.waitingPush = setTimeout(() => {
|
|
16044
16071
|
this.waitingPush = null;
|
|
16045
16072
|
if (this.waitingNow() !== this.waitingSent) this.exchangeWaiting();
|
|
@@ -16049,7 +16076,7 @@ var LedungServer = class {
|
|
|
16049
16076
|
/** Sends this computer's count to every linked one and keeps theirs; one that doesn't answer loses its badge. */
|
|
16050
16077
|
exchangeWaiting() {
|
|
16051
16078
|
const linked = this.linked;
|
|
16052
|
-
if (!linked) return;
|
|
16079
|
+
if (!linked || this.closed) return;
|
|
16053
16080
|
const count = this.waitingNow();
|
|
16054
16081
|
this.waitingSent = count;
|
|
16055
16082
|
for (const link of linked.list()) {
|
|
@@ -16058,6 +16085,13 @@ var LedungServer = class {
|
|
|
16058
16085
|
}
|
|
16059
16086
|
/** A linked computer's count, or undefined when it is offline: the open pages get its tab's badge at once. */
|
|
16060
16087
|
noteWaiting(id, raw) {
|
|
16088
|
+
if (this.closed) {
|
|
16089
|
+
if (!exercised463) {
|
|
16090
|
+
exercised463 = true;
|
|
16091
|
+
exercisedLine("EXERCISED #463 a linked computer's answer after close was let go");
|
|
16092
|
+
}
|
|
16093
|
+
return;
|
|
16094
|
+
}
|
|
16061
16095
|
const count = typeof raw === "number" && Number.isInteger(raw) && raw >= 0 && raw < 1e6 ? raw : void 0;
|
|
16062
16096
|
if (this.waitingThere.get(id) === count) return;
|
|
16063
16097
|
if (count === void 0) this.waitingThere.delete(id);
|
|
@@ -21142,6 +21176,8 @@ async function main() {
|
|
|
21142
21176
|
}
|
|
21143
21177
|
if (PACKAGED && createDefaultConfig()) console.log(`Wrote a default config to ${configPath()}.`);
|
|
21144
21178
|
const config = loadConfig();
|
|
21179
|
+
const startPath = envSetting("START_PATH");
|
|
21180
|
+
if (startPath) exercisedLine(`EXERCISED #464 started with the ${startPath === "login" ? "login shell's" : "fallback"} PATH`);
|
|
21145
21181
|
const remote = config.remote ?? savedRemote(remoteDir());
|
|
21146
21182
|
if (!fs38.existsSync(databaseFile())) console.log(ANTHROPIC_NOTICE);
|
|
21147
21183
|
const db = openDatabase();
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ledung",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "ledung",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.3.0",
|
|
10
10
|
"license": "AGPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/claude-agent-sdk": "0.3.295",
|
package/package.json
CHANGED