codelark 0.1.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/LICENSE +21 -0
- package/README.md +193 -0
- package/SECURITY.md +34 -0
- package/SKILL.md +67 -0
- package/agents/openai.yaml +4 -0
- package/dist/cli.mjs +8794 -0
- package/dist/daemon.mjs +47172 -0
- package/dist/ui-server.mjs +22165 -0
- package/package.json +73 -0
- package/schemas/config.v1.schema.json +259 -0
- package/schemas/data/audit.v1.schema.json +44 -0
- package/schemas/data/auto-tasks.v1.schema.json +94 -0
- package/schemas/data/channel-chats.v1.schema.json +159 -0
- package/schemas/data/channel-default-targets.v1.schema.json +43 -0
- package/schemas/data/messages.v1.schema.json +23 -0
- package/schemas/data/number-map.v1.schema.json +9 -0
- package/schemas/data/permissions.v1.schema.json +35 -0
- package/schemas/data/sessions.v1.schema.json +330 -0
- package/schemas/data/string-map.v1.schema.json +9 -0
- package/schemas/manifest.json +121 -0
- package/scripts/analyze-bridge-log.js +838 -0
- package/scripts/build-preflight.d.ts +21 -0
- package/scripts/build-preflight.js +70 -0
- package/scripts/build.js +53 -0
- package/scripts/check-npm-pack.js +46 -0
- package/scripts/daemon.ps1 +16 -0
- package/scripts/daemon.sh +206 -0
- package/scripts/doctor.ps1 +27 -0
- package/scripts/doctor.sh +185 -0
- package/scripts/hot-update-bridge.sh +298 -0
- package/scripts/install-codex-skills.sh +127 -0
- package/scripts/install-codex.sh +10 -0
- package/scripts/migrate-bindings-to-channel-chats.js +228 -0
- package/scripts/patch-codex-sdk-windows-hide.js +96 -0
- package/scripts/real-feishu-e2e.ts +5804 -0
- package/scripts/run-tests.js +83 -0
- package/scripts/setup-wizard-real-e2e.ts +195 -0
- package/scripts/supervisor-linux.sh +49 -0
- package/scripts/supervisor-macos.sh +167 -0
- package/scripts/supervisor-windows.ps1 +481 -0
- package/skills/codelark/SKILL.md +67 -0
- package/skills/codelark-auto/SKILL.md +80 -0
- package/skills/codelark-question/SKILL.md +54 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Windows daemon manager for CodeLark bridge.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Manages the bridge process on Windows.
|
|
7
|
+
Preferred: WinSW or NSSM wrapping as a Windows Service.
|
|
8
|
+
Fallback: Start-Process with hidden window + PID tracking.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
powershell -File scripts\daemon.ps1 start
|
|
12
|
+
powershell -File scripts\daemon.ps1 stop
|
|
13
|
+
powershell -File scripts\daemon.ps1 status
|
|
14
|
+
powershell -File scripts\daemon.ps1 logs [N]
|
|
15
|
+
powershell -File scripts\daemon.ps1 install-service # WinSW/NSSM setup
|
|
16
|
+
powershell -File scripts\daemon.ps1 uninstall-service
|
|
17
|
+
#>
|
|
18
|
+
|
|
19
|
+
param(
|
|
20
|
+
[Parameter(Position=0)]
|
|
21
|
+
[ValidateSet('start','stop','status','logs','install-service','uninstall-service','help')]
|
|
22
|
+
[string]$Command = 'help',
|
|
23
|
+
|
|
24
|
+
[Parameter(Position=1)]
|
|
25
|
+
[int]$LogLines = 50
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
$ErrorActionPreference = 'Stop'
|
|
29
|
+
|
|
30
|
+
# ── Paths ──
|
|
31
|
+
$CodelarkHome = if ($env:CODELARK_HOME) { $env:CODELARK_HOME } else { Join-Path $env:USERPROFILE '.codelark' }
|
|
32
|
+
$SkillDir = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
|
|
33
|
+
$RuntimeDir = Join-Path $CodelarkHome 'runtime'
|
|
34
|
+
$PidFile = Join-Path $RuntimeDir 'bridge.pid'
|
|
35
|
+
$StatusFile = Join-Path $RuntimeDir 'status.json'
|
|
36
|
+
$LogFile = Join-Path (Join-Path $CodelarkHome 'logs') 'bridge.log'
|
|
37
|
+
$ErrLogFile = Join-Path (Join-Path $CodelarkHome 'logs') 'bridge.stderr.log'
|
|
38
|
+
$DaemonMjs = Join-Path (Join-Path $SkillDir 'dist') 'daemon.mjs'
|
|
39
|
+
$ConfigFile = Join-Path $CodelarkHome 'config.env'
|
|
40
|
+
|
|
41
|
+
$ServiceName = 'CodeLarkBridge'
|
|
42
|
+
|
|
43
|
+
# ── Helpers ──
|
|
44
|
+
|
|
45
|
+
function Ensure-Dirs {
|
|
46
|
+
@('data','logs','runtime','data/messages') | ForEach-Object {
|
|
47
|
+
$dir = Join-Path $CodelarkHome $_
|
|
48
|
+
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Ensure-Built {
|
|
53
|
+
if (-not (Test-Path $DaemonMjs)) {
|
|
54
|
+
Write-Host "Building daemon bundle..."
|
|
55
|
+
Push-Location $SkillDir
|
|
56
|
+
npm run build
|
|
57
|
+
Pop-Location
|
|
58
|
+
} else {
|
|
59
|
+
$srcFiles = Get-ChildItem -Path (Join-Path $SkillDir 'src') -Filter '*.ts' -Recurse
|
|
60
|
+
$bundleTime = (Get-Item $DaemonMjs).LastWriteTime
|
|
61
|
+
$stale = $srcFiles | Where-Object { $_.LastWriteTime -gt $bundleTime } | Select-Object -First 1
|
|
62
|
+
if ($stale) {
|
|
63
|
+
Write-Host "Rebuilding daemon bundle (source changed)..."
|
|
64
|
+
Push-Location $SkillDir
|
|
65
|
+
npm run build
|
|
66
|
+
Pop-Location
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Get-ConfigEnvironment {
|
|
72
|
+
$configEnv = @{}
|
|
73
|
+
if (-not (Test-Path $ConfigFile)) {
|
|
74
|
+
return $configEnv
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
foreach ($line in Get-Content $ConfigFile) {
|
|
78
|
+
$trimmed = $line.Trim()
|
|
79
|
+
if (-not $trimmed -or $trimmed.StartsWith('#')) { continue }
|
|
80
|
+
$eqIndex = $trimmed.IndexOf('=')
|
|
81
|
+
if ($eqIndex -lt 1) { continue }
|
|
82
|
+
|
|
83
|
+
$name = $trimmed.Substring(0, $eqIndex).Trim()
|
|
84
|
+
$value = $trimmed.Substring($eqIndex + 1).Trim()
|
|
85
|
+
if (
|
|
86
|
+
($value.StartsWith('"') -and $value.EndsWith('"')) -or
|
|
87
|
+
($value.StartsWith("'") -and $value.EndsWith("'"))
|
|
88
|
+
) {
|
|
89
|
+
$value = $value.Substring(1, $value.Length - 2)
|
|
90
|
+
}
|
|
91
|
+
$configEnv[$name] = $value
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return $configEnv
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function Read-Pid {
|
|
98
|
+
if (Test-Path $PidFile) { return (Get-Content $PidFile -Raw).Trim() }
|
|
99
|
+
return $null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function Test-PidAlive {
|
|
103
|
+
param([string]$ProcessId)
|
|
104
|
+
if (-not $ProcessId) { return $false }
|
|
105
|
+
try { $null = Get-Process -Id ([int]$ProcessId) -ErrorAction Stop; return $true }
|
|
106
|
+
catch { return $false }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function Test-StatusRunning {
|
|
110
|
+
if (-not (Test-Path $StatusFile)) { return $false }
|
|
111
|
+
$json = Get-Content $StatusFile -Raw | ConvertFrom-Json
|
|
112
|
+
return $json.running -eq $true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function Show-LastExitReason {
|
|
116
|
+
if (Test-Path $StatusFile) {
|
|
117
|
+
$json = Get-Content $StatusFile -Raw | ConvertFrom-Json
|
|
118
|
+
if ($json.lastExitReason) {
|
|
119
|
+
Write-Host "Last exit reason: $($json.lastExitReason)"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function Show-FailureHelp {
|
|
125
|
+
Write-Host ""
|
|
126
|
+
Write-Host "Recent logs:"
|
|
127
|
+
if (Test-Path $LogFile) {
|
|
128
|
+
Get-Content $LogFile -Tail 20
|
|
129
|
+
} else {
|
|
130
|
+
Write-Host " (no log file)"
|
|
131
|
+
}
|
|
132
|
+
if (Test-Path $ErrLogFile) {
|
|
133
|
+
Write-Host ""
|
|
134
|
+
Write-Host "Recent stderr:"
|
|
135
|
+
Get-Content $ErrLogFile -Tail 20
|
|
136
|
+
}
|
|
137
|
+
Write-Host ""
|
|
138
|
+
Write-Host "Next steps:"
|
|
139
|
+
Write-Host " 1. Run diagnostics: powershell -File `"$SkillDir\scripts\doctor.ps1`""
|
|
140
|
+
Write-Host " 2. Check full logs: powershell -File `"$SkillDir\scripts\daemon.ps1`" logs 100"
|
|
141
|
+
Write-Host " 3. Rebuild bundle: cd `"$SkillDir`"; npm run build"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function Get-NodePath {
|
|
145
|
+
$nodePath = (Get-Command node -ErrorAction SilentlyContinue).Source
|
|
146
|
+
if (-not $nodePath) {
|
|
147
|
+
Write-Error "Node.js not found in PATH. Install Node.js >= 20."
|
|
148
|
+
exit 1
|
|
149
|
+
}
|
|
150
|
+
return $nodePath
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# ── WinSW / NSSM detection ──
|
|
154
|
+
|
|
155
|
+
function Find-ServiceManager {
|
|
156
|
+
# Prefer WinSW, then NSSM
|
|
157
|
+
$winsw = Get-Command 'WinSW.exe' -ErrorAction SilentlyContinue
|
|
158
|
+
if ($winsw) { return @{ type = 'winsw'; path = $winsw.Source } }
|
|
159
|
+
|
|
160
|
+
$nssm = Get-Command 'nssm.exe' -ErrorAction SilentlyContinue
|
|
161
|
+
if ($nssm) { return @{ type = 'nssm'; path = $nssm.Source } }
|
|
162
|
+
|
|
163
|
+
return $null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function Install-WinSWService {
|
|
167
|
+
param([string]$WinSWPath)
|
|
168
|
+
$nodePath = Get-NodePath
|
|
169
|
+
$xmlPath = Join-Path $SkillDir "$ServiceName.xml"
|
|
170
|
+
$configEnv = Get-ConfigEnvironment
|
|
171
|
+
|
|
172
|
+
# Run as current user so the service can access ~/.codelark and Codex login state
|
|
173
|
+
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
174
|
+
Write-Host "Service will run as: $currentUser"
|
|
175
|
+
$cred = Get-Credential -UserName $currentUser -Message "Enter password for '$currentUser' (required for Windows Service logon)"
|
|
176
|
+
$plainPwd = $cred.GetNetworkCredential().Password
|
|
177
|
+
|
|
178
|
+
# Generate WinSW config XML
|
|
179
|
+
$xml = @"
|
|
180
|
+
<service>
|
|
181
|
+
<id>$ServiceName</id>
|
|
182
|
+
<name>CodeLark Bridge</name>
|
|
183
|
+
<description>CodeLark bridge daemon</description>
|
|
184
|
+
<executable>$nodePath</executable>
|
|
185
|
+
<arguments>$DaemonMjs</arguments>
|
|
186
|
+
<workingdirectory>$SkillDir</workingdirectory>
|
|
187
|
+
<serviceaccount>
|
|
188
|
+
<username>$currentUser</username>
|
|
189
|
+
<password>$([System.Security.SecurityElement]::Escape($plainPwd))</password>
|
|
190
|
+
<allowservicelogon>true</allowservicelogon>
|
|
191
|
+
</serviceaccount>
|
|
192
|
+
<env name="USERPROFILE" value="$env:USERPROFILE"/>
|
|
193
|
+
<env name="APPDATA" value="$env:APPDATA"/>
|
|
194
|
+
<env name="LOCALAPPDATA" value="$env:LOCALAPPDATA"/>
|
|
195
|
+
<env name="PATH" value="$env:PATH"/>
|
|
196
|
+
<env name="CODELARK_HOME" value="$CodelarkHome"/>
|
|
197
|
+
<logpath>$(Join-Path $CodelarkHome 'logs')</logpath>
|
|
198
|
+
<log mode="append">
|
|
199
|
+
<logfile>bridge-service.log</logfile>
|
|
200
|
+
</log>
|
|
201
|
+
<onfailure action="restart" delay="10 sec"/>
|
|
202
|
+
<onfailure action="restart" delay="30 sec"/>
|
|
203
|
+
<onfailure action="none"/>
|
|
204
|
+
</service>
|
|
205
|
+
"@
|
|
206
|
+
|
|
207
|
+
$extraEnvXml = ($configEnv.GetEnumerator() | ForEach-Object {
|
|
208
|
+
' <env name="{0}" value="{1}"/>' -f $_.Key, [System.Security.SecurityElement]::Escape($_.Value)
|
|
209
|
+
}) -join "`r`n"
|
|
210
|
+
if ($extraEnvXml) {
|
|
211
|
+
$xml = $xml -replace ' <logpath>', "$extraEnvXml`r`n <logpath>"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
$xml | Set-Content -Path $xmlPath -Encoding UTF8
|
|
215
|
+
|
|
216
|
+
# Copy WinSW next to the XML with matching name
|
|
217
|
+
$winswCopy = Join-Path $SkillDir "$ServiceName.exe"
|
|
218
|
+
Copy-Item $WinSWPath $winswCopy -Force
|
|
219
|
+
|
|
220
|
+
& $winswCopy install
|
|
221
|
+
Write-Host "Service '$ServiceName' installed via WinSW."
|
|
222
|
+
Write-Host " Service account: $currentUser"
|
|
223
|
+
Write-Host "Start with: & `"$winswCopy`" start"
|
|
224
|
+
Write-Host "Or: sc.exe start $ServiceName"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function Install-NSSMService {
|
|
228
|
+
param([string]$NSSMPath)
|
|
229
|
+
$nodePath = Get-NodePath
|
|
230
|
+
$configEnv = Get-ConfigEnvironment
|
|
231
|
+
|
|
232
|
+
# Run as current user so the service can access ~/.codelark and Codex login state
|
|
233
|
+
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
234
|
+
Write-Host "Service will run as: $currentUser"
|
|
235
|
+
$cred = Get-Credential -UserName $currentUser -Message "Enter password for '$currentUser' (required for Windows Service logon)"
|
|
236
|
+
$plainPwd = $cred.GetNetworkCredential().Password
|
|
237
|
+
|
|
238
|
+
& $NSSMPath install $ServiceName $nodePath $DaemonMjs
|
|
239
|
+
& $NSSMPath set $ServiceName AppDirectory $SkillDir
|
|
240
|
+
& $NSSMPath set $ServiceName ObjectName $currentUser $plainPwd
|
|
241
|
+
& $NSSMPath set $ServiceName AppStdout $LogFile
|
|
242
|
+
& $NSSMPath set $ServiceName AppStderr $LogFile
|
|
243
|
+
& $NSSMPath set $ServiceName AppStdoutCreationDisposition 4
|
|
244
|
+
& $NSSMPath set $ServiceName AppStderrCreationDisposition 4
|
|
245
|
+
& $NSSMPath set $ServiceName Description "CodeLark bridge daemon"
|
|
246
|
+
& $NSSMPath set $ServiceName AppRestartDelay 10000
|
|
247
|
+
$envArgs = @(
|
|
248
|
+
"USERPROFILE=$env:USERPROFILE",
|
|
249
|
+
"APPDATA=$env:APPDATA",
|
|
250
|
+
"LOCALAPPDATA=$env:LOCALAPPDATA",
|
|
251
|
+
"CODELARK_HOME=$CodelarkHome"
|
|
252
|
+
)
|
|
253
|
+
foreach ($entry in $configEnv.GetEnumerator()) {
|
|
254
|
+
$envArgs += "$($entry.Key)=$($entry.Value)"
|
|
255
|
+
}
|
|
256
|
+
& $NSSMPath set $ServiceName AppEnvironmentExtra @envArgs
|
|
257
|
+
|
|
258
|
+
Write-Host "Service '$ServiceName' installed via NSSM."
|
|
259
|
+
Write-Host " Service account: $currentUser"
|
|
260
|
+
Write-Host "Start with: nssm start $ServiceName"
|
|
261
|
+
Write-Host "Or: sc.exe start $ServiceName"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# ── Fallback: Start-Process (no service manager) ──
|
|
265
|
+
|
|
266
|
+
function Start-Fallback {
|
|
267
|
+
$nodePath = Get-NodePath
|
|
268
|
+
$configEnv = Get-ConfigEnvironment
|
|
269
|
+
|
|
270
|
+
# Clean env
|
|
271
|
+
$envClone = [System.Collections.Hashtable]::new()
|
|
272
|
+
foreach ($key in [System.Environment]::GetEnvironmentVariables().Keys) {
|
|
273
|
+
$envClone[$key] = [System.Environment]::GetEnvironmentVariable($key)
|
|
274
|
+
}
|
|
275
|
+
# Remove CLAUDECODE
|
|
276
|
+
[System.Environment]::SetEnvironmentVariable('CLAUDECODE', $null)
|
|
277
|
+
[System.Environment]::SetEnvironmentVariable('CODELARK_HOME', $CodelarkHome, 'Process')
|
|
278
|
+
|
|
279
|
+
foreach ($entry in $configEnv.GetEnumerator()) {
|
|
280
|
+
[System.Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process')
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
$proc = Start-Process -FilePath $nodePath `
|
|
285
|
+
-ArgumentList $DaemonMjs `
|
|
286
|
+
-WorkingDirectory $SkillDir `
|
|
287
|
+
-WindowStyle Hidden `
|
|
288
|
+
-RedirectStandardOutput $LogFile `
|
|
289
|
+
-RedirectStandardError $ErrLogFile `
|
|
290
|
+
-PassThru
|
|
291
|
+
} finally {
|
|
292
|
+
foreach ($key in $envClone.Keys) {
|
|
293
|
+
[System.Environment]::SetEnvironmentVariable($key, $envClone[$key], 'Process')
|
|
294
|
+
}
|
|
295
|
+
foreach ($entry in $configEnv.GetEnumerator()) {
|
|
296
|
+
if (-not $envClone.ContainsKey($entry.Key)) {
|
|
297
|
+
[System.Environment]::SetEnvironmentVariable($entry.Key, $null, 'Process')
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (-not $envClone.ContainsKey('CODELARK_HOME')) {
|
|
301
|
+
[System.Environment]::SetEnvironmentVariable('CODELARK_HOME', $null, 'Process')
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# Write initial PID (main.ts will overwrite with real PID)
|
|
306
|
+
Set-Content -Path $PidFile -Value $proc.Id
|
|
307
|
+
return $proc.Id
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# ── Commands ──
|
|
311
|
+
|
|
312
|
+
switch ($Command) {
|
|
313
|
+
'start' {
|
|
314
|
+
Ensure-Dirs
|
|
315
|
+
Ensure-Built
|
|
316
|
+
|
|
317
|
+
$existingPid = Read-Pid
|
|
318
|
+
if ($existingPid -and (Test-PidAlive $existingPid)) {
|
|
319
|
+
Write-Host "Bridge already running (PID: $existingPid)"
|
|
320
|
+
if (Test-Path $StatusFile) { Get-Content $StatusFile -Raw }
|
|
321
|
+
exit 1
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Check if registered as Windows Service
|
|
325
|
+
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
|
326
|
+
if ($svc) {
|
|
327
|
+
Write-Host "Starting bridge via Windows Service..."
|
|
328
|
+
Start-Service -Name $ServiceName
|
|
329
|
+
Start-Sleep -Seconds 3
|
|
330
|
+
|
|
331
|
+
$newPid = Read-Pid
|
|
332
|
+
if ($newPid -and (Test-PidAlive $newPid) -and (Test-StatusRunning)) {
|
|
333
|
+
Write-Host "Bridge started (PID: $newPid, managed by Windows Service)"
|
|
334
|
+
if (Test-Path $StatusFile) { Get-Content $StatusFile -Raw }
|
|
335
|
+
} else {
|
|
336
|
+
Write-Host "Failed to start bridge via service."
|
|
337
|
+
Show-LastExitReason
|
|
338
|
+
Show-FailureHelp
|
|
339
|
+
exit 1
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
Write-Host "Starting bridge (background process)..."
|
|
343
|
+
$bridgePid = Start-Fallback
|
|
344
|
+
Start-Sleep -Seconds 3
|
|
345
|
+
|
|
346
|
+
$newPid = Read-Pid
|
|
347
|
+
if ($newPid -and (Test-PidAlive $newPid) -and (Test-StatusRunning)) {
|
|
348
|
+
Write-Host "Bridge started (PID: $newPid)"
|
|
349
|
+
if (Test-Path $StatusFile) { Get-Content $StatusFile -Raw }
|
|
350
|
+
} else {
|
|
351
|
+
Write-Host "Failed to start bridge."
|
|
352
|
+
if (-not $newPid -or -not (Test-PidAlive $newPid)) {
|
|
353
|
+
Write-Host " Process exited immediately."
|
|
354
|
+
}
|
|
355
|
+
Show-LastExitReason
|
|
356
|
+
Show-FailureHelp
|
|
357
|
+
exit 1
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
'stop' {
|
|
363
|
+
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
|
364
|
+
if ($svc -and $svc.Status -eq 'Running') {
|
|
365
|
+
Write-Host "Stopping bridge via Windows Service..."
|
|
366
|
+
Stop-Service -Name $ServiceName -Force
|
|
367
|
+
Write-Host "Bridge stopped"
|
|
368
|
+
if (Test-Path $PidFile) { Remove-Item $PidFile -Force }
|
|
369
|
+
} else {
|
|
370
|
+
$bridgePid = Read-Pid
|
|
371
|
+
if (-not $bridgePid) { Write-Host "No bridge running"; exit 0 }
|
|
372
|
+
if (Test-PidAlive $bridgePid) {
|
|
373
|
+
Stop-Process -Id ([int]$bridgePid) -Force
|
|
374
|
+
Write-Host "Bridge stopped"
|
|
375
|
+
} else {
|
|
376
|
+
Write-Host "Bridge was not running (stale PID file)"
|
|
377
|
+
}
|
|
378
|
+
if (Test-Path $PidFile) { Remove-Item $PidFile -Force }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
'status' {
|
|
383
|
+
$bridgePid = Read-Pid
|
|
384
|
+
|
|
385
|
+
# Check Windows Service
|
|
386
|
+
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
|
387
|
+
if ($svc) {
|
|
388
|
+
Write-Host "Windows Service '$ServiceName': $($svc.Status)"
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if ($bridgePid -and (Test-PidAlive $bridgePid)) {
|
|
392
|
+
Write-Host "Bridge process is running (PID: $bridgePid)"
|
|
393
|
+
if (Test-StatusRunning) {
|
|
394
|
+
Write-Host "Bridge status: running"
|
|
395
|
+
} else {
|
|
396
|
+
Write-Host "Bridge status: process alive but status.json not reporting running"
|
|
397
|
+
}
|
|
398
|
+
if (Test-Path $StatusFile) { Get-Content $StatusFile -Raw }
|
|
399
|
+
} else {
|
|
400
|
+
Write-Host "Bridge is not running"
|
|
401
|
+
if (Test-Path $PidFile) { Remove-Item $PidFile -Force }
|
|
402
|
+
Show-LastExitReason
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
'logs' {
|
|
407
|
+
if (Test-Path $LogFile) {
|
|
408
|
+
Get-Content $LogFile -Tail $LogLines | ForEach-Object {
|
|
409
|
+
$_ -replace '(token|secret|password)(["'']?\s*[:=]\s*["'']?)[^\s"]+', '$1$2*****'
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
Write-Host "No log file found at $LogFile"
|
|
413
|
+
}
|
|
414
|
+
if (Test-Path $ErrLogFile) {
|
|
415
|
+
Write-Host ""
|
|
416
|
+
Write-Host "stderr:"
|
|
417
|
+
Get-Content $ErrLogFile -Tail $LogLines
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
'install-service' {
|
|
422
|
+
Ensure-Dirs
|
|
423
|
+
Ensure-Built
|
|
424
|
+
|
|
425
|
+
$mgr = Find-ServiceManager
|
|
426
|
+
if (-not $mgr) {
|
|
427
|
+
Write-Host "No service manager found. Install one of:"
|
|
428
|
+
Write-Host " WinSW: https://github.com/winsw/winsw/releases"
|
|
429
|
+
Write-Host " NSSM: https://nssm.cc/download"
|
|
430
|
+
Write-Host ""
|
|
431
|
+
Write-Host "After installing, add it to PATH and re-run:"
|
|
432
|
+
Write-Host " powershell -File `"$PSCommandPath`" install-service"
|
|
433
|
+
exit 1
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
switch ($mgr.type) {
|
|
437
|
+
'winsw' { Install-WinSWService -WinSWPath $mgr.path }
|
|
438
|
+
'nssm' { Install-NSSMService -NSSMPath $mgr.path }
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
'uninstall-service' {
|
|
443
|
+
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
|
|
444
|
+
if (-not $svc) {
|
|
445
|
+
Write-Host "Service '$ServiceName' is not installed."
|
|
446
|
+
exit 0
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if ($svc.Status -eq 'Running') {
|
|
450
|
+
Stop-Service -Name $ServiceName -Force
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
$mgr = Find-ServiceManager
|
|
454
|
+
if ($mgr -and $mgr.type -eq 'nssm') {
|
|
455
|
+
& $mgr.path remove $ServiceName confirm
|
|
456
|
+
} else {
|
|
457
|
+
# WinSW or generic
|
|
458
|
+
$winswExe = Join-Path $SkillDir "$ServiceName.exe"
|
|
459
|
+
if (Test-Path $winswExe) {
|
|
460
|
+
& $winswExe uninstall
|
|
461
|
+
Remove-Item $winswExe -Force -ErrorAction SilentlyContinue
|
|
462
|
+
Remove-Item (Join-Path $SkillDir "$ServiceName.xml") -Force -ErrorAction SilentlyContinue
|
|
463
|
+
} else {
|
|
464
|
+
sc.exe delete $ServiceName
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
Write-Host "Service '$ServiceName' uninstalled."
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
'help' {
|
|
471
|
+
Write-Host "Usage: daemon.ps1 {start|stop|status|logs [N]|install-service|uninstall-service}"
|
|
472
|
+
Write-Host ""
|
|
473
|
+
Write-Host "Commands:"
|
|
474
|
+
Write-Host " start Start the bridge daemon"
|
|
475
|
+
Write-Host " stop Stop the bridge daemon"
|
|
476
|
+
Write-Host " status Show bridge status"
|
|
477
|
+
Write-Host " logs [N] Show last N log lines (default 50)"
|
|
478
|
+
Write-Host " install-service Install as Windows Service (requires WinSW or NSSM)"
|
|
479
|
+
Write-Host " uninstall-service Remove the Windows Service"
|
|
480
|
+
}
|
|
481
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codelark
|
|
3
|
+
description: Use this skill when working through CodeLark and you need Codex to send a local image or file back to the current IM chat. It teaches the attachment-send protocol and when to use it.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CodeLark Attachment Sending
|
|
7
|
+
|
|
8
|
+
Use this skill only when the user is chatting through CodeLark and wants you to send a generated or existing local artifact back to the IM chat.
|
|
9
|
+
|
|
10
|
+
## What this skill does
|
|
11
|
+
|
|
12
|
+
CodeLark can send local files back to the chat after your reply. Trigger that by including one or more `<clk-send>...</clk-send>` blocks in your final answer.
|
|
13
|
+
|
|
14
|
+
Supported artifact kinds:
|
|
15
|
+
|
|
16
|
+
- `image`
|
|
17
|
+
- `file`
|
|
18
|
+
|
|
19
|
+
## Required format
|
|
20
|
+
|
|
21
|
+
Output valid JSON inside the block, with no markdown fence.
|
|
22
|
+
|
|
23
|
+
Single artifact:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
<clk-send>
|
|
27
|
+
{"type":"image","path":"D:\\path\\to\\result.png","caption":"optional caption"}
|
|
28
|
+
</clk-send>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
<clk-send>
|
|
35
|
+
{"type":"file","path":"D:\\path\\to\\report.pdf","caption":"optional caption"}
|
|
36
|
+
</clk-send>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Multiple artifacts:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
<clk-send>
|
|
43
|
+
{"items":[
|
|
44
|
+
{"type":"image","path":"D:\\path\\to\\result.png"},
|
|
45
|
+
{"type":"file","path":"D:\\path\\to\\report.pdf"}
|
|
46
|
+
]}
|
|
47
|
+
</clk-send>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
- The `path` must be an absolute local path.
|
|
53
|
+
- The file must already exist before you emit the block.
|
|
54
|
+
- Keep normal user-facing explanation outside the `<clk-send>` block.
|
|
55
|
+
- Do not invent artifacts that were not actually created or found.
|
|
56
|
+
- If you are unsure whether the file exists, verify first.
|
|
57
|
+
- If the channel probably does not support the artifact well, explain that plainly instead of inventing a successful send.
|
|
58
|
+
|
|
59
|
+
## When to use
|
|
60
|
+
|
|
61
|
+
Use this protocol when the user asks you to:
|
|
62
|
+
|
|
63
|
+
- send a generated chart, diagram, screenshot, or edited image
|
|
64
|
+
- send a report, archive, patch, PDF, spreadsheet, or other output file
|
|
65
|
+
- send the final result as an attachment instead of only pasting text
|
|
66
|
+
|
|
67
|
+
Do not use it for ordinary text replies.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codelark-auto
|
|
3
|
+
description: Use this skill when the user wants to create a local script for CodeLark /auto-script automation. The script should wait for or detect a trigger condition, then print the exact prompt that /auto-script should send back into the bound bridge session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CodeLark /auto-script creation
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to create an automation script for:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
/auto-script new <absolute-script-path> <times>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`times` is a positive integer. `/auto-script new` does not accept `0`; use `/auto set <index> 0` later to pause an existing task.
|
|
15
|
+
|
|
16
|
+
## Contract
|
|
17
|
+
|
|
18
|
+
Create a local executable script that:
|
|
19
|
+
|
|
20
|
+
- lives under the current Codex home, preferably `~/.codex/auto-scripts/`
|
|
21
|
+
- waits for the requested timing or condition
|
|
22
|
+
- checks any relevant local process, log, job, Ray cluster, file, or command state
|
|
23
|
+
- prints exactly one useful Codex prompt to stdout
|
|
24
|
+
- exits with code 0 when stdout is ready for Codex
|
|
25
|
+
- exits non-zero only when the script itself cannot run
|
|
26
|
+
|
|
27
|
+
The bridge runs the script repeatedly. Each stdout becomes the next prompt sent to the bridge session that owns the `/auto-script` task.
|
|
28
|
+
|
|
29
|
+
## Output to the user
|
|
30
|
+
|
|
31
|
+
After creating the script, tell the user:
|
|
32
|
+
|
|
33
|
+
- the script content
|
|
34
|
+
- the absolute script path
|
|
35
|
+
- the suggested `/auto-script new <absolute-script-path> <positive-times>` command
|
|
36
|
+
|
|
37
|
+
Name the script after the trigger timing or condition, for example `check_experiment_progress_every_20m.sh`, and place it under `~/.codex/auto-scripts/`.
|
|
38
|
+
|
|
39
|
+
## Script pattern
|
|
40
|
+
|
|
41
|
+
Prefer Bash on Unix-like systems:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
#!/usr/bin/env bash
|
|
45
|
+
set -euo pipefail
|
|
46
|
+
|
|
47
|
+
prompt="20分钟过去了,请检查一下实验进度。"
|
|
48
|
+
deadline=$((SECONDS + 20 * 60))
|
|
49
|
+
|
|
50
|
+
while (( SECONDS < deadline )); do
|
|
51
|
+
sleep 5
|
|
52
|
+
|
|
53
|
+
# Replace this block with task-specific checks.
|
|
54
|
+
if ! pgrep -f "experiment-command-or-name" >/dev/null 2>&1; then
|
|
55
|
+
prompt="实验进程似乎已经退出。请检查实验进度、最近日志和退出原因。"
|
|
56
|
+
break
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if command -v ray >/dev/null 2>&1; then
|
|
60
|
+
ray_status="$(ray status 2>&1 || true)"
|
|
61
|
+
if printf '%s\n' "$ray_status" | grep -qiE "failed|error|dead|unhealthy"; then
|
|
62
|
+
prompt="ray status 显示异常,请检查实验进度并诊断:\n$ray_status"
|
|
63
|
+
break
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
printf '%b\n' "$prompt"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Make the script executable with `chmod +x`.
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- Use absolute paths inside the script when checking known files or logs.
|
|
76
|
+
- Store the script itself under `~/.codex/auto-scripts/`; scripts outside Codex home are rejected by `/auto-script new`.
|
|
77
|
+
- Always provide the exact creation command in this shape: `/auto-script new /home/<user>/.codex/auto-scripts/<script>.sh <times>`.
|
|
78
|
+
- Keep stdout focused on the Codex prompt. Put debug logs on stderr if needed.
|
|
79
|
+
- Do not start long-running experiment work unless the user asked for that; `/auto-script` scripts should usually observe and report.
|
|
80
|
+
- If the user asks for “every N minutes”, the script itself should sleep for N minutes, checking every few seconds when useful.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codelark-question
|
|
3
|
+
description: 在 CodeLark 中需要向 IM 用户发起结构化确认、选择或批准时使用;用户说“找我确认”“问我 xxx”“请我批准”“让我选择”“需要我确认/批准”时必须触发。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CodeLark 问题卡片
|
|
7
|
+
|
|
8
|
+
当当前会话通过 CodeLark 连接到 IM,并且你需要用户做选择、确认、批准或补充一个很短的信息后才能继续时,使用这个 skill。
|
|
9
|
+
|
|
10
|
+
## 触发条件
|
|
11
|
+
|
|
12
|
+
只要用户表达了下面任一意图,就应该触发这个 skill:
|
|
13
|
+
|
|
14
|
+
- “找我确认”“让我确认”“需要我确认”。
|
|
15
|
+
- “问我 xxx”“向我提问”“让我选择 xxx”。
|
|
16
|
+
- “请我批准”“需要我批准”“找我审批”。
|
|
17
|
+
- 需要用户在继续执行前从几个互斥选项中选择一个。
|
|
18
|
+
- 需要用户批准高影响操作,例如合并、删除、部署、发布、改配置或执行不可逆动作。
|
|
19
|
+
|
|
20
|
+
不要只因为普通聊天里出现问号就触发;只有你确实需要用户输入来推进任务时才使用。
|
|
21
|
+
|
|
22
|
+
## 这个 skill 做什么
|
|
23
|
+
|
|
24
|
+
CodeLark 会把最终回复里的 `<clk-ask>` 块转换成飞书/Lark 问题卡片。用户提交后的结果会作为下一条用户消息回到同一个 bridge session。
|
|
25
|
+
|
|
26
|
+
## 输出格式
|
|
27
|
+
|
|
28
|
+
在一个 `<clk-ask>` 块里输出合法 JSON。不要把 JSON 放进 markdown 代码块。
|
|
29
|
+
|
|
30
|
+
简单选择:
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
<clk-ask>
|
|
35
|
+
{"question":"请选择发布策略","options":["灰度","全量"],"allowTextReply":true}
|
|
36
|
+
</clk-ask>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
选择加可选文本:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
<clk-ask>
|
|
43
|
+
{"question":"是否继续执行部署?","options":["继续","取消"],"input":{"label":"补充说明","placeholder":"可留空"},"submitText":"提交","allowTextReply":true}
|
|
44
|
+
</clk-ask>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 规则
|
|
48
|
+
|
|
49
|
+
- 只有在确实需要用户输入才能继续时才使用。
|
|
50
|
+
- 普通说明放在 `<clk-ask>` 块外。
|
|
51
|
+
- 只是在总结、写代码、汇报已完成工作时,不要发问题卡片。
|
|
52
|
+
- `options` 要短,并且互斥;最多展示 8 个选项。
|
|
53
|
+
- 如果普通文本问题已经足够,不要创建问题卡片。
|
|
54
|
+
- 除非用户询问桥接协议,否则不要向用户解释 `<clk-ask>` 协议本身。
|