nixamp 0.5.6 → 0.5.8

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/admin.d.ts CHANGED
@@ -28,6 +28,19 @@ interface Snapshot {
28
28
  export interface AdminOptions {
29
29
  url: string;
30
30
  key: string | null;
31
+ /**
32
+ * Every address the server found, labelled, share key not yet applied.
33
+ *
34
+ * `url` is the one this process talks to, which for a local daemon is
35
+ * loopback -- correct for asking it questions and useless for handing to
36
+ * anybody. These are the ones worth reading off the screen.
37
+ */
38
+ links: {
39
+ label: string;
40
+ url: string;
41
+ }[];
42
+ /** What it is serving, so the admin view says so without being asked. */
43
+ source: string;
31
44
  }
32
45
  /** Where to point, from the flags or from the daemon that is running. */
33
46
  export declare function resolveTarget(argv: string[]): AdminOptions;
@@ -42,6 +55,13 @@ export interface View {
42
55
  error: string;
43
56
  typing: boolean;
44
57
  restreaming: string;
58
+ /** Labelled addresses, and the key that makes them work. */
59
+ links: {
60
+ label: string;
61
+ url: string;
62
+ }[];
63
+ key: string | null;
64
+ source: string;
45
65
  }
46
66
  export declare function draw(ui: Container, theme: Theme, view: View): void;
47
67
  export {};
package/dist/admin.js CHANGED
@@ -14,13 +14,22 @@ export function resolveTarget(argv) {
14
14
  const keyAt = argv.findIndex((a) => a === "--key");
15
15
  const url = at === -1 ? null : argv[at + 1];
16
16
  const key = keyAt === -1 ? null : (argv[keyAt + 1] ?? null);
17
- if (url)
18
- return { url: url.replace(/\/+$/, ""), key };
17
+ // Pointed somewhere by hand, that address is the only one known.
18
+ if (url) {
19
+ const bare = url.replace(/\/+$/, "");
20
+ return { url: bare, key, links: [{ label: "there", url: bare }], source: "" };
21
+ }
19
22
  const state = readState();
20
23
  if (state === null) {
21
24
  throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
22
25
  }
23
- return { url: daemonUrl(state), key: key ?? state.key };
26
+ return {
27
+ url: daemonUrl(state),
28
+ key: key ?? state.key,
29
+ // A state file written before 0.5.3 has no list; loopback stands in.
30
+ links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
31
+ source: state.source,
32
+ };
24
33
  }
25
34
  /** Seconds as something a person reads at a glance. */
26
35
  export function since(ms) {
@@ -118,6 +127,7 @@ export async function admin(argv) {
118
127
  app.on("exit", () => clearInterval(timer));
119
128
  app.render(({ ui, theme }) => draw(ui, theme, {
120
129
  url: target.url, report, snapshot, error, typing, restreaming,
130
+ links: target.links, key: target.key, source: target.source,
121
131
  }));
122
132
  await app.start();
123
133
  clearInterval(timer);
@@ -159,6 +169,22 @@ export function draw(ui, theme, view) {
159
169
  ]);
160
170
  });
161
171
  });
