token-larper 1.0.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/README.md +334 -0
- package/Start-TokenLarper.vbs +5 -0
- package/bin/cli.js +34 -0
- package/llms.txt +59 -0
- package/package.json +61 -0
- package/scripts/docs-demo-server.ts +141 -0
- package/scripts/launch-on-default-desktop.ps1 +42 -0
- package/scripts/launch-silent.vbs +6 -0
- package/scripts/run-server.ps1 +59 -0
- package/scripts/tray-host.ps1 +181 -0
- package/src/ccusage.ts +1075 -0
- package/src/client/App.tsx +560 -0
- package/src/client/charts.tsx +341 -0
- package/src/client/components/Calendar.tsx +207 -0
- package/src/client/components/DateRangePicker.tsx +131 -0
- package/src/client/components/KpiCards.tsx +122 -0
- package/src/client/components/RankChip.tsx +22 -0
- package/src/client/components/SelectMenu.tsx +116 -0
- package/src/client/components/SettingsDialog.tsx +207 -0
- package/src/client/components/ThemeSettings.tsx +131 -0
- package/src/client/context.tsx +60 -0
- package/src/client/index.html +54 -0
- package/src/client/lib/aggregate.ts +266 -0
- package/src/client/lib/range.ts +87 -0
- package/src/client/lib/rank.ts +323 -0
- package/src/client/lib/shareCard.ts +139 -0
- package/src/client/logo.svg +8 -0
- package/src/client/main.tsx +12 -0
- package/src/client/styles.css +1113 -0
- package/src/client/themes.ts +268 -0
- package/src/client/utils.ts +61 -0
- package/src/client/views/Models.tsx +71 -0
- package/src/client/views/Overview.tsx +280 -0
- package/src/client/views/Projects.tsx +88 -0
- package/src/client/views/Rank.tsx +255 -0
- package/src/client/views/Sessions.tsx +108 -0
- package/src/client/views/Tools.tsx +114 -0
- package/src/projects.ts +214 -0
- package/src/server.ts +173 -0
- package/src/startup.ts +110 -0
- package/src/tray.ts +293 -0
- package/src/types.ts +155 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Add-Type @"
|
|
2
|
+
using System;
|
|
3
|
+
using System.Runtime.InteropServices;
|
|
4
|
+
public class DefaultDesktopLauncher {
|
|
5
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
6
|
+
public struct STARTUPINFO {
|
|
7
|
+
public int cb;
|
|
8
|
+
public string lpReserved;
|
|
9
|
+
public string lpDesktop;
|
|
10
|
+
public string lpTitle;
|
|
11
|
+
public int dwX; public int dwY; public int dwXSize; public int dwYSize;
|
|
12
|
+
public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute;
|
|
13
|
+
public int dwFlags; public short wShowWindow; public short cbReserved2;
|
|
14
|
+
public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError;
|
|
15
|
+
}
|
|
16
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
17
|
+
public struct PROCESS_INFORMATION {
|
|
18
|
+
public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId;
|
|
19
|
+
}
|
|
20
|
+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
21
|
+
public static extern bool CreateProcess(
|
|
22
|
+
string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,
|
|
23
|
+
bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
|
|
24
|
+
ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
|
25
|
+
|
|
26
|
+
public static int LaunchOnDefaultDesktop(string cmdLine, string workDir) {
|
|
27
|
+
STARTUPINFO si = new STARTUPINFO();
|
|
28
|
+
si.cb = Marshal.SizeOf(si);
|
|
29
|
+
si.lpDesktop = "WinSta0\\Default";
|
|
30
|
+
si.dwFlags = 1;
|
|
31
|
+
si.wShowWindow = 0;
|
|
32
|
+
PROCESS_INFORMATION pi;
|
|
33
|
+
uint CREATE_NO_WINDOW = 0x08000000;
|
|
34
|
+
bool ok = CreateProcess(null, cmdLine, IntPtr.Zero, IntPtr.Zero, false, CREATE_NO_WINDOW, IntPtr.Zero, workDir, ref si, out pi);
|
|
35
|
+
if (!ok) return -Marshal.GetLastWin32Error();
|
|
36
|
+
return pi.dwProcessId;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
"@
|
|
40
|
+
$cmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -Sta -File `"F:\Just Some Files\opensource\tokenlarper\scripts\tray-host.ps1`""
|
|
41
|
+
$launchedPid = [DefaultDesktopLauncher]::LaunchOnDefaultDesktop($cmd, "F:\Just Some Files\opensource\tokenlarper")
|
|
42
|
+
Write-Output $launchedPid
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Set fso = CreateObject("Scripting.FileSystemObject")
|
|
2
|
+
scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
|
|
3
|
+
rootDir = fso.GetParentFolderName(scriptDir)
|
|
4
|
+
Set WshShell = CreateObject("WScript.Shell")
|
|
5
|
+
WshShell.CurrentDirectory = rootDir
|
|
6
|
+
WshShell.Run "powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File """ & fso.BuildPath(scriptDir, "run-server.ps1") & """", 0, False
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
$ErrorActionPreference = "Stop"
|
|
2
|
+
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
3
|
+
$cacheDir = Join-Path $projectRoot ".cache"
|
|
4
|
+
$settingsPath = Join-Path $cacheDir "settings.json"
|
|
5
|
+
$port = 4269
|
|
6
|
+
$openBrowser = $false
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
if (Test-Path -LiteralPath $settingsPath) {
|
|
10
|
+
$settings = Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json
|
|
11
|
+
$savedPort = 0
|
|
12
|
+
if ([int]::TryParse([string]$settings.port, [ref]$savedPort) -and $savedPort -gt 0 -and $savedPort -le 65535) {
|
|
13
|
+
$port = $savedPort
|
|
14
|
+
}
|
|
15
|
+
$openBrowser = $settings.openBrowserOnBoot -eq $true
|
|
16
|
+
}
|
|
17
|
+
} catch {
|
|
18
|
+
$port = 4269
|
|
19
|
+
$openBrowser = $false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
$baseUrl = "http://127.0.0.1:$port"
|
|
23
|
+
try {
|
|
24
|
+
$status = Invoke-RestMethod -Uri "$baseUrl/api/tray-status" -TimeoutSec 2
|
|
25
|
+
if ($null -ne $status.shortTooltip) {
|
|
26
|
+
if ($openBrowser) { Start-Process $baseUrl }
|
|
27
|
+
exit 0
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
# The server is not running yet.
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
$bunCommand = Get-Command bun.exe -ErrorAction SilentlyContinue
|
|
35
|
+
$bunPath = if ($bunCommand) { $bunCommand.Source } else { Join-Path $env:USERPROFILE ".bun\bin\bun.exe" }
|
|
36
|
+
if (-not (Test-Path -LiteralPath $bunPath)) { throw "Bun was not found at $bunPath" }
|
|
37
|
+
|
|
38
|
+
$env:PORT = [string]$port
|
|
39
|
+
$serverEntry = Join-Path $projectRoot "src\server.ts"
|
|
40
|
+
$server = Start-Process -FilePath $bunPath -ArgumentList "`"$serverEntry`"" -WorkingDirectory $projectRoot -WindowStyle Hidden -PassThru
|
|
41
|
+
|
|
42
|
+
$ready = $false
|
|
43
|
+
for ($attempt = 0; $attempt -lt 40; $attempt++) {
|
|
44
|
+
if ($server.HasExited) { throw "Token Larper exited before the dashboard was ready" }
|
|
45
|
+
try {
|
|
46
|
+
$status = Invoke-RestMethod -Uri "$baseUrl/api/tray-status" -TimeoutSec 1
|
|
47
|
+
if ($null -ne $status.shortTooltip) { $ready = $true; break }
|
|
48
|
+
} catch {
|
|
49
|
+
Start-Sleep -Milliseconds 250
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (-not $ready) { throw "Token Larper did not respond on port $port" }
|
|
53
|
+
Remove-Item -LiteralPath (Join-Path $cacheDir "startup-error.log") -ErrorAction SilentlyContinue
|
|
54
|
+
if ($openBrowser) { Start-Process $baseUrl }
|
|
55
|
+
} catch {
|
|
56
|
+
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
|
|
57
|
+
Set-Content -LiteralPath (Join-Path $cacheDir "startup-error.log") -Value $_.Exception.Message
|
|
58
|
+
exit 1
|
|
59
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
2
|
+
Add-Type -AssemblyName System.Drawing
|
|
3
|
+
|
|
4
|
+
$ServerPort = 4269
|
|
5
|
+
$ServerPid = 8904
|
|
6
|
+
$BaseUrl = "http://127.0.0.1:$ServerPort"
|
|
7
|
+
|
|
8
|
+
# Opt into DPI awareness so the icon is drawn at the tray's real pixel size
|
|
9
|
+
# (20px at 125%, 24px at 150%) instead of being drawn at 16px and blurred up.
|
|
10
|
+
Add-Type -Namespace TokenLarper -Name Dpi -MemberDefinition '[DllImport("user32.dll")] public static extern bool SetProcessDPIAware();'
|
|
11
|
+
[TokenLarper.Dpi]::SetProcessDPIAware() | Out-Null
|
|
12
|
+
|
|
13
|
+
# The t. monogram from src/client/logo.svg, drawn in its 64-unit coordinates.
|
|
14
|
+
$iconSize = [Math]::Max(16, [System.Windows.Forms.SystemInformation]::SmallIconSize.Width)
|
|
15
|
+
$bmp = New-Object System.Drawing.Bitmap($iconSize, $iconSize)
|
|
16
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
17
|
+
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
|
18
|
+
$g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
|
|
19
|
+
$g.Clear([System.Drawing.Color]::Transparent)
|
|
20
|
+
$g.ScaleTransform($iconSize / 64, $iconSize / 64)
|
|
21
|
+
|
|
22
|
+
$green = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(255, 47, 90, 67))
|
|
23
|
+
$cream = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(255, 243, 227, 191))
|
|
24
|
+
$gold = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(255, 225, 183, 92))
|
|
25
|
+
|
|
26
|
+
# Rounded square: x 3, y 3, size 58, corner radius 14.
|
|
27
|
+
$tile = New-Object System.Drawing.Drawing2D.GraphicsPath
|
|
28
|
+
$tile.AddArc(3, 3, 28, 28, 180, 90)
|
|
29
|
+
$tile.AddArc(33, 3, 28, 28, 270, 90)
|
|
30
|
+
$tile.AddArc(33, 33, 28, 28, 0, 90)
|
|
31
|
+
$tile.AddArc(3, 33, 28, 28, 90, 90)
|
|
32
|
+
$tile.CloseFigure()
|
|
33
|
+
$g.FillPath($green, $tile)
|
|
34
|
+
|
|
35
|
+
# Letter t: a stem with a curved foot, plus the crossbar.
|
|
36
|
+
$stem = New-Object System.Drawing.Drawing2D.GraphicsPath
|
|
37
|
+
$stem.AddLine(20, 12, 28, 12)
|
|
38
|
+
$stem.AddLine(28, 12, 28, 43.5)
|
|
39
|
+
$stem.AddBezier(28, 43.5, 28, 45.2, 28.8, 46, 30.5, 46)
|
|
40
|
+
$stem.AddLine(30.5, 46, 35, 46)
|
|
41
|
+
$stem.AddLine(35, 46, 35, 53)
|
|
42
|
+
$stem.AddLine(35, 53, 28.5, 53)
|
|
43
|
+
$stem.AddBezier(28.5, 53, 22.8, 53, 20, 50.2, 20, 44.5)
|
|
44
|
+
$stem.CloseFigure()
|
|
45
|
+
$g.FillPath($cream, $stem)
|
|
46
|
+
$g.FillRectangle($cream, 13, 21, 22, 7)
|
|
47
|
+
|
|
48
|
+
# The dot.
|
|
49
|
+
$g.FillEllipse($gold, 39.5, 42, 11, 11)
|
|
50
|
+
|
|
51
|
+
$hIcon = $bmp.GetHicon()
|
|
52
|
+
$icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
53
|
+
|
|
54
|
+
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
55
|
+
$notifyIcon.Icon = $icon
|
|
56
|
+
$notifyIcon.Text = "Token Larper - AI Coding Telemetry (Port $ServerPort)"
|
|
57
|
+
$notifyIcon.Visible = $true
|
|
58
|
+
|
|
59
|
+
# Context Menu
|
|
60
|
+
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
61
|
+
|
|
62
|
+
$itemHeader = New-Object System.Windows.Forms.ToolStripMenuItem("Token Larper Telemetry")
|
|
63
|
+
$itemHeader.Enabled = $false
|
|
64
|
+
$menu.Items.Add($itemHeader) | Out-Null
|
|
65
|
+
|
|
66
|
+
$itemStats = New-Object System.Windows.Forms.ToolStripMenuItem("Loading usage stats...")
|
|
67
|
+
$itemStats.Enabled = $false
|
|
68
|
+
$menu.Items.Add($itemStats) | Out-Null
|
|
69
|
+
|
|
70
|
+
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
71
|
+
|
|
72
|
+
$itemOpen = New-Object System.Windows.Forms.ToolStripMenuItem("Open Dashboard (http://localhost:$ServerPort)")
|
|
73
|
+
$itemOpen.Font = New-Object System.Drawing.Font($itemOpen.Font, [System.Drawing.FontStyle]::Bold)
|
|
74
|
+
$itemOpen.Add_Click({
|
|
75
|
+
Start-Process "$BaseUrl"
|
|
76
|
+
})
|
|
77
|
+
$menu.Items.Add($itemOpen) | Out-Null
|
|
78
|
+
|
|
79
|
+
$itemSync = New-Object System.Windows.Forms.ToolStripMenuItem("Sync Harnesses Now (ccusage)")
|
|
80
|
+
$itemSync.Add_Click({
|
|
81
|
+
try {
|
|
82
|
+
$notifyIcon.ShowBalloonTip(2000, "Token Larper", "Syncing coding harnesses via ccusage...", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
83
|
+
Invoke-RestMethod -Uri "$BaseUrl/api/usage?refresh=1" -Method Get -TimeoutSec 20 | Out-Null
|
|
84
|
+
Update-TrayStatus
|
|
85
|
+
$notifyIcon.ShowBalloonTip(2500, "Token Larper Synced", $itemStats.Text, [System.Windows.Forms.ToolTipIcon]::Info)
|
|
86
|
+
} catch {
|
|
87
|
+
$notifyIcon.ShowBalloonTip(2500, "Token Larper", "Failed to sync harnesses.", [System.Windows.Forms.ToolTipIcon]::Warning)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
$menu.Items.Add($itemSync) | Out-Null
|
|
91
|
+
|
|
92
|
+
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
93
|
+
|
|
94
|
+
$itemBoot = New-Object System.Windows.Forms.ToolStripMenuItem("Start on Windows Boot")
|
|
95
|
+
$itemBoot.CheckOnClick = $false
|
|
96
|
+
$itemBoot.Add_Click({
|
|
97
|
+
try {
|
|
98
|
+
$nextState = -not $itemBoot.Checked
|
|
99
|
+
$body = @{ enabled = $nextState; openBrowserOnBoot = $itemOpenOnBoot.Checked } | ConvertTo-Json
|
|
100
|
+
$res = Invoke-RestMethod -Uri "$BaseUrl/api/startup" -Method Post -Body $body -ContentType "application/json" -TimeoutSec 5
|
|
101
|
+
$itemBoot.Checked = [bool]$res.enabled
|
|
102
|
+
$itemOpenOnBoot.Checked = [bool]$res.openBrowserOnBoot
|
|
103
|
+
} catch {}
|
|
104
|
+
})
|
|
105
|
+
$menu.Items.Add($itemBoot) | Out-Null
|
|
106
|
+
|
|
107
|
+
$itemOpenOnBoot = New-Object System.Windows.Forms.ToolStripMenuItem("Auto-Open Browser on Boot")
|
|
108
|
+
$itemOpenOnBoot.CheckOnClick = $false
|
|
109
|
+
$itemOpenOnBoot.Add_Click({
|
|
110
|
+
try {
|
|
111
|
+
$nextOpen = -not $itemOpenOnBoot.Checked
|
|
112
|
+
$body = @{ enabled = $itemBoot.Checked; openBrowserOnBoot = $nextOpen } | ConvertTo-Json
|
|
113
|
+
$res = Invoke-RestMethod -Uri "$BaseUrl/api/startup" -Method Post -Body $body -ContentType "application/json" -TimeoutSec 5
|
|
114
|
+
$itemBoot.Checked = [bool]$res.enabled
|
|
115
|
+
$itemOpenOnBoot.Checked = [bool]$res.openBrowserOnBoot
|
|
116
|
+
} catch {}
|
|
117
|
+
})
|
|
118
|
+
$menu.Items.Add($itemOpenOnBoot) | Out-Null
|
|
119
|
+
|
|
120
|
+
$menu.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
|
|
121
|
+
|
|
122
|
+
$itemQuit = New-Object System.Windows.Forms.ToolStripMenuItem("Quit Token Larper")
|
|
123
|
+
$itemQuit.Add_Click({
|
|
124
|
+
$notifyIcon.Visible = $false
|
|
125
|
+
try {
|
|
126
|
+
Invoke-RestMethod -Uri "$BaseUrl/api/shutdown" -Method Post -ContentType "application/json" -TimeoutSec 3 | Out-Null
|
|
127
|
+
} catch {}
|
|
128
|
+
try {
|
|
129
|
+
Stop-Process -Id $ServerPid -Force -ErrorAction SilentlyContinue
|
|
130
|
+
} catch {}
|
|
131
|
+
$notifyIcon.Dispose()
|
|
132
|
+
[System.Windows.Forms.Application]::Exit()
|
|
133
|
+
})
|
|
134
|
+
$menu.Items.Add($itemQuit) | Out-Null
|
|
135
|
+
|
|
136
|
+
$notifyIcon.ContextMenuStrip = $menu
|
|
137
|
+
|
|
138
|
+
# Double-click tray icon opens dashboard
|
|
139
|
+
$notifyIcon.Add_DoubleClick({
|
|
140
|
+
Start-Process "$BaseUrl"
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
function Update-TrayStatus {
|
|
144
|
+
try {
|
|
145
|
+
$st = Invoke-RestMethod -Uri "$BaseUrl/api/tray-status" -Method Get -TimeoutSec 4
|
|
146
|
+
if ($null -ne $st) {
|
|
147
|
+
$itemStats.Text = $st.summaryText
|
|
148
|
+
$tooltip = "Token Larper: $($st.shortTooltip)"
|
|
149
|
+
if ($tooltip.Length -gt 63) {
|
|
150
|
+
$tooltip = $tooltip.Substring(0, 63)
|
|
151
|
+
}
|
|
152
|
+
$notifyIcon.Text = $tooltip
|
|
153
|
+
$itemBoot.Checked = [bool]$st.bootEnabled
|
|
154
|
+
$itemOpenOnBoot.Checked = [bool]$st.openBrowserOnBoot
|
|
155
|
+
}
|
|
156
|
+
} catch {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Timer to check parent Bun server health & update stats
|
|
160
|
+
$timer = New-Object System.Windows.Forms.Timer
|
|
161
|
+
$timer.Interval = 5000
|
|
162
|
+
$script:tickCount = 0
|
|
163
|
+
$timer.Add_Tick({
|
|
164
|
+
$proc = Get-Process -Id $ServerPid -ErrorAction SilentlyContinue
|
|
165
|
+
if ($null -eq $proc) {
|
|
166
|
+
$notifyIcon.Visible = $false
|
|
167
|
+
$notifyIcon.Dispose()
|
|
168
|
+
[System.Windows.Forms.Application]::Exit()
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
$script:tickCount++
|
|
172
|
+
if ($script:tickCount -eq 1 -or ($script:tickCount % 3 -eq 0)) {
|
|
173
|
+
Update-TrayStatus
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
$timer.Start()
|
|
177
|
+
|
|
178
|
+
Update-TrayStatus
|
|
179
|
+
$notifyIcon.ShowBalloonTip(2500, "Token Larper Running", "Right-click or double-click the green t. icon in your system tray.", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
180
|
+
|
|
181
|
+
[System.Windows.Forms.Application]::Run()
|