nixamp 0.5.2 → 0.5.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/dist/daemon.d.ts +25 -0
- package/dist/daemon.js +34 -0
- package/dist/main.js +2 -7
- package/dist/server.js +23 -8
- package/package.json +1 -1
- package/src/daemon.ts +62 -0
- package/src/main.ts +1 -7
- package/src/server.ts +27 -8
- package/web/dist/sw.js +1 -1
package/dist/daemon.d.ts
CHANGED
|
@@ -8,6 +8,20 @@ export interface DaemonState {
|
|
|
8
8
|
startedAt: number;
|
|
9
9
|
/** Where its output went, for when it died and you want to know why. */
|
|
10
10
|
log: string;
|
|
11
|
+
/**
|
|
12
|
+
* The labelled addresses the server itself worked out, share key not applied.
|
|
13
|
+
*
|
|
14
|
+
* Recorded rather than recomputed because only the server knows them: a
|
|
15
|
+
* daemon bound to every interface has no single host to print, and the
|
|
16
|
+
* public one may be a tunnel it was told about rather than an interface
|
|
17
|
+
* anybody here can see. Absent on a state file written by an older nixamp.
|
|
18
|
+
*/
|
|
19
|
+
urls?: {
|
|
20
|
+
label: string;
|
|
21
|
+
url: string;
|
|
22
|
+
}[];
|
|
23
|
+
/** The firewall standing between this port and the rest of the network. */
|
|
24
|
+
firewall?: string | null;
|
|
11
25
|
}
|
|
12
26
|
/** XDG, with the usual fallback. One daemon per user, which is one too few for nobody. */
|
|
13
27
|
export declare function stateDir(): string;
|
|
@@ -29,6 +43,17 @@ export declare function status(): {
|
|
|
29
43
|
};
|
|
30
44
|
/** The URL an admin client should talk to. */
|
|
31
45
|
export declare function daemonUrl(state: DaemonState): string;
|
|
46
|
+
/**
|
|
47
|
+
* What `nixamp daemon start` prints, as lines, so it can be tested without
|
|
48
|
+
* starting a daemon.
|
|
49
|
+
*
|
|
50
|
+
* Every address the server found, not one loopback link: the point of a daemon
|
|
51
|
+
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
52
|
+
* cannot be handed to anybody. The firewall warning comes with it because the
|
|
53
|
+
* server writes that into a log file nobody reads, not to the person who just
|
|
54
|
+
* typed the command.
|
|
55
|
+
*/
|
|
56
|
+
export declare function daemonLines(state: DaemonState): string[];
|
|
32
57
|
/**
|
|
33
58
|
* Start one, detached, and wait until it is actually answering before saying
|
|
34
59
|
* it started. Reporting success and leaving the user to discover a crash in a
|
package/dist/daemon.js
CHANGED
|
@@ -10,6 +10,7 @@ import { spawn } from "node:child_process";
|
|
|
10
10
|
import { existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
+
import { portCommands } from "./share.js";
|
|
13
14
|
/** XDG, with the usual fallback. One daemon per user, which is one too few for nobody. */
|
|
14
15
|
export function stateDir() {
|
|
15
16
|
const base = process.env["XDG_STATE_HOME"] || join(homedir(), ".local", "state");
|
|
@@ -63,6 +64,36 @@ export function daemonUrl(state) {
|
|
|
63
64
|
const host = state.host === "0.0.0.0" || state.host === "::" ? "127.0.0.1" : state.host;
|
|
64
65
|
return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
|
|
65
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* What `nixamp daemon start` prints, as lines, so it can be tested without
|
|
69
|
+
* starting a daemon.
|
|
70
|
+
*
|
|
71
|
+
* Every address the server found, not one loopback link: the point of a daemon
|
|
72
|
+
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
73
|
+
* cannot be handed to anybody. The firewall warning comes with it because the
|
|
74
|
+
* server writes that into a log file nobody reads, not to the person who just
|
|
75
|
+
* typed the command.
|
|
76
|
+
*/
|
|
77
|
+
export function daemonLines(state) {
|
|
78
|
+
const link = (url) => (state.key ? `${url}/s/${state.key}` : url);
|
|
79
|
+
// A state file written by an older nixamp has no list, so host and port
|
|
80
|
+
// still stand in rather than printing nothing at all.
|
|
81
|
+
const addresses = state.urls ?? [{ label: "here", url: daemonUrl(state) }];
|
|
82
|
+
const width = Math.max(...addresses.map((a) => a.label.length), "source".length);
|
|
83
|
+
const lines = [`nixamp daemon running (pid ${state.pid})`];
|
|
84
|
+
for (const { label, url } of addresses)
|
|
85
|
+
lines.push(` ${label.padEnd(width)} ${link(url)}`);
|
|
86
|
+
lines.push(` ${"source".padEnd(width)} ${state.source}`);
|
|
87
|
+
if (state.firewall) {
|
|
88
|
+
const { open } = portCommands(state.firewall, state.port);
|
|
89
|
+
lines.push("", ` ${state.firewall} is running, so nothing else can reach port ${state.port} yet:`, ` sudo ${open.join(" ")}`, " or `nixamp daemon stop` and start again with --open-port.");
|
|
90
|
+
}
|
|
91
|
+
if (!addresses.some((a) => a.label === "on the internet")) {
|
|
92
|
+
lines.push("", " None of those work from outside this network. If it should:", " nixamp daemon start ... --public-url https://your-tunnel.example.com");
|
|
93
|
+
}
|
|
94
|
+
lines.push("", " nixamp attach the player, in front of it", " nixamp admin who is connected", " nixamp daemon stop when you are done");
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
66
97
|
/**
|
|
67
98
|
* Start one, detached, and wait until it is actually answering before saying
|
|
68
99
|
* it started. Reporting success and leaving the user to discover a crash in a
|
|
@@ -119,11 +150,14 @@ async function waitForAnnounce(log, timeoutMs) {
|
|
|
119
150
|
try {
|
|
120
151
|
const parsed = JSON.parse(line);
|
|
121
152
|
if (parsed["nixamp"] === "listening") {
|
|
153
|
+
const urls = parsed["urls"];
|
|
122
154
|
return {
|
|
123
155
|
host: String(parsed["host"]),
|
|
124
156
|
port: Number(parsed["port"]),
|
|
125
157
|
key: parsed["key"] ?? null,
|
|
126
158
|
source: String(parsed["source"]),
|
|
159
|
+
...(Array.isArray(urls) ? { urls: urls } : {}),
|
|
160
|
+
...(typeof parsed["firewall"] === "string" ? { firewall: parsed["firewall"] } : {}),
|
|
127
161
|
};
|
|
128
162
|
}
|
|
129
163
|
}
|
package/dist/main.js
CHANGED
|
@@ -195,13 +195,8 @@ async function runDaemon(argv) {
|
|
|
195
195
|
if (action === "start") {
|
|
196
196
|
try {
|
|
197
197
|
const state = await d.start(rest, entry);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
console.log(` ${state.key ? `${url}/s/${state.key}` : url}`);
|
|
201
|
-
console.log(` ${state.source}`);
|
|
202
|
-
console.log("");
|
|
203
|
-
console.log(" nixamp admin who is connected");
|
|
204
|
-
console.log(" nixamp daemon stop when you are done");
|
|
198
|
+
for (const line of d.daemonLines(state))
|
|
199
|
+
console.log(line);
|
|
205
200
|
return 0;
|
|
206
201
|
}
|
|
207
202
|
catch (error) {
|
package/dist/server.js
CHANGED
|
@@ -2085,8 +2085,30 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
2085
2085
|
return { status: done.status, stdout: done.stdout ?? "" };
|
|
2086
2086
|
},
|
|
2087
2087
|
};
|
|
2088
|
+
// The link, not the address. Without the key the address is a 401, so
|
|
2089
|
+
// printing a bare host:port would be printing something that does not work.
|
|
2090
|
+
const addresses = reachableAddresses(options.host, port, options.publicUrl);
|
|
2091
|
+
// Listening on every interface proves the socket is open here and nothing
|
|
2092
|
+
// about the path between here and the phone.
|
|
2093
|
+
const listening = options.host === "0.0.0.0" || options.host === "::";
|
|
2094
|
+
const firewall = listening ? firewallInUse(io) : null;
|
|
2088
2095
|
if (options.announce) {
|
|
2089
|
-
|
|
2096
|
+
// Everything `nixamp daemon start` needs to print what this would have
|
|
2097
|
+
// printed. Without the addresses it could only reconstruct host and port,
|
|
2098
|
+
// which for a server bound to every interface means it printed 127.0.0.1 --
|
|
2099
|
+
// an address that works on exactly the machine you are already sitting at.
|
|
2100
|
+
// The firewall matters for the same reason: the warning was going into a
|
|
2101
|
+
// log file nobody reads instead of to the person who just typed the
|
|
2102
|
+
// command.
|
|
2103
|
+
console.log(JSON.stringify({
|
|
2104
|
+
nixamp: "listening",
|
|
2105
|
+
host: options.host,
|
|
2106
|
+
port,
|
|
2107
|
+
key,
|
|
2108
|
+
source: root,
|
|
2109
|
+
urls: addresses,
|
|
2110
|
+
firewall,
|
|
2111
|
+
}));
|
|
2090
2112
|
}
|
|
2091
2113
|
// The other half of "names now, tags later". It runs while the banner is
|
|
2092
2114
|
// printed and while the publish prompt waits, and it is deliberately not
|
|
@@ -2102,9 +2124,6 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
2102
2124
|
}
|
|
2103
2125
|
console.log(`nixamp serve — ${tracks.length} tracks under ${root}`);
|
|
2104
2126
|
console.log("");
|
|
2105
|
-
// The link, not the address. Without the key the address is a 401, so
|
|
2106
|
-
// printing a bare host:port would be printing something that does not work.
|
|
2107
|
-
const addresses = reachableAddresses(options.host, port, options.publicUrl);
|
|
2108
2127
|
const width = Math.max(...addresses.map((a) => a.label.length));
|
|
2109
2128
|
for (const { label, url } of addresses) {
|
|
2110
2129
|
console.log(` ${label.padEnd(width)} ${shareLink(url, key)}`);
|
|
@@ -2161,10 +2180,6 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
2161
2180
|
}
|
|
2162
2181
|
if (web === null)
|
|
2163
2182
|
console.log(" No built PWA found, so / has nothing to serve: run `bun run web:build`.");
|
|
2164
|
-
// Listening on every interface proves the socket is open here and nothing
|
|
2165
|
-
// about the path between here and the phone.
|
|
2166
|
-
const listening = options.host === "0.0.0.0" || options.host === "::";
|
|
2167
|
-
const firewall = listening ? firewallInUse(io) : null;
|
|
2168
2183
|
let closePort = null;
|
|
2169
2184
|
if (firewall !== null) {
|
|
2170
2185
|
const { open, close } = portCommands(firewall, port);
|
package/package.json
CHANGED
package/src/daemon.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { spawn } from "node:child_process";
|
|
|
10
10
|
import { existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
+
import { type Firewall, portCommands } from "./share.ts";
|
|
13
14
|
|
|
14
15
|
export interface DaemonState {
|
|
15
16
|
pid: number;
|
|
@@ -21,6 +22,17 @@ export interface DaemonState {
|
|
|
21
22
|
startedAt: number;
|
|
22
23
|
/** Where its output went, for when it died and you want to know why. */
|
|
23
24
|
log: string;
|
|
25
|
+
/**
|
|
26
|
+
* The labelled addresses the server itself worked out, share key not applied.
|
|
27
|
+
*
|
|
28
|
+
* Recorded rather than recomputed because only the server knows them: a
|
|
29
|
+
* daemon bound to every interface has no single host to print, and the
|
|
30
|
+
* public one may be a tunnel it was told about rather than an interface
|
|
31
|
+
* anybody here can see. Absent on a state file written by an older nixamp.
|
|
32
|
+
*/
|
|
33
|
+
urls?: { label: string; url: string }[];
|
|
34
|
+
/** The firewall standing between this port and the rest of the network. */
|
|
35
|
+
firewall?: string | null;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
/** XDG, with the usual fallback. One daemon per user, which is one too few for nobody. */
|
|
@@ -82,6 +94,53 @@ export function daemonUrl(state: DaemonState): string {
|
|
|
82
94
|
return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
/**
|
|
98
|
+
* What `nixamp daemon start` prints, as lines, so it can be tested without
|
|
99
|
+
* starting a daemon.
|
|
100
|
+
*
|
|
101
|
+
* Every address the server found, not one loopback link: the point of a daemon
|
|
102
|
+
* is the phone in the other room, and 127.0.0.1 is the single address that
|
|
103
|
+
* cannot be handed to anybody. The firewall warning comes with it because the
|
|
104
|
+
* server writes that into a log file nobody reads, not to the person who just
|
|
105
|
+
* typed the command.
|
|
106
|
+
*/
|
|
107
|
+
export function daemonLines(state: DaemonState): string[] {
|
|
108
|
+
const link = (url: string): string => (state.key ? `${url}/s/${state.key}` : url);
|
|
109
|
+
// A state file written by an older nixamp has no list, so host and port
|
|
110
|
+
// still stand in rather than printing nothing at all.
|
|
111
|
+
const addresses = state.urls ?? [{ label: "here", url: daemonUrl(state) }];
|
|
112
|
+
const width = Math.max(...addresses.map((a) => a.label.length), "source".length);
|
|
113
|
+
|
|
114
|
+
const lines = [`nixamp daemon running (pid ${state.pid})`];
|
|
115
|
+
for (const { label, url } of addresses) lines.push(` ${label.padEnd(width)} ${link(url)}`);
|
|
116
|
+
lines.push(` ${"source".padEnd(width)} ${state.source}`);
|
|
117
|
+
|
|
118
|
+
if (state.firewall) {
|
|
119
|
+
const { open } = portCommands(state.firewall as Firewall, state.port);
|
|
120
|
+
lines.push(
|
|
121
|
+
"",
|
|
122
|
+
` ${state.firewall} is running, so nothing else can reach port ${state.port} yet:`,
|
|
123
|
+
` sudo ${open.join(" ")}`,
|
|
124
|
+
" or `nixamp daemon stop` and start again with --open-port.",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (!addresses.some((a) => a.label === "on the internet")) {
|
|
128
|
+
lines.push(
|
|
129
|
+
"",
|
|
130
|
+
" None of those work from outside this network. If it should:",
|
|
131
|
+
" nixamp daemon start ... --public-url https://your-tunnel.example.com",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
lines.push(
|
|
136
|
+
"",
|
|
137
|
+
" nixamp attach the player, in front of it",
|
|
138
|
+
" nixamp admin who is connected",
|
|
139
|
+
" nixamp daemon stop when you are done",
|
|
140
|
+
);
|
|
141
|
+
return lines;
|
|
142
|
+
}
|
|
143
|
+
|
|
85
144
|
/**
|
|
86
145
|
* Start one, detached, and wait until it is actually answering before saying
|
|
87
146
|
* it started. Reporting success and leaving the user to discover a crash in a
|
|
@@ -142,11 +201,14 @@ async function waitForAnnounce(
|
|
|
142
201
|
try {
|
|
143
202
|
const parsed = JSON.parse(line) as { nixamp?: string } & Record<string, unknown>;
|
|
144
203
|
if (parsed["nixamp"] === "listening") {
|
|
204
|
+
const urls = parsed["urls"];
|
|
145
205
|
return {
|
|
146
206
|
host: String(parsed["host"]),
|
|
147
207
|
port: Number(parsed["port"]),
|
|
148
208
|
key: (parsed["key"] as string | null) ?? null,
|
|
149
209
|
source: String(parsed["source"]),
|
|
210
|
+
...(Array.isArray(urls) ? { urls: urls as { label: string; url: string }[] } : {}),
|
|
211
|
+
...(typeof parsed["firewall"] === "string" ? { firewall: parsed["firewall"] } : {}),
|
|
150
212
|
};
|
|
151
213
|
}
|
|
152
214
|
} catch {
|
package/src/main.ts
CHANGED
|
@@ -224,13 +224,7 @@ async function runDaemon(argv: string[]): Promise<number> {
|
|
|
224
224
|
if (action === "start") {
|
|
225
225
|
try {
|
|
226
226
|
const state = await d.start(rest, entry);
|
|
227
|
-
|
|
228
|
-
const url = d.daemonUrl(state);
|
|
229
|
-
console.log(` ${state.key ? `${url}/s/${state.key}` : url}`);
|
|
230
|
-
console.log(` ${state.source}`);
|
|
231
|
-
console.log("");
|
|
232
|
-
console.log(" nixamp admin who is connected");
|
|
233
|
-
console.log(" nixamp daemon stop when you are done");
|
|
227
|
+
for (const line of d.daemonLines(state)) console.log(line);
|
|
234
228
|
return 0;
|
|
235
229
|
} catch (error) {
|
|
236
230
|
console.error((error as Error).message);
|
package/src/server.ts
CHANGED
|
@@ -2390,8 +2390,34 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
|
|
|
2390
2390
|
},
|
|
2391
2391
|
};
|
|
2392
2392
|
|
|
2393
|
+
// The link, not the address. Without the key the address is a 401, so
|
|
2394
|
+
// printing a bare host:port would be printing something that does not work.
|
|
2395
|
+
const addresses = reachableAddresses(options.host, port, options.publicUrl);
|
|
2396
|
+
|
|
2397
|
+
// Listening on every interface proves the socket is open here and nothing
|
|
2398
|
+
// about the path between here and the phone.
|
|
2399
|
+
const listening = options.host === "0.0.0.0" || options.host === "::";
|
|
2400
|
+
const firewall = listening ? firewallInUse(io) : null;
|
|
2401
|
+
|
|
2393
2402
|
if (options.announce) {
|
|
2394
|
-
|
|
2403
|
+
// Everything `nixamp daemon start` needs to print what this would have
|
|
2404
|
+
// printed. Without the addresses it could only reconstruct host and port,
|
|
2405
|
+
// which for a server bound to every interface means it printed 127.0.0.1 --
|
|
2406
|
+
// an address that works on exactly the machine you are already sitting at.
|
|
2407
|
+
// The firewall matters for the same reason: the warning was going into a
|
|
2408
|
+
// log file nobody reads instead of to the person who just typed the
|
|
2409
|
+
// command.
|
|
2410
|
+
console.log(
|
|
2411
|
+
JSON.stringify({
|
|
2412
|
+
nixamp: "listening",
|
|
2413
|
+
host: options.host,
|
|
2414
|
+
port,
|
|
2415
|
+
key,
|
|
2416
|
+
source: root,
|
|
2417
|
+
urls: addresses,
|
|
2418
|
+
firewall,
|
|
2419
|
+
}),
|
|
2420
|
+
);
|
|
2395
2421
|
}
|
|
2396
2422
|
|
|
2397
2423
|
// The other half of "names now, tags later". It runs while the banner is
|
|
@@ -2410,9 +2436,6 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
|
|
|
2410
2436
|
console.log(`nixamp serve — ${tracks.length} tracks under ${root}`);
|
|
2411
2437
|
console.log("");
|
|
2412
2438
|
|
|
2413
|
-
// The link, not the address. Without the key the address is a 401, so
|
|
2414
|
-
// printing a bare host:port would be printing something that does not work.
|
|
2415
|
-
const addresses = reachableAddresses(options.host, port, options.publicUrl);
|
|
2416
2439
|
const width = Math.max(...addresses.map((a) => a.label.length));
|
|
2417
2440
|
for (const { label, url } of addresses) {
|
|
2418
2441
|
console.log(` ${label.padEnd(width)} ${shareLink(url, key)}`);
|
|
@@ -2469,10 +2492,6 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
|
|
|
2469
2492
|
}
|
|
2470
2493
|
if (web === null) console.log(" No built PWA found, so / has nothing to serve: run `bun run web:build`.");
|
|
2471
2494
|
|
|
2472
|
-
// Listening on every interface proves the socket is open here and nothing
|
|
2473
|
-
// about the path between here and the phone.
|
|
2474
|
-
const listening = options.host === "0.0.0.0" || options.host === "::";
|
|
2475
|
-
const firewall = listening ? firewallInUse(io) : null;
|
|
2476
2495
|
let closePort: (() => void) | null = null;
|
|
2477
2496
|
|
|
2478
2497
|
if (firewall !== null) {
|
package/web/dist/sw.js
CHANGED