nixamp 0.75.0 → 0.76.1

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.
Files changed (48) hide show
  1. package/dist/ai-label.js +510 -0
  2. package/dist/library-mcp.d.ts +6 -6
  3. package/dist/server.d.ts +3 -0
  4. package/dist/server.js +53 -0
  5. package/dist/signup-profiles.d.ts +15 -0
  6. package/dist/signup-profiles.js +26 -0
  7. package/dist/station-mcp.d.ts +6 -6
  8. package/package.json +2 -1
  9. package/src/ai-label.ts +510 -0
  10. package/src/server.ts +56 -0
  11. package/src/signup-profiles.ts +33 -0
  12. package/web/dist/assets/{ar-DWO4AD-h.js → ar-NhWfdGfS.js} +1 -1
  13. package/web/dist/assets/{chunk-Q2XQMA4B-DdHrVlSJ.css → chunk-Q2XQMA4B-DR1XNWCJ.css} +1 -1
  14. package/web/dist/assets/{chunk-Q2XQMA4B-BaoaSqsD.js → chunk-Q2XQMA4B-OBCD3Aev.js} +1 -1
  15. package/web/dist/assets/{cs-BYkdeLUB.js → cs-D1orW8p8.js} +1 -1
  16. package/web/dist/assets/{da-D1-4-VhC.js → da-C9c7DdFK.js} +1 -1
  17. package/web/dist/assets/{de-CaklUWbI.js → de-DsPQqmTX.js} +1 -1
  18. package/web/dist/assets/{es-CG6nwbN9.js → es-STqShmKZ.js} +1 -1
  19. package/web/dist/assets/{fi-DWfrod_V.js → fi-v5vNrYcE.js} +1 -1
  20. package/web/dist/assets/{fr-CpWd4lRG.js → fr-lKpl6rLi.js} +1 -1
  21. package/web/dist/assets/{hi-BIwcJMqA.js → hi-C1w6fo1p.js} +1 -1
  22. package/web/dist/assets/{hls-OR5FY6YP-B-70I17r.js → hls-OR5FY6YP-B2RNEz5K.js} +1 -1
  23. package/web/dist/assets/{hu-yaKuIx_R.js → hu-DFUXXymd.js} +1 -1
  24. package/web/dist/assets/{id-B20X5Jpx.js → id-BQiiFqsd.js} +1 -1
  25. package/web/dist/assets/{it-C_0lQi83.js → it-CxVVOs4D.js} +1 -1
  26. package/web/dist/assets/main-DuUFn357.js +26 -0
  27. package/web/dist/assets/{mpegts-7CN75AI2-D-bdbDNw.js → mpegts-7CN75AI2-1Qj6py7N.js} +1 -1
  28. package/web/dist/assets/{mpegts-C_-pZIIG.js → mpegts-BcF3gJrf.js} +1 -1
  29. package/web/dist/assets/music-Day-DtFd.js +1 -0
  30. package/web/dist/assets/{nl-vH0eIRvu.js → nl-Bg9nYWdM.js} +1 -1
  31. package/web/dist/assets/{radio-dUHZb5wV.js → radio-Dx44o-ug.js} +1 -1
  32. package/web/dist/assets/{ru-CyWPMStM.js → ru-Cq7_qZpi.js} +1 -1
  33. package/web/dist/assets/{station-KW1WLKF4.js → station-UibBpdBh.js} +1 -1
  34. package/web/dist/assets/{sv-pXXM1l8a.js → sv-zqKojzxL.js} +1 -1
  35. package/web/dist/assets/{uk-DcNuaLei.js → uk-BRjxYLjm.js} +1 -1
  36. package/web/dist/assets/{vi-QYkJg9a4.js → vi-Bq9TGPGs.js} +1 -1
  37. package/web/dist/assets/{vod-CdMhdfvg.js → vod-CkR6hzN7.js} +1 -1
  38. package/web/dist/assets/{watch-fq5gXk7x.js → watch-BO-wMY8T.js} +1 -1
  39. package/web/dist/assets/{zh-9sYPz_Vm.js → zh-B6Rlwv_6.js} +1 -1
  40. package/web/dist/index.html +13 -3
  41. package/web/dist/music.html +1 -1
  42. package/web/dist/radio.html +3 -3
  43. package/web/dist/station.html +3 -3
  44. package/web/dist/sw.js +29 -29
  45. package/web/dist/vod.html +3 -3
  46. package/web/dist/watch.html +3 -3
  47. package/web/dist/assets/main-reQmYdJ0.js +0 -22
  48. package/web/dist/assets/music-C8ITlUNF.js +0 -1
