@hellolynk-ai/codex-plugin 0.0.0-stage → 0.2.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/LICENSE +92 -0
  3. package/README.md +86 -2
  4. package/bin/hellolynk-client-setup.mjs +2748 -0
  5. package/bin/hellolynk-mcp-config-shape.mjs +23 -0
  6. package/hellolynk-client-pack-manifest.json +619 -0
  7. package/hellolynk-client-pack-pin.json +4 -0
  8. package/package.json +58 -4
  9. package/plugins/hellolynk-codex/.app.json +3 -0
  10. package/plugins/hellolynk-codex/.codex-plugin/plugin.json +38 -0
  11. package/plugins/hellolynk-codex/.mcp.json +19 -0
  12. package/plugins/hellolynk-codex/assets/hellolynk-mark.png +0 -0
  13. package/plugins/hellolynk-codex/package.json +11 -0
  14. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +180 -0
  15. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-core.mjs +770 -0
  16. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +191 -0
  17. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-auth.mjs +347 -0
  18. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +2721 -0
  19. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-local-process-lock.mjs +157 -0
  20. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  21. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +773 -0
  22. package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-runtime-safety.cjs +471 -0
  23. package/plugins/hellolynk-codex/runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  24. package/plugins/hellolynk-codex/runtime/connect-activate.mjs +837 -0
  25. package/plugins/hellolynk-codex/runtime/core/hellolynk-agent-configurator.mjs +701 -0
  26. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +180 -0
  27. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-core.mjs +770 -0
  28. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +191 -0
  29. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +31 -0
  30. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
  31. package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +453 -0
  32. package/plugins/hellolynk-codex/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
  33. package/plugins/hellolynk-codex/runtime/core/hellolynk-device-enrollment.mjs +435 -0
  34. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
  35. package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
  36. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-auth.mjs +347 -0
  37. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
  38. package/plugins/hellolynk-codex/runtime/core/hellolynk-host-transport-client.mjs +946 -0
  39. package/plugins/hellolynk-codex/runtime/core/hellolynk-host.mjs +2374 -0
  40. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
  41. package/plugins/hellolynk-codex/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
  42. package/plugins/hellolynk-codex/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
  43. package/plugins/hellolynk-codex/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
  44. package/plugins/hellolynk-codex/runtime/core/hellolynk-instance-conversation.mjs +185 -0
  45. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
  46. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contacts.mjs +1228 -0
  47. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-migration.mjs +766 -0
  48. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +747 -0
  49. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
  50. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-messaging.mjs +3043 -0
  51. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
  52. package/plugins/hellolynk-codex/runtime/core/hellolynk-local-process-lock.mjs +157 -0
  53. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-inbox.mjs +230 -0
  54. package/plugins/hellolynk-codex/runtime/core/hellolynk-model-notices.cjs +60 -0
  55. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
  56. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-conversations.mjs +280 -0
  57. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
  58. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
  59. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-notice.mjs +50 -0
  60. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
  61. package/plugins/hellolynk-codex/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
  62. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
  63. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
  64. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
  65. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
  66. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
  67. package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
  68. package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
  69. package/plugins/hellolynk-codex/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
  70. package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +160 -0
  71. package/plugins/hellolynk-codex/runtime/core/hellolynk-runtime-safety.cjs +471 -0
  72. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
  73. package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +234 -0
  74. package/plugins/hellolynk-codex/runtime/neutral-host-bootstrap.mjs +64 -0
  75. package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +70 -0
  76. package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +156 -0
  77. package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +358 -0
  78. package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +4 -0
  79. package/plugins/hellolynk-codex/runtime/platform/windows/configure-agent.ps1 +281 -0
  80. package/plugins/hellolynk-codex/runtime/platform/windows/credential-store.ps1 +844 -0
  81. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
  82. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
  83. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
  84. package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
  85. package/plugins/hellolynk-codex/runtime/workbench/candidate-bootstrap.mjs +54 -0
  86. package/plugins/hellolynk-codex/runtime/workbench/contract.mjs +1314 -0
  87. package/plugins/hellolynk-codex/runtime/workbench/hellolynk-workbench-build.json +215 -0
  88. package/plugins/hellolynk-codex/runtime/workbench/host-capabilities.mjs +80 -0
  89. package/plugins/hellolynk-codex/runtime/workbench/i18n.mjs +1127 -0
  90. package/plugins/hellolynk-codex/runtime/workbench/loopback-surface.mjs +740 -0
  91. package/plugins/hellolynk-codex/runtime/workbench/mcp-app-bundle.mjs +97 -0
  92. package/plugins/hellolynk-codex/runtime/workbench/service.mjs +485 -0
  93. package/plugins/hellolynk-codex/runtime/workbench/state.mjs +421 -0
  94. package/plugins/hellolynk-codex/runtime/workbench/transports.mjs +171 -0
  95. package/plugins/hellolynk-codex/runtime/workbench/ui/app.mjs +6120 -0
  96. package/plugins/hellolynk-codex/runtime/workbench/ui/connection-form.mjs +155 -0
  97. package/plugins/hellolynk-codex/runtime/workbench/ui/hellolynk-mark.png +0 -0
  98. package/plugins/hellolynk-codex/runtime/workbench/ui/http-transport.mjs +247 -0
  99. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/arrow-left.svg +3 -0
  100. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/close.svg +3 -0
  101. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/contacts.svg +3 -0
  102. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/inbox.svg +3 -0
  103. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/refresh.svg +3 -0
  104. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/search.svg +3 -0
  105. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/security.svg +3 -0
  106. package/plugins/hellolynk-codex/runtime/workbench/ui/icons/settings.svg +3 -0
  107. package/plugins/hellolynk-codex/runtime/workbench/ui/index.html +139 -0
  108. package/plugins/hellolynk-codex/runtime/workbench/ui/loopback-entry.mjs +26 -0
  109. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-client.mjs +151 -0
  110. package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
  111. package/plugins/hellolynk-codex/runtime/workbench/ui/styles.css +686 -0
  112. package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +520 -0
  113. package/plugins/hellolynk-codex/server/mcp-proxy.mjs +306 -0
  114. package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -0
  115. package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -0
  116. package/plugins/hellolynk-codex/server/native-reply-routes.mjs +32 -0
  117. package/plugins/hellolynk-codex/server/native-task-client.mjs +382 -0
  118. package/plugins/hellolynk-codex/server/native-task-runtime.mjs +8 -0
  119. package/plugins/hellolynk-codex/server/neutral-host-adapter.mjs +649 -0
  120. package/plugins/hellolynk-codex/server/workbench-runtime.mjs +180 -0
  121. package/plugins/hellolynk-codex/skills/hellolynk-open/SKILL.md +11 -0
  122. package/plugins/hellolynk-codex/skills/hellolynk-open/agents/openai.yaml +4 -0
  123. package/tools/hellolynk-activate-neutral-host-clients.mjs +837 -0
