@hellolynk-ai/codex-plugin 0.2.101 → 0.2.102-macos.1
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 +17 -17
- package/bin/hellolynk-client-setup.mjs +72 -11
- package/hellolynk-client-pack-manifest.json +109 -69
- package/hellolynk-client-pack-pin.json +1 -1
- package/package.json +8 -4
- package/plugins/hellolynk-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/hellolynk-codex/.mcp.json +20 -19
- package/plugins/hellolynk-codex/package.json +1 -1
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +25 -2
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-host.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-vault.mjs +153 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +20 -8
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +3 -7
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +68 -14
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.bin +0 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.swift +149 -0
- package/plugins/hellolynk-codex/runtime/connect-activate.mjs +5 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +25 -2
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +7 -3
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +24 -8
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +23 -9
- package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-host.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-vault.mjs +153 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +20 -8
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +53 -6
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +22 -7
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +7 -5
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +3 -7
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +9 -3
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +68 -14
- package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +78 -2
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +5 -3
- package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +58 -26
- package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +1 -1
- package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.bin +0 -0
- package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.swift +149 -0
- package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +15 -9
- package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -1193
- package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -33
- package/plugins/hellolynk-codex/server/workbench-runtime.mjs +42 -42
- package/tools/hellolynk-activate-neutral-host-clients.mjs +5 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Rebuild in this directory: xcrun swiftc -O -target arm64-apple-macos13.0 -module-name HelloLynkKeychain hellolynk-keychain-helper.swift -o hellolynk-keychain-helper.bin
|
|
2
|
+
import AppKit
|
|
3
|
+
import CryptoKit
|
|
4
|
+
import Foundation
|
|
5
|
+
import Security
|
|
6
|
+
|
|
7
|
+
func fail(_ code: String) -> Never {
|
|
8
|
+
FileHandle.standardError.write(Data((code + "\n").utf8))
|
|
9
|
+
exit(1)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func requireNoGrantACL(_ path: String) {
|
|
13
|
+
guard let acl = acl_get_file(path, ACL_TYPE_EXTENDED) else {
|
|
14
|
+
if errno == ENOENT { return } // A first write creates the file with mode 0600.
|
|
15
|
+
fail("macos_vault_acl_unavailable")
|
|
16
|
+
}
|
|
17
|
+
defer { acl_free(UnsafeMutableRawPointer(acl)) }
|
|
18
|
+
var entry: acl_entry_t?
|
|
19
|
+
var selector = ACL_FIRST_ENTRY
|
|
20
|
+
while acl_get_entry(acl, Int32(selector.rawValue), &entry) == 0 {
|
|
21
|
+
var tag = ACL_UNDEFINED_TAG
|
|
22
|
+
guard acl_get_tag_type(entry, &tag) == 0, tag == ACL_EXTENDED_DENY else {
|
|
23
|
+
fail("macos_vault_acl_invalid")
|
|
24
|
+
}
|
|
25
|
+
selector = ACL_NEXT_ENTRY
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The caller verifies the release path's ancestors and package hashes. Walk
|
|
30
|
+
// with lstat so a symlink or hard link cannot redirect a permission change.
|
|
31
|
+
func secureRelease(_ path: String, verifyOnly: Bool) {
|
|
32
|
+
var pending = [path]
|
|
33
|
+
while let entry = pending.popLast() {
|
|
34
|
+
var st = stat()
|
|
35
|
+
guard lstat(entry, &st) == 0, st.st_uid == getuid() else { fail("macos_vault_release_invalid") }
|
|
36
|
+
let type = st.st_mode & S_IFMT
|
|
37
|
+
guard type == S_IFDIR || (type == S_IFREG && st.st_nlink == 1) else { fail("macos_vault_release_invalid") }
|
|
38
|
+
let mode: mode_t = type == S_IFDIR || entry.hasSuffix(".bin") ? 0o700 : 0o600
|
|
39
|
+
if (st.st_mode & 0o7777) != mode {
|
|
40
|
+
guard !verifyOnly, chmod(entry, mode) == 0 else { fail("macos_vault_release_permissions_invalid") }
|
|
41
|
+
}
|
|
42
|
+
requireNoGrantACL(entry)
|
|
43
|
+
if type == S_IFDIR {
|
|
44
|
+
guard let names = try? FileManager.default.contentsOfDirectory(atPath: entry) else { fail("macos_vault_release_invalid") }
|
|
45
|
+
pending.append(contentsOf: names.map { entry + "/" + $0 })
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func masterKey(_ root: String, create: Bool) -> Data {
|
|
51
|
+
let account = SHA256.hash(data: Data(root.utf8)).map { String(format: "%02x", $0) }.joined()
|
|
52
|
+
let base: [String: Any] = [
|
|
53
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
54
|
+
kSecAttrService as String: "com.hellolynk.codex.macos-vault.v1",
|
|
55
|
+
kSecAttrAccount as String: account
|
|
56
|
+
]
|
|
57
|
+
var lookup = base
|
|
58
|
+
lookup[kSecReturnData as String] = true
|
|
59
|
+
lookup[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
60
|
+
var result: CFTypeRef?
|
|
61
|
+
let status = SecItemCopyMatching(lookup as CFDictionary, &result)
|
|
62
|
+
if status == errSecSuccess {
|
|
63
|
+
guard let key = result as? Data, key.count == 32 else { fail("macos_vault_key_invalid") }
|
|
64
|
+
return key
|
|
65
|
+
}
|
|
66
|
+
guard status == errSecItemNotFound else { fail("macos_keychain_access_\(abs(Int(status)))") }
|
|
67
|
+
guard create else { fail("macos_vault_key_missing") }
|
|
68
|
+
let key = SymmetricKey(size: .bits256).withUnsafeBytes { Data($0) }
|
|
69
|
+
var add = base
|
|
70
|
+
add[kSecValueData as String] = key
|
|
71
|
+
add[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
|
72
|
+
let inserted = SecItemAdd(add as CFDictionary, nil)
|
|
73
|
+
if inserted == errSecDuplicateItem { return masterKey(root, create: false) }
|
|
74
|
+
guard inserted == errSecSuccess else { fail("macos_keychain_access_\(abs(Int(inserted)))") }
|
|
75
|
+
return key
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func nativeConfirmation() {
|
|
79
|
+
let raw = FileHandle.standardInput.readDataToEndOfFile()
|
|
80
|
+
guard raw.count <= 20480 * 6 + 8192,
|
|
81
|
+
var v = (try? JSONSerialization.jsonObject(with: raw)) as? [String: Any],
|
|
82
|
+
let body = v["displayText"] as? String, !body.isEmpty, body.utf8.count <= 20480,
|
|
83
|
+
let encoded = v["hmacKey"] as? String, encoded.count == 43,
|
|
84
|
+
let key = Data(base64Encoded: encoded.replacingOccurrences(of: "-", with: "+")
|
|
85
|
+
.replacingOccurrences(of: "_", with: "/") + "="), key.count == 32,
|
|
86
|
+
let recipient = v["recipientHelloLynkId"] as? String,
|
|
87
|
+
v["contract"] as? String == "hellolynk-native-human-confirmation-v1",
|
|
88
|
+
v["textDigest"] as? String == SHA256.hash(data: Data(body.utf8)).map({ String(format: "%02x", $0) }).joined()
|
|
89
|
+
else { fail("human_confirmation_request_invalid") }
|
|
90
|
+
|
|
91
|
+
var fields = ["contract", "actionId", "decision", "principalId", "delegateId", "hostInstallationId",
|
|
92
|
+
"adapterId", "adapterInstanceId", "hostGeneration", "recipientHelloLynkId"]
|
|
93
|
+
var label = "收件人:" + recipient
|
|
94
|
+
if v["recipientDelegateId"] != nil || v["recipientInstanceDisplayName"] != nil {
|
|
95
|
+
guard let name = v["recipientInstanceDisplayName"] as? String, !name.isEmpty, name.utf8.count <= 512,
|
|
96
|
+
name.range(of: #"[\x00-\x1F\x7F-\x9F\u2028\u2029\u202A-\u202E\u2066-\u2069]"#, options: .regularExpression) == nil,
|
|
97
|
+
v["recipientDelegateId"] is String else { fail("human_confirmation_request_invalid") }
|
|
98
|
+
fields += ["recipientDelegateId", "recipientInstanceDisplayName"]
|
|
99
|
+
label += "\n目标 AI:" + name
|
|
100
|
+
}
|
|
101
|
+
fields += ["clientMessageId", "textDigest", "createdAtMs", "expiresAtMs", "challenge"]
|
|
102
|
+
var expected = Set(fields)
|
|
103
|
+
expected.remove("decision")
|
|
104
|
+
expected.formUnion(["displayText", "hmacKey"])
|
|
105
|
+
guard expected == Set(v.keys) else { fail("human_confirmation_request_invalid") }
|
|
106
|
+
let app = NSApplication.shared
|
|
107
|
+
app.setActivationPolicy(.accessory)
|
|
108
|
+
app.activate(ignoringOtherApps: true)
|
|
109
|
+
let dialog = NSAlert()
|
|
110
|
+
dialog.messageText = "是否允许 HelloLynk 发送以下文本?"
|
|
111
|
+
dialog.informativeText = label
|
|
112
|
+
dialog.addButton(withTitle: "取消")
|
|
113
|
+
dialog.addButton(withTitle: "批准发送")
|
|
114
|
+
let scroll = NSScrollView(frame: NSRect(x: 0, y: 0, width: 500, height: 230))
|
|
115
|
+
scroll.hasVerticalScroller = true
|
|
116
|
+
let view = NSTextView(frame: scroll.contentView.bounds)
|
|
117
|
+
view.isEditable = false
|
|
118
|
+
view.isRichText = false
|
|
119
|
+
view.isVerticallyResizable = true
|
|
120
|
+
view.autoresizingMask = [.width]
|
|
121
|
+
view.textContainer?.widthTracksTextView = true
|
|
122
|
+
view.string = body
|
|
123
|
+
scroll.documentView = view
|
|
124
|
+
dialog.accessoryView = scroll
|
|
125
|
+
v["decision"] = dialog.runModal() == .alertSecondButtonReturn ? "approve" : "cancel"
|
|
126
|
+
let message = fields.map { field -> String in
|
|
127
|
+
if let text = v[field] as? String { return text }
|
|
128
|
+
if ["createdAtMs","expiresAtMs"].contains(field), let n = v[field] as? NSNumber { return n.stringValue }
|
|
129
|
+
fail("human_confirmation_request_invalid")
|
|
130
|
+
}.joined(separator: "\n")
|
|
131
|
+
let proof = HMAC<SHA256>.authenticationCode(for: Data(message.utf8), using: SymmetricKey(data: key))
|
|
132
|
+
v["proof"] = proof.map { String(format: "%02x", $0) }.joined()
|
|
133
|
+
for field in ["hmacKey", "challenge", "displayText"] { v.removeValue(forKey: field) }
|
|
134
|
+
guard let output = try? JSONSerialization.data(withJSONObject: v) else { fail("human_confirmation_response_invalid") }
|
|
135
|
+
FileHandle.standardOutput.write(output)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let args = Array(CommandLine.arguments.dropFirst())
|
|
139
|
+
if args == ["confirm"] { nativeConfirmation() }
|
|
140
|
+
else if args.count == 3, args[0] == "key", args[1].hasPrefix("/"), ["load","create"].contains(args[2]) {
|
|
141
|
+
requireNoGrantACL(args[1])
|
|
142
|
+
requireNoGrantACL(args[1] + "/protected-v1.sqlite")
|
|
143
|
+
let key = masterKey(args[1], create: args[2] == "create")
|
|
144
|
+
FileHandle(fileDescriptor: 3, closeOnDealloc: false).write(key)
|
|
145
|
+
} else if args.count == 2, ["verify-tree", "secure-tree"].contains(args[0]), args[1].hasPrefix("/") {
|
|
146
|
+
secureRelease(args[1], verifyOnly: args[0] == "verify-tree")
|
|
147
|
+
} else if args.count == 2, args[0] == "verify-acl", args[1].hasPrefix("/") {
|
|
148
|
+
requireNoGrantACL(args[1])
|
|
149
|
+
} else { fail("macos_vault_helper_arguments_invalid") }
|
|
@@ -8,6 +8,7 @@ import { launchPinnedNeutralHostBootstrap, verifyPinnedNeutralHostRuntime } from
|
|
|
8
8
|
import { resolveClientAccountRoot } from "../runtime/core/hellolynk-client-account.mjs";
|
|
9
9
|
import { startInstalledClientUpdate } from "../runtime/core/hellolynk-client-update-entry.mjs";
|
|
10
10
|
import { recordCodexReleaseProcess } from "../runtime/core/hellolynk-release-retention.mjs";
|
|
11
|
+
import { DARWIN_VAULT_FILE } from "../runtime/core/hellolynk-darwin-vault.mjs";
|
|
11
12
|
|
|
12
13
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const PLUGIN_ROOT = path.dirname(MODULE_DIR);
|
|
@@ -19,17 +20,22 @@ const MAX_DISCOVERY_WAIT_MS = 120_000;
|
|
|
19
20
|
let installedPrepare = null;
|
|
20
21
|
const TRUSTED_BOOTSTRAP_FILES = Object.freeze(new Map([
|
|
21
22
|
["runtime/bootstrap/core/hellolynk-local-process-lock.mjs", "5212990a98b9ad2eb83fa2d5fb8a123d687c5ef6daf6cba49a4b6d04a7fe251e"],
|
|
22
|
-
["runtime/bootstrap/core/hellolynk-client-account.mjs", "
|
|
23
|
-
["runtime/neutral-host-release-loader.mjs", "
|
|
23
|
+
["runtime/bootstrap/core/hellolynk-client-account.mjs", "6e03a05127d4dc86be6391567b51a956d1a987acf4e23f5699ce0e00c3187b7e"],
|
|
24
|
+
["runtime/neutral-host-release-loader.mjs", "283a20c6a9b90481bc03c143ab51d5289abf7b7a86fbe51fba7ea2ae1e6d2c60"],
|
|
24
25
|
["runtime/neutral-host-bootstrap.mjs", "53513deed7f0155a6603a1489d1609fae37776442358181ff7650d0cd9351f0a"],
|
|
25
26
|
["runtime/bootstrap/core/hellolynk-runtime-safety.cjs", "43d576d9f774409f4a735793c1503a6f397e787b10c59a4ac9cc9257299f5efe"],
|
|
26
|
-
["runtime/bootstrap/core/hellolynk-host
|
|
27
|
-
["runtime/bootstrap/core/hellolynk-
|
|
27
|
+
["runtime/bootstrap/core/hellolynk-darwin-host.mjs", "0772d7a23280b18d5bc59383a6a14fd2ecfd9de1ec53b351be0250770e3e2e70"],
|
|
28
|
+
["runtime/bootstrap/core/hellolynk-darwin-vault.mjs", "b6094d856af016e674e4a0183a4e474397bbe92e66638743fa62fe085b554d30"],
|
|
29
|
+
["runtime/bootstrap/core/hellolynk-host-protected-store.mjs", "10f81f4f6a5dd2fe9a742e847b2eb47dbf20f802f4414f46a05ccbc3505148e2"],
|
|
30
|
+
["runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs", "42ae1e7deb876a8308b7bfed6a7b891d1834117b62504fc5befe3f91a0b4c740"],
|
|
28
31
|
["runtime/bootstrap/core/hellolynk-host-auth.mjs", "db1ec56f4e60e6450fba50188eecf89748f39f5d7a75c2456c30a6053233555d"],
|
|
29
|
-
["runtime/bootstrap/core/hellolynk-client-profile.mjs", "
|
|
30
|
-
["runtime/bootstrap/core/hellolynk-platform-credential-store.mjs", "
|
|
32
|
+
["runtime/bootstrap/core/hellolynk-client-profile.mjs", "8be37831c96610e3d0a64848ada86488970cc6079e49764b47d8a8b2ba852269"],
|
|
33
|
+
["runtime/bootstrap/core/hellolynk-platform-credential-store.mjs", "3969708bafefc3b267bb34a9dcc115804c874ad14f4a036426d727c02ff70d42"],
|
|
31
34
|
["runtime/bootstrap/core/hellolynk-client-core.mjs", "94bbad1d0ccee969bbffed176449e427188f1b892512a64167ab31f63d258e2b"],
|
|
32
35
|
["runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1", "24357e8ce54090b4ec68e457f02c9e834a80f3273ef3c03a8c0fa7258797fb49"],
|
|
36
|
+
...(process.platform === "darwin" ? [
|
|
37
|
+
["runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.bin", "f1a782c08229ebc0e8d1704b6390e91c5695e084b4df0a1a6dc043f6b23700ab"],
|
|
38
|
+
] : []),
|
|
33
39
|
]));
|
|
34
40
|
|
|
35
41
|
// A still-open cache may load a different installed release during an update.
|
|
@@ -150,7 +156,7 @@ export async function prepareCodexNeutralRelease(options = {}, finalBindingDefer
|
|
|
150
156
|
releaseRoot: bootstrapReleaseRoot,
|
|
151
157
|
});
|
|
152
158
|
const storeRoot = resolveClientAccountRoot("codex", absolutePath(
|
|
153
|
-
options.storeRoot || env.HELLOLYNK_NEUTRAL_STORE_ROOT || bootstrapTrusted.defaultStoreRoot(
|
|
159
|
+
options.storeRoot || env.HELLOLYNK_NEUTRAL_STORE_ROOT || bootstrapTrusted.defaultStoreRoot(process.platform, env),
|
|
154
160
|
"neutral_codex_binding_configuration_required",
|
|
155
161
|
));
|
|
156
162
|
const bootstrapInstallationStore = bootstrapTrusted.createInstallationStore({ root: storeRoot });
|
|
@@ -198,7 +204,7 @@ async function prepareSelectedRelease(options, verifiedInitial = null) {
|
|
|
198
204
|
const releaseRoot = path.dirname(path.dirname(loaderPath));
|
|
199
205
|
const trusted = await loadTrustedBootstrap({ loaderPath, releaseRoot });
|
|
200
206
|
const storeRoot = resolveClientAccountRoot("codex", absolutePath(
|
|
201
|
-
options.storeRoot || env.HELLOLYNK_NEUTRAL_STORE_ROOT || trusted.defaultStoreRoot(
|
|
207
|
+
options.storeRoot || env.HELLOLYNK_NEUTRAL_STORE_ROOT || trusted.defaultStoreRoot(process.platform, env),
|
|
202
208
|
"neutral_codex_binding_configuration_required",
|
|
203
209
|
));
|
|
204
210
|
const installationStore = trusted.createInstallationStore({ root: storeRoot });
|
|
@@ -268,7 +274,7 @@ async function initializationFingerprint(storeRoot, binding = null) {
|
|
|
268
274
|
if (parent === path.dirname(parent)) break;
|
|
269
275
|
}
|
|
270
276
|
};
|
|
271
|
-
await stat(path.join(storeRoot, "capabilities-v1.dpapi.clixml"), false);
|
|
277
|
+
await stat(path.join(storeRoot, process.platform === "darwin" ? DARWIN_VAULT_FILE : "capabilities-v1.dpapi.clixml"), false);
|
|
272
278
|
await stat(path.join(storeRoot, "installations-v1.json"), false);
|
|
273
279
|
const state = JSON.stringify([...seen]);
|
|
274
280
|
if (binding) {
|