package/dist/server.d.ts CHANGED
@@ -12,6 +12,7 @@ import { Handles } from "./handles.ts";
12
12
  import { OpenDirs } from "./opendirs.ts";
13
13
  import { Servers } from "./servers.ts";
14
14
  import { AccountProfiles } from "./profiles.ts";
15
+ import { type ProfileStore } from "@profullstack/profile";
15
16
  import { NixampLinks } from "./nixamp-link.ts";
16
17
  import { SignIn } from "./oauth.ts";
17
18
  import { AuthorizationServer } from "./oauth-server.ts";
@@ -778,6 +779,8 @@ export interface HandlerOptions {
778
779
  handles?: Handles;
779
780
  /** The account's card: a name, a homepage, a photo, a line. One table, read by every site here. */
780
781
  accountProfiles?: AccountProfiles;
782
+ /** Name, company, title and LinkedIn from the sign-up form (@profullstack/profile). */
783
+ signupProfiles?: ProfileStore;
781
784
  /** nixamp accounts connected to accounts here, where this site is a client of nixamp.com. */
782
785
  links?: NixampLinks;
783
786
  /** Open directories people have found, which anyone may read. */
package/dist/server.js CHANGED
@@ -37,6 +37,8 @@ import { anonymousHandle, Handles } from "./handles.js";
37
37
  import { OpenDirs } from "./opendirs.js";
38
38
  import { Servers } from "./servers.js";
39
39
  import { AccountProfiles, hostCard, PHOTO_LIMIT } from "./profiles.js";
40
+ import { createProfileHandlers, saveSignupProfile, validateProfile } from "@profullstack/profile";
41
+ import { SIGNUP_PROFILE_FIELDS, signupProfiles } from "./signup-profiles.js";
40
42
  import { NixampLinks, nixampExchange } from "./nixamp-link.js";
41
43
  import { handleNixampLinkApi, nixampLinkPath } from "./nixamp-link-api.js";
42
44
  import { DeviceGrants } from "./device.js";
@@ -3428,9 +3430,28 @@ export function createHandler(engine, options) {
3428
3430
  return;
3429
3431
  }
3430
3432
  }
3433
+ // The web form sends the profile fields, and then first and last name are
3434
+ // required; a terminal or an older app that sends none is not asked.
3435
+ const asked = signingUp && options.signupProfiles
3436
+ && SIGNUP_PROFILE_FIELDS.some((field) => field in body);
3437
+ const profile = asked ? validateProfile(body) : null;
3438
+ if (profile && !profile.ok) {
3439
+ json(response, 400, { error: Object.values(profile.errors)[0] ?? "some fields need fixing", errors: profile.errors });
3440
+ return;
3441
+ }
3431
3442
  const result = signingUp
3432
3443
  ? await accounts.signUp(body.email, body.password)
3433
3444
  : await accounts.signIn(body.email, body.password);
3445
+ // Kept once the account exists. A profile that does not save is filled
3446
+ // in later from the Account panel, never a sign-up that failed.
3447
+ if (profile && result.ok && result.account && options.signupProfiles) {
3448
+ try {
3449
+ await saveSignupProfile(options.signupProfiles, result.account.id, profile.profile, { email: result.account.email });
3450
+ }
3451
+ catch (error) {
3452
+ console.error("nixamp: sign-up profile not saved:", error.message);
3453
+ }
3454
+ }
3434
3455
  // A handle asked for at sign-up, or one nobody has to think about. Never
3435
3456
  // derived from the address: turning anthony@… into "anthony" is the leak
3436
3457
  // this whole idea exists to avoid, and it is one nobody would notice
