@scopebond/agent 0.4.6 → 0.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/README.md +35 -6
- package/dist/agent.d.ts +2 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +4 -4
- package/dist/agent.js.map +1 -1
- package/dist/allowance-sender.d.ts +8 -0
- package/dist/allowance-sender.d.ts.map +1 -0
- package/dist/allowance-sender.js +80 -0
- package/dist/allowance-sender.js.map +1 -0
- package/dist/autostart.d.ts +13 -0
- package/dist/autostart.d.ts.map +1 -1
- package/dist/autostart.js +60 -10
- package/dist/autostart.js.map +1 -1
- package/dist/cli-main.d.ts +3 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +218 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/cli.js +4 -185
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/ipc.d.ts +7 -1
- package/dist/ipc.d.ts.map +1 -1
- package/dist/ipc.js +64 -12
- package/dist/ipc.js.map +1 -1
- package/dist/log-file.d.ts +6 -0
- package/dist/log-file.d.ts.map +1 -0
- package/dist/log-file.js +40 -0
- package/dist/log-file.js.map +1 -0
- package/dist/native-update.d.ts +51 -0
- package/dist/native-update.d.ts.map +1 -0
- package/dist/native-update.js +120 -0
- package/dist/native-update.js.map +1 -0
- package/dist/prompt.d.ts +11 -1
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +57 -23
- package/dist/prompt.js.map +1 -1
- package/dist/reconnect.d.ts +19 -0
- package/dist/reconnect.d.ts.map +1 -0
- package/dist/reconnect.js +60 -0
- package/dist/reconnect.js.map +1 -0
- package/dist/self.d.ts +5 -0
- package/dist/self.d.ts.map +1 -0
- package/dist/self.js +15 -0
- package/dist/self.js.map +1 -0
- package/dist/service.d.ts +50 -4
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +292 -18
- package/dist/service.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +4 -9
- package/dist/setup.js.map +1 -1
- package/dist/summary.d.ts +16 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +47 -0
- package/dist/summary.js.map +1 -0
- package/dist/tray-model.d.ts +91 -0
- package/dist/tray-model.d.ts.map +1 -0
- package/dist/tray-model.js +159 -0
- package/dist/tray-model.js.map +1 -0
- package/dist/tray.d.ts.map +1 -1
- package/dist/tray.js +103 -29
- package/dist/tray.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +7 -4
- package/dist/update.js.map +1 -1
- package/package.json +4 -4
package/dist/tray.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Where a person sees the agent. Windows: a tray icon from Windows' own signed PowerShell (no extra program to install or
|
|
2
|
-
// sign)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// sign). It draws the "S" tile with a status badge (D143: shape and colour together, no seal), shows the agent's tray model
|
|
3
|
+
// (`GET /tray`: one headline, the rows that have data, the one fix, the actions that apply now), says what "Check now" found,
|
|
4
|
+
// and shows a balloon only when the state gets worse (after five minutes for "needs attention", so sleep and wake do not
|
|
5
|
+
// flap) or recovers, as the person's notification setting allows. It asks the agent's local channel every 30 seconds and
|
|
6
|
+
// closes itself when the agent stops. macOS and Linux: a system notification when the state gets worse, and once more when
|
|
7
|
+
// it recovers.
|
|
5
8
|
import { spawn } from "node:child_process";
|
|
6
9
|
import { AGENT_FILE, TOKEN_HEADER } from "./ipc.js";
|
|
7
10
|
const b64 = (s) => Buffer.from(s, "utf8").toString("base64");
|
|
@@ -12,53 +15,124 @@ $ErrorActionPreference = 'SilentlyContinue'
|
|
|
12
15
|
Add-Type -AssemblyName System.Windows.Forms
|
|
13
16
|
Add-Type -AssemblyName System.Drawing
|
|
14
17
|
function T($s) { [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s)) }
|
|
15
|
-
$
|
|
18
|
+
$home0 = T '${b64(home)}'
|
|
19
|
+
$endpointFile = Join-Path $home0 '${AGENT_FILE}'
|
|
16
20
|
$agentPid = ${Math.trunc(agentPid)}
|
|
17
|
-
function
|
|
18
|
-
|
|
21
|
+
function C($hex) { [Drawing.ColorTranslator]::FromHtml($hex) }
|
|
22
|
+
# The "S" tile (navy; slate when disconnected) with a badge: grey dash offline, amber "!" attention, red x problem,
|
|
23
|
+
# blue arrow working, a slash when disconnected, none when protected.
|
|
24
|
+
function Tile($state) {
|
|
25
|
+
$b = New-Object Drawing.Bitmap 32, 32
|
|
19
26
|
$g = [Drawing.Graphics]::FromImage($b)
|
|
20
|
-
$g.SmoothingMode = 'AntiAlias'
|
|
21
|
-
$
|
|
27
|
+
$g.SmoothingMode = 'AntiAlias'; $g.TextRenderingHint = 'AntiAliasGridFit'
|
|
28
|
+
$tile = $(if ($state -eq 'disconnected') { C '#57606A' } else { C '#1B3A5C' })
|
|
29
|
+
$path = New-Object Drawing.Drawing2D.GraphicsPath
|
|
30
|
+
$r = 8; $path.AddArc(1, 1, $r * 2, $r * 2, 180, 90); $path.AddArc(31 - $r * 2, 1, $r * 2, $r * 2, 270, 90)
|
|
31
|
+
$path.AddArc(31 - $r * 2, 31 - $r * 2, $r * 2, $r * 2, 0, 90); $path.AddArc(1, 31 - $r * 2, $r * 2, $r * 2, 90, 90); $path.CloseFigure()
|
|
32
|
+
$g.FillPath((New-Object Drawing.SolidBrush $tile), $path)
|
|
33
|
+
$font = New-Object Drawing.Font('Segoe UI', 17, [Drawing.FontStyle]::Bold, [Drawing.GraphicsUnit]::Pixel)
|
|
34
|
+
$fmt = New-Object Drawing.StringFormat; $fmt.Alignment = 'Center'; $fmt.LineAlignment = 'Center'
|
|
35
|
+
$g.DrawString('S', $font, (New-Object Drawing.SolidBrush (C '#F7F4EE')), (New-Object Drawing.RectangleF(0, 0, 30, 32)), $fmt)
|
|
36
|
+
$white = New-Object Drawing.Pen((C '#FFFFFF'), 2.2); $white.StartCap = 'Round'; $white.EndCap = 'Round'
|
|
37
|
+
if ($state -eq 'disconnected') { $g.DrawLine((New-Object Drawing.Pen((C '#F7F4EE'), 2)), 6, 26, 26, 6) }
|
|
38
|
+
elseif ($state -ne 'protected') {
|
|
39
|
+
$fill = @{ offline = '#6E7781'; attention = '#DB9A04'; problem = '#CF222E'; working = '#0969DA' }[$state]
|
|
40
|
+
$g.FillEllipse((New-Object Drawing.SolidBrush (C $fill)), 17, 17, 14, 14)
|
|
41
|
+
$g.DrawEllipse((New-Object Drawing.Pen((C '#F7F4EE'), 1.5)), 17, 17, 14, 14)
|
|
42
|
+
switch ($state) {
|
|
43
|
+
'offline' { $g.DrawLine($white, 21, 24, 27, 24) }
|
|
44
|
+
'attention' { $dark = New-Object Drawing.Pen((C '#1F1A12'), 2.2); $dark.StartCap = 'Round'; $dark.EndCap = 'Round'; $g.DrawLine($dark, 24, 20, 24, 25); $g.FillEllipse((New-Object Drawing.SolidBrush (C '#1F1A12')), 23, 26.5, 2.4, 2.4) }
|
|
45
|
+
'problem' { $g.DrawLine($white, 21.5, 21.5, 26.5, 26.5); $g.DrawLine($white, 26.5, 21.5, 21.5, 26.5) }
|
|
46
|
+
'working' { $g.DrawArc($white, 20.5, 20.5, 7, 7, 300, 280) }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
22
49
|
$g.Dispose()
|
|
23
50
|
[Drawing.Icon]::FromHandle($b.GetHicon())
|
|
24
51
|
}
|
|
25
|
-
$icons = @{
|
|
52
|
+
$icons = @{}
|
|
53
|
+
foreach ($s in 'protected', 'working', 'offline', 'attention', 'problem', 'disconnected') { $icons[$s] = Tile $s }
|
|
26
54
|
$tray = New-Object Windows.Forms.NotifyIcon
|
|
27
|
-
$tray.Icon = $icons.
|
|
55
|
+
$tray.Icon = $icons.offline
|
|
28
56
|
$tray.Text = 'Scopebond'
|
|
29
57
|
$tray.Visible = $true
|
|
30
58
|
$menu = New-Object Windows.Forms.ContextMenuStrip
|
|
31
59
|
$tray.ContextMenuStrip = $menu
|
|
32
|
-
$script:
|
|
33
|
-
$script:
|
|
34
|
-
|
|
60
|
+
$script:state = ''
|
|
61
|
+
$script:worseSince = $null
|
|
62
|
+
$script:told = ''
|
|
63
|
+
$script:settings = @{ notifications = 'problems' }
|
|
64
|
+
function Call($method, $route, $body) {
|
|
35
65
|
$e = Get-Content -Raw $endpointFile | ConvertFrom-Json
|
|
36
66
|
$uri = 'http://127.0.0.1:' + $e.port + $route
|
|
37
67
|
$headers = @{ '${TOKEN_HEADER}' = $e.token }
|
|
38
|
-
if ($method -eq 'POST') { Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -ContentType 'application/json' -Body
|
|
68
|
+
if ($method -eq 'POST') { $json = $(if ($body) { $body | ConvertTo-Json -Compress } else { '{}' }); Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -ContentType 'application/json' -Body $json -TimeoutSec 300 }
|
|
39
69
|
else { Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -TimeoutSec 10 }
|
|
40
70
|
}
|
|
71
|
+
function Tell($title, $text, $kind) { if ($script:settings.notifications -ne 'off') { $tray.ShowBalloonTip(6000, $title, $text, $kind) } }
|
|
72
|
+
function Item($parent, $text, $enabled, $action) {
|
|
73
|
+
$i = $parent.Items.Add($text); $i.Enabled = $enabled
|
|
74
|
+
if ($action) { $i.Add_Click($action) }
|
|
75
|
+
$i
|
|
76
|
+
}
|
|
77
|
+
function Run($route) { try { Call 'POST' $route | Out-Null } catch {} ; Refresh }
|
|
41
78
|
function Refresh {
|
|
42
79
|
if (-not (Get-Process -Id $agentPid)) { $tray.Visible = $false; [Windows.Forms.Application]::Exit(); return }
|
|
43
|
-
try { $
|
|
44
|
-
if (
|
|
45
|
-
$
|
|
46
|
-
$
|
|
47
|
-
$tray.Text = $(if ($
|
|
80
|
+
try { $answer = Call 'GET' '/tray' } catch { $answer = $null }
|
|
81
|
+
if ($answer) { $m = $answer.tray; if ($answer.settings) { $script:settings = $answer.settings } }
|
|
82
|
+
else { $m = [pscustomobject]@{ state = 'problem'; headline = 'The Scopebond Agent is not answering'; tooltip = 'Scopebond — the agent is not answering'; rows = @(); fix = $null; actions = @(); hint = $null; recent_blocks = @() } }
|
|
83
|
+
$tray.Icon = $icons[$m.state]
|
|
84
|
+
$tray.Text = $(if ($m.tooltip.Length -gt 63) { $m.tooltip.Substring(0, 62) } else { $m.tooltip })
|
|
48
85
|
$menu.Items.Clear()
|
|
49
|
-
$head = $menu
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
86
|
+
$head = Item $menu ('Scopebond — ' + $m.headline) $false $null
|
|
87
|
+
$head.Font = New-Object Drawing.Font($head.Font, [Drawing.FontStyle]::Bold)
|
|
88
|
+
foreach ($row in $m.rows) { [void](Item $menu (' ' + $row.label + ': ' + $row.value) $false $null) }
|
|
89
|
+
if ($m.hint) { [void](Item $menu $m.hint $false $null) }
|
|
90
|
+
[void]$menu.Items.Add('-')
|
|
91
|
+
if ($m.fix) {
|
|
92
|
+
$route = $m.fix.route
|
|
93
|
+
if ($route -eq '/reconnect') { $f = Item $menu $m.fix.label $true ({ try { $r = Call 'POST' '/reconnect'; if ($r.user_code) { [Windows.Forms.MessageBox]::Show(('Approve this computer in your workspace (the page is opening in your browser).' + [Environment]::NewLine + [Environment]::NewLine + 'Check that it shows the code ' + $r.user_code), 'Scopebond: reconnect') | Out-Null } else { Tell 'Scopebond' ('Could not start the sign-in: ' + $r.error) 'Warning' } } catch {} ; Refresh }) }
|
|
94
|
+
else { $f = Item $menu $m.fix.label $true ({ Run $route }.GetNewClosure()) }
|
|
95
|
+
$f.Font = New-Object Drawing.Font($f.Font, [Drawing.FontStyle]::Bold)
|
|
96
|
+
}
|
|
97
|
+
foreach ($a in $m.actions) {
|
|
98
|
+
$route = $a.route
|
|
99
|
+
if ($route -eq '/check') { [void](Item $menu $a.label $true ({ try { $r = Call 'POST' '/check'; Tell 'Scopebond' $r.text 'Info' } catch { Tell 'Scopebond' 'Check could not reach the Scopebond Agent' 'Warning' } ; Refresh })) }
|
|
100
|
+
else { [void](Item $menu $a.label $true ({ Run $route }.GetNewClosure())) }
|
|
101
|
+
}
|
|
102
|
+
if ($m.recent_blocks.Count -gt 0) {
|
|
103
|
+
[void]$menu.Items.Add('-')
|
|
104
|
+
$blocks = $menu.Items.Add('Recently blocked')
|
|
105
|
+
foreach ($b in $m.recent_blocks) {
|
|
106
|
+
$when = $(try { ([datetime]$b.at).ToLocalTime().ToString('HH:mm') } catch { '' })
|
|
107
|
+
$note = $(if ($b.acted -eq 'allowed') { ' (allowed)' } elseif ($b.acted -eq 'asked') { ' (asked an admin)' } elseif ($b.can_act) { ' Allow or ask…' } else { '' })
|
|
108
|
+
$row = $blocks.DropDownItems.Add($b.summary + ' ' + $when + $note)
|
|
109
|
+
# D144: the Scopebond window asks; the person allows it (once, 15 min, always) or asks an admin. Nothing runs again by itself.
|
|
110
|
+
if ($b.can_act) { $id = $b.action_id; $row.Add_Click({ try { $r = Call 'POST' '/blocked' @{ action_id = $id }; [Windows.Forms.MessageBox]::Show($r.text, 'Scopebond') | Out-Null } catch {} ; Refresh }.GetNewClosure()) }
|
|
111
|
+
else { $row.Enabled = $false }
|
|
112
|
+
}
|
|
55
113
|
}
|
|
56
114
|
[void]$menu.Items.Add('-')
|
|
57
|
-
$
|
|
58
|
-
$
|
|
59
|
-
|
|
60
|
-
|
|
115
|
+
$notes = $menu.Items.Add('Notifications')
|
|
116
|
+
foreach ($choice in @(@('all', 'All'), @('problems', 'Problems only'), @('off', 'Off'))) {
|
|
117
|
+
$value = $choice[0]
|
|
118
|
+
$n = $notes.DropDownItems.Add($choice[1]); $n.Checked = ($script:settings.notifications -eq $value)
|
|
119
|
+
$n.Add_Click({ try { $script:settings = (Call 'POST' '/settings' @{ notifications = $value }).settings } catch {} ; Refresh }.GetNewClosure())
|
|
120
|
+
}
|
|
121
|
+
$help = $menu.Items.Add('Help')
|
|
122
|
+
[void]$help.DropDownItems.Add('Copy diagnostics', $null, { try { (Call 'GET' '/status') | ConvertTo-Json -Depth 8 | Set-Clipboard; Tell 'Scopebond' 'Diagnostics copied (no keys or credentials)' 'Info' } catch {} })
|
|
123
|
+
[void]$help.DropDownItems.Add('Open the Scopebond folder', $null, { Start-Process explorer.exe $home0 })
|
|
124
|
+
[void]$help.DropDownItems.Add('Scopebond documentation', $null, { Start-Process 'https://scopebond.com/documentation' })
|
|
125
|
+
[void]$help.DropDownItems.Add('About', $null, { try { $st = Call 'GET' '/status'; [Windows.Forms.MessageBox]::Show(('Scopebond Agent' + [Environment]::NewLine + 'Publisher: Avouro LLC' + [Environment]::NewLine + 'Agent: ' + $st.agent.version + [Environment]::NewLine + 'Hook: ' + $st.version + [Environment]::NewLine + 'Computer: ' + $st.identity.installation_id), 'About Scopebond') | Out-Null } catch {} })
|
|
126
|
+
[void](Item $menu 'Hide icon' $true { $tray.Visible = $false; [Windows.Forms.Application]::Exit() })
|
|
127
|
+
# Balloons: worse states as the setting allows ("needs attention" only after five minutes), and once when protected again.
|
|
128
|
+
$rank = @{ protected = 0; working = 0; offline = 1; attention = 2; disconnected = 3; problem = 3 }
|
|
129
|
+
if ($rank[$m.state] -ge 2) { if (-not $script:worseSince) { $script:worseSince = Get-Date } } else { $script:worseSince = $null }
|
|
130
|
+
$due = $script:worseSince -and ($m.state -ne 'attention' -or ((Get-Date) - $script:worseSince).TotalMinutes -ge 5)
|
|
131
|
+
if ($due -and $script:told -ne $m.state) { Tell 'Scopebond' $m.headline $(if ($rank[$m.state] -ge 3) { 'Error' } else { 'Warning' }); $script:told = $m.state }
|
|
132
|
+
elseif ($m.state -eq 'protected' -and $script:told -ne '' -and $script:told -ne 'protected') { Tell 'Scopebond' 'Scopebond is protecting again' 'Info'; $script:told = '' }
|
|
133
|
+
$script:state = $m.state
|
|
61
134
|
}
|
|
135
|
+
$tray.Add_MouseClick({ if ($_.Button -eq 'Left') { Refresh; $m = [Windows.Forms.NotifyIcon].GetMethod('ShowContextMenu', [Reflection.BindingFlags]'Instance,NonPublic'); $m.Invoke($tray, $null) } })
|
|
62
136
|
$timer = New-Object Windows.Forms.Timer
|
|
63
137
|
$timer.Interval = 30000
|
|
64
138
|
$timer.Add_Tick({ Refresh })
|
package/dist/tray.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"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","sourcesContent":["// Where a person sees the agent. Windows: a tray icon from Windows' own signed PowerShell (no extra program to install or\n// sign): a green, amber or red dot, the headline as its tooltip, a menu with the one fix, and a balloon when it turns amber or\n// red. It asks the agent's local channel every 30 seconds and closes itself when the agent stops. macOS and Linux: a system\n// notification when the state gets worse, and once more when it recovers.\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { AGENT_FILE, TOKEN_HEADER } from \"./ipc.js\";\nimport type { Health, HealthLevel } from \"./health.js\";\n\nconst b64 = (s: string) => Buffer.from(s, \"utf8\").toString(\"base64\");\n\n/** The tray script. The Scopebond home and the agent's process id reach it as base64 data, never as code. */\nexport function trayScript(home: string, agentPid: number): string {\n return `\n$ErrorActionPreference = 'SilentlyContinue'\nAdd-Type -AssemblyName System.Windows.Forms\nAdd-Type -AssemblyName System.Drawing\nfunction T($s) { [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s)) }\n$endpointFile = Join-Path (T '${b64(home)}') '${AGENT_FILE}'\n$agentPid = ${Math.trunc(agentPid)}\nfunction Dot($color) {\n $b = New-Object Drawing.Bitmap 16, 16\n $g = [Drawing.Graphics]::FromImage($b)\n $g.SmoothingMode = 'AntiAlias'\n $g.FillEllipse((New-Object Drawing.SolidBrush $color), 2, 2, 12, 12)\n $g.Dispose()\n [Drawing.Icon]::FromHandle($b.GetHicon())\n}\n$icons = @{ green = (Dot ([Drawing.Color]::FromArgb(46, 160, 67))); amber = (Dot ([Drawing.Color]::FromArgb(219, 154, 4))); red = (Dot ([Drawing.Color]::FromArgb(207, 34, 46))) }\n$tray = New-Object Windows.Forms.NotifyIcon\n$tray.Icon = $icons.amber\n$tray.Text = 'Scopebond'\n$tray.Visible = $true\n$menu = New-Object Windows.Forms.ContextMenuStrip\n$tray.ContextMenuStrip = $menu\n$script:level = ''\n$script:fix = $null\nfunction Call($method, $route) {\n $e = Get-Content -Raw $endpointFile | ConvertFrom-Json\n $uri = 'http://127.0.0.1:' + $e.port + $route\n $headers = @{ '${TOKEN_HEADER}' = $e.token }\n if ($method -eq 'POST') { Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -ContentType 'application/json' -Body '{}' -TimeoutSec 300 }\n else { Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -TimeoutSec 10 }\n}\nfunction Refresh {\n if (-not (Get-Process -Id $agentPid)) { $tray.Visible = $false; [Windows.Forms.Application]::Exit(); return }\n try { $h = (Call 'GET' '/status').health } catch { $h = $null }\n if (-not $h) { $h = @{ level = 'red'; headline = 'The Scopebond Agent is not answering'; fix = $null; hint = $null } }\n $tray.Icon = $icons[$h.level]\n $tip = 'Scopebond: ' + $h.headline\n $tray.Text = $(if ($tip.Length -gt 63) { $tip.Substring(0, 60) + '...' } else { $tip })\n $menu.Items.Clear()\n $head = $menu.Items.Add($h.headline); $head.Enabled = $false\n if ($h.hint) { $hint = $menu.Items.Add($h.hint); $hint.Enabled = $false }\n if ($h.fix) {\n $route = $h.fix.route\n $item = $menu.Items.Add($h.fix.label)\n $item.Add_Click({ try { Call 'POST' $route | Out-Null } catch {} ; Refresh }.GetNewClosure())\n }\n [void]$menu.Items.Add('-')\n $check = $menu.Items.Add('Check now'); $check.Add_Click({ try { Call 'POST' '/maintain' | Out-Null } catch {} ; Refresh })\n $quit = $menu.Items.Add('Hide this icon'); $quit.Add_Click({ $tray.Visible = $false; [Windows.Forms.Application]::Exit() })\n if ($script:level -ne $h.level -and $h.level -ne 'green' -and $script:level -ne '') { $tray.ShowBalloonTip(8000, 'Scopebond', $h.headline, $(if ($h.level -eq 'red') { 'Error' } else { 'Warning' })) }\n $script:level = $h.level\n}\n$timer = New-Object Windows.Forms.Timer\n$timer.Interval = 30000\n$timer.Add_Tick({ Refresh })\n$timer.Start()\nRefresh\n[Windows.Forms.Application]::Run()\n$tray.Dispose()\n`;\n}\n\n/** Start the tray icon for this agent (Windows only). Returns the process so the agent can close it when it stops. */\nexport function startTray(home: string, agentPid = process.pid): ChildProcess | null {\n if (process.platform !== \"win32\") return null;\n try {\n const encoded = Buffer.from(trayScript(home, agentPid), \"utf16le\").toString(\"base64\");\n const child = spawn(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-STA\", \"-WindowStyle\", \"Hidden\", \"-EncodedCommand\", encoded], { stdio: \"ignore\", windowsHide: true });\n child.on(\"error\", () => { /* no PowerShell, or blocked: the agent keeps working without an icon */ });\n return child;\n } catch { return null; }\n}\n\nconst RANK: Record<HealthLevel, number> = { green: 0, amber: 1, red: 2 };\n\n/** macOS and Linux: notify when the state gets worse, and once when it is green again. Returns whether it notified. */\nexport function notifyChange(previous: HealthLevel | null, health: Health, run: typeof spawn = spawn): boolean {\n if (process.platform === \"win32\" || previous === null || previous === health.level) return false;\n if (RANK[health.level] < RANK[previous] && health.level !== \"green\") return false;\n const text = health.level === \"green\" ? \"Working again: \" + health.headline : health.headline + (health.hint ? `. ${health.hint}` : \"\");\n try {\n const child = process.platform === \"darwin\"\n ? run(\"osascript\", [\"-e\", \"on run argv\", \"-e\", \"display notification (item 2 of argv) with title (item 1 of argv)\", \"-e\", \"end run\", \"Scopebond\", text], { stdio: \"ignore\" })\n : run(\"notify-send\", [\"Scopebond\", text], { stdio: \"ignore\" });\n child.on(\"error\", () => { /* no notification service */ });\n return true;\n } catch { return false; }\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"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","sourcesContent":["// Where a person sees the agent. Windows: a tray icon from Windows' own signed PowerShell (no extra program to install or\n// sign). It draws the \"S\" tile with a status badge (D143: shape and colour together, no seal), shows the agent's tray model\n// (`GET /tray`: one headline, the rows that have data, the one fix, the actions that apply now), says what \"Check now\" found,\n// and shows a balloon only when the state gets worse (after five minutes for \"needs attention\", so sleep and wake do not\n// flap) or recovers, as the person's notification setting allows. It asks the agent's local channel every 30 seconds and\n// closes itself when the agent stops. macOS and Linux: a system notification when the state gets worse, and once more when\n// it recovers.\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { AGENT_FILE, TOKEN_HEADER } from \"./ipc.js\";\nimport type { Health, HealthLevel } from \"./health.js\";\n\nconst b64 = (s: string) => Buffer.from(s, \"utf8\").toString(\"base64\");\n\n/** The tray script. The Scopebond home and the agent's process id reach it as base64 data, never as code. */\nexport function trayScript(home: string, agentPid: number): string {\n return `\n$ErrorActionPreference = 'SilentlyContinue'\nAdd-Type -AssemblyName System.Windows.Forms\nAdd-Type -AssemblyName System.Drawing\nfunction T($s) { [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s)) }\n$home0 = T '${b64(home)}'\n$endpointFile = Join-Path $home0 '${AGENT_FILE}'\n$agentPid = ${Math.trunc(agentPid)}\nfunction C($hex) { [Drawing.ColorTranslator]::FromHtml($hex) }\n# The \"S\" tile (navy; slate when disconnected) with a badge: grey dash offline, amber \"!\" attention, red x problem,\n# blue arrow working, a slash when disconnected, none when protected.\nfunction Tile($state) {\n $b = New-Object Drawing.Bitmap 32, 32\n $g = [Drawing.Graphics]::FromImage($b)\n $g.SmoothingMode = 'AntiAlias'; $g.TextRenderingHint = 'AntiAliasGridFit'\n $tile = $(if ($state -eq 'disconnected') { C '#57606A' } else { C '#1B3A5C' })\n $path = New-Object Drawing.Drawing2D.GraphicsPath\n $r = 8; $path.AddArc(1, 1, $r * 2, $r * 2, 180, 90); $path.AddArc(31 - $r * 2, 1, $r * 2, $r * 2, 270, 90)\n $path.AddArc(31 - $r * 2, 31 - $r * 2, $r * 2, $r * 2, 0, 90); $path.AddArc(1, 31 - $r * 2, $r * 2, $r * 2, 90, 90); $path.CloseFigure()\n $g.FillPath((New-Object Drawing.SolidBrush $tile), $path)\n $font = New-Object Drawing.Font('Segoe UI', 17, [Drawing.FontStyle]::Bold, [Drawing.GraphicsUnit]::Pixel)\n $fmt = New-Object Drawing.StringFormat; $fmt.Alignment = 'Center'; $fmt.LineAlignment = 'Center'\n $g.DrawString('S', $font, (New-Object Drawing.SolidBrush (C '#F7F4EE')), (New-Object Drawing.RectangleF(0, 0, 30, 32)), $fmt)\n $white = New-Object Drawing.Pen((C '#FFFFFF'), 2.2); $white.StartCap = 'Round'; $white.EndCap = 'Round'\n if ($state -eq 'disconnected') { $g.DrawLine((New-Object Drawing.Pen((C '#F7F4EE'), 2)), 6, 26, 26, 6) }\n elseif ($state -ne 'protected') {\n $fill = @{ offline = '#6E7781'; attention = '#DB9A04'; problem = '#CF222E'; working = '#0969DA' }[$state]\n $g.FillEllipse((New-Object Drawing.SolidBrush (C $fill)), 17, 17, 14, 14)\n $g.DrawEllipse((New-Object Drawing.Pen((C '#F7F4EE'), 1.5)), 17, 17, 14, 14)\n switch ($state) {\n 'offline' { $g.DrawLine($white, 21, 24, 27, 24) }\n 'attention' { $dark = New-Object Drawing.Pen((C '#1F1A12'), 2.2); $dark.StartCap = 'Round'; $dark.EndCap = 'Round'; $g.DrawLine($dark, 24, 20, 24, 25); $g.FillEllipse((New-Object Drawing.SolidBrush (C '#1F1A12')), 23, 26.5, 2.4, 2.4) }\n 'problem' { $g.DrawLine($white, 21.5, 21.5, 26.5, 26.5); $g.DrawLine($white, 26.5, 21.5, 21.5, 26.5) }\n 'working' { $g.DrawArc($white, 20.5, 20.5, 7, 7, 300, 280) }\n }\n }\n $g.Dispose()\n [Drawing.Icon]::FromHandle($b.GetHicon())\n}\n$icons = @{}\nforeach ($s in 'protected', 'working', 'offline', 'attention', 'problem', 'disconnected') { $icons[$s] = Tile $s }\n$tray = New-Object Windows.Forms.NotifyIcon\n$tray.Icon = $icons.offline\n$tray.Text = 'Scopebond'\n$tray.Visible = $true\n$menu = New-Object Windows.Forms.ContextMenuStrip\n$tray.ContextMenuStrip = $menu\n$script:state = ''\n$script:worseSince = $null\n$script:told = ''\n$script:settings = @{ notifications = 'problems' }\nfunction Call($method, $route, $body) {\n $e = Get-Content -Raw $endpointFile | ConvertFrom-Json\n $uri = 'http://127.0.0.1:' + $e.port + $route\n $headers = @{ '${TOKEN_HEADER}' = $e.token }\n if ($method -eq 'POST') { $json = $(if ($body) { $body | ConvertTo-Json -Compress } else { '{}' }); Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -ContentType 'application/json' -Body $json -TimeoutSec 300 }\n else { Invoke-RestMethod -Method Get -Uri $uri -Headers $headers -TimeoutSec 10 }\n}\nfunction Tell($title, $text, $kind) { if ($script:settings.notifications -ne 'off') { $tray.ShowBalloonTip(6000, $title, $text, $kind) } }\nfunction Item($parent, $text, $enabled, $action) {\n $i = $parent.Items.Add($text); $i.Enabled = $enabled\n if ($action) { $i.Add_Click($action) }\n $i\n}\nfunction Run($route) { try { Call 'POST' $route | Out-Null } catch {} ; Refresh }\nfunction Refresh {\n if (-not (Get-Process -Id $agentPid)) { $tray.Visible = $false; [Windows.Forms.Application]::Exit(); return }\n try { $answer = Call 'GET' '/tray' } catch { $answer = $null }\n if ($answer) { $m = $answer.tray; if ($answer.settings) { $script:settings = $answer.settings } }\n else { $m = [pscustomobject]@{ state = 'problem'; headline = 'The Scopebond Agent is not answering'; tooltip = 'Scopebond — the agent is not answering'; rows = @(); fix = $null; actions = @(); hint = $null; recent_blocks = @() } }\n $tray.Icon = $icons[$m.state]\n $tray.Text = $(if ($m.tooltip.Length -gt 63) { $m.tooltip.Substring(0, 62) } else { $m.tooltip })\n $menu.Items.Clear()\n $head = Item $menu ('Scopebond — ' + $m.headline) $false $null\n $head.Font = New-Object Drawing.Font($head.Font, [Drawing.FontStyle]::Bold)\n foreach ($row in $m.rows) { [void](Item $menu (' ' + $row.label + ': ' + $row.value) $false $null) }\n if ($m.hint) { [void](Item $menu $m.hint $false $null) }\n [void]$menu.Items.Add('-')\n if ($m.fix) {\n $route = $m.fix.route\n if ($route -eq '/reconnect') { $f = Item $menu $m.fix.label $true ({ try { $r = Call 'POST' '/reconnect'; if ($r.user_code) { [Windows.Forms.MessageBox]::Show(('Approve this computer in your workspace (the page is opening in your browser).' + [Environment]::NewLine + [Environment]::NewLine + 'Check that it shows the code ' + $r.user_code), 'Scopebond: reconnect') | Out-Null } else { Tell 'Scopebond' ('Could not start the sign-in: ' + $r.error) 'Warning' } } catch {} ; Refresh }) }\n else { $f = Item $menu $m.fix.label $true ({ Run $route }.GetNewClosure()) }\n $f.Font = New-Object Drawing.Font($f.Font, [Drawing.FontStyle]::Bold)\n }\n foreach ($a in $m.actions) {\n $route = $a.route\n if ($route -eq '/check') { [void](Item $menu $a.label $true ({ try { $r = Call 'POST' '/check'; Tell 'Scopebond' $r.text 'Info' } catch { Tell 'Scopebond' 'Check could not reach the Scopebond Agent' 'Warning' } ; Refresh })) }\n else { [void](Item $menu $a.label $true ({ Run $route }.GetNewClosure())) }\n }\n if ($m.recent_blocks.Count -gt 0) {\n [void]$menu.Items.Add('-')\n $blocks = $menu.Items.Add('Recently blocked')\n foreach ($b in $m.recent_blocks) {\n $when = $(try { ([datetime]$b.at).ToLocalTime().ToString('HH:mm') } catch { '' })\n $note = $(if ($b.acted -eq 'allowed') { ' (allowed)' } elseif ($b.acted -eq 'asked') { ' (asked an admin)' } elseif ($b.can_act) { ' Allow or ask…' } else { '' })\n $row = $blocks.DropDownItems.Add($b.summary + ' ' + $when + $note)\n # D144: the Scopebond window asks; the person allows it (once, 15 min, always) or asks an admin. Nothing runs again by itself.\n if ($b.can_act) { $id = $b.action_id; $row.Add_Click({ try { $r = Call 'POST' '/blocked' @{ action_id = $id }; [Windows.Forms.MessageBox]::Show($r.text, 'Scopebond') | Out-Null } catch {} ; Refresh }.GetNewClosure()) }\n else { $row.Enabled = $false }\n }\n }\n [void]$menu.Items.Add('-')\n $notes = $menu.Items.Add('Notifications')\n foreach ($choice in @(@('all', 'All'), @('problems', 'Problems only'), @('off', 'Off'))) {\n $value = $choice[0]\n $n = $notes.DropDownItems.Add($choice[1]); $n.Checked = ($script:settings.notifications -eq $value)\n $n.Add_Click({ try { $script:settings = (Call 'POST' '/settings' @{ notifications = $value }).settings } catch {} ; Refresh }.GetNewClosure())\n }\n $help = $menu.Items.Add('Help')\n [void]$help.DropDownItems.Add('Copy diagnostics', $null, { try { (Call 'GET' '/status') | ConvertTo-Json -Depth 8 | Set-Clipboard; Tell 'Scopebond' 'Diagnostics copied (no keys or credentials)' 'Info' } catch {} })\n [void]$help.DropDownItems.Add('Open the Scopebond folder', $null, { Start-Process explorer.exe $home0 })\n [void]$help.DropDownItems.Add('Scopebond documentation', $null, { Start-Process 'https://scopebond.com/documentation' })\n [void]$help.DropDownItems.Add('About', $null, { try { $st = Call 'GET' '/status'; [Windows.Forms.MessageBox]::Show(('Scopebond Agent' + [Environment]::NewLine + 'Publisher: Avouro LLC' + [Environment]::NewLine + 'Agent: ' + $st.agent.version + [Environment]::NewLine + 'Hook: ' + $st.version + [Environment]::NewLine + 'Computer: ' + $st.identity.installation_id), 'About Scopebond') | Out-Null } catch {} })\n [void](Item $menu 'Hide icon' $true { $tray.Visible = $false; [Windows.Forms.Application]::Exit() })\n # Balloons: worse states as the setting allows (\"needs attention\" only after five minutes), and once when protected again.\n $rank = @{ protected = 0; working = 0; offline = 1; attention = 2; disconnected = 3; problem = 3 }\n if ($rank[$m.state] -ge 2) { if (-not $script:worseSince) { $script:worseSince = Get-Date } } else { $script:worseSince = $null }\n $due = $script:worseSince -and ($m.state -ne 'attention' -or ((Get-Date) - $script:worseSince).TotalMinutes -ge 5)\n if ($due -and $script:told -ne $m.state) { Tell 'Scopebond' $m.headline $(if ($rank[$m.state] -ge 3) { 'Error' } else { 'Warning' }); $script:told = $m.state }\n elseif ($m.state -eq 'protected' -and $script:told -ne '' -and $script:told -ne 'protected') { Tell 'Scopebond' 'Scopebond is protecting again' 'Info'; $script:told = '' }\n $script:state = $m.state\n}\n$tray.Add_MouseClick({ if ($_.Button -eq 'Left') { Refresh; $m = [Windows.Forms.NotifyIcon].GetMethod('ShowContextMenu', [Reflection.BindingFlags]'Instance,NonPublic'); $m.Invoke($tray, $null) } })\n$timer = New-Object Windows.Forms.Timer\n$timer.Interval = 30000\n$timer.Add_Tick({ Refresh })\n$timer.Start()\nRefresh\n[Windows.Forms.Application]::Run()\n$tray.Dispose()\n`;\n}\n\n/** Start the tray icon for this agent (Windows only). Returns the process so the agent can close it when it stops. */\nexport function startTray(home: string, agentPid = process.pid): ChildProcess | null {\n if (process.platform !== \"win32\") return null;\n try {\n const encoded = Buffer.from(trayScript(home, agentPid), \"utf16le\").toString(\"base64\");\n const child = spawn(\"powershell.exe\", [\"-NoProfile\", \"-NonInteractive\", \"-STA\", \"-WindowStyle\", \"Hidden\", \"-EncodedCommand\", encoded], { stdio: \"ignore\", windowsHide: true });\n child.on(\"error\", () => { /* no PowerShell, or blocked: the agent keeps working without an icon */ });\n return child;\n } catch { return null; }\n}\n\nconst RANK: Record<HealthLevel, number> = { green: 0, amber: 1, red: 2 };\n\n/** macOS and Linux: notify when the state gets worse, and once when it is green again. Returns whether it notified. */\nexport function notifyChange(previous: HealthLevel | null, health: Health, run: typeof spawn = spawn): boolean {\n if (process.platform === \"win32\" || previous === null || previous === health.level) return false;\n if (RANK[health.level] < RANK[previous] && health.level !== \"green\") return false;\n const text = health.level === \"green\" ? \"Working again: \" + health.headline : health.headline + (health.hint ? `. ${health.hint}` : \"\");\n try {\n const child = process.platform === \"darwin\"\n ? run(\"osascript\", [\"-e\", \"on run argv\", \"-e\", \"display notification (item 2 of argv) with title (item 1 of argv)\", \"-e\", \"end run\", \"Scopebond\", text], { stdio: \"ignore\" })\n : run(\"notify-send\", [\"Scopebond\", text], { stdio: \"ignore\" });\n child.on(\"error\", () => { /* no notification service */ });\n return true;\n } catch { return false; }\n}\n\n"]}
|
package/dist/update.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAQA,OAAO,EAEmD,KAAK,OAAO,EAAE,KAAK,cAAc,EAC1F,MAAM,iBAAiB,CAAC;AAOzB,wBAAgB,YAAY,IAAI,MAAM,CAMrC;AAED,MAAM,WAAW,aAAa;IAAG,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAE5G,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,YAAY,CAAC,EAAE,SAAS,GAAE,OAAO,KAAa,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAUnL;AAED,mDAAmD;AACnD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,mGAAmG;AACnG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIjE;AAED;mGACmG;AACnG;;2DAE2D;AAC3D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,SAAgB,GAAG,MAAM,CAUvF;AAED,yGAAyG;AACzG,wBAAgB,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAGlD;AAED;2EAC2E;AAC3E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,OAAO,SAAgB,EAAE,YAAY,UAAO,GAAG,KAAK,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAajK;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAa,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB9G"}
|
package/dist/update.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
// the agent settings are moved to the hook version the agent carries.
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { createRequire } from "node:module";
|
|
8
7
|
import { dirname, join } from "node:path";
|
|
9
|
-
import { absoluteHookCommand, ensureDurableRuntime,
|
|
8
|
+
import { absoluteHookCommand, configuredHookCommands, ensureDurableRuntime, hookCliPath, hookCommandResolves, hookVersion, isScopebondHookCommand, isSingleExecutable, nativeHookCommand, userHarnessFile, writeHarnessConfig, } from "@scopebond/hook";
|
|
10
9
|
const VERSION = /^[0-9]+\.[0-9]+\.[0-9]+$/;
|
|
11
10
|
export function agentVersion() {
|
|
11
|
+
if (typeof __SCOPEBOND_AGENT_VERSION__ === "string")
|
|
12
|
+
return __SCOPEBOND_AGENT_VERSION__;
|
|
12
13
|
try {
|
|
13
14
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
14
15
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
@@ -52,10 +53,12 @@ export function commandHookVersion(command) {
|
|
|
52
53
|
* and it needs neither the registry nor npx on the coding tool's PATH); another version, or a pin that
|
|
53
54
|
* cannot be made, falls back to the portable npx form. */
|
|
54
55
|
export function maintainedHookCommand(harness, version = hookVersion()) {
|
|
56
|
+
// The single executable carries its own hook, at the path the installer keeps.
|
|
57
|
+
if (isSingleExecutable())
|
|
58
|
+
return nativeHookCommand(harness);
|
|
55
59
|
if (version === hookVersion()) {
|
|
56
60
|
try {
|
|
57
|
-
const
|
|
58
|
-
const pin = ensureDurableRuntime(cli, version);
|
|
61
|
+
const pin = ensureDurableRuntime(hookCliPath(), version);
|
|
59
62
|
if (pin.cli)
|
|
60
63
|
return absoluteHookCommand(pin.cli, harness);
|
|
61
64
|
}
|
package/dist/update.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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","sourcesContent":["// Updates under the workspace's control (D130). The agent asks the workspace which versions this\n// computer should run; with \"recommended\" it installs the named agent from npm (which brings the\n// matching hook, pinned exactly) and restarts; with \"hold\" it changes nothing. The hook entries in\n// the agent settings are moved to the hook version the agent carries.\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport {\n absoluteHookCommand, ensureDurableRuntime,\n configuredHookCommands, hookCommandResolves, hookVersion, isScopebondHookCommand, userHarnessFile, writeHarnessConfig,\n type Harness, type HookConnection,\n} from \"@scopebond/hook\";\n\nconst VERSION = /^[0-9]+\\.[0-9]+\\.[0-9]+$/;\n\nexport function agentVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n } catch { return \"0.0.0\"; }\n}\n\nexport interface ClientVersion { policy: \"recommended\" | \"hold\"; hook: string | null; agent: string | null }\n\nexport async function fetchClientVersion(connection: Pick<HookConnection, \"url\" | \"credential\">, fetchImpl: typeof fetch = fetch, timeoutMs = 10_000): Promise<ClientVersion | null> {\n try {\n const res = await fetchImpl(new URL(\"/v1/client-version\", connection.url).toString(), {\n headers: { authorization: `Bearer ${connection.credential}` }, redirect: \"error\", signal: AbortSignal.timeout(timeoutMs),\n });\n if (!res.ok) return null;\n const body = await res.json() as Partial<ClientVersion>;\n const ok = (v: unknown) => typeof v === \"string\" && VERSION.test(v) ? v : null;\n return { policy: body.policy === \"hold\" ? \"hold\" : \"recommended\", hook: ok(body.hook), agent: ok(body.agent) };\n } catch { return null; }\n}\n\n/** Compare x.y.z versions: negative when a < b. */\nexport function compareVersions(a: string, b: string): number {\n const pa = a.split(\".\").map(Number), pb = b.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);\n return 0;\n}\n\n/** The hook version a settings command runs, when it names one (an `npx` pin or a pinned copy). */\nexport function commandHookVersion(command: string): string | null {\n return /@scopebond\\/hook@([0-9]+\\.[0-9]+\\.[0-9]+)/.exec(command)?.[1]\n ?? /[\\\\/]runtime[\\\\/]([0-9]+\\.[0-9]+\\.[0-9]+)[\\\\/]/.exec(command)?.[1]\n ?? null;\n}\n\n/** The command an agent-maintained settings entry runs: the hook version this agent carries,\n * through npx, so it resolves Node at run time and an agent update never swaps files under it. */\n/** The hook command upkeep writes. The hook this agent carries is pinned by its absolute path (fast,\n * and it needs neither the registry nor npx on the coding tool's PATH); another version, or a pin that\n * cannot be made, falls back to the portable npx form. */\nexport function maintainedHookCommand(harness: Harness, version = hookVersion()): string {\n if (version === hookVersion()) {\n try {\n const cli = join(dirname(createRequire(import.meta.url).resolve(\"@scopebond/hook\")), \"cli.js\");\n const pin = ensureDurableRuntime(cli, version);\n if (pin.cli) return absoluteHookCommand(pin.cli, harness);\n } catch { /* fall back below */ }\n }\n return `npx -y @scopebond/hook@${version} ${harness}`;\n}\n\n/** This Node's own npm, run directly (no shell, and never another Node's npm first on PATH), or null. */\nexport function ownNpm(): [string, string[]] | null {\n const cli = join(dirname(process.execPath), process.platform === \"win32\" ? \"\" : \"../lib\", \"node_modules\", \"npm\", \"bin\", \"npm-cli.js\");\n return existsSync(cli) ? [process.execPath, [cli]] : null;\n}\n\n/** Bring the user-level Scopebond hook entries to `version`, and repair any that cannot start.\n * Entries of other tools are never touched. Returns the files changed. */\nexport function maintainHookEntries(harnesses: Harness[], version = hookVersion(), moveVersions = true): Array<{ harness: Harness; file: string; reason: string }> {\n const changed: Array<{ harness: Harness; file: string; reason: string }> = [];\n for (const harness of harnesses) {\n const file = userHarnessFile(harness);\n const ours = configuredHookCommands(file).filter((c) => isScopebondHookCommand(c));\n if (!ours.length) continue;\n const broken = ours.some((c) => !hookCommandResolves(c));\n const behind = moveVersions && ours.some((c) => { const v = commandHookVersion(c); return v !== null && compareVersions(v, version) < 0; });\n if (!broken && !behind) continue;\n writeHarnessConfig(file, harness, maintainedHookCommand(harness, version));\n changed.push({ harness, file, reason: broken ? \"the hook command could not start\" : `moved to hook ${version}` });\n }\n return changed;\n}\n\n/** Install `@scopebond/agent@version` globally with this computer's npm. */\nexport function installAgent(version: string, timeoutMs = 5 * 60_000): Promise<{ ok: boolean; output: string }> {\n if (!VERSION.test(version)) return Promise.resolve({ ok: false, output: \"not a version\" });\n return new Promise((resolve) => {\n const win = process.platform === \"win32\";\n const args = [\"install\", \"-g\", `@scopebond/agent@${version}`, \"--no-fund\", \"--no-audit\"];\n // This Node's own npm when it can be found; otherwise npm on PATH (a .cmd on Windows, which Node only\n // starts through a shell; the arguments are fixed and the version is validated).\n const own = ownNpm();\n const child = own ? spawn(own[0], [...own[1], ...args], { windowsHide: true }) : spawn(win ? \"npm.cmd\" : \"npm\", args, { shell: win, windowsHide: true });\n let output = \"\";\n child.stdout?.on(\"data\", (d) => { output += d; });\n child.stderr?.on(\"data\", (d) => { output += d; });\n const timer = setTimeout(() => { child.kill(); resolve({ ok: false, output: output + \"\\ntimed out\" }); }, timeoutMs);\n child.on(\"error\", (error) => { clearTimeout(timer); resolve({ ok: false, output: String(error) }); });\n child.on(\"close\", (code) => { clearTimeout(timer); resolve({ ok: code === 0, output: output.slice(-2000) }); });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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","sourcesContent":["// Updates under the workspace's control (D130). The agent asks the workspace which versions this\n// computer should run; with \"recommended\" it installs the named agent from npm (which brings the\n// matching hook, pinned exactly) and restarts; with \"hold\" it changes nothing. The hook entries in\n// the agent settings are moved to the hook version the agent carries.\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n absoluteHookCommand, configuredHookCommands, ensureDurableRuntime, hookCliPath, hookCommandResolves, hookVersion, isScopebondHookCommand, isSingleExecutable,\n nativeHookCommand, userHarnessFile, writeHarnessConfig, type Harness, type HookConnection,\n} from \"@scopebond/hook\";\n\nconst VERSION = /^[0-9]+\\.[0-9]+\\.[0-9]+$/;\n\n/** Set by the single executable's build (esbuild `define`); absent from the npm package. */\ndeclare const __SCOPEBOND_AGENT_VERSION__: string | undefined;\n\nexport function agentVersion(): string {\n if (typeof __SCOPEBOND_AGENT_VERSION__ === \"string\") return __SCOPEBOND_AGENT_VERSION__;\n try {\n const pkg = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\")) as { version?: string };\n return typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n } catch { return \"0.0.0\"; }\n}\n\nexport interface ClientVersion { policy: \"recommended\" | \"hold\"; hook: string | null; agent: string | null }\n\nexport async function fetchClientVersion(connection: Pick<HookConnection, \"url\" | \"credential\">, fetchImpl: typeof fetch = fetch, timeoutMs = 10_000): Promise<ClientVersion | null> {\n try {\n const res = await fetchImpl(new URL(\"/v1/client-version\", connection.url).toString(), {\n headers: { authorization: `Bearer ${connection.credential}` }, redirect: \"error\", signal: AbortSignal.timeout(timeoutMs),\n });\n if (!res.ok) return null;\n const body = await res.json() as Partial<ClientVersion>;\n const ok = (v: unknown) => typeof v === \"string\" && VERSION.test(v) ? v : null;\n return { policy: body.policy === \"hold\" ? \"hold\" : \"recommended\", hook: ok(body.hook), agent: ok(body.agent) };\n } catch { return null; }\n}\n\n/** Compare x.y.z versions: negative when a < b. */\nexport function compareVersions(a: string, b: string): number {\n const pa = a.split(\".\").map(Number), pb = b.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);\n return 0;\n}\n\n/** The hook version a settings command runs, when it names one (an `npx` pin or a pinned copy). */\nexport function commandHookVersion(command: string): string | null {\n return /@scopebond\\/hook@([0-9]+\\.[0-9]+\\.[0-9]+)/.exec(command)?.[1]\n ?? /[\\\\/]runtime[\\\\/]([0-9]+\\.[0-9]+\\.[0-9]+)[\\\\/]/.exec(command)?.[1]\n ?? null;\n}\n\n/** The command an agent-maintained settings entry runs: the hook version this agent carries,\n * through npx, so it resolves Node at run time and an agent update never swaps files under it. */\n/** The hook command upkeep writes. The hook this agent carries is pinned by its absolute path (fast,\n * and it needs neither the registry nor npx on the coding tool's PATH); another version, or a pin that\n * cannot be made, falls back to the portable npx form. */\nexport function maintainedHookCommand(harness: Harness, version = hookVersion()): string {\n // The single executable carries its own hook, at the path the installer keeps.\n if (isSingleExecutable()) return nativeHookCommand(harness);\n if (version === hookVersion()) {\n try {\n const pin = ensureDurableRuntime(hookCliPath(), version);\n if (pin.cli) return absoluteHookCommand(pin.cli, harness);\n } catch { /* fall back below */ }\n }\n return `npx -y @scopebond/hook@${version} ${harness}`;\n}\n\n/** This Node's own npm, run directly (no shell, and never another Node's npm first on PATH), or null. */\nexport function ownNpm(): [string, string[]] | null {\n const cli = join(dirname(process.execPath), process.platform === \"win32\" ? \"\" : \"../lib\", \"node_modules\", \"npm\", \"bin\", \"npm-cli.js\");\n return existsSync(cli) ? [process.execPath, [cli]] : null;\n}\n\n/** Bring the user-level Scopebond hook entries to `version`, and repair any that cannot start.\n * Entries of other tools are never touched. Returns the files changed. */\nexport function maintainHookEntries(harnesses: Harness[], version = hookVersion(), moveVersions = true): Array<{ harness: Harness; file: string; reason: string }> {\n const changed: Array<{ harness: Harness; file: string; reason: string }> = [];\n for (const harness of harnesses) {\n const file = userHarnessFile(harness);\n const ours = configuredHookCommands(file).filter((c) => isScopebondHookCommand(c));\n if (!ours.length) continue;\n const broken = ours.some((c) => !hookCommandResolves(c));\n const behind = moveVersions && ours.some((c) => { const v = commandHookVersion(c); return v !== null && compareVersions(v, version) < 0; });\n if (!broken && !behind) continue;\n writeHarnessConfig(file, harness, maintainedHookCommand(harness, version));\n changed.push({ harness, file, reason: broken ? \"the hook command could not start\" : `moved to hook ${version}` });\n }\n return changed;\n}\n\n/** Install `@scopebond/agent@version` globally with this computer's npm. */\nexport function installAgent(version: string, timeoutMs = 5 * 60_000): Promise<{ ok: boolean; output: string }> {\n if (!VERSION.test(version)) return Promise.resolve({ ok: false, output: \"not a version\" });\n return new Promise((resolve) => {\n const win = process.platform === \"win32\";\n const args = [\"install\", \"-g\", `@scopebond/agent@${version}`, \"--no-fund\", \"--no-audit\"];\n // This Node's own npm when it can be found; otherwise npm on PATH (a .cmd on Windows, which Node only\n // starts through a shell; the arguments are fixed and the version is validated).\n const own = ownNpm();\n const child = own ? spawn(own[0], [...own[1], ...args], { windowsHide: true }) : spawn(win ? \"npm.cmd\" : \"npm\", args, { shell: win, windowsHide: true });\n let output = \"\";\n child.stdout?.on(\"data\", (d) => { output += d; });\n child.stderr?.on(\"data\", (d) => { output += d; });\n const timer = setTimeout(() => { child.kill(); resolve({ ok: false, output: output + \"\\ntimed out\" }); }, timeoutMs);\n child.on(\"error\", (error) => { clearTimeout(timer); resolve({ ok: false, output: String(error) }); });\n child.on(\"close\", (code) => { clearTimeout(timer); resolve({ ok: code === 0, output: output.slice(-2000) }); });\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scopebond/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Free, open-source Scopebond Agent companion: deliver queued records, maintain rules and connections, and report computer health to the separate proprietary Scopebond Cloud workspace.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Avouro LLC",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@scopebond/gateway": "0.
|
|
43
|
-
"@scopebond/hook": "0.
|
|
44
|
-
"@scopebond/sdk": "0.1.
|
|
42
|
+
"@scopebond/gateway": "0.17.0",
|
|
43
|
+
"@scopebond/hook": "0.21.0",
|
|
44
|
+
"@scopebond/sdk": "0.1.5"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public"
|