@audiofool/moondog 0.1.0 → 0.2.0-beta.2

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 (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +153 -68
  3. package/contracts/v1/definitions.schema.json +1 -169
  4. package/package.json +7 -23
  5. package/scripts/contract-lib.mjs +0 -7
  6. package/scripts/contract-semantics.mjs +0 -1904
  7. package/scripts/moondog.mjs +285 -74
  8. package/src/core/apple-library-source-status.mjs +0 -16
  9. package/src/core/apple-projection-domain-services.mjs +168 -36
  10. package/src/core/application-update.mjs +320 -0
  11. package/src/core/capability-catalog.mjs +278 -55
  12. package/src/core/listening-now.mjs +108 -0
  13. package/src/core/listening-profile-domain-services.mjs +153 -41
  14. package/src/core/listening-upkeep.mjs +31 -0
  15. package/src/core/moondog-application.mjs +1908 -104
  16. package/src/core/playback-preferences.mjs +97 -0
  17. package/src/core/spotify-listening-intent.mjs +121 -0
  18. package/src/core/synthetic-domain-services.mjs +24 -3
  19. package/src/demo/moondog-demo.mjs +4 -22
  20. package/src/i18n/index.mjs +285 -0
  21. package/src/i18n/messages/de.mjs +128 -0
  22. package/src/i18n/messages/en.mjs +129 -0
  23. package/src/i18n/messages/es.mjs +128 -0
  24. package/src/i18n/messages/fr.mjs +128 -0
  25. package/src/i18n/messages/it.mjs +128 -0
  26. package/src/i18n/messages/ja.mjs +128 -0
  27. package/src/i18n/messages/ko.mjs +128 -0
  28. package/src/i18n/messages/pt.mjs +128 -0
  29. package/src/i18n/messages/zh-Hant.mjs +128 -0
  30. package/src/i18n/messages/zh.mjs +128 -0
  31. package/src/i18n/preferences.mjs +34 -0
  32. package/src/i18n/screen/es.mjs +966 -0
  33. package/src/i18n/screen/ja.mjs +966 -0
  34. package/src/i18n/screen/pt.mjs +966 -0
  35. package/src/i18n/screen/zh.mjs +966 -0
  36. package/src/importers/apple-music-library/index.mjs +0 -16
  37. package/src/importers/apple-music-library/sqlite-projection.mjs +51 -9
  38. package/src/integrations/apple-music/catalog.mjs +123 -14
  39. package/src/integrations/apple-music/music-app.mjs +232 -0
  40. package/src/integrations/apple-music/storefront.mjs +28 -0
  41. package/src/integrations/apple-music/terminal-preview-player.mjs +221 -0
  42. package/src/integrations/lastfm/history.mjs +221 -0
  43. package/src/integrations/lastfm/settings.mjs +75 -0
  44. package/src/integrations/netease/ncm-cli-player.mjs +321 -0
  45. package/src/integrations/open-music-similarity/artist-radio.mjs +26 -18
  46. package/src/integrations/public-playlists.mjs +2 -1
  47. package/src/integrations/spotify/account-data-profile.mjs +14 -6
  48. package/src/integrations/spotify/authentication.mjs +149 -15
  49. package/src/integrations/spotify/catalog-resolver.mjs +8 -4
  50. package/src/integrations/spotify/connection.mjs +2 -1
  51. package/src/integrations/spotify/credential-store.mjs +4 -0
  52. package/src/integrations/spotify/extended-streaming-history.mjs +11 -11
  53. package/src/integrations/spotify/history-archive.mjs +1 -1
  54. package/src/integrations/spotify/library-snapshot.mjs +227 -0
  55. package/src/integrations/spotify/recent-activity.mjs +2 -0
  56. package/src/integrations/spotify/service.mjs +873 -69
  57. package/src/integrations/spotify/web-api-client.mjs +407 -127
  58. package/src/integrations/web/brave-web.mjs +45 -0
  59. package/src/integrations/web/codex-web.mjs +15 -82
  60. package/src/integrations/web/deepseek-web.mjs +81 -0
  61. package/src/integrations/web/web-research.mjs +119 -0
  62. package/src/integrations/web/web-result.mjs +215 -0
  63. package/src/integrations/youtube-music/takeout.mjs +3 -1
  64. package/src/memory/local-memory-store.mjs +469 -41
  65. package/src/profile/history-import.mjs +42 -6
  66. package/src/profile/listener-profile-build.mjs +378 -0
  67. package/src/profile/listener-profile-store.mjs +130 -0
  68. package/src/profile/listener-profile-verification.mjs +68 -0
  69. package/src/profile/listener-profile-worker.mjs +172 -0
  70. package/src/profile/listening-history-store.mjs +424 -8
  71. package/src/profile/listening-profile-projection.mjs +242 -76
  72. package/src/profile/local-music-data.mjs +1 -1
  73. package/src/profile/music-providers.mjs +11 -7
  74. package/src/profile/profile-exploration.mjs +84 -0
  75. package/src/profile/recorded-measures.mjs +24 -0
  76. package/src/profile/spotify-archive-taste.mjs +0 -4
  77. package/src/runtime/pi/agent-runtime.mjs +2204 -605
  78. package/src/runtime/pi/authentication.mjs +34 -1
  79. package/src/runtime/pi/configured-runtime.mjs +22 -6
  80. package/src/runtime/pi/conversation-name.mjs +53 -0
  81. package/src/runtime/pi/memory-agent-runtime.mjs +2 -2
  82. package/src/runtime/pi/model-catalog.mjs +28 -5
  83. package/src/runtime/pi/model-settings.mjs +202 -0
  84. package/src/runtime/pi/model-transport.mjs +10 -0
  85. package/src/runtime/pi/persistent-credential-store.mjs +67 -31
  86. package/src/runtime/pi/profile-build-runtime.mjs +160 -0
  87. package/src/runtime/pi/public-model-catalog.mjs +288 -0
  88. package/src/runtime/pi/runtime-settings.mjs +10 -2
  89. package/src/runtime/pi/skills/listener-profile/SKILL.md +46 -0
  90. package/src/runtime/pi/tool-trace.mjs +37 -0
  91. package/src/surfaces/cli/auth-command.mjs +12 -4
  92. package/src/surfaces/cli/brand-components.mjs +310 -132
  93. package/src/surfaces/cli/brand-theme.mjs +51 -28
  94. package/src/surfaces/cli/command-completions.mjs +58 -42
  95. package/src/surfaces/cli/format-output.mjs +199 -388
  96. package/src/surfaces/cli/history-import-view.mjs +292 -139
  97. package/src/surfaces/cli/import-guides.mjs +2 -0
  98. package/src/surfaces/cli/listener-profile-command.mjs +92 -0
  99. package/src/surfaces/cli/listening-menu.mjs +7 -4
  100. package/src/surfaces/cli/profile-command.mjs +30 -21
  101. package/src/surfaces/cli/seasons-chart.mjs +87 -0
  102. package/src/surfaces/cli/spotify-command.mjs +54 -6
  103. package/src/surfaces/cli/taste-profile-model.mjs +78 -79
  104. package/src/surfaces/cli/taste-profile-view.mjs +33 -26
  105. package/src/surfaces/cli/terminal-art.mjs +140 -35
  106. package/src/surfaces/cli/tui.mjs +1066 -357
  107. package/src/surfaces/cli/update-command.mjs +32 -0
  108. package/src/surfaces/cli/web-command.mjs +53 -14
  109. package/src/surfaces/html/tasteprint.mjs +43 -70
  110. package/contracts/v1/capability-policy.schema.json +0 -572
  111. package/contracts/v1/profile-evidence.schema.json +0 -237
  112. package/contracts/v1/tool-invocation.schema.json +0 -318
  113. package/contracts/v1/tool-receipt.schema.json +0 -1066
  114. package/scripts/demo-studio.mjs +0 -58
  115. package/src/core/apple-data-migration.mjs +0 -50
  116. package/src/demo/moondog-interactive-tasteprint-demo.mjs +0 -293
  117. package/src/demo/moondog-tasteprint-demo.mjs +0 -631
  118. package/src/integrations/spotify/index.mjs +0 -80
  119. package/src/surfaces/web/showcase.mjs +0 -272
  120. package/src/surfaces/web/studio-page.mjs +0 -4148
  121. package/src/surfaces/web/studio.mjs +0 -2288
@@ -1,80 +0,0 @@
1
- export {
2
- SPOTIFY_WEB_API_BASE_URL,
3
- SPOTIFY_WEB_API_LIMITS,
4
- SpotifyWebApiError,
5
- createSpotifyWebApiClient,
6
- } from "./web-api-client.mjs";
7
-
8
- export {
9
- SPOTIFY_SERVICE_LIMITS,
10
- SpotifyServiceError,
11
- createSpotifyService,
12
- } from "./service.mjs";
13
-
14
- export {
15
- SPOTIFY_DEFAULT_REDIRECT_URI,
16
- SPOTIFY_DEFAULT_SCOPES,
17
- SpotifyAuthenticationError,
18
- createSpotifyAuthentication,
19
- } from "./authentication.mjs";
20
-
21
- export { createSpotifyCredentialStore } from "./credential-store.mjs";
22
- export { openSpotifyConnection } from "./connection.mjs";
23
-
24
- export {
25
- SPOTIFY_CATALOG_RESOLVER_LIMITS,
26
- SpotifyCatalogResolverError,
27
- createSpotifyCatalogResolver,
28
- } from "./catalog-resolver.mjs";
29
-
30
- export {
31
- SPOTIFY_RESOLUTION_CACHE_KEY_VERSION,
32
- SPOTIFY_RESOLUTION_CACHE_SCHEMA_VERSION,
33
- createInMemorySpotifyResolutionCache,
34
- defaultSpotifyResolutionCachePath,
35
- openSpotifyResolutionCache,
36
- resolveSpotifyResolutionCachePath,
37
- spotifyResolutionCacheKey,
38
- } from "./resolution-cache.mjs";
39
-
40
- export {
41
- SPOTIFY_LISTENING_EVENT_NAMESPACE,
42
- SPOTIFY_RECENT_ACTIVITY_SOURCE,
43
- SPOTIFY_TRACK_NAMESPACE,
44
- projectSpotifyRecentActivity,
45
- } from "./recent-activity.mjs";
46
-
47
- export {
48
- SPOTIFY_ACCOUNT_DATA_BATCH_NAMESPACE,
49
- SPOTIFY_ACCOUNT_DATA_EVENT_NAMESPACE,
50
- SPOTIFY_ACCOUNT_DATA_HISTORY_FORMAT,
51
- SPOTIFY_ACCOUNT_DATA_HISTORY_LIMITS,
52
- SPOTIFY_ACCOUNT_DATA_HISTORY_SCOPE,
53
- SPOTIFY_ACCOUNT_DATA_HISTORY_SOURCE,
54
- SPOTIFY_ACCOUNT_DATA_TRACK_NAMESPACE,
55
- SpotifyAccountDataHistoryError,
56
- projectSpotifyAccountDataHistory,
57
- readSpotifyAccountDataHistoryArchive,
58
- spotifyAccountDataEventIdentity,
59
- spotifyAccountDataTrackIdentity,
60
- } from "./account-data-history.mjs";
61
-
62
- export {
63
- SPOTIFY_EXTENDED_HISTORY_BATCH_NAMESPACE,
64
- SPOTIFY_EXTENDED_HISTORY_EVENT_NAMESPACE,
65
- SPOTIFY_EXTENDED_HISTORY_FORMAT,
66
- SPOTIFY_EXTENDED_HISTORY_LIMITS,
67
- SPOTIFY_EXTENDED_HISTORY_SCOPE,
68
- SPOTIFY_EXTENDED_HISTORY_SOURCE,
69
- SpotifyExtendedHistoryError,
70
- projectSpotifyExtendedStreamingHistory,
71
- readSpotifyExtendedStreamingHistoryArchive,
72
- } from "./extended-streaming-history.mjs";
73
-
74
- export { readSpotifyHistoryArchive } from "./history-archive.mjs";
75
-
76
- export {
77
- readSpotifyConfiguration,
78
- resolveSpotifySettingsFile,
79
- writeSpotifyConfiguration,
80
- } from "./settings.mjs";
@@ -1,272 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { readFile } from "node:fs/promises";
3
- import { createServer } from "node:http";
4
- import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- export const MOONDOG_SHOWCASE_HOST = "127.0.0.1";
8
-
9
- const repositoryRoot = path.resolve(
10
- path.dirname(fileURLToPath(import.meta.url)),
11
- "../../..",
12
- );
13
-
14
- const showcaseContentSecurityPolicy = [
15
- "default-src 'none'",
16
- "base-uri 'none'",
17
- "form-action 'none'",
18
- "frame-ancestors 'none'",
19
- "img-src 'self'",
20
- "style-src 'unsafe-inline'",
21
- ].join("; ");
22
-
23
- const artifactContentSecurityPolicy = [
24
- "default-src 'none'",
25
- "base-uri 'none'",
26
- "form-action 'none'",
27
- "frame-ancestors 'none'",
28
- "img-src data:",
29
- "style-src 'unsafe-inline'",
30
- ].join("; ");
31
-
32
- export const MOONDOG_SHOWCASE_ROUTES = Object.freeze([
33
- {
34
- pathnames: ["/", "/showcase", "/showcase/", "/index.html"],
35
- relativePath: "showcase/index.html",
36
- contentType: "text/html; charset=utf-8",
37
- contentSecurityPolicy: showcaseContentSecurityPolicy,
38
- },
39
- {
40
- pathnames: [
41
- "/assets/brand/moondog-lunar-record/moondog-logo-1x1.png",
42
- ],
43
- relativePath:
44
- "assets/brand/moondog-lunar-record/moondog-logo-1x1.png",
45
- contentType: "image/png",
46
- contentSecurityPolicy: "default-src 'none'; frame-ancestors 'none'",
47
- },
48
- {
49
- pathnames: ["/assets/demo/moondog-time-machine-preview.png"],
50
- relativePath: "assets/demo/moondog-time-machine-preview.png",
51
- contentType: "image/png",
52
- contentSecurityPolicy: "default-src 'none'; frame-ancestors 'none'",
53
- },
54
- {
55
- pathnames: ["/assets/demo/moondog-offline-demo.gif"],
56
- relativePath: "assets/demo/moondog-offline-demo.gif",
57
- contentType: "image/gif",
58
- contentSecurityPolicy: "default-src 'none'; frame-ancestors 'none'",
59
- },
60
- {
61
- pathnames: ["/assets/demo/moondog-tasteprint-card-preview.png"],
62
- relativePath: "assets/demo/moondog-tasteprint-card-preview.png",
63
- contentType: "image/png",
64
- contentSecurityPolicy: "default-src 'none'; frame-ancestors 'none'",
65
- },
66
- {
67
- pathnames: ["/assets/demo/moondog-tasteprint-demo.html"],
68
- relativePath: "assets/demo/moondog-tasteprint-demo.html",
69
- contentType: "text/html; charset=utf-8",
70
- contentSecurityPolicy: artifactContentSecurityPolicy,
71
- },
72
- {
73
- pathnames: ["/assets/demo/moondog-tasteprint-card-demo.html"],
74
- relativePath: "assets/demo/moondog-tasteprint-card-demo.html",
75
- contentType: "text/html; charset=utf-8",
76
- contentSecurityPolicy: artifactContentSecurityPolicy,
77
- },
78
- ]);
79
-
80
- function baseHeaders(contentType, contentSecurityPolicy) {
81
- return {
82
- "Cache-Control": "no-store, max-age=0",
83
- "Content-Security-Policy": contentSecurityPolicy,
84
- "Content-Type": contentType,
85
- "Cross-Origin-Opener-Policy": "same-origin",
86
- "Cross-Origin-Resource-Policy": "same-origin",
87
- "Permissions-Policy": "camera=(), microphone=(), geolocation=(), payment=()",
88
- "Referrer-Policy": "no-referrer",
89
- "X-Content-Type-Options": "nosniff",
90
- "X-Frame-Options": "DENY",
91
- };
92
- }
93
-
94
- function send(response, statusCode, body, headers, { headOnly = false } = {}) {
95
- const buffer = Buffer.isBuffer(body) ? body : Buffer.from(String(body));
96
- response.writeHead(statusCode, {
97
- ...headers,
98
- "Content-Length": buffer.length,
99
- });
100
- response.end(headOnly ? undefined : buffer);
101
- }
102
-
103
- function plainTextHeaders() {
104
- return baseHeaders(
105
- "text/plain; charset=utf-8",
106
- "default-src 'none'; frame-ancestors 'none'",
107
- );
108
- }
109
-
110
- async function loadRoutes(root) {
111
- const routeMap = new Map();
112
- for (const route of MOONDOG_SHOWCASE_ROUTES) {
113
- const body = await readFile(path.join(root, route.relativePath));
114
- for (const pathname of route.pathnames) {
115
- routeMap.set(pathname, {
116
- body,
117
- headers: baseHeaders(
118
- route.contentType,
119
- route.contentSecurityPolicy,
120
- ),
121
- });
122
- }
123
- }
124
- return routeMap;
125
- }
126
-
127
- async function openBrowser(url) {
128
- const [command, args] = process.platform === "darwin"
129
- ? ["open", [url]]
130
- : process.platform === "win32"
131
- ? ["rundll32", ["url.dll,FileProtocolHandler", url]]
132
- : ["xdg-open", [url]];
133
- await new Promise((resolve, reject) => {
134
- const child = spawn(command, args, {
135
- detached: true,
136
- stdio: "ignore",
137
- windowsHide: true,
138
- });
139
- child.once("error", reject);
140
- child.once("spawn", () => {
141
- child.unref();
142
- resolve();
143
- });
144
- });
145
- }
146
-
147
- export async function startMoondogShowcase({
148
- host = MOONDOG_SHOWCASE_HOST,
149
- port = 0,
150
- root = repositoryRoot,
151
- } = {}) {
152
- if (host !== MOONDOG_SHOWCASE_HOST) {
153
- throw new TypeError("The Moondog showcase may only listen on 127.0.0.1.");
154
- }
155
- if (!Number.isInteger(port) || port < 0 || port > 65_535) {
156
- throw new TypeError("The Moondog showcase port is invalid.");
157
- }
158
- if (typeof root !== "string" || !path.isAbsolute(root)) {
159
- throw new TypeError("The Moondog showcase root must be absolute.");
160
- }
161
-
162
- const routes = await loadRoutes(root);
163
- const server = createServer((request, response) => {
164
- const method = request.method ?? "GET";
165
- if (method !== "GET" && method !== "HEAD") {
166
- request.resume();
167
- send(response, 405, "Method not allowed.\n", {
168
- ...plainTextHeaders(),
169
- Allow: "GET, HEAD",
170
- });
171
- return;
172
- }
173
-
174
- let pathname;
175
- try {
176
- pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
177
- } catch {
178
- send(response, 400, "Bad request.\n", plainTextHeaders(), {
179
- headOnly: method === "HEAD",
180
- });
181
- return;
182
- }
183
- const route = routes.get(pathname);
184
- if (!route) {
185
- send(response, 404, "Not found.\n", plainTextHeaders(), {
186
- headOnly: method === "HEAD",
187
- });
188
- return;
189
- }
190
- send(response, 200, route.body, route.headers, {
191
- headOnly: method === "HEAD",
192
- });
193
- });
194
-
195
- server.requestTimeout = 15_000;
196
- server.headersTimeout = 10_000;
197
- server.maxRequestsPerSocket = 50;
198
- server.on("clientError", (error, socket) => {
199
- if (error?.code !== "ECONNRESET" && socket.writable && !socket.destroyed) {
200
- socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
201
- }
202
- });
203
-
204
- await new Promise((resolve, reject) => {
205
- const onError = (error) => {
206
- server.off("listening", onListening);
207
- reject(error);
208
- };
209
- const onListening = () => {
210
- server.off("error", onError);
211
- resolve();
212
- };
213
- server.once("error", onError);
214
- server.once("listening", onListening);
215
- server.listen(port, host);
216
- });
217
-
218
- const address = server.address();
219
- if (!address || typeof address === "string") {
220
- await new Promise((resolve) => server.close(resolve));
221
- throw new Error("The Moondog showcase did not receive a loopback address.");
222
- }
223
- const url = `http://${host}:${address.port}/`;
224
- let closed = false;
225
-
226
- return {
227
- url,
228
- async close() {
229
- if (closed) return;
230
- closed = true;
231
- await new Promise((resolve, reject) => {
232
- server.close((error) => (error ? reject(error) : resolve()));
233
- });
234
- },
235
- };
236
- }
237
-
238
- export async function runMoondogShowcase({
239
- environment = process.env,
240
- stdout = process.stdout,
241
- stderr = process.stderr,
242
- launchBrowser = openBrowser,
243
- } = {}) {
244
- const showcase = await startMoondogShowcase();
245
- stdout.write(
246
- `Moondog showcase is ready at ${showcase.url}\nPublic-safe product tour with fictional personal-profile data and one dated public-catalog proof. No private listening history, analytics, or network resources. Press Ctrl+C to stop.\n`,
247
- );
248
- if (environment.MOONDOG_SHOWCASE_NO_OPEN !== "1") {
249
- try {
250
- await launchBrowser(showcase.url);
251
- } catch {
252
- stderr.write(
253
- "Moondog could not open a browser automatically. Use the local URL above.\n",
254
- );
255
- }
256
- }
257
-
258
- let stop;
259
- const stopped = new Promise((resolve) => {
260
- stop = resolve;
261
- });
262
- const onSignal = () => stop();
263
- process.once("SIGINT", onSignal);
264
- process.once("SIGTERM", onSignal);
265
- try {
266
- await stopped;
267
- } finally {
268
- process.off("SIGINT", onSignal);
269
- process.off("SIGTERM", onSignal);
270
- await showcase.close();
271
- }
272
- }