anyray-connect 0.11.165 → 0.11.166
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/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var BR="http://localhost:8787",Ga="anyray-placeholder",Ee=e=>{let t=e?.trim();re
|
|
|
22
22
|
<key>NSHumanReadableCopyright</key><string>Othentic Labs LTD</string>
|
|
23
23
|
</dict>
|
|
24
24
|
</plist>
|
|
25
|
-
`,cT="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",lT=e=>{try{sT(cT,["-f",e],{stdio:"ignore",timeout:1e4})}catch{}},mw=async(e={})=>{if((e.os??uw())!=="darwin")return{ok:!0,messages:[]};let n=e.home??zu(),r=e.binaryPath??qn();if(Ue(r))return{ok:!1,messages:[`Skipped the Login Items identity app \u2014 anyray-connect is running from a temporary path (${r}).`]};if(es())return{ok:!1,messages:["Skipped the Login Items identity app \u2014 HOME is redirected away from the real account."]};let o=fw(n),a=fc(o,"Contents","MacOS"),s=fc(a,hw),i=e.version??"0.0.0";try{await pw(o,{recursive:!0,force:!0}),await nT(a,{recursive:!0}),await rT(fc(o,"Contents","Info.plist"),iT(i),"utf8");try{await oT(r,s)}catch{await aT(r,s)}return(e.register??lT)(o),{ok:!0,messages:[`Registered ${ts} as the Login Items identity.`]}}catch(c){return{ok:!1,messages:[`Could not write the Login Items identity app (${c?.name??"Error"}). Background items will show the signing certificate name instead.`]}}},yw=async(e={})=>{if((e.os??uw())!=="darwin")return{ok:!0,messages:[]};if(es())return{ok:!0,messages:[]};let n=fw(e.home??zu());try{return await pw(n,{recursive:!0,force:!0}),{ok:!0,messages:[`Removed ${ts}.app.`]}}catch(r){return{ok:!1,messages:[`Could not remove the Login Items identity app (${r?.name??"Error"}).`]}}};var Ae="0.11.
|
|
25
|
+
`,cT="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister",lT=e=>{try{sT(cT,["-f",e],{stdio:"ignore",timeout:1e4})}catch{}},mw=async(e={})=>{if((e.os??uw())!=="darwin")return{ok:!0,messages:[]};let n=e.home??zu(),r=e.binaryPath??qn();if(Ue(r))return{ok:!1,messages:[`Skipped the Login Items identity app \u2014 anyray-connect is running from a temporary path (${r}).`]};if(es())return{ok:!1,messages:["Skipped the Login Items identity app \u2014 HOME is redirected away from the real account."]};let o=fw(n),a=fc(o,"Contents","MacOS"),s=fc(a,hw),i=e.version??"0.0.0";try{await pw(o,{recursive:!0,force:!0}),await nT(a,{recursive:!0}),await rT(fc(o,"Contents","Info.plist"),iT(i),"utf8");try{await oT(r,s)}catch{await aT(r,s)}return(e.register??lT)(o),{ok:!0,messages:[`Registered ${ts} as the Login Items identity.`]}}catch(c){return{ok:!1,messages:[`Could not write the Login Items identity app (${c?.name??"Error"}). Background items will show the signing certificate name instead.`]}}},yw=async(e={})=>{if((e.os??uw())!=="darwin")return{ok:!0,messages:[]};if(es())return{ok:!0,messages:[]};let n=fw(e.home??zu());try{return await pw(n,{recursive:!0,force:!0}),{ok:!0,messages:[`Removed ${ts}.app.`]}}catch(r){return{ok:!1,messages:[`Could not remove the Login Items identity app (${r?.name??"Error"}).`]}}};var Ae="0.11.166";var tn=(e=process.env)=>e.CODESPACES==="true"||e.CODESPACE_NAME?"codespaces":e.REMOTE_CONTAINERS==="true"||e.REMOTE_CONTAINERS_IPC||e.DEVCONTAINER?"devcontainer":e.SSH_CONNECTION||e.SSH_TTY?"ssh":null,$r=(e=process.env)=>tn(e)!==null,kn=e=>e==="codespaces"||e==="devcontainer",Oo=e=>{switch(e){case"codespaces":return"a GitHub Codespace";case"devcontainer":return"a dev container";case"ssh":return"an SSH session"}};var gw=()=>Br(wc(),".anyray","logs"),ep=()=>Br(gw(),"connect-refresh.log"),gc=1800,bc="ai.anyray.connect.refresh",os="anyray-refresh",np="\\AnyrayConnectKeyRefresh",Ac=bc,as=os,rs=np,fT=String(gc/60),rp=(e,t)=>{let n=pT(e,t,{stdio:"ignore"});return{ok:!n.error&&n.status===0}},op=()=>Br(wc(),"Library","LaunchAgents",`${Ac}.plist`),ap=()=>Br(wc(),".config","systemd","user"),ww=()=>Br(ap(),`${as}.service`),yc=()=>Br(ap(),`${as}.timer`),mc=e=>e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),Cc=()=>zn(jn),hT=(e=Cc())=>{let{command:t,args:n}=e,r=[t,...n].map(o=>` <string>${mc(o)}</string>`).join(`
|
|
26
26
|
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
27
27
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
28
28
|
<plist version="1.0">
|
|
@@ -43,18 +43,40 @@ public struct SecurityDeviceIdentityStore {
|
|
|
43
43
|
"ai.anyray.connect.device-identity.v1".utf8
|
|
44
44
|
)
|
|
45
45
|
public static let label = "Anyray Connect Device Identity v1"
|
|
46
|
+
// Not keychain-access-groups: that gets a Developer ID CLI AMFI-killed at launch.
|
|
47
|
+
public static let accessGroupEntitlement = "com.apple.security.application-groups"
|
|
46
48
|
|
|
47
49
|
private let applicationTag: Data
|
|
48
50
|
private let label: String
|
|
51
|
+
private let accessGroup: String?
|
|
49
52
|
|
|
50
53
|
public init() {
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
self.init(
|
|
55
|
+
applicationTag: Self.applicationTag,
|
|
56
|
+
label: Self.label,
|
|
57
|
+
accessGroup: Self.entitledAccessGroup()
|
|
58
|
+
)
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
init(applicationTag: Data, label: String) {
|
|
61
|
+
init(applicationTag: Data, label: String, accessGroup: String?) {
|
|
56
62
|
self.applicationTag = applicationTag
|
|
57
63
|
self.label = label
|
|
64
|
+
self.accessGroup = accessGroup
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public static func entitledAccessGroup() -> String? {
|
|
68
|
+
guard let task = SecTaskCreateFromSelf(nil),
|
|
69
|
+
let groups = SecTaskCopyValueForEntitlement(
|
|
70
|
+
task,
|
|
71
|
+
accessGroupEntitlement as CFString,
|
|
72
|
+
nil
|
|
73
|
+
) as? [String],
|
|
74
|
+
let first = groups.first,
|
|
75
|
+
!first.isEmpty
|
|
76
|
+
else {
|
|
77
|
+
return nil
|
|
78
|
+
}
|
|
79
|
+
return first
|
|
58
80
|
}
|
|
59
81
|
|
|
60
82
|
public func loadOrCreate() throws -> SecurityDeviceIdentityKey {
|
|
@@ -103,7 +125,7 @@ public struct SecurityDeviceIdentityStore {
|
|
|
103
125
|
private func findPrivateKey() throws -> SecKey? {
|
|
104
126
|
let authenticationContext = LAContext()
|
|
105
127
|
authenticationContext.interactionNotAllowed = true
|
|
106
|
-
|
|
128
|
+
var query: [CFString: Any] = [
|
|
107
129
|
kSecClass: kSecClassKey,
|
|
108
130
|
kSecAttrApplicationTag: applicationTag,
|
|
109
131
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
|
@@ -114,6 +136,7 @@ public struct SecurityDeviceIdentityStore {
|
|
|
114
136
|
kSecUseDataProtectionKeychain: true,
|
|
115
137
|
kSecUseAuthenticationContext: authenticationContext,
|
|
116
138
|
]
|
|
139
|
+
if let accessGroup { query[kSecAttrAccessGroup] = accessGroup }
|
|
117
140
|
var result: CFTypeRef?
|
|
118
141
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
119
142
|
if status == errSecItemNotFound { return nil }
|
|
@@ -127,7 +150,7 @@ public struct SecurityDeviceIdentityStore {
|
|
|
127
150
|
}
|
|
128
151
|
|
|
129
152
|
private func createPrivateKey(accessible: CFString) throws -> SecKey {
|
|
130
|
-
|
|
153
|
+
var privateAttributes: [CFString: Any] = [
|
|
131
154
|
kSecAttrIsPermanent: true,
|
|
132
155
|
kSecAttrApplicationTag: applicationTag,
|
|
133
156
|
kSecAttrLabel: label,
|
|
@@ -135,6 +158,7 @@ public struct SecurityDeviceIdentityStore {
|
|
|
135
158
|
kSecAttrAccessible: accessible,
|
|
136
159
|
kSecAttrSynchronizable: false,
|
|
137
160
|
]
|
|
161
|
+
if let accessGroup { privateAttributes[kSecAttrAccessGroup] = accessGroup }
|
|
138
162
|
let attributes: [CFString: Any] = [
|
|
139
163
|
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
|
140
164
|
kSecAttrKeySizeInBits: 256,
|
|
@@ -9,11 +9,9 @@ import Testing
|
|
|
9
9
|
struct SecurityDeviceIdentityTests {
|
|
10
10
|
@Test
|
|
11
11
|
func permanentKeySignsReloadsAndCannotBeExported() throws {
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// legacy Keychain (which ignores kSecAttrIsExtractable=false).
|
|
16
|
-
guard hasKeychainAccessGroup() else { return }
|
|
12
|
+
// Only a signed host has an app group; never fall back to the legacy keychain.
|
|
13
|
+
guard let accessGroup = SecurityDeviceIdentityStore.entitledAccessGroup()
|
|
14
|
+
else { return }
|
|
17
15
|
|
|
18
16
|
let suffix = UUID().uuidString.lowercased()
|
|
19
17
|
let tag = Data("ai.anyray.connect.device-identity.test.\(suffix)".utf8)
|
|
@@ -22,7 +20,8 @@ struct SecurityDeviceIdentityTests {
|
|
|
22
20
|
|
|
23
21
|
let store = SecurityDeviceIdentityStore(
|
|
24
22
|
applicationTag: tag,
|
|
25
|
-
label: label
|
|
23
|
+
label: label,
|
|
24
|
+
accessGroup: accessGroup
|
|
26
25
|
)
|
|
27
26
|
let first = try store.loadOrCreate()
|
|
28
27
|
let message = Data("{\"issuedAt\":\"fresh\",\"nonce\":\"test\"}".utf8)
|
|
@@ -45,6 +44,12 @@ struct SecurityDeviceIdentityTests {
|
|
|
45
44
|
)
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
@Test
|
|
48
|
+
func unsignedHostHasNoEntitledAccessGroup() {
|
|
49
|
+
guard !hostIsSignedWithAppGroup() else { return }
|
|
50
|
+
#expect(SecurityDeviceIdentityStore.entitledAccessGroup() == nil)
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
private func keyQuery(tag: Data) -> [CFString: Any] {
|
|
49
54
|
[
|
|
50
55
|
kSecClass: kSecClassKey,
|
|
@@ -76,11 +81,11 @@ struct SecurityDeviceIdentityTests {
|
|
|
76
81
|
_ = SecItemDelete(keyQuery(tag: tag) as CFDictionary)
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
private func
|
|
84
|
+
private func hostIsSignedWithAppGroup() -> Bool {
|
|
80
85
|
guard let task = SecTaskCreateFromSelf(nil),
|
|
81
86
|
let groups = SecTaskCopyValueForEntitlement(
|
|
82
87
|
task,
|
|
83
|
-
|
|
88
|
+
SecurityDeviceIdentityStore.accessGroupEntitlement as CFString,
|
|
84
89
|
nil
|
|
85
90
|
) as? [String]
|
|
86
91
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anyray-connect",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.166",
|
|
4
4
|
"description": "Anyray connect \u2014 points local coding tools (Claude Code, Cursor, Windsurf, SDKs) at the Anyray gateway through supported configuration and enrolled personal authentication.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|