@@ -0,0 +1,281 @@
1
+ param(
2
+ [ValidateSet("OpenClaw")]
3
+ [string]$Client = "OpenClaw",
4
+
5
+ [string]$OpenClawConfig = (Join-Path $env:USERPROFILE ".openclaw\openclaw.json"),
6
+
7
+ [switch]$WaitForUser,
8
+
9
+ [switch]$AtomicReplace
10
+ )
11
+
12
+ $ErrorActionPreference = "Stop"
13
+ [Console]::SetIn([IO.StreamReader]::new([Console]::OpenStandardInput(), [Text.UTF8Encoding]::new($false, $true)))
14
+
15
+ function Write-AtomicResult([string]$Code, [int]$ExitCode) {
16
+ [Console]::Out.Write($Code)
17
+ exit $ExitCode
18
+ }
19
+
20
+ function Assert-PlainPath([string]$Candidate, [string]$ExpectedParent = "") {
21
+ $absolute = [IO.Path]::GetFullPath($Candidate)
22
+ if ($ExpectedParent -and -not [StringComparer]::OrdinalIgnoreCase.Equals([IO.Path]::GetDirectoryName($absolute), $ExpectedParent)) {
23
+ throw "agent_setup_config_path_invalid"
24
+ }
25
+ $cursor = if ([IO.File]::Exists($absolute)) { $absolute } else { [IO.Path]::GetDirectoryName($absolute) }
26
+ while ($cursor) {
27
+ if (([IO.File]::GetAttributes($cursor) -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
28
+ throw "agent_setup_config_reparse_rejected"
29
+ }
30
+ $parent = [IO.Path]::GetDirectoryName($cursor)
31
+ if (-not $parent -or $parent -eq $cursor) { break }
32
+ $cursor = $parent
33
+ }
34
+ return $absolute
35
+ }
36
+
37
+ function Initialize-NativeFileIdentity {
38
+ if ("HelloLynk.NativeFileIdentity" -as [type]) { return }
39
+ Add-Type -TypeDefinition @'
40
+ using System;
41
+ using System.ComponentModel;
42
+ using System.Runtime.InteropServices;
43
+ using Microsoft.Win32.SafeHandles;
44
+
45
+ namespace HelloLynk {
46
+ public static class NativeFileIdentity {
47
+ [StructLayout(LayoutKind.Sequential)]
48
+ private struct ByHandleFileInformation {
49
+ public uint FileAttributes;
50
+ public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
51
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
52
+ public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
53
+ public uint VolumeSerialNumber;
54
+ public uint FileSizeHigh;
55
+ public uint FileSizeLow;
56
+ public uint NumberOfLinks;
57
+ public uint FileIndexHigh;
58
+ public uint FileIndexLow;
59
+ }
60
+
61
+ [DllImport("kernel32.dll", SetLastError = true)]
62
+ private static extern bool GetFileInformationByHandle(
63
+ SafeFileHandle handle,
64
+ out ByHandleFileInformation information
65
+ );
66
+
67
+ public static string Get(SafeFileHandle handle, out uint attributes) {
68
+ ByHandleFileInformation information;
69
+ if (!GetFileInformationByHandle(handle, out information)) {
70
+ throw new Win32Exception(Marshal.GetLastWin32Error());
71
+ }
72
+ attributes = information.FileAttributes;
73
+ return String.Format("{0:X8}:{1:X8}{2:X8}",
74
+ information.VolumeSerialNumber,
75
+ information.FileIndexHigh,
76
+ information.FileIndexLow);
77
+ }
78
+ }
79
+ }
80
+ '@
81
+ }
82
+
83
+ function Get-FileSnapshot([string]$Candidate) {
84
+ Initialize-NativeFileIdentity
85
+ $stream = [IO.File]::Open($Candidate, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::Read)
86
+ try {
87
+ [uint32]$attributes = 0
88
+ $identity = [HelloLynk.NativeFileIdentity]::Get($stream.SafeFileHandle, [ref]$attributes)
89
+ if (($attributes -band [uint32][IO.FileAttributes]::ReparsePoint) -ne 0) {
90
+ throw "agent_setup_config_reparse_rejected"
91
+ }
92
+ $hasher = [Security.Cryptography.SHA256]::Create()
93
+ try { $hash = [Convert]::ToHexString($hasher.ComputeHash($stream)) } finally { $hasher.Dispose() }
94
+ $security = [IO.FileSystemAclExtensions]::GetAccessControl($stream)
95
+ return [pscustomobject]@{
96
+ Identity = $identity
97
+ Hash = $hash
98
+ Dacl = $security.GetSecurityDescriptorSddlForm([Security.AccessControl.AccessControlSections]::Access)
99
+ Owner = $security.GetOwner([Security.Principal.SecurityIdentifier]).Value
100
+ Group = $security.GetGroup([Security.Principal.SecurityIdentifier]).Value
101
+ Security = $security.GetSecurityDescriptorSddlForm(
102
+ [Security.AccessControl.AccessControlSections]::Access -bor
103
+ [Security.AccessControl.AccessControlSections]::Owner -bor
104
+ [Security.AccessControl.AccessControlSections]::Group
105
+ )
106
+ }
107
+ } finally { $stream.Dispose() }
108
+ }
109
+
110
+ function Set-Dacl([string]$Candidate, [Security.AccessControl.FileSecurity]$Security, [string]$ExpectedHash) {
111
+ $snapshot = Get-FileSnapshot $Candidate
112
+ if (-not [StringComparer]::OrdinalIgnoreCase.Equals($snapshot.Hash, $ExpectedHash)) {
113
+ throw "agent_setup_config_changed"
114
+ }
115
+ [IO.FileSystemAclExtensions]::SetAccessControl([IO.FileInfo]::new($Candidate), $Security)
116
+ }
117
+
118
+ function Restore-FileSecurity([string]$Candidate, [string]$ExpectedHash, [string]$ExpectedSecurity) {
119
+ $snapshot = Get-FileSnapshot $Candidate
120
+ if ($snapshot.Hash -ne $ExpectedHash) { throw "agent_setup_config_rollback_failed" }
121
+ $security = [Security.AccessControl.FileSecurity]::new()
122
+ $security.SetSecurityDescriptorSddlForm(
123
+ $ExpectedSecurity,
124
+ [Security.AccessControl.AccessControlSections]::Access -bor
125
+ [Security.AccessControl.AccessControlSections]::Owner -bor
126
+ [Security.AccessControl.AccessControlSections]::Group
127
+ )
128
+ [IO.FileSystemAclExtensions]::SetAccessControl([IO.FileInfo]::new($Candidate), $security)
129
+ }
130
+
131
+ function Invoke-AtomicReplace {
132
+ try {
133
+ $controlText = [Console]::In.ReadToEnd()
134
+ if (-not $controlText -or $controlText.Length -gt 32768) { throw "agent_setup_config_control_invalid" }
135
+ $control = $controlText | ConvertFrom-Json -ErrorAction Stop
136
+ if (@($control.psobject.Properties.Name).Count -ne 4 -or
137
+ -not $control.configPath -or -not $control.temporary -or
138
+ $control.expectedSha256 -notmatch '^[A-Fa-f0-9]{64}$' -or
139
+ $control.replacementSha256 -notmatch '^[A-Fa-f0-9]{64}$') {
140
+ throw "agent_setup_config_control_invalid"
141
+ }
142
+
143
+ $target = Assert-PlainPath ([string]$control.configPath)
144
+ $parent = [IO.Path]::GetDirectoryName($target)
145
+ $temporary = Assert-PlainPath ([string]$control.temporary) $parent
146
+ if (-not [IO.File]::Exists($temporary) -or [IO.Directory]::Exists($temporary)) {
147
+ throw "agent_setup_config_temporary_invalid"
148
+ }
149
+
150
+ if (-not [IO.File]::Exists($target)) {
151
+ $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
152
+ $security = [Security.AccessControl.FileSecurity]::new()
153
+ $security.SetOwner($identity.User)
154
+ $security.SetAccessRuleProtection($true, $false)
155
+ $rule = [Security.AccessControl.FileSystemAccessRule]::new(
156
+ $identity.User,
157
+ [Security.AccessControl.FileSystemRights]::FullControl,
158
+ [Security.AccessControl.AccessControlType]::Allow
159
+ )
160
+ $security.AddAccessRule($rule)
161
+ Set-Dacl $temporary $security ([string]$control.replacementSha256)
162
+ $securedTemporary = Get-FileSnapshot $temporary
163
+ [IO.File]::Move($temporary, $target, $false)
164
+ $newSnapshot = Get-FileSnapshot $target
165
+ if ($newSnapshot.Hash -ne [string]$control.replacementSha256 -or
166
+ $newSnapshot.Dacl -ne $securedTemporary.Dacl -or
167
+ $newSnapshot.Owner -ne $securedTemporary.Owner -or
168
+ $newSnapshot.Group -ne $securedTemporary.Group) {
169
+ throw "agent_setup_config_acl_unsafe"
170
+ }
171
+ Write-AtomicResult "agent_setup_config_replace_ok" 0
172
+ }
173
+
174
+ if ([IO.Directory]::Exists($target)) { throw "agent_setup_config_invalid" }
175
+ $before = Get-FileSnapshot $target
176
+ if (-not [StringComparer]::OrdinalIgnoreCase.Equals($before.Hash, [string]$control.expectedSha256)) {
177
+ throw "agent_setup_config_changed"
178
+ }
179
+ $replacement = Get-FileSnapshot $temporary
180
+ if ($replacement.Hash -ne [string]$control.replacementSha256) {
181
+ throw "agent_setup_config_temporary_changed"
182
+ }
183
+ # ReplaceFileW can retain the temporary file's owner and add an explicit ACE
184
+ # for the original owner. Give it the exact original security first; do not
185
+ # weaken the post-replace comparison to accept that metadata change.
186
+ $replacementSecurity = [Security.AccessControl.FileSecurity]::new()
187
+ $replacementSecurity.SetSecurityDescriptorSddlForm(
188
+ $before.Security,
189
+ [Security.AccessControl.AccessControlSections]::Access -bor
190
+ [Security.AccessControl.AccessControlSections]::Owner -bor
191
+ [Security.AccessControl.AccessControlSections]::Group
192
+ )
193
+ Set-Dacl $temporary $replacementSecurity ([string]$control.replacementSha256)
194
+ # HELLOLYNK_TEST_PRE_REPLACE_IDENTITY_INJECTION_POINT
195
+ # A second snapshot narrows the unavoidable path-based ReplaceFileW window. A process
196
+ # already running as this Windows account remains outside this configurator's trust boundary.
197
+ $target = Assert-PlainPath $target
198
+ $immediate = Get-FileSnapshot $target
199
+ if ($immediate.Identity -ne $before.Identity -or
200
+ $immediate.Hash -ne $before.Hash -or
201
+ $immediate.Dacl -ne $before.Dacl -or
202
+ $immediate.Owner -ne $before.Owner -or
203
+ $immediate.Group -ne $before.Group) {
204
+ throw "agent_setup_config_changed"
205
+ }
206
+ $securedReplacement = Get-FileSnapshot $temporary
207
+ if ($securedReplacement.Identity -ne $replacement.Identity -or
208
+ $securedReplacement.Hash -ne [string]$control.replacementSha256 -or
209
+ $securedReplacement.Dacl -ne $before.Dacl -or
210
+ $securedReplacement.Owner -ne $before.Owner -or
211
+ $securedReplacement.Group -ne $before.Group) {
212
+ throw "agent_setup_config_temporary_changed"
213
+ }
214
+
215
+ $backup = Join-Path $parent (".{0}.{1}.bak" -f [IO.Path]::GetFileName($target), [guid]::NewGuid().ToString("N"))
216
+ [IO.File]::Replace($temporary, $target, $backup, $true)
217
+ # HELLOLYNK_TEST_POST_REPLACE_ACL_INJECTION_POINT
218
+ try {
219
+ $after = Get-FileSnapshot $target
220
+ if ($after.Hash -ne [string]$control.replacementSha256 -or
221
+ $after.Dacl -ne $before.Dacl -or
222
+ $after.Owner -ne $before.Owner -or
223
+ $after.Group -ne $before.Group) {
224
+ throw "agent_setup_config_acl_unsafe"
225
+ }
226
+ [IO.File]::Delete($backup)
227
+ } catch {
228
+ $failureCode = [string]$_.Exception.Message
229
+ try {
230
+ if (-not [IO.File]::Exists($backup)) { throw "agent_setup_config_rollback_failed" }
231
+ [IO.File]::Replace($backup, $target, $temporary, $true)
232
+ Restore-FileSecurity $target $before.Hash $before.Security
233
+ $restored = Get-FileSnapshot $target
234
+ if ($restored.Hash -ne $before.Hash -or $restored.Dacl -ne $before.Dacl -or
235
+ $restored.Owner -ne $before.Owner -or $restored.Group -ne $before.Group) {
236
+ throw "agent_setup_config_rollback_failed"
237
+ }
238
+ if ([IO.File]::Exists($temporary)) { [IO.File]::Delete($temporary) }
239
+ if ([IO.File]::Exists($temporary) -or [IO.File]::Exists($backup)) {
240
+ throw "agent_setup_config_rollback_failed"
241
+ }
242
+ } catch {
243
+ throw "agent_setup_config_rollback_failed"
244
+ }
245
+ throw $failureCode
246
+ }
247
+ Write-AtomicResult "agent_setup_config_replace_ok" 0
248
+ } catch {
249
+ $code = [string]$_.Exception.Message
250
+ if ($code -notmatch '^[a-z][a-z0-9_]{0,127}$') { $code = "agent_setup_config_replace_failed" }
251
+ Write-AtomicResult $code 1
252
+ }
253
+ }
254
+
255
+ if ($AtomicReplace) { Invoke-AtomicReplace }
256
+
257
+ $projectRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\.."))
258
+ $configurator = Join-Path $projectRoot "clients\core\hellolynk-agent-configurator.mjs"
259
+ $pluginPath = Join-Path $projectRoot "integrations\openclaw-channel"
260
+ $nodePath = (Get-Command node -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source
261
+
262
+ if (-not (Test-Path -LiteralPath $configurator -PathType Leaf)) { throw "hellolynk_agent_configurator_missing" }
263
+ if (-not (Test-Path -LiteralPath (Join-Path $pluginPath "openclaw.plugin.json") -PathType Leaf)) { throw "hellolynk_openclaw_plugin_missing" }
264
+ if (-not (Test-Path -LiteralPath $OpenClawConfig -PathType Leaf)) { throw "openclaw_config_missing" }
265
+
266
+ try {
267
+ Write-Host "HelloLynk 正在为 OpenClaw(开放爪)配置本机中立 Host 表面。" -ForegroundColor Cyan
268
+ Write-Host "本步骤不读取旧 profile、不输入 KEY、不写入设备凭据。"
269
+ Write-Host "OpenClaw 的受保护 capability binding 必须已由 HelloLynk Host 安全配置;若缺失将安全停止。"
270
+
271
+ $configureOutput = & $nodePath $configurator openclaw `
272
+ --config-path $OpenClawConfig `
273
+ --plugin-path $pluginPath
274
+ if ($LASTEXITCODE -ne 0) { throw "openclaw_neutral_configuration_failed" }
275
+
276
+ Write-Host "配置完成:OpenClaw 将仅通过本机 HelloLynk Host 受限表面工作。" -ForegroundColor Green
277
+ Write-Host "请先关闭本窗口,再由人类重启 OpenClaw;配置程序不会自行启停 Host 或 OpenClaw。" -ForegroundColor Yellow
278
+ Write-Output '{"ok":true,"client":"openclaw","mode":"neutral","bindingRef":"protected-store:openclaw","secretRead":false,"secretPrinted":false,"restartRequired":true}'
279
+ } finally {
280
+ if ($WaitForUser) { Read-Host "按回车关闭此窗口" | Out-Null }
281
+ }