website-xp-phone 1.5.0
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/.astro/content-assets.mjs +1 -0
- package/.astro/content-modules.mjs +1 -0
- package/.astro/content.d.ts +199 -0
- package/.astro/data-store.json +1 -0
- package/.astro/settings.json +8 -0
- package/.astro/types.d.ts +1 -0
- package/.devcontainer/devcontainer.json +23 -0
- package/.env.firebase.example +8 -0
- package/.firebaserc +5 -0
- package/.gitattributes +2 -0
- package/.github/copilot-instructions.md +131 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/ci.yml +45 -0
- package/.github/workflows/deploy-admin.yml +48 -0
- package/.github/workflows/static.yml +43 -0
- package/.gitmodules +5 -0
- package/FIREBASE_SETUP.md +69 -0
- package/README.md +63 -0
- package/SECURITY.md +11 -0
- package/admin/Admin.csproj +7 -0
- package/admin/Dockerfile +14 -0
- package/admin/Program.cs +8 -0
- package/deploy-admin-cloud-run.md +229 -0
- package/eslint.config.js +28 -0
- package/firebase.json +5 -0
- package/firestore.rules +29 -0
- package/index.html +52 -0
- package/package.json +48 -0
- package/pagerts_output.json +1 -0
- package/public/5.html +967 -0
- package/public/BAHNSCHRIFT.TTF +0 -0
- package/public/Beep.ogg +0 -0
- package/public/Clippy.png +0 -0
- package/public/Layered Network Security Model for Home Networks (slides).pdf +0 -0
- package/public/Layered Network Security Model for Home Networks.pdf +0 -0
- package/public/TODO.pdf +0 -0
- package/public/WoW_Config.zip +3 -0
- package/public/addons/energy-swing.txt +1 -0
- package/public/addons/lego-yoda-death-readme.txt +11 -0
- package/public/addons/lego-yoda-death.mp3 +0 -0
- package/public/addons/mana-blast.txt +1 -0
- package/public/addons/rage-volley.txt +1 -0
- package/public/addons/rueg-cell.txt +1 -0
- package/public/addons/rueg-elvui-profile.txt +1 -0
- package/public/addons/rueg-grid2.txt +214 -0
- package/public/addons/rueg-plater-smol.txt +1 -0
- package/public/addons/rueg-plater.txt +1 -0
- package/public/addons/rueg-wa-druid.txt +1 -0
- package/public/addons/rueg-wa-priest.txt +1 -0
- package/public/addons/rueg-wa-rogue.txt +1 -0
- package/public/addons/rueg-wa-shaman.txt +1 -0
- package/public/addons/rueg-wa-warrior.txt +1 -0
- package/public/addons/spirit-smash.txt +1 -0
- package/public/avatar.jpg +0 -0
- package/public/avatar.png +0 -0
- package/public/crunchy_kick.ogg +0 -0
- package/public/documents/resume.html +312 -0
- package/public/favicon.ico +0 -0
- package/public/images/Ateric1.png +0 -0
- package/public/images/Ateric2.png +0 -0
- package/public/images/equal1.png +0 -0
- package/public/images/hyperawareofwhatacatis.png +0 -0
- package/public/images/kogg1.png +0 -0
- package/public/images/kogg2.png +0 -0
- package/public/images/rueg1.png +0 -0
- package/public/images/rueg2.png +0 -0
- package/public/incorrect_responses.txt +126 -0
- package/public/loading.css +51 -0
- package/public/resume.pdf +0 -0
- package/public/robots.txt +9 -0
- package/public/soundcloud.json +57 -0
- package/public/spinner.svg +12 -0
- package/public/tada.wav +0 -0
- package/public/yooh.mp3 +0 -0
- package/render.yaml +5 -0
- package/scripts/ensure-blog-worktree.mjs +24 -0
- package/scripts/generate-soundcloud-json.mjs +198 -0
- package/scripts/git-worktree-helper.mjs +122 -0
- package/scripts/hoist-dev-blog-local.mjs +149 -0
- package/scripts/music-schema.mjs +56 -0
- package/scripts/publish-soundcloud-json.mjs +32 -0
- package/scripts/sync-music-links-from-worktree.mjs +32 -0
- package/src/App.tsx +1500 -0
- package/src/addons.json +76 -0
- package/src/components/Addon.tsx +223 -0
- package/src/components/BlogContent.tsx +103 -0
- package/src/components/CopyToClipboardButton.tsx +21 -0
- package/src/components/MenuBar.tsx +151 -0
- package/src/components/MenuBarWithContext.tsx +6 -0
- package/src/components/Modal.tsx +17 -0
- package/src/components/MusicContent.tsx +309 -0
- package/src/components/NavBarController.tsx +55 -0
- package/src/components/NavBarControllerWrapper.tsx +13 -0
- package/src/components/Page.tsx +56 -0
- package/src/components/SitemapContent.tsx +125 -0
- package/src/contacts.json +32 -0
- package/src/env.d.ts +13 -0
- package/src/lib/assistantStateMachine.ts +80 -0
- package/src/lib/audioOverlap.ts +99 -0
- package/src/lib/keyboardInputUtils.ts +182 -0
- package/src/lib/musicSchema.ts +85 -0
- package/src/lib/naggingAssistantClient.ts +241 -0
- package/src/lib/resumeAnalytics.ts +163 -0
- package/src/main.tsx +35 -0
- package/src/pages.json +50 -0
- package/src/sections.json +243 -0
- package/src/src+addons.zip +3 -0
- package/src/styles/main.css +465 -0
- package/src/utils/blogSecurity.ts +87 -0
- package/src/utils/menuItems.ts +33 -0
- package/src/windowing/MinimizedSections.tsx +86 -0
- package/src/windowing/Section.tsx +586 -0
- package/src/windowing/context.tsx +13 -0
- package/src/windowing/hooks.ts +10 -0
- package/src/windowing/index.ts +7 -0
- package/src/windowing/provider.tsx +74 -0
- package/src/windowing/server.ts +3 -0
- package/src/windowing/types.ts +33 -0
- package/src/windowing/utils.ts +135 -0
- package/tests/generate-soundcloud-json.test.mjs +63 -0
- package/tests/music-schema.test.mjs +53 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +304 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
5be31d58-407d-4262-9c23
|
|
2
|
+
|
|
3
|
+
================================================================================
|
|
4
|
+
INCORRECT RESPONSE REPORT
|
|
5
|
+
Generated from: benchmark_results.sqlite
|
|
6
|
+
Total incorrect responses: 18
|
|
7
|
+
================================================================================
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
######################################################################
|
|
11
|
+
HOST: minifridge (http://minifridge:11434)
|
|
12
|
+
######################################################################
|
|
13
|
+
|
|
14
|
+
MODEL: devstral:latest
|
|
15
|
+
────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
TEST CASE: Simple Greeting
|
|
18
|
+
|
|
19
|
+
Run : 20260423_122322_113ab5fc (2026-04-23 12:23:22)
|
|
20
|
+
Latency : 5.99s
|
|
21
|
+
Prompt : Say 'Hello World'
|
|
22
|
+
Response : Hello, I'm Devstral! How can I assist you today?
|
|
23
|
+
|
|
24
|
+
Run : 20260423_123535_a966363f (2026-04-23 12:35:35)
|
|
25
|
+
Latency : 3.58s
|
|
26
|
+
Prompt : Say 'Hello World'
|
|
27
|
+
Response : Hello! How can I assist you today?
|
|
28
|
+
|
|
29
|
+
Run : 20260423_131155_86d5f17e (2026-04-23 13:11:55)
|
|
30
|
+
Latency : 3.41s
|
|
31
|
+
Prompt : Say 'Hello World'
|
|
32
|
+
Response : Hello! How can I assist you today?
|
|
33
|
+
|
|
34
|
+
Run : 20260423_132359_a83b6f5b (2026-04-23 13:23:59)
|
|
35
|
+
Latency : 3.29s
|
|
36
|
+
Prompt : Say 'Hello World'
|
|
37
|
+
Response : Hello! How can I assist you today?
|
|
38
|
+
|
|
39
|
+
MODEL: glm-4.7-flash:latest
|
|
40
|
+
────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
TEST CASE: Simple Greeting
|
|
43
|
+
|
|
44
|
+
Run : 20260423_122322_113ab5fc (2026-04-23 12:23:22)
|
|
45
|
+
Latency : 13.52s
|
|
46
|
+
Prompt : Say 'Hello World'
|
|
47
|
+
Response : Hello, World!
|
|
48
|
+
|
|
49
|
+
Run : 20260423_123535_a966363f (2026-04-23 12:35:35)
|
|
50
|
+
Latency : 10.63s
|
|
51
|
+
Prompt : Say 'Hello World'
|
|
52
|
+
Response : Hello, World!
|
|
53
|
+
|
|
54
|
+
Run : 20260423_124655_ce871a4d (2026-04-23 12:46:55)
|
|
55
|
+
Latency : 12.74s
|
|
56
|
+
Prompt : Say 'Hello World'
|
|
57
|
+
Response : Hello, World
|
|
58
|
+
|
|
59
|
+
MODEL: llama3.1:8b-instruct-q4_K_M
|
|
60
|
+
────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
TEST CASE: Simple Greeting
|
|
63
|
+
|
|
64
|
+
Run : 20260423_114440_c0c853d7 (2026-04-23 11:44:40)
|
|
65
|
+
Latency : 0.27s
|
|
66
|
+
Prompt : Say 'Hello World'
|
|
67
|
+
Response : Hello, World!
|
|
68
|
+
|
|
69
|
+
Run : 20260423_121008_e767634b (2026-04-23 12:10:08)
|
|
70
|
+
Latency : 0.28s
|
|
71
|
+
Prompt : Say 'Hello World'
|
|
72
|
+
Response : Hello, World!
|
|
73
|
+
|
|
74
|
+
Run : 20260423_123535_a966363f (2026-04-23 12:35:35)
|
|
75
|
+
Latency : 0.27s
|
|
76
|
+
Prompt : Say 'Hello World'
|
|
77
|
+
Response : Hello, World!
|
|
78
|
+
|
|
79
|
+
Run : 20260423_125902_05a87892 (2026-04-23 12:59:02)
|
|
80
|
+
Latency : 0.27s
|
|
81
|
+
Prompt : Say 'Hello World'
|
|
82
|
+
Response : Hello, World!
|
|
83
|
+
|
|
84
|
+
Run : 20260423_131155_86d5f17e (2026-04-23 13:11:55)
|
|
85
|
+
Latency : 0.29s
|
|
86
|
+
Prompt : Say 'Hello World'
|
|
87
|
+
Response : Hello, World!
|
|
88
|
+
|
|
89
|
+
Run : 20260423_133705_cd17867d (2026-04-23 13:37:05)
|
|
90
|
+
Latency : 0.28s
|
|
91
|
+
Prompt : Say 'Hello World'
|
|
92
|
+
Response : Hello, World!
|
|
93
|
+
|
|
94
|
+
MODEL: llama3.1:latest
|
|
95
|
+
────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
TEST CASE: Simple Greeting
|
|
98
|
+
|
|
99
|
+
Run : 20260423_114440_c0c853d7 (2026-04-23 11:44:40)
|
|
100
|
+
Latency : 0.27s
|
|
101
|
+
Prompt : Say 'Hello World'
|
|
102
|
+
Response : Hello, World!
|
|
103
|
+
|
|
104
|
+
Run : 20260423_115628_38518096 (2026-04-23 11:56:28)
|
|
105
|
+
Latency : 0.28s
|
|
106
|
+
Prompt : Say 'Hello World'
|
|
107
|
+
Response : Hello, World!
|
|
108
|
+
|
|
109
|
+
Run : 20260423_124655_ce871a4d (2026-04-23 12:46:55)
|
|
110
|
+
Latency : 0.27s
|
|
111
|
+
Prompt : Say 'Hello World'
|
|
112
|
+
Response : Hello, World!
|
|
113
|
+
|
|
114
|
+
Run : 20260423_125902_05a87892 (2026-04-23 12:59:02)
|
|
115
|
+
Latency : 0.29s
|
|
116
|
+
Prompt : Say 'Hello World'
|
|
117
|
+
Response : Hello, World!
|
|
118
|
+
|
|
119
|
+
Run : 20260423_131155_86d5f17e (2026-04-23 13:11:55)
|
|
120
|
+
Latency : 0.28s
|
|
121
|
+
Prompt : Say 'Hello World'
|
|
122
|
+
Response : Hello, World!
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
// why would you do this to me
|
|
126
|
+
// 20c8f20342fb
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--text-color: #000;
|
|
3
|
+
--primary-color: #2f2f2f;
|
|
4
|
+
--background-color: #2f2f2f;
|
|
5
|
+
|
|
6
|
+
--mid-color: color-mix(in srgb, #f5f5dc, #2f2f2f 50%);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Hide content until styles are loaded, but keep menu bar visible */
|
|
10
|
+
body:not(.styles-loaded) main,
|
|
11
|
+
body:not(.styles-loaded) main *,
|
|
12
|
+
body:not(.styles-loaded) main h1,
|
|
13
|
+
body:not(.styles-loaded) main h2,
|
|
14
|
+
body:not(.styles-loaded) main h3,
|
|
15
|
+
body:not(.styles-loaded) main h4,
|
|
16
|
+
body:not(.styles-loaded) main h5,
|
|
17
|
+
body:not(.styles-loaded) main h6,
|
|
18
|
+
body:not(.styles-loaded) main p,
|
|
19
|
+
body:not(.styles-loaded) main span,
|
|
20
|
+
body:not(.styles-loaded) main label,
|
|
21
|
+
body:not(.styles-loaded) main li,
|
|
22
|
+
body:not(.styles-loaded) main a,
|
|
23
|
+
body:not(.styles-loaded) main button,
|
|
24
|
+
body:not(.styles-loaded) main input,
|
|
25
|
+
body:not(.styles-loaded) main textarea {
|
|
26
|
+
color: transparent !important;
|
|
27
|
+
opacity: 0 !important;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.light-mode-switch {
|
|
31
|
+
border-radius: 2em;
|
|
32
|
+
border: 2px solid --var(--text-color);
|
|
33
|
+
font-family: "Courier New", Courier, monospace;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@media (prefers-color-scheme: light) {
|
|
37
|
+
:root {
|
|
38
|
+
--text-color: #2f2f2f;
|
|
39
|
+
--primary-color: #f5f5dc;
|
|
40
|
+
--background-color: #f5f5dc;
|
|
41
|
+
}
|
|
42
|
+
.light-mode-switch {
|
|
43
|
+
display: none;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
body,
|
|
48
|
+
#root {
|
|
49
|
+
background-color: var(--mid-color);
|
|
50
|
+
margin: 0;
|
|
51
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Crawl directives for akinevz.com.
|
|
2
|
+
# Full syntax: https://developers.google.com/search/docs/advanced/robots/create-robots-txt
|
|
3
|
+
|
|
4
|
+
# Keep the canonical site indexable while reducing duplicate query-string URLs.
|
|
5
|
+
User-agent: *
|
|
6
|
+
Allow: /
|
|
7
|
+
Disallow: /*?*
|
|
8
|
+
|
|
9
|
+
Sitemap: https://akinevz.com/sitemap.xml
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "https://soundcloud.com/akinevz",
|
|
3
|
+
"generatedAt": "2026-07-03T02:00:43.893Z",
|
|
4
|
+
"trackCount": 10,
|
|
5
|
+
"tracks": [
|
|
6
|
+
{
|
|
7
|
+
"path": "/akinevz/hope-kine-reeq",
|
|
8
|
+
"title": "hope-kine-reeq",
|
|
9
|
+
"url": "https://soundcloud.com/akinevz/hope-kine-reeq"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "/akinevz/try-try-try",
|
|
13
|
+
"title": "try-try-try",
|
|
14
|
+
"url": "https://soundcloud.com/akinevz/try-try-try"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": "/akinevz/space-space-space",
|
|
18
|
+
"title": "space-space-space",
|
|
19
|
+
"url": "https://soundcloud.com/akinevz/space-space-space"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "/akinevz/late-late-late",
|
|
23
|
+
"title": "late-late-late",
|
|
24
|
+
"url": "https://soundcloud.com/akinevz/late-late-late"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": "/akinevz/repeating-static-noise",
|
|
28
|
+
"title": "repeating-static-noise",
|
|
29
|
+
"url": "https://soundcloud.com/akinevz/repeating-static-noise"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": "/akinevz/door-jamming-button",
|
|
33
|
+
"title": "door-jamming-button",
|
|
34
|
+
"url": "https://soundcloud.com/akinevz/door-jamming-button"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"path": "/akinevz/resummoning-fried-beans",
|
|
38
|
+
"title": "resummoning-fried-beans",
|
|
39
|
+
"url": "https://soundcloud.com/akinevz/resummoning-fried-beans"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"path": "/akinevz/calculated-reception",
|
|
43
|
+
"title": "calculated-reception",
|
|
44
|
+
"url": "https://soundcloud.com/akinevz/calculated-reception"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"path": "/akinevz/out-of-breath-loop",
|
|
48
|
+
"title": "out-of-breath-loop",
|
|
49
|
+
"url": "https://soundcloud.com/akinevz/out-of-breath-loop"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"path": "/akinevz/microtonal-vudoo",
|
|
53
|
+
"title": "microtonal-vudoo",
|
|
54
|
+
"url": "https://soundcloud.com/akinevz/microtonal-vudoo"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" role="img" aria-label="Loading">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="spinner-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
4
|
+
<stop offset="0%" stop-color="#0a246a" stop-opacity="1" />
|
|
5
|
+
<stop offset="100%" stop-color="#a6caf0" stop-opacity="0.2" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="#d9d9d9" stroke-width="6" />
|
|
9
|
+
<path d="M24 6a18 18 0 0 1 18 18" fill="none" stroke="url(#spinner-gradient)" stroke-width="6" stroke-linecap="round">
|
|
10
|
+
<animateTransform attributeName="transform" type="rotate" from="0 24 24" to="360 24 24" dur="0.9s" repeatCount="indefinite" />
|
|
11
|
+
</path>
|
|
12
|
+
</svg>
|
package/public/tada.wav
ADDED
|
Binary file
|
package/public/yooh.mp3
ADDED
|
Binary file
|
package/render.yaml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Verifies that the blog-posts branch CDN assets are reachable.
|
|
3
|
+
// music-links.json now lives on the blog-posts branch and is fetched
|
|
4
|
+
// at runtime by MusicContent.tsx — no local worktree is required.
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const MUSIC_LINKS_URL =
|
|
8
|
+
"https://raw.githubusercontent.com/akinevz2/frontend/blog-posts/music-links.json";
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(MUSIC_LINKS_URL, { method: "HEAD" });
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
process.stderr.write(
|
|
14
|
+
`blog-posts CDN asset unreachable: ${MUSIC_LINKS_URL} returned HTTP ${response.status}\n`,
|
|
15
|
+
);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
process.stdout.write(`blog-posts CDN OK: ${MUSIC_LINKS_URL}\n`);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
process.stderr.write(
|
|
21
|
+
`blog-posts CDN check failed: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
const USER_PATH_PREFIX = "/akinevz/";
|
|
8
|
+
const SOURCE_URL = "https://soundcloud.com/akinevz";
|
|
9
|
+
const RESERVED_PROFILE_ROUTES = new Set([
|
|
10
|
+
"likes",
|
|
11
|
+
"sets",
|
|
12
|
+
"tracks",
|
|
13
|
+
"comments",
|
|
14
|
+
"reposts",
|
|
15
|
+
"popular-tracks",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
const outputFile = resolve(__dirname, "../public/soundcloud.json");
|
|
21
|
+
const cacheDir = resolve(__dirname, "../.cache");
|
|
22
|
+
const cachedArtistPage = resolve(cacheDir, "soundcloud-artist.html");
|
|
23
|
+
|
|
24
|
+
const runCommand = ({ command, args, cwd }) =>
|
|
25
|
+
new Promise((resolvePromise, reject) => {
|
|
26
|
+
const child = spawn(command, args, {
|
|
27
|
+
cwd,
|
|
28
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let stdout = "";
|
|
32
|
+
let stderr = "";
|
|
33
|
+
|
|
34
|
+
child.stdout.on("data", (chunk) => {
|
|
35
|
+
stdout += chunk.toString();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
child.stderr.on("data", (chunk) => {
|
|
39
|
+
stderr += chunk.toString();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
child.on("error", reject);
|
|
43
|
+
child.on("close", (exitCode) => {
|
|
44
|
+
resolvePromise({ exitCode, stdout, stderr });
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const parseOutput = (raw) => {
|
|
49
|
+
const trimmed = raw.trim();
|
|
50
|
+
const start = trimmed.indexOf("[");
|
|
51
|
+
|
|
52
|
+
if (start === -1) {
|
|
53
|
+
throw new Error("pagerts output does not contain JSON payload");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let inString = false;
|
|
57
|
+
let isEscaped = false;
|
|
58
|
+
let depth = 0;
|
|
59
|
+
let end = -1;
|
|
60
|
+
|
|
61
|
+
for (let i = start; i < trimmed.length; i += 1) {
|
|
62
|
+
const char = trimmed[i];
|
|
63
|
+
|
|
64
|
+
if (inString) {
|
|
65
|
+
if (isEscaped) {
|
|
66
|
+
isEscaped = false;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (char === "\\") {
|
|
71
|
+
isEscaped = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (char === '"') {
|
|
76
|
+
inString = false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (char === '"') {
|
|
83
|
+
inString = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (char === "[") {
|
|
88
|
+
depth += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (char === "]") {
|
|
93
|
+
depth -= 1;
|
|
94
|
+
if (depth === 0) {
|
|
95
|
+
end = i;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (end === -1) {
|
|
102
|
+
throw new Error("pagerts output contains incomplete JSON payload");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const payload = JSON.parse(trimmed.slice(start, end + 1));
|
|
106
|
+
if (!Array.isArray(payload) || payload.length === 0) {
|
|
107
|
+
throw new Error("pagerts returned an empty payload");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return payload;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const isTrackPath = (value) =>
|
|
114
|
+
typeof value === "string" &&
|
|
115
|
+
value.startsWith(USER_PATH_PREFIX) &&
|
|
116
|
+
value !== "/akinevz" &&
|
|
117
|
+
!value.startsWith("/akinevz/sets/") &&
|
|
118
|
+
!RESERVED_PROFILE_ROUTES.has(value.split("/").filter(Boolean).at(-1) ?? "") &&
|
|
119
|
+
/^\/akinevz\/[^/]+$/.test(value);
|
|
120
|
+
|
|
121
|
+
export const titleFromPath = (value) => value.split("/").filter(Boolean).at(-1) ?? value;
|
|
122
|
+
|
|
123
|
+
export const buildTracks = (resources) => {
|
|
124
|
+
const seen = new Set();
|
|
125
|
+
|
|
126
|
+
return resources
|
|
127
|
+
.filter((resource) => isTrackPath(resource?.link?.value))
|
|
128
|
+
.map((resource) => resource.link.value)
|
|
129
|
+
.filter((value) => {
|
|
130
|
+
if (seen.has(value)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
seen.add(value);
|
|
134
|
+
return true;
|
|
135
|
+
})
|
|
136
|
+
.map((path) => ({
|
|
137
|
+
path,
|
|
138
|
+
title: titleFromPath(path),
|
|
139
|
+
url: `https://soundcloud.com${path}`,
|
|
140
|
+
}));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const run = async () => {
|
|
144
|
+
const projectRoot = resolve(__dirname, "..");
|
|
145
|
+
await mkdir(cacheDir, { recursive: true });
|
|
146
|
+
|
|
147
|
+
const curlResult = await runCommand({
|
|
148
|
+
command: "curl",
|
|
149
|
+
args: ["-fLsS", SOURCE_URL, "-o", cachedArtistPage],
|
|
150
|
+
cwd: projectRoot,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (curlResult.exitCode !== 0) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`curl failed with exit code ${curlResult.exitCode}: ${curlResult.stderr.trim()}`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const pagertsResult = await runCommand({
|
|
160
|
+
command: "npx",
|
|
161
|
+
args: ["--yes", "pagerts@latest", cachedArtistPage],
|
|
162
|
+
cwd: projectRoot,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (pagertsResult.exitCode !== 0) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`pagerts failed with exit code ${pagertsResult.exitCode}: ${pagertsResult.stderr.trim()}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const payload = parseOutput(pagertsResult.stdout);
|
|
172
|
+
const resources = payload[0]?.resources;
|
|
173
|
+
|
|
174
|
+
if (!Array.isArray(resources)) {
|
|
175
|
+
throw new Error("pagerts payload is missing resources");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const tracks = buildTracks(resources);
|
|
179
|
+
|
|
180
|
+
const result = {
|
|
181
|
+
source: SOURCE_URL,
|
|
182
|
+
generatedAt: new Date().toISOString(),
|
|
183
|
+
trackCount: tracks.length,
|
|
184
|
+
tracks,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await writeFile(outputFile, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
|
188
|
+
process.stdout.write(
|
|
189
|
+
`Fetched artist page to ${cachedArtistPage} and wrote ${tracks.length} tracks to ${outputFile}\n`,
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
194
|
+
run().catch((error) => {
|
|
195
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { unlinkSync, rmdirSync, existsSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Execute a callback within a git worktree
|
|
12
|
+
* @param {string} dir - Directory path for the worktree
|
|
13
|
+
* @param {string} branch - Branch name to checkout
|
|
14
|
+
* @param {Function} callback - Function to execute within the worktree
|
|
15
|
+
*/
|
|
16
|
+
export async function withGitWorktree(dir, branch, callback) {
|
|
17
|
+
const worktreeDir = resolve(__dirname, "..", dir);
|
|
18
|
+
|
|
19
|
+
console.log(`🌳 Creating git worktree at ${worktreeDir} for branch ${branch}...`);
|
|
20
|
+
|
|
21
|
+
// Create worktree
|
|
22
|
+
const gitWorktreeAdd = spawn("git", ["worktree", "add", worktreeDir, branch], {
|
|
23
|
+
stdio: "inherit"
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
gitWorktreeAdd.on("close", (code) => {
|
|
28
|
+
if (code !== 0) {
|
|
29
|
+
reject(new Error("Git worktree add failed"));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
resolve();
|
|
33
|
+
});
|
|
34
|
+
gitWorktreeAdd.on("error", reject);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Execute callback
|
|
39
|
+
await callback(worktreeDir);
|
|
40
|
+
} finally {
|
|
41
|
+
// Before removing the work tree, commit and push the added content
|
|
42
|
+
|
|
43
|
+
// Step 3: Commit and push to blog-posts branch
|
|
44
|
+
console.log("📤 Committing and pushing to blog-posts branch...");
|
|
45
|
+
const gitAdd = spawn("git", ["add", "-A"], {
|
|
46
|
+
cwd: worktreeDir,
|
|
47
|
+
stdio: "inherit"
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await new Promise((resolve, reject) => {
|
|
51
|
+
gitAdd.on("close", (code) => {
|
|
52
|
+
if (code !== 0) {
|
|
53
|
+
reject(new Error("Git add failed"));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
58
|
+
gitAdd.on("error", reject);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const gitCommit = spawn("git", ["commit"], {
|
|
62
|
+
cwd: worktreeDir,
|
|
63
|
+
stdio: "inherit"
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
gitCommit.on("close", (code) => {
|
|
68
|
+
if (code !== 0) {
|
|
69
|
+
reject(new Error("Git commit failed"));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
resolve();
|
|
73
|
+
});
|
|
74
|
+
gitCommit.on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const gitPush = spawn("git", ["push", "origin", "blog-posts"], {
|
|
78
|
+
cwd: worktreeDir,
|
|
79
|
+
stdio: "inherit"
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
gitPush.on("close", (code) => {
|
|
84
|
+
if (code !== 0) {
|
|
85
|
+
reject(new Error("Git push failed"));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
resolve();
|
|
89
|
+
});
|
|
90
|
+
gitPush.on("error", reject);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
console.log(`🗑️ Removing git worktree at ${worktreeDir}...`);
|
|
94
|
+
try {
|
|
95
|
+
const gitWorktreeRemove = spawn("git", ["worktree", "remove", worktreeDir], {
|
|
96
|
+
stdio: "inherit"
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await new Promise((resolve, reject) => {
|
|
100
|
+
gitWorktreeRemove.on("close", (code) => {
|
|
101
|
+
if (code !== 0) {
|
|
102
|
+
reject(new Error("Git worktree remove failed"));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
resolve();
|
|
106
|
+
});
|
|
107
|
+
gitWorktreeRemove.on("error", reject);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
console.log("✅ Worktree removed successfully");
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error("⚠️ Failed to remove worktree, please remove manually:", worktreeDir);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Example usage:
|
|
119
|
+
// await withGitWorktree(".worktree-blog", "blog-posts", async (worktreeDir) => {
|
|
120
|
+
// // Your code here
|
|
121
|
+
// console.log("Working in worktree:", worktreeDir);
|
|
122
|
+
// });
|