@@ -4450,6 +4471,37 @@ export function createHandler(engine, options) {
4450
4471
  //
4451
4472
  // A name, a homepage, a photo and a line, said once. A class is made from
4452
4473
  // it, a settings page edits it, and an OpenProfile.md fills it in one move.
4474
+ // The account's own profile fields (name, company, title, LinkedIn), from
4475
+ // the shared @profullstack/profile handlers. Under the host card's path
4476
+ // because /api/v1/me/profile is already that card, which strangers see.
4477
+ if (path === "/api/v1/me/profile/fields" && options.signupProfiles && options.accounts) {
4478
+ const accounts = options.accounts;
4479
+ const handlers = createProfileHandlers({
4480
+ store: options.signupProfiles,
4481
+ getUser: () => accounts.whoIs(tokenFrom(request.headers)),
4482
+ });
4483
+ const method = request.method ?? "GET";
4484
+ const handle = method === "GET" ? handlers.GET : method === "PUT" ? handlers.PUT : method === "PATCH" ? handlers.PATCH : null;
4485
+ if (handle === null) {
4486
+ json(response, 405, { error: "GET, PUT or PATCH" });
4487
+ return;
4488
+ }
4489
+ const bare = requestFor(request);
4490
+ bare.headers.delete("content-length");
4491
+ bare.headers.delete("transfer-encoding");
4492
+ let body;
4493
+ if (method !== "GET") {
4494
+ try {
4495
+ body = await readBody(request);
4496
+ }
4497
+ catch {
4498
+ json(response, 413, { error: "too large" });
4499
+ return;
4500
+ }
4501
+ }
4502
+ await answerWith(response, await handle(new Request(bare.url, { method, headers: bare.headers, body })));
4503
+ return;
4504
+ }
4453
4505
  if ((path === "/api/v1/me/profile" || path.startsWith("/api/v1/me/profile/")) && options.accountProfiles && options.accounts) {
4454
4506
  const profiles = options.accountProfiles;
4455
4507
  const who = await options.accounts.whoIs(tokenFrom(request.headers));
@@ -8459,6 +8511,7 @@ export async function serve(argv, version = "0.1.0") {
8459
8511
  handles: new Handles(pool),
8460
8512
  openDirs: new OpenDirs(pool),
8461
8513
  accountProfiles: new AccountProfiles(pool),
8514
+ signupProfiles: signupProfiles(pool),
8462
8515
  // The school's side of "Connect nixamp": tokens from nixamp.com,
8463
8516
  // kept against the school account. The issuer is this same
8464
8517
  // program in production, reached over HTTP like any client.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The shared profile every Profullstack property asks for at sign-up: first
3
+ * and last name, company domain, job title and LinkedIn, beside the email.
4
+ *
5
+ * Kept by @profullstack/profile in its own `user_profiles` table, keyed by the
6
+ * auth-system user id, and never in `nixamp_profiles`: that one is the host
7
+ * card a classroom shows to strangers, and these fields are the account's own.
8
+ * The table is made on first use, like every other nixamp table, because
9
+ * nixamp carries no migration runner.
10
+ */
11
+ import { type ProfileStore } from "@profullstack/profile";
12
+ import type { Queryable } from "./follows.ts";
13
+ /** The fields the web sign-up form sends; a client that sends none is not asked for them. */
14
+ export declare const SIGNUP_PROFILE_FIELDS: readonly ["first_name", "last_name", "company_domain", "job_title", "linkedin_url"];
15
+ export declare function signupProfiles(db: Queryable): ProfileStore;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The shared profile every Profullstack property asks for at sign-up: first
3
+ * and last name, company domain, job title and LinkedIn, beside the email.
4
+ *
5
+ * Kept by @profullstack/profile in its own `user_profiles` table, keyed by the
6
+ * auth-system user id, and never in `nixamp_profiles`: that one is the host
7
+ * card a classroom shows to strangers, and these fields are the account's own.
8
+ * The table is made on first use, like every other nixamp table, because
9
+ * nixamp carries no migration runner.
10
+ */
11
+ import { createProfileStore, schemaSql } from "@profullstack/profile";
12
+ /** The fields the web sign-up form sends; a client that sends none is not asked for them. */
13
+ export const SIGNUP_PROFILE_FIELDS = ["first_name", "last_name", "company_domain", "job_title", "linkedin_url"];
14
+ export function signupProfiles(db) {
15
+ let ready = null;
16
+ const ensure = () => (ready ??= db.query(schemaSql({ keyType: "text" })).catch((error) => {
17
+ ready = null;
18
+ throw error;
19
+ }));
20
+ return createProfileStore({
21
+ query: async (text, values) => {
22
+ await ensure();
23
+ return db.query(text, values);
24
+ },
25
+ }, { keyType: "text" });
26
+ }
@@ -2,7 +2,6 @@ export declare const STATION_TOOLS: ({
2
2
  name: string;
3
3
  description: string;
4
4
  inputSchema: {
5
- required?: undefined;
6
5
  type: string;
7
6
  properties: {
8
7
  server: {
@@ -73,6 +72,7 @@ export declare const STATION_TOOLS: ({
73
72
  id?: undefined;
74
73
  private?: undefined;
75
74
  };
75
+ required?: undefined;
76
76
  };
77
77
  } | {
78
78
  name: string;
@@ -531,7 +531,6 @@ export declare const STATION_TOOLS: ({
531
531
  name: string;
532
532
  description: string;
533
533
  inputSchema: {
534
- required?: undefined;
535
534
  type: string;
536
535
  properties: {
537
536
  playlist?: undefined;
@@ -616,12 +615,12 @@ export declare const STATION_TOOLS: ({
616
615
  id?: undefined;
617
616
  private?: undefined;
618
617
  };
618
+ required?: undefined;
619
619
  };
620
620
  } | {
621
621
  name: string;
622
622
  description: string;
623
623
  inputSchema: {
624
- required?: undefined;
625
624
  type: string;
626
625
  properties: {
627
626
  playlist?: undefined;
@@ -719,12 +718,12 @@ export declare const STATION_TOOLS: ({
719
718
  id?: undefined;
720
719
  private?: undefined;
721
720
  };
721
+ required?: undefined;
722
722
  };
723
723
  } | {
724
724
  name: string;
725
725
  description: string;
726
726
  inputSchema: {
727
- required?: undefined;
728
727
  type: string;
729
728
  properties: {
730
729
  playlist?: undefined;
@@ -863,12 +862,12 @@ export declare const STATION_TOOLS: ({
863
862
  id?: undefined;
864
863
  private?: undefined;
865
864
  };
865
+ required?: undefined;
866
866
  };
867
867
  } | {
868
868
  name: string;
869
869
  description: string;
870
870
  inputSchema: {
871
- required?: undefined;
872
871
  type: string;
873
872
  properties: {
874
873
  playlist?: undefined;
@@ -961,12 +960,12 @@ export declare const STATION_TOOLS: ({
961
960
  license?: undefined;
962
961
  private?: undefined;
963
962
  };
963
+ required?: undefined;
964
964
  };
965
965
  } | {
966
966
  name: string;
967
967
  description: string;
968
968
  inputSchema: {
969
- required?: undefined;
970
969
  type: string;
971
970
  properties: {
972
971
  playlist?: undefined;
@@ -1061,6 +1060,7 @@ export declare const STATION_TOOLS: ({
1061
1060
  };
1062
1061
  private?: undefined;
1063
1062
  };
1063
+ required?: undefined;
1064
1064
  };
1065
1065
  } | {
1066
1066
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nixamp",
3
- "version": "0.75.0",
3
+ "version": "0.76.1",
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",
@@ -49,6 +49,7 @@
49
49
  "@profullstack/hqtui": "^0.5.0",
50
50
  "@profullstack/live-translate": "^0.3.0",
51
51
  "@profullstack/notifications": "^0.1.0",
52
+ "@profullstack/profile": "0.1.3",
52
53
  "@profullstack/synconfig": "^0.1.2",
53
54
  "@profullstack/throttle": "^0.3.3",
54
55
  "@profullstack/x402-gateway": "^0.11.0",