172
+ // The links, because the point of a daemon is the phone in the other room
173
+ // and the address this process happens to talk to is the one that will not
174
+ // reach it. The key is on them: without it every address is a 401.
175
+ if (view.links.length > 0) {
176
+ const width = Math.max(...view.links.map((link) => link.label.length));
177
+ ui.panel({ title: "Share links", size: view.links.length + (view.source ? 3 : 2) }, (p) => {
178
+ for (const link of view.links) {
179
+ const full = view.key === null ? link.url : `${link.url}/s/${view.key}`;
180
+ p.text(`${link.label.padEnd(width)} ${full}`, {
181
+ fg: link.label === "on the internet" ? theme.accent : theme.foreground,
182
+ });
183
+ }
184
+ if (view.source)
185
+ p.label(view.source);
186
+ });
187
+ }
162
188
  ui.panel({ title: `Connections (${report?.active ?? 0} live)` }, (p) => {
163
189
  if (view.error) {
164
190
  p.text(view.error, { fg: theme.danger });
package/dist/daemon.d.ts CHANGED
@@ -49,8 +49,8 @@ export declare function status(): {
49
49
  /** The URL an admin client should talk to. */
50
50
  export declare function daemonUrl(state: DaemonState): string;
51
51
  /**
52
- * What `nixamp daemon start` prints, as lines, so it can be tested without
53
- * starting a daemon.
52
+ * What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
53
+ * can be tested without starting a daemon.
54
54
  *
55
55
  * Every address the server found, not one loopback link: the point of a daemon
56
56
  * is the phone in the other room, and 127.0.0.1 is the single address that
@@ -58,7 +58,7 @@ export declare function daemonUrl(state: DaemonState): string;
58
58
  * server writes that into a log file nobody reads, not to the person who just
59
59
  * typed the command.
60
60
  */
61
- export declare function daemonLines(state: DaemonState): string[];
61
+ export declare function daemonLines(state: DaemonState, uptimeMs?: number): string[];
62
62
  /**
63
63
  * Start one, detached, and wait until it is actually answering before saying
64
64
  * it started. Reporting success and leaving the user to discover a crash in a
package/dist/daemon.js CHANGED
@@ -65,8 +65,25 @@ export function daemonUrl(state) {
65
65
  return `http://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
66
66
  }
67
67
  /**
68
- * What `nixamp daemon start` prints, as lines, so it can be tested without
69
- * starting a daemon.
68
+ * How long it has been up, as a person would say it. Written here rather than
69
+ * borrowed from admin.ts, which would drag the whole terminal UI into a code
70
+ * path that only prints four lines.
71
+ */
72
+ function spell(ms) {
73
+ const seconds = Math.max(0, Math.round(ms / 1000));
74
+ if (seconds < 60)
75
+ return `${seconds}s`;
76
+ const minutes = Math.floor(seconds / 60);
77
+ if (minutes < 60)
78
+ return `${minutes}m ${seconds % 60}s`;
79
+ const hours = Math.floor(minutes / 60);
80
+ if (hours < 24)
81
+ return `${hours}h ${minutes % 60}m`;
82
+ return `${Math.floor(hours / 24)}d ${hours % 24}h`;
83
+ }
84
+ /**
85
+ * What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
86
+ * can be tested without starting a daemon.
70
87
  *
71
88
  * Every address the server found, not one loopback link: the point of a daemon
72
89
  * is the phone in the other room, and 127.0.0.1 is the single address that
@@ -74,7 +91,7 @@ export function daemonUrl(state) {
74
91
  * server writes that into a log file nobody reads, not to the person who just
75
92
  * typed the command.
76
93
  */
77
- export function daemonLines(state) {
94
+ export function daemonLines(state, uptimeMs) {
78
95
  const link = (url) => (state.key ? `${url}/s/${state.key}` : url);
79
96
  // A state file written by an older nixamp has no list, so host and port
80
97
  // still stand in rather than printing nothing at all.
@@ -84,6 +101,8 @@ export function daemonLines(state) {
84
101
  for (const { label, url } of addresses)
85
102
  lines.push(` ${label.padEnd(width)} ${link(url)}`);
86
103
  lines.push(` ${"source".padEnd(width)} ${state.source}`);
104
+ if (uptimeMs !== undefined)
105
+ lines.push(` ${"up".padEnd(width)} ${spell(uptimeMs)}`);
87
106
  if (state.guessedPublic) {
88
107
  lines.push("", " That internet address is this machine's router, not this port.", ` Nothing outside reaches it until ${state.port} is forwarded here.`);
89
108
  }
package/dist/main.js CHANGED
@@ -223,11 +223,11 @@ async function runDaemon(argv) {
223
223
  console.log(`nixamp: the daemon (pid ${state.pid}) is gone. See ${state.log}`);
224
224
  return 1;
225
225
  }
226
- const url = d.daemonUrl(state);
227
- console.log(`nixamp daemon running (pid ${state.pid})`);
228
- console.log(` ${state.key ? `${url}/s/${state.key}` : url}`);
229
- console.log(` ${state.source}`);
230
- console.log(` up ${Math.round((Date.now() - state.startedAt) / 1000)}s`);
226
+ // The same lines start prints, because the question "where is it" has the
227
+ // same answer however you ask it -- and printing loopback alone was the
228
+ // one address that cannot be handed to anybody.
229
+ for (const line of d.daemonLines(state, Date.now() - state.startedAt))
230
+ console.log(line);
231
231
  return 0;
232
232
  }
233
233
  // `nixamp daemon attach` is what people try before `nixamp attach`, so it is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nixamp",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/admin.ts CHANGED
@@ -30,6 +30,16 @@ interface Snapshot {
30
30
  export interface AdminOptions {
31
31
  url: string;
32
32
  key: string | null;
33
+ /**
34
+ * Every address the server found, labelled, share key not yet applied.
35
+ *
36
+ * `url` is the one this process talks to, which for a local daemon is
37
+ * loopback -- correct for asking it questions and useless for handing to
38
+ * anybody. These are the ones worth reading off the screen.
39
+ */
40
+ links: { label: string; url: string }[];
41
+ /** What it is serving, so the admin view says so without being asked. */
42
+ source: string;
33
43
  }
34
44
 
35
45
  /** Where to point, from the flags or from the daemon that is running. */
@@ -39,13 +49,23 @@ export function resolveTarget(argv: string[]): AdminOptions {
39
49
  const url = at === -1 ? null : argv[at + 1];
40
50
  const key = keyAt === -1 ? null : (argv[keyAt + 1] ?? null);
41
51
 
42
- if (url) return { url: url.replace(/\/+$/, ""), key };
52
+ // Pointed somewhere by hand, that address is the only one known.
53
+ if (url) {
54
+ const bare = url.replace(/\/+$/, "");
55
+ return { url: bare, key, links: [{ label: "there", url: bare }], source: "" };
56
+ }
43
57
 
44
58
  const state = readState();
45
59
  if (state === null) {
46
60
  throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
47
61
  }
48
- return { url: daemonUrl(state), key: key ?? state.key };
62
+ return {
63
+ url: daemonUrl(state),
64
+ key: key ?? state.key,
65
+ // A state file written before 0.5.3 has no list; loopback stands in.
66
+ links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
67
+ source: state.source,
68
+ };
49
69
  }
50
70
 
51
71
  /** Seconds as something a person reads at a glance. */
@@ -134,6 +154,7 @@ export async function admin(argv: string[]): Promise<void> {
134
154
  app.on("exit", () => clearInterval(timer));
135
155
  app.render(({ ui, theme }) => draw(ui, theme, {
136
156
  url: target.url, report, snapshot, error, typing, restreaming,
157
+ links: target.links, key: target.key, source: target.source,
137
158
  }));
138
159
 
139
160
  await app.start();
@@ -161,6 +182,10 @@ export interface View {
161
182
  error: string;
162
183
  typing: boolean;
163
184
  restreaming: string;
185
+ /** Labelled addresses, and the key that makes them work. */
186
+ links: { label: string; url: string }[];
187
+ key: string | null;
188
+ source: string;
164
189
  }
165
190
 
166
191
  export function draw(ui: Container, theme: Theme, view: View): void {
@@ -189,6 +214,22 @@ export function draw(ui: Container, theme: Theme, view: View): void {
189
214
  });
190
215
  });
191
216
 
217
+ // The links, because the point of a daemon is the phone in the other room
218
+ // and the address this process happens to talk to is the one that will not
219
+ // reach it. The key is on them: without it every address is a 401.
220
+ if (view.links.length > 0) {
221
+ const width = Math.max(...view.links.map((link) => link.label.length));
222
+ ui.panel({ title: "Share links", size: view.links.length + (view.source ? 3 : 2) }, (p) => {
223
+ for (const link of view.links) {
224
+ const full = view.key === null ? link.url : `${link.url}/s/${view.key}`;
225
+ p.text(`${link.label.padEnd(width)} ${full}`, {
226
+ fg: link.label === "on the internet" ? theme.accent : theme.foreground,
227
+ });
228
+ }
229
+ if (view.source) p.label(view.source);
230
+ });
231
+ }
232
+
192
233
  ui.panel({ title: `Connections (${report?.active ?? 0} live)` }, (p) => {
193
234
  if (view.error) {
194
235
  p.text(view.error, { fg: theme.danger });
package/src/daemon.ts CHANGED
@@ -100,8 +100,23 @@ export function daemonUrl(state: DaemonState): string {
100
100
  }
101
101
 
102
102
  /**
103
- * What `nixamp daemon start` prints, as lines, so it can be tested without
104
- * starting a daemon.
103
+ * How long it has been up, as a person would say it. Written here rather than
104
+ * borrowed from admin.ts, which would drag the whole terminal UI into a code
105
+ * path that only prints four lines.
106
+ */
107
+ function spell(ms: number): string {
108
+ const seconds = Math.max(0, Math.round(ms / 1000));
109
+ if (seconds < 60) return `${seconds}s`;
110
+ const minutes = Math.floor(seconds / 60);
111
+ if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
112
+ const hours = Math.floor(minutes / 60);
113
+ if (hours < 24) return `${hours}h ${minutes % 60}m`;
114
+ return `${Math.floor(hours / 24)}d ${hours % 24}h`;
115
+ }
116
+
117
+ /**
118
+ * What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
119
+ * can be tested without starting a daemon.
105
120
  *
106
121
  * Every address the server found, not one loopback link: the point of a daemon
107
122
  * is the phone in the other room, and 127.0.0.1 is the single address that
@@ -109,7 +124,7 @@ export function daemonUrl(state: DaemonState): string {
109
124
  * server writes that into a log file nobody reads, not to the person who just
110
125
  * typed the command.
111
126
  */
112
- export function daemonLines(state: DaemonState): string[] {
127
+ export function daemonLines(state: DaemonState, uptimeMs?: number): string[] {
113
128
  const link = (url: string): string => (state.key ? `${url}/s/${state.key}` : url);
114
129
  // A state file written by an older nixamp has no list, so host and port
115
130
  // still stand in rather than printing nothing at all.
@@ -119,6 +134,7 @@ export function daemonLines(state: DaemonState): string[] {
119
134
  const lines = [`nixamp daemon running (pid ${state.pid})`];
120
135
  for (const { label, url } of addresses) lines.push(` ${label.padEnd(width)} ${link(url)}`);
121
136
  lines.push(` ${"source".padEnd(width)} ${state.source}`);
137
+ if (uptimeMs !== undefined) lines.push(` ${"up".padEnd(width)} ${spell(uptimeMs)}`);
122
138
 
123
139
  if (state.guessedPublic) {
124
140
  lines.push(
package/src/main.ts CHANGED
@@ -252,11 +252,10 @@ async function runDaemon(argv: string[]): Promise<number> {
252
252
  console.log(`nixamp: the daemon (pid ${state.pid}) is gone. See ${state.log}`);
253
253
  return 1;
254
254
  }
255
- const url = d.daemonUrl(state);
256
- console.log(`nixamp daemon running (pid ${state.pid})`);
257
- console.log(` ${state.key ? `${url}/s/${state.key}` : url}`);
258
- console.log(` ${state.source}`);
259
- console.log(` up ${Math.round((Date.now() - state.startedAt) / 1000)}s`);
255
+ // The same lines start prints, because the question "where is it" has the
256
+ // same answer however you ask it -- and printing loopback alone was the
257
+ // one address that cannot be handed to anybody.
258
+ for (const line of d.daemonLines(state, Date.now() - state.startedAt)) console.log(line);
260
259
  return 0;
261
260
  }
262
261
 
package/web/dist/sw.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* nixamp service worker — generated, do not edit */
2
- const CACHE = "nixamp-1788939876425";
2
+ const CACHE = "nixamp-1788940827651";
3
3
  const PRECACHE = [
4
4
  "/",
5
5
  "/apple-touch-icon.png",