localdeck 1.2.1 → 1.2.3
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 +14 -0
- package/README.md +1 -0
- package/dashboard/assets/index-nFZAC5Mt.js +20 -0
- package/dashboard/index.html +1 -1
- package/dist/{index-19n321tr.js → index-6r7h3h6p.js} +22 -4
- package/dist/index.js +3 -3
- package/dist/{mcp-vkez9mq7.js → mcp-67ae2y6m.js} +1 -1
- package/dist/{update-nx00r38m.js → update-7rehj1x3.js} +47 -3
- package/docs/commands.md +5 -1
- package/package.json +1 -1
- package/dashboard/assets/index-BzX_uJtU.js +0 -20
package/dashboard/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>LocalDeck</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="/localdeck-icon.png" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-nFZAC5Mt.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-DbQzubLF.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -2991,6 +2991,11 @@ var PORTS_FILE = path.join(HOME_DIR, "ports.json");
|
|
|
2991
2991
|
var PROJECTS_FILE = path.join(HOME_DIR, "projects.json");
|
|
2992
2992
|
var DAEMON_LOG = path.join(HOME_DIR, "daemon.log");
|
|
2993
2993
|
var DEFAULT_API_PORT = Number(process.env.LOCALDECK_PORT ?? 7777);
|
|
2994
|
+
function assertUserRuntime(uid = process.getuid?.(), env = process.env) {
|
|
2995
|
+
if (uid === 0 && env.SUDO_UID && env.SUDO_UID !== "0") {
|
|
2996
|
+
throw new Error("Do not run the LocalDeck runtime with sudo. Run localdeck --update or localdeck daemon start without sudo. The updater requests administrator access only for npm when needed.");
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2994
2999
|
function ensureDirs() {
|
|
2995
3000
|
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
2996
3001
|
}
|
|
@@ -8143,11 +8148,12 @@ async function waitForProcessGroupExit(groupId, timeoutMs) {
|
|
|
8143
8148
|
return true;
|
|
8144
8149
|
}
|
|
8145
8150
|
// ../daemon/package.json
|
|
8146
|
-
var version = "1.2.
|
|
8151
|
+
var version = "1.2.3";
|
|
8147
8152
|
|
|
8148
8153
|
// ../daemon/src/index.ts
|
|
8149
8154
|
var VERSION = version;
|
|
8150
8155
|
async function startDaemon(opts = {}) {
|
|
8156
|
+
assertUserRuntime();
|
|
8151
8157
|
ensureDirs();
|
|
8152
8158
|
const dependencies = {
|
|
8153
8159
|
createStore: () => new Store,
|
|
@@ -8294,8 +8300,19 @@ function createOperationTracker() {
|
|
|
8294
8300
|
};
|
|
8295
8301
|
return { track, waitForIdle };
|
|
8296
8302
|
}
|
|
8297
|
-
async function findDaemon() {
|
|
8298
|
-
|
|
8303
|
+
async function findDaemon(stateFile = STATE_FILE) {
|
|
8304
|
+
let state;
|
|
8305
|
+
try {
|
|
8306
|
+
state = JSON.parse(fs10.readFileSync(stateFile, "utf8"));
|
|
8307
|
+
} catch (error) {
|
|
8308
|
+
const code = error.code;
|
|
8309
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
8310
|
+
throw new Error(`Cannot read LocalDeck daemon state at ${stateFile}: permission denied. The daemon may still be running under another user (often after sudo). If it was started with sudo, run sudo localdeck daemon stop, then localdeck daemon start without sudo. Do not make the state file world-readable; it contains a private control token.`);
|
|
8311
|
+
}
|
|
8312
|
+
if (code === "ENOENT" || error instanceof SyntaxError)
|
|
8313
|
+
return null;
|
|
8314
|
+
throw new Error(`Cannot read LocalDeck daemon state at ${stateFile}${code ? ` (${code})` : ""}. Check the file and directory permissions.`);
|
|
8315
|
+
}
|
|
8299
8316
|
if (!state)
|
|
8300
8317
|
return null;
|
|
8301
8318
|
try {
|
|
@@ -8337,6 +8354,7 @@ function daemonCommand() {
|
|
|
8337
8354
|
return [process.execPath, [entry, "daemon", "run"]];
|
|
8338
8355
|
}
|
|
8339
8356
|
async function ensureDaemon() {
|
|
8357
|
+
assertUserRuntime();
|
|
8340
8358
|
const running = await findDaemon();
|
|
8341
8359
|
if (running)
|
|
8342
8360
|
return running;
|
|
@@ -8410,4 +8428,4 @@ class Api {
|
|
|
8410
8428
|
shutdown = () => this.call("POST", "/api/shutdown");
|
|
8411
8429
|
}
|
|
8412
8430
|
|
|
8413
|
-
export { __toESM, __commonJS, __export, __require, wrapper_default, startForwarder, HOME_DIR, STATE_FILE, DEFAULT_API_PORT, findProjectFile, loadProject, loadProjectFile, shellCommand, isPortFree, getFreePort, serviceKey, processTree, listeningPorts, chooseUpstream, connectHost, streamLines, killProcessTree, stripAnsi, redactDiagnostic, environmentValues, listProjectSecrets, resolveProjectSecrets, startupGuidance, inferPortEnvironment, startupLayers, waitForReadiness, secretOutput, VERSION, startDaemon, findDaemon, dashboardDir, ensureDaemon, Api };
|
|
8431
|
+
export { __toESM, __commonJS, __export, __require, wrapper_default, startForwarder, HOME_DIR, STATE_FILE, DEFAULT_API_PORT, assertUserRuntime, findProjectFile, loadProject, loadProjectFile, shellCommand, isPortFree, getFreePort, serviceKey, processTree, listeningPorts, chooseUpstream, connectHost, streamLines, killProcessTree, stripAnsi, redactDiagnostic, environmentValues, listProjectSecrets, resolveProjectSecrets, startupGuidance, inferPortEnvironment, startupLayers, waitForReadiness, secretOutput, VERSION, startDaemon, findDaemon, dashboardDir, ensureDaemon, Api };
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
stripAnsi,
|
|
31
31
|
waitForReadiness,
|
|
32
32
|
wrapper_default
|
|
33
|
-
} from "./index-
|
|
33
|
+
} from "./index-6r7h3h6p.js";
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
36
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -907,13 +907,13 @@ async function main(argv, overrides = {}) {
|
|
|
907
907
|
case "--update":
|
|
908
908
|
if (rest.length)
|
|
909
909
|
throw new Error("Usage: localdeck --update");
|
|
910
|
-
return (await import("./update-
|
|
910
|
+
return (await import("./update-7rehj1x3.js")).updateLocalDeck();
|
|
911
911
|
case "up":
|
|
912
912
|
return cmdUp(rest, dependencies);
|
|
913
913
|
case "init":
|
|
914
914
|
return cmdInit(rest, dependencies);
|
|
915
915
|
case "mcp":
|
|
916
|
-
return (await import("./mcp-
|
|
916
|
+
return (await import("./mcp-67ae2y6m.js")).runMcp(rest);
|
|
917
917
|
case "doctor":
|
|
918
918
|
return cmdDoctor(rest);
|
|
919
919
|
case "forward":
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
listProjectSecrets,
|
|
10
10
|
redactDiagnostic,
|
|
11
11
|
resolveProjectSecrets
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-6r7h3h6p.js";
|
|
13
13
|
|
|
14
14
|
// ../../node_modules/.bun/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
15
15
|
var require_code = __commonJS((exports) => {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Api,
|
|
3
3
|
STATE_FILE,
|
|
4
|
+
assertUserRuntime,
|
|
4
5
|
findDaemon
|
|
5
|
-
} from "./index-
|
|
6
|
+
} from "./index-6r7h3h6p.js";
|
|
6
7
|
|
|
7
8
|
// src/update.ts
|
|
8
9
|
import { spawn } from "node:child_process";
|
|
@@ -55,6 +56,46 @@ async function execute(command, capture = false) {
|
|
|
55
56
|
child.once("close", (code, signal) => code === 0 ? resolve(output.trim()) : reject(new Error(`Command failed (${signal ?? `exit ${code}`}).`)));
|
|
56
57
|
});
|
|
57
58
|
}
|
|
59
|
+
function installationWritable(packageRoot, prefix, platform = process.platform) {
|
|
60
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
61
|
+
for (const directory of [packageRoot, paths.dirname(packageRoot), platform === "win32" ? prefix : paths.join(prefix, "bin")]) {
|
|
62
|
+
try {
|
|
63
|
+
fs.accessSync(directory, fs.constants.W_OK | fs.constants.X_OK);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const code = error.code;
|
|
66
|
+
if (code === "EACCES" || code === "EPERM")
|
|
67
|
+
return false;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
async function installUpdate(options) {
|
|
74
|
+
const platform = options.platform ?? process.platform;
|
|
75
|
+
const run = options.execute ?? execute;
|
|
76
|
+
const command = [
|
|
77
|
+
...options.npm,
|
|
78
|
+
"install",
|
|
79
|
+
"--global",
|
|
80
|
+
"--prefix",
|
|
81
|
+
options.prefix,
|
|
82
|
+
"localdeck@latest",
|
|
83
|
+
"--registry=https://registry.npmjs.org/",
|
|
84
|
+
"--ignore-scripts"
|
|
85
|
+
];
|
|
86
|
+
if ((options.writable ?? installationWritable)(options.packageRoot, options.prefix, platform)) {
|
|
87
|
+
await run(command);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (platform !== "darwin" && platform !== "linux") {
|
|
91
|
+
throw new Error("The installation requires administrator access. Install localdeck@latest with npm in an administrator terminal, then restart LocalDeck as your normal user.");
|
|
92
|
+
}
|
|
93
|
+
if (!(options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
|
|
94
|
+
throw new Error("The installation requires administrator access. Run localdeck --update in an interactive terminal so npm can request permission. Do not run LocalDeck with sudo.");
|
|
95
|
+
}
|
|
96
|
+
(options.log ?? console.log)(`Administrator access is required to update ${options.prefix}. Only npm will run with sudo; LocalDeck will continue as your normal user.`);
|
|
97
|
+
await run(["/usr/bin/sudo", "-H", "--", ...command]);
|
|
98
|
+
}
|
|
58
99
|
async function applyUpdate(steps) {
|
|
59
100
|
await steps.install();
|
|
60
101
|
const version = await steps.validate();
|
|
@@ -63,6 +104,7 @@ async function applyUpdate(steps) {
|
|
|
63
104
|
return version;
|
|
64
105
|
}
|
|
65
106
|
async function updateLocalDeck() {
|
|
107
|
+
assertUserRuntime();
|
|
66
108
|
const root = fs.realpathSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."));
|
|
67
109
|
const npm = npmCommand();
|
|
68
110
|
const globalRoot = await execute([...npm, "root", "--global"], true);
|
|
@@ -74,9 +116,9 @@ async function updateLocalDeck() {
|
|
|
74
116
|
const version = await applyUpdate({
|
|
75
117
|
install: async () => {
|
|
76
118
|
try {
|
|
77
|
-
await
|
|
119
|
+
await installUpdate({ npm, packageRoot: root, prefix });
|
|
78
120
|
} catch (error) {
|
|
79
|
-
throw new Error(`npm update failed. The daemon was not restarted.
|
|
121
|
+
throw new Error(`npm update failed. The daemon was not restarted. ${error instanceof Error ? error.message : error}`);
|
|
80
122
|
}
|
|
81
123
|
},
|
|
82
124
|
validate: async () => {
|
|
@@ -122,6 +164,8 @@ async function updateLocalDeck() {
|
|
|
122
164
|
}
|
|
123
165
|
export {
|
|
124
166
|
updateLocalDeck,
|
|
167
|
+
installationWritable,
|
|
125
168
|
installationPrefix,
|
|
169
|
+
installUpdate,
|
|
126
170
|
applyUpdate
|
|
127
171
|
};
|
package/docs/commands.md
CHANGED
|
@@ -101,6 +101,10 @@ localdeck --update
|
|
|
101
101
|
|
|
102
102
|
Available starting with 1.0.3. Installs the latest public npm release into the current global npm prefix, verifies the updated CLI, and restarts the daemon. Restarting stops daemon-managed services and public shares. Start services again afterward; terminal sessions may need to be rerun to reconnect.
|
|
103
103
|
|
|
104
|
-
A failed installation leaves the daemon running.
|
|
104
|
+
A failed installation leaves the daemon running. Starting with 1.2.3, LocalDeck checks installation permissions before updating. On macOS and Linux, a protected prefix such as `/usr/local` requests administrator access through sudo for the npm installation only. Run `localdeck --update` as your normal user in an interactive terminal; verification and daemon restart retain your user account. Cancelling authentication leaves the daemon running. Writable prefixes update without elevation. npm lifecycle scripts are disabled because the published package is prebuilt.
|
|
105
|
+
|
|
106
|
+
Non-interactive sessions cannot request administrator access. On Windows, a protected installation must be updated with npm from an administrator terminal, then LocalDeck restarted as the normal user. Source checkouts, linked builds, local installs, npx caches, and unsupported package-manager layouts must be updated manually. Custom Unix prefixes are preserved. On Windows the installation must match npm's active global root.
|
|
107
|
+
|
|
108
|
+
To get this fix when an older updater fails on a protected macOS/Linux installation, run `sudo npm install -g --prefix /usr/local localdeck@latest` (use your actual installation prefix), then `localdeck daemon stop` and `localdeck daemon start` without sudo. Future updates use `localdeck --update`.
|
|
105
109
|
|
|
106
110
|
Older versions without this command can upgrade with `npm install -g localdeck@latest` using their existing npm prefix.
|