refactor: migrate to shell-env and custom desktop notifications bridge
This commit is contained in:
Generated
+128
-282
@@ -6402,128 +6402,6 @@
|
|||||||
"node": ">=20.0"
|
"node": ">=20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@openai/codex": {
|
|
||||||
"version": "0.114.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0.tgz",
|
|
||||||
"integrity": "sha512-HMo8LRR6CtfKkaa28xvFK6eOarmBFTDfsrS9GJtEoaspGGemFok494CpafDspiTZaHZZGHfSUe5SWTUxYq7OaA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"codex": "bin/codex.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@openai/codex-darwin-arm64": "npm:@openai/codex@0.114.0-darwin-arm64",
|
|
||||||
"@openai/codex-darwin-x64": "npm:@openai/codex@0.114.0-darwin-x64",
|
|
||||||
"@openai/codex-linux-arm64": "npm:@openai/codex@0.114.0-linux-arm64",
|
|
||||||
"@openai/codex-linux-x64": "npm:@openai/codex@0.114.0-linux-x64",
|
|
||||||
"@openai/codex-win32-arm64": "npm:@openai/codex@0.114.0-win32-arm64",
|
|
||||||
"@openai/codex-win32-x64": "npm:@openai/codex@0.114.0-win32-x64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-darwin-arm64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-darwin-arm64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-arm64.tgz",
|
|
||||||
"integrity": "sha512-c9dlgo9O+66alr3s3G36fKE3KaO2+xrLZ/QcU3oujDruV86pqgVSEK2njHi+WIyyOa6m2AyWxdaHLEbKxPCNRg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-darwin-x64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-darwin-x64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-x64.tgz",
|
|
||||||
"integrity": "sha512-oPAPeZSaJZ1AQneFPSJu44uqbZr63Bxut9FOTWZwuSqYx4YEees7i0HJmJcqQc0XnCbYtHJdqo/i07Uv374NLw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-linux-arm64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-linux-arm64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-arm64.tgz",
|
|
||||||
"integrity": "sha512-oUFZGZgMiEqmo85C+dfhckpVApH25alLWfwBgfKr2IRgdx/tovy8vN101f6kj01E6nDDe4LfJdNSZGtZht4fRA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-linux-x64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-linux-x64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-x64.tgz",
|
|
||||||
"integrity": "sha512-MF6RhxfBoccccd5CYII2C7TL2TvYzombBQhanDZfQAajr6n7IuoazCmoHDlrFHS4AcSw9bYA4MRlrwEjbEjgsw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-win32-arm64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-win32-arm64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-arm64.tgz",
|
|
||||||
"integrity": "sha512-CnRMHopj3en9aqQ2UaDW7EgpEGkxHdZVLLRq2cOy5D0HyuzF6Qb6595ADoFVJHEmPeN5Iz/KUbiGs5GLDjUwOA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@openai/codex-win32-x64": {
|
|
||||||
"name": "@openai/codex",
|
|
||||||
"version": "0.114.0-win32-x64",
|
|
||||||
"resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-x64.tgz",
|
|
||||||
"integrity": "sha512-ztRsH5Z+gPVZ5ZInx6HzXsTFFkuEdjk3Ay0UGVOhRRCWvevXQi/SL4eU8hwysCYrs8K/WRq3mo4c95w9zQ2wLw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@opencode-ai/sdk": {
|
"node_modules/@opencode-ai/sdk": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.2.6.tgz",
|
||||||
@@ -12835,6 +12713,18 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/default-shell": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/default-shell/-/default-shell-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-sPpMZcVhRQ0nEMDtuMJ+RtCxt7iHPAMBU+I4tAlo5dU1sjRpNax0crj6nR3qKpvVnckaQ9U38enXcwW9nZJeCw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/defaults": {
|
"node_modules/defaults": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
|
||||||
@@ -14462,6 +14352,35 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/execa": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"get-stream": "^6.0.0",
|
||||||
|
"human-signals": "^2.1.0",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"npm-run-path": "^4.0.1",
|
||||||
|
"onetime": "^5.1.2",
|
||||||
|
"signal-exit": "^3.0.3",
|
||||||
|
"strip-final-newline": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/execa/node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/expect-type": {
|
"node_modules/expect-type": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||||
@@ -17102,6 +17021,18 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-stream": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-symbol-description": {
|
"node_modules/get-symbol-description": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
|
||||||
@@ -17686,6 +17617,15 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/human-signals": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/humanize-ms": {
|
"node_modules/humanize-ms": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||||
@@ -18346,7 +18286,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -20787,7 +20726,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -21368,6 +21306,18 @@
|
|||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/npm-run-path": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nth-check": {
|
"node_modules/nth-check": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
@@ -21586,7 +21536,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mimic-fn": "^2.1.0"
|
"mimic-fn": "^2.1.0"
|
||||||
@@ -21682,161 +21631,6 @@
|
|||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/opencode-ai": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-ai/-/opencode-ai-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-LaSoATkVEF6jyXNnAPrkqYpmHsZfuf2uPLDSEGJkL4hT6HYFq8LkoknHDjCGIiE5Be3MmId4+l17eSIKDfmddw==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"opencode": "bin/opencode"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"opencode-darwin-arm64": "1.2.24",
|
|
||||||
"opencode-darwin-x64": "1.2.24",
|
|
||||||
"opencode-darwin-x64-baseline": "1.2.24",
|
|
||||||
"opencode-linux-arm64": "1.2.24",
|
|
||||||
"opencode-linux-arm64-musl": "1.2.24",
|
|
||||||
"opencode-linux-x64": "1.2.24",
|
|
||||||
"opencode-linux-x64-baseline": "1.2.24",
|
|
||||||
"opencode-linux-x64-baseline-musl": "1.2.24",
|
|
||||||
"opencode-linux-x64-musl": "1.2.24",
|
|
||||||
"opencode-windows-x64": "1.2.24",
|
|
||||||
"opencode-windows-x64-baseline": "1.2.24"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/opencode-darwin-arm64": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-darwin-arm64/-/opencode-darwin-arm64-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-mk5AkNbmATLwjnczd09Fu9dLTPcW4GSlRlIGWJBbLuGSPe7DWfbuhA5tj00VuaGeC09wDKVnW06BxWp/gRzgMQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-darwin-x64": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-darwin-x64/-/opencode-darwin-x64-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-SuG7ItUejx42z9Bz46t0R3LLFIM34vZUcwJAkqqpS6MRd1KV9G4OQIJl9BR+Cwwvk70iqCnoDxj5NgFZjeeNOw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-darwin-x64-baseline": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-darwin-x64-baseline/-/opencode-darwin-x64-baseline-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-+MT/ZkgXxeID/kOcT04SBn8Vg+4tk8CytKldaNDLgUnsi/78vTxDXWs0Cc21Ohc894r45fEtOOb5npSNIMOVfQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-arm64": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-arm64/-/opencode-linux-arm64-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-zSpbVCwVfUQ/Bey1MEGkJg3A52EiIPgnpS44HwmSANkiLPVXRBVuwbFYN2qJ7eJVAH939K9+QPmDOW4X8DWKeQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-arm64-musl": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-arm64-musl/-/opencode-linux-arm64-musl-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-LY28mspJOJ2s32E0AbrfbdWH5Bw+5YQkuB3ACJiVxW1MafjhRgPOsXjnw38WMiFjZ+NbhSPBOKnL0guKseVd4Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-x64": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-x64/-/opencode-linux-x64-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-OM3umRJ2ZpDe4YtDOGp32cQCqvMPuFJBKKngfvWL4zz4EWDzLPttb0sNL7xdC1xR/XidtADRYcvtzc2rr7/4Rw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-x64-baseline": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-x64-baseline/-/opencode-linux-x64-baseline-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-yakykM+GxCMZ5WK2G0CQk5cGJkoKt09A/8Y0bhKy7l+Jc/QiTV2ZIlBwT5FO15dnuGlNH6zvukLyOQvOZinM+Q==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-x64-baseline-musl": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-x64-baseline-musl/-/opencode-linux-x64-baseline-musl-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-XHPuf9Rj9nWkVX7JJZ5NtYx9UWT0hPiem/kAhZzYITseRGmQMMfFAhIOBYYvvlzJzgdhcrLi198kJHaKCdXjag==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-linux-x64-musl": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-linux-x64-musl/-/opencode-linux-x64-musl-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-nFT4J4m4wKiZh4RsfgQJlW6W2w/VtMNnXnMiQk93FnncSf0nUXEB5Nam3P+vCjdR3i4cs355JiOvbshnMiJGSA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-windows-x64": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-windows-x64/-/opencode-windows-x64-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-7nkwFgS8Jn35kgw+6JIWRU6PmbggSuTDiQ+RnFGoySFgYW/Sz/R30Z4KmnbfJLElzEef0kX+nZQasfVl0pSf6A==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/opencode-windows-x64-baseline": {
|
|
||||||
"version": "1.2.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/opencode-windows-x64-baseline/-/opencode-windows-x64-baseline-1.2.24.tgz",
|
|
||||||
"integrity": "sha512-hXp1nPnkrHgM6cid1AogRJvjz931UbZ/G165B6AAR1gOb5EqtIh2gZcT0IRUyjooEpwH7+oGFRhT+3Kql/M0WA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@@ -24688,6 +24482,50 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/shell-env": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/shell-env/-/shell-env-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-Ioe5h+hCDZ7pKL5+JGzbtPvZ5ESMHePZ8nLxohlDL+twmlcmutttMhRkrQOed8DeLT8mkYBgbwZfohe8pqaA3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"default-shell": "^2.0.0",
|
||||||
|
"execa": "^5.1.1",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shell-env/node_modules/ansi-regex": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shell-env/node_modules/strip-ansi": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shell-quote": {
|
"node_modules/shell-quote": {
|
||||||
"version": "1.8.3",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||||
@@ -25427,6 +25265,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strip-final-newline": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-indent": {
|
"node_modules/strip-indent": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz",
|
||||||
@@ -28073,7 +27920,6 @@
|
|||||||
"@lezer/markdown": "^1.6.2",
|
"@lezer/markdown": "^1.6.2",
|
||||||
"@lezer/python": "^1.1.18",
|
"@lezer/python": "^1.1.18",
|
||||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||||
"@openai/codex": "^0.114.0",
|
|
||||||
"@opencode-ai/sdk": "1.2.6",
|
"@opencode-ai/sdk": "1.2.6",
|
||||||
"@sctg/sentencepiece-js": "^1.1.0",
|
"@sctg/sentencepiece-js": "^1.1.0",
|
||||||
"@xterm/headless": "^6.0.0",
|
"@xterm/headless": "^6.0.0",
|
||||||
@@ -28088,11 +27934,11 @@
|
|||||||
"node-pty": "1.2.0-beta.11",
|
"node-pty": "1.2.0-beta.11",
|
||||||
"onnxruntime-node": "^1.23.0",
|
"onnxruntime-node": "^1.23.0",
|
||||||
"openai": "^4.20.0",
|
"openai": "^4.20.0",
|
||||||
"opencode-ai": "^1.2.24",
|
|
||||||
"pino": "^10.2.0",
|
"pino": "^10.2.0",
|
||||||
"pino-pretty": "^13.1.3",
|
"pino-pretty": "^13.1.3",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"rotating-file-stream": "^3.2.9",
|
"rotating-file-stream": "^3.2.9",
|
||||||
|
"shell-env": "^4.0.3",
|
||||||
"sherpa-onnx": "1.12.28",
|
"sherpa-onnx": "1.12.28",
|
||||||
"sherpa-onnx-node": "1.12.28",
|
"sherpa-onnx-node": "1.12.28",
|
||||||
"strip-ansi": "^7.1.2",
|
"strip-ansi": "^7.1.2",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { getIsTauri } from "@/constants/layout";
|
|||||||
import { CommandCenter } from "@/components/command-center";
|
import { CommandCenter } from "@/components/command-center";
|
||||||
import { ProjectPickerModal } from "@/components/project-picker-modal";
|
import { ProjectPickerModal } from "@/components/project-picker-modal";
|
||||||
import { KeyboardShortcutsDialog } from "@/components/keyboard-shortcuts-dialog";
|
import { KeyboardShortcutsDialog } from "@/components/keyboard-shortcuts-dialog";
|
||||||
|
import { listenToDesktopNotificationClicks } from "@/desktop/notifications/desktop-notifications";
|
||||||
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
|
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
|
||||||
import { queryClient } from "@/query/query-client";
|
import { queryClient } from "@/query/query-client";
|
||||||
import {
|
import {
|
||||||
@@ -89,20 +90,39 @@ function PushNotificationRouter() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Platform.OS === "web") {
|
if (Platform.OS === "web") {
|
||||||
if (getTauri()) {
|
if (getTauri()) {
|
||||||
void ensureOsNotificationPermission().then((granted) => {
|
void ensureOsNotificationPermission();
|
||||||
console.log(
|
|
||||||
"[OSNotifications][Tauri] Startup permission preflight result:",
|
let disposed = false;
|
||||||
granted ? "granted" : "not-granted"
|
let unlisten: (() => void) | null = null;
|
||||||
);
|
|
||||||
});
|
void listenToDesktopNotificationClicks((payload) => {
|
||||||
|
router.push(buildNotificationRoute(payload.data) as any);
|
||||||
|
})
|
||||||
|
.then((cleanup) => {
|
||||||
|
if (disposed) {
|
||||||
|
cleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unlisten = cleanup;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(
|
||||||
|
"[OSNotifications][Desktop] Failed to register notification click listener",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
disposed = true;
|
||||||
|
unlisten?.();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = globalThis as unknown as EventTarget;
|
const target = globalThis as unknown as EventTarget;
|
||||||
const openFromWebClick = (event: Event) => {
|
const openFromWebClick = (event: Event) => {
|
||||||
const customEvent = event as CustomEvent<WebNotificationClickDetail>;
|
const customEvent = event as CustomEvent<WebNotificationClickDetail>;
|
||||||
const route = buildNotificationRoute(customEvent.detail?.data);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
router.push(route as any);
|
router.push(buildNotificationRoute(customEvent.detail?.data) as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
target.addEventListener(
|
target.addEventListener(
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
import { useDraftStore } from "@/stores/draft-store";
|
import { useDraftStore } from "@/stores/draft-store";
|
||||||
import type { AgentDirectoryEntry } from "@/types/agent-directory";
|
import type { AgentDirectoryEntry } from "@/types/agent-directory";
|
||||||
import { sendOsNotification } from "@/utils/os-notifications";
|
import { sendOsNotification } from "@/utils/os-notifications";
|
||||||
|
import { getIsAppActivelyVisible } from "@/utils/app-visibility";
|
||||||
import {
|
import {
|
||||||
getInitKey,
|
getInitKey,
|
||||||
getInitDeferred,
|
getInitDeferred,
|
||||||
@@ -570,18 +571,10 @@ function SessionProviderInternal({
|
|||||||
if (params.reason === "error") {
|
if (params.reason === "error") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isActive = appState ? appState === "active" : true;
|
const isActivelyVisible = getIsAppActivelyVisible(appState);
|
||||||
const isAwayFromAgent = !isActive || focusedAgentId !== params.agentId;
|
const isAwayFromAgent =
|
||||||
|
!isActivelyVisible || focusedAgentId !== params.agentId;
|
||||||
if (!isAwayFromAgent) {
|
if (!isAwayFromAgent) {
|
||||||
console.log(
|
|
||||||
"[OSNotifications] Skipping attention notification: user already focused on agent",
|
|
||||||
{
|
|
||||||
agentId: params.agentId,
|
|
||||||
reason: params.reason,
|
|
||||||
appState,
|
|
||||||
focusedAgentId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { invokeDesktopCommand } from "@/desktop/tauri/invoke-desktop-command";
|
||||||
|
import { getTauri } from "@/utils/tauri";
|
||||||
|
|
||||||
|
export const DESKTOP_NOTIFICATION_CLICK_EVENT = "desktop-notification-click";
|
||||||
|
|
||||||
|
export interface DesktopNotificationInput {
|
||||||
|
title: string;
|
||||||
|
body?: string;
|
||||||
|
data?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesktopNotificationClickPayload {
|
||||||
|
data?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DesktopNotificationClickHandler = (
|
||||||
|
payload: DesktopNotificationClickPayload
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
export type DesktopNotificationClickUnlisten = () => void;
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendDesktopNotification(
|
||||||
|
input: DesktopNotificationInput
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await invokeDesktopCommand("send_desktop_notification", { input });
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("[OSNotifications][Desktop] Failed to send desktop notification", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listenToDesktopNotificationClicks(
|
||||||
|
handler: DesktopNotificationClickHandler
|
||||||
|
): Promise<DesktopNotificationClickUnlisten> {
|
||||||
|
const listen = getTauri()?.event?.listen;
|
||||||
|
if (typeof listen !== "function") {
|
||||||
|
throw new Error("Tauri event API is unavailable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlisten = await listen(DESKTOP_NOTIFICATION_CLICK_EVENT, (event: unknown) => {
|
||||||
|
const payload = isRecord(event) && isRecord(event.payload) ? event.payload : null;
|
||||||
|
if (!payload) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler({
|
||||||
|
data: isRecord(payload.data) ? payload.data : undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return typeof unlisten === "function" ? unlisten : () => {};
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
shouldClearAgentAttention,
|
shouldClearAgentAttention,
|
||||||
type AgentAttentionClearTrigger,
|
type AgentAttentionClearTrigger,
|
||||||
} from "@/utils/agent-attention";
|
} from "@/utils/agent-attention";
|
||||||
|
import { getIsAppActivelyVisible } from "@/utils/app-visibility";
|
||||||
|
|
||||||
type AttentionReason = "finished" | "error" | "permission" | null | undefined;
|
type AttentionReason = "finished" | "error" | "permission" | null | undefined;
|
||||||
|
|
||||||
@@ -23,20 +24,6 @@ interface AgentAttentionClearController {
|
|||||||
clearOnAgentBlur: () => void;
|
clearOnAgentBlur: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIsAppVisible(): boolean {
|
|
||||||
const isAppStateActive = AppState.currentState === "active";
|
|
||||||
if (Platform.OS !== "web") {
|
|
||||||
return isAppStateActive;
|
|
||||||
}
|
|
||||||
const documentVisible =
|
|
||||||
typeof document === "undefined" || document.visibilityState === "visible";
|
|
||||||
const windowFocused =
|
|
||||||
typeof document === "undefined" ||
|
|
||||||
typeof document.hasFocus !== "function" ||
|
|
||||||
document.hasFocus();
|
|
||||||
return isAppStateActive && documentVisible && windowFocused;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useAgentAttentionClear({
|
export function useAgentAttentionClear({
|
||||||
agentId,
|
agentId,
|
||||||
client,
|
client,
|
||||||
@@ -45,12 +32,14 @@ export function useAgentAttentionClear({
|
|||||||
attentionReason,
|
attentionReason,
|
||||||
isScreenFocused,
|
isScreenFocused,
|
||||||
}: UseAgentAttentionClearParams): AgentAttentionClearController {
|
}: UseAgentAttentionClearParams): AgentAttentionClearController {
|
||||||
const [isAppVisible, setIsAppVisible] = useState<boolean>(() => getIsAppVisible());
|
const [isAppVisible, setIsAppVisible] = useState<boolean>(() =>
|
||||||
|
getIsAppActivelyVisible()
|
||||||
|
);
|
||||||
const deferredFocusEntryClearRef = useRef(false);
|
const deferredFocusEntryClearRef = useRef(false);
|
||||||
const prevRequiresAttentionRef = useRef(Boolean(requiresAttention));
|
const prevRequiresAttentionRef = useRef(Boolean(requiresAttention));
|
||||||
const prevActivelyViewedRef = useRef(isScreenFocused && getIsAppVisible());
|
const prevActivelyViewedRef = useRef(isScreenFocused && getIsAppActivelyVisible());
|
||||||
const prevScreenFocusedRef = useRef(false);
|
const prevScreenFocusedRef = useRef(false);
|
||||||
const prevAppVisibleRef = useRef(getIsAppVisible());
|
const prevAppVisibleRef = useRef(getIsAppActivelyVisible());
|
||||||
|
|
||||||
const clearAttention = useCallback(
|
const clearAttention = useCallback(
|
||||||
(trigger: AgentAttentionClearTrigger) => {
|
(trigger: AgentAttentionClearTrigger) => {
|
||||||
@@ -78,7 +67,7 @@ export function useAgentAttentionClear({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateVisibility = () => {
|
const updateVisibility = () => {
|
||||||
setIsAppVisible(getIsAppVisible());
|
setIsAppVisible(getIsAppActivelyVisible());
|
||||||
};
|
};
|
||||||
|
|
||||||
const appStateSubscription = AppState.addEventListener(
|
const appStateSubscription = AppState.addEventListener(
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { AppState, Platform } from "react-native";
|
||||||
|
|
||||||
|
export function getIsAppActivelyVisible(
|
||||||
|
appState: string = AppState.currentState
|
||||||
|
): boolean {
|
||||||
|
if (appState !== "active") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Platform.OS !== "web") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentVisible =
|
||||||
|
typeof document === "undefined" || document.visibilityState === "visible";
|
||||||
|
const windowFocused =
|
||||||
|
typeof document === "undefined" ||
|
||||||
|
typeof document.hasFocus !== "function" ||
|
||||||
|
document.hasFocus();
|
||||||
|
|
||||||
|
return documentVisible && windowFocused;
|
||||||
|
}
|
||||||
@@ -71,6 +71,7 @@ describe("sendOsNotification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
vi.doUnmock("@/desktop/notifications/desktop-notifications");
|
||||||
vi.doUnmock("react-native");
|
vi.doUnmock("react-native");
|
||||||
vi.restoreAllMocks();
|
vi.restoreAllMocks();
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
@@ -167,18 +168,13 @@ describe("sendOsNotification", () => {
|
|||||||
expect(assign).toHaveBeenCalledWith("/h/srv%20with%20space/agent/agent%2F1");
|
expect(assign).toHaveBeenCalledWith("/h/srv%20with%20space/agent/agent%2F1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses Tauri notification module when available", async () => {
|
it("uses the desktop notification bridge when Tauri is available", async () => {
|
||||||
const isPermissionGranted = vi.fn(async () => false);
|
const sendDesktopNotification = vi.fn(async () => true);
|
||||||
const requestPermission = vi.fn(async () => "granted");
|
vi.doMock("@/desktop/notifications/desktop-notifications", () => ({
|
||||||
const sendNotification = vi.fn(async () => undefined);
|
sendDesktopNotification,
|
||||||
|
}));
|
||||||
|
|
||||||
(globalThis as { __TAURI__?: unknown }).__TAURI__ = {
|
(globalThis as { __TAURI__?: unknown }).__TAURI__ = {};
|
||||||
notification: {
|
|
||||||
isPermissionGranted,
|
|
||||||
requestPermission,
|
|
||||||
sendNotification,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { sendOsNotification } = await loadModuleForPlatform("web");
|
const { sendOsNotification } = await loadModuleForPlatform("web");
|
||||||
const sent = await sendOsNotification({
|
const sent = await sendOsNotification({
|
||||||
@@ -188,11 +184,10 @@ describe("sendOsNotification", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(sent).toBe(true);
|
expect(sent).toBe(true);
|
||||||
expect(isPermissionGranted).toHaveBeenCalledTimes(1);
|
expect(sendDesktopNotification).toHaveBeenCalledWith({
|
||||||
expect(requestPermission).toHaveBeenCalledTimes(1);
|
|
||||||
expect(sendNotification).toHaveBeenCalledWith({
|
|
||||||
title: "Agent finished",
|
title: "Agent finished",
|
||||||
body: "Done",
|
body: "Done",
|
||||||
|
data: { serverId: "srv-1", agentId: "agent-1" },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
|
import { sendDesktopNotification } from "@/desktop/notifications/desktop-notifications";
|
||||||
import { buildNotificationRoute } from "./notification-routing";
|
import { buildNotificationRoute } from "./notification-routing";
|
||||||
import { getTauri, type TauriNotificationApi } from "@/utils/tauri";
|
import { getTauri, type TauriNotificationApi } from "@/utils/tauri";
|
||||||
|
|
||||||
@@ -14,13 +15,16 @@ export type WebNotificationClickDetail = {
|
|||||||
|
|
||||||
type WebNotificationInstance = {
|
type WebNotificationInstance = {
|
||||||
onclick?: ((event: Event) => void) | null;
|
onclick?: ((event: Event) => void) | null;
|
||||||
|
onclose?: ((event: Event) => void) | null;
|
||||||
addEventListener?: (type: string, listener: (event: Event) => void) => void;
|
addEventListener?: (type: string, listener: (event: Event) => void) => void;
|
||||||
|
removeEventListener?: (type: string, listener: (event: Event) => void) => void;
|
||||||
close?: () => void;
|
close?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WEB_NOTIFICATION_CLICK_EVENT = "paseo:web-notification-click";
|
export const WEB_NOTIFICATION_CLICK_EVENT = "paseo:web-notification-click";
|
||||||
|
|
||||||
let permissionRequest: Promise<boolean> | null = null;
|
let permissionRequest: Promise<boolean> | null = null;
|
||||||
|
const activeWebNotifications = new Set<WebNotificationInstance>();
|
||||||
|
|
||||||
function getTauriNotificationModule(): TauriNotificationApi | null {
|
function getTauriNotificationModule(): TauriNotificationApi | null {
|
||||||
if (Platform.OS !== "web") {
|
if (Platform.OS !== "web") {
|
||||||
@@ -113,35 +117,6 @@ async function ensureTauriNotificationPermission(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendTauriNotification(
|
|
||||||
payload: OsNotificationPayload,
|
|
||||||
notificationModule: TauriNotificationApi
|
|
||||||
): Promise<boolean> {
|
|
||||||
if (typeof notificationModule.sendNotification !== "function") {
|
|
||||||
console.warn(
|
|
||||||
"[OSNotifications][Tauri] notification.sendNotification is unavailable"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const granted = await ensureTauriNotificationPermission(notificationModule);
|
|
||||||
if (!granted) {
|
|
||||||
console.log("[OSNotifications][Tauri] Permission not granted");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await notificationModule.sendNotification({
|
|
||||||
title: payload.title,
|
|
||||||
body: payload.body,
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn("[OSNotifications][Tauri] Failed to send notification", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchWebNotificationClick(detail: WebNotificationClickDetail): boolean {
|
function dispatchWebNotificationClick(detail: WebNotificationClickDetail): boolean {
|
||||||
const dispatch = (globalThis as { dispatchEvent?: (event: Event) => boolean }).dispatchEvent;
|
const dispatch = (globalThis as { dispatchEvent?: (event: Event) => boolean }).dispatchEvent;
|
||||||
const CustomEventConstructor = (globalThis as { CustomEvent?: typeof CustomEvent })
|
const CustomEventConstructor = (globalThis as { CustomEvent?: typeof CustomEvent })
|
||||||
@@ -183,6 +158,12 @@ function attachWebClickHandler(
|
|||||||
notification: WebNotificationInstance,
|
notification: WebNotificationInstance,
|
||||||
data: Record<string, unknown> | undefined
|
data: Record<string, unknown> | undefined
|
||||||
): void {
|
): void {
|
||||||
|
activeWebNotifications.add(notification);
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
activeWebNotifications.delete(notification);
|
||||||
|
};
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
const focus = (globalThis as { focus?: () => void }).focus;
|
const focus = (globalThis as { focus?: () => void }).focus;
|
||||||
if (typeof focus === "function") {
|
if (typeof focus === "function") {
|
||||||
@@ -194,17 +175,24 @@ function attachWebClickHandler(
|
|||||||
fallbackNavigateToNotificationTarget(data);
|
fallbackNavigateToNotificationTarget(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
if (typeof notification.close === "function") {
|
if (typeof notification.close === "function") {
|
||||||
notification.close();
|
notification.close();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
cleanup();
|
||||||
|
};
|
||||||
|
|
||||||
if (typeof notification.addEventListener === "function") {
|
if (typeof notification.addEventListener === "function") {
|
||||||
notification.addEventListener("click", onClick);
|
notification.addEventListener("click", onClick);
|
||||||
|
notification.addEventListener("close", onClose);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.onclick = onClick;
|
notification.onclick = onClick;
|
||||||
|
notification.onclose = onClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendOsNotification(
|
export async function sendOsNotification(
|
||||||
@@ -215,34 +203,27 @@ export async function sendOsNotification(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tauriNotification = getTauriNotificationModule();
|
if (getTauri()) {
|
||||||
if (tauriNotification) {
|
return await sendDesktopNotification(payload);
|
||||||
console.log("[OSNotifications] Using Tauri notification module");
|
|
||||||
return await sendTauriNotification(payload, tauriNotification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotificationConstructor = getWebNotificationConstructor();
|
const NotificationConstructor = getWebNotificationConstructor();
|
||||||
if (!NotificationConstructor) {
|
if (NotificationConstructor) {
|
||||||
console.log(
|
const granted = await ensureNotificationPermission();
|
||||||
"[OSNotifications][Web] Notification constructor unavailable",
|
if (!granted) {
|
||||||
typeof window !== "undefined" && window.location
|
console.log(
|
||||||
? window.location.origin
|
"[OSNotifications][Web] Permission not granted:",
|
||||||
: "unknown-origin"
|
NotificationConstructor.permission
|
||||||
);
|
);
|
||||||
return false;
|
} else {
|
||||||
|
const notification = new NotificationConstructor(payload.title, {
|
||||||
|
body: payload.body,
|
||||||
|
data: payload.data,
|
||||||
|
}) as WebNotificationInstance;
|
||||||
|
attachWebClickHandler(notification, payload.data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const granted = await ensureOsNotificationPermission();
|
|
||||||
if (!granted) {
|
return false;
|
||||||
console.log(
|
|
||||||
"[OSNotifications][Web] Permission not granted:",
|
|
||||||
NotificationConstructor.permission
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const notification = new NotificationConstructor(payload.title, {
|
|
||||||
body: payload.body,
|
|
||||||
data: payload.data,
|
|
||||||
}) as WebNotificationInstance;
|
|
||||||
attachWebClickHandler(notification, payload.data);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function attachConsole(): void {
|
|||||||
|
|
||||||
console.log = (...args: unknown[]) => {
|
console.log = (...args: unknown[]) => {
|
||||||
originalLog.apply(console, args);
|
originalLog.apply(console, args);
|
||||||
forwardToRust(0, args); // debug
|
forwardToRust(1, args); // info
|
||||||
};
|
};
|
||||||
console.info = (...args: unknown[]) => {
|
console.info = (...args: unknown[]) => {
|
||||||
originalInfo.apply(console, args);
|
originalInfo.apply(console, args);
|
||||||
|
|||||||
@@ -480,71 +480,12 @@ async function pruneClaudeAgentSdk(runtimeRoot) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pruneCodexCli(runtimeRoot) {
|
|
||||||
const codexTargetTriple = {
|
|
||||||
darwin: { arm64: "aarch64-apple-darwin", x64: "x86_64-apple-darwin" },
|
|
||||||
linux: { arm64: "aarch64-unknown-linux-musl", x64: "x86_64-unknown-linux-musl" },
|
|
||||||
win32: { arm64: "aarch64-pc-windows-msvc", x64: "x86_64-pc-windows-msvc" },
|
|
||||||
};
|
|
||||||
const codexPlatformPackages = [
|
|
||||||
"@openai/codex-darwin-arm64",
|
|
||||||
"@openai/codex-darwin-x64",
|
|
||||||
"@openai/codex-linux-arm64",
|
|
||||||
"@openai/codex-linux-x64",
|
|
||||||
"@openai/codex-win32-arm64",
|
|
||||||
"@openai/codex-win32-x64",
|
|
||||||
];
|
|
||||||
const currentTriple = codexTargetTriple[process.platform]?.[process.arch];
|
|
||||||
const currentPackage = currentTriple
|
|
||||||
? `@openai/codex-${process.platform === "win32" ? "win32" : process.platform}-${process.arch}`
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const openaiScope = path.join(runtimeRoot, "node_modules", "@openai");
|
|
||||||
for (const pkg of codexPlatformPackages) {
|
|
||||||
const pkgName = pkg.replace("@openai/", "");
|
|
||||||
if (currentPackage && pkg === currentPackage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await removeIfExists(path.join(openaiScope, pkgName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pruneOpenCodeCli(runtimeRoot) {
|
|
||||||
const opencodePlatformPackages = [
|
|
||||||
"opencode-darwin-arm64",
|
|
||||||
"opencode-darwin-x64",
|
|
||||||
"opencode-linux-arm64",
|
|
||||||
"opencode-linux-x64",
|
|
||||||
"opencode-linux-x64-baseline",
|
|
||||||
"opencode-linux-arm64-musl",
|
|
||||||
"opencode-linux-x64-musl",
|
|
||||||
"opencode-linux-x64-baseline-musl",
|
|
||||||
"opencode-windows-x64",
|
|
||||||
"opencode-windows-arm64",
|
|
||||||
];
|
|
||||||
const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" };
|
|
||||||
const currentPlatform = platformMap[process.platform];
|
|
||||||
const currentPackage = currentPlatform
|
|
||||||
? `opencode-${currentPlatform}-${process.arch}`
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const nodeModules = path.join(runtimeRoot, "node_modules");
|
|
||||||
for (const pkg of opencodePlatformPackages) {
|
|
||||||
if (currentPackage && pkg === currentPackage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await removeIfExists(path.join(nodeModules, pkg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pruneManagedRuntime(runtimeRoot) {
|
async function pruneManagedRuntime(runtimeRoot) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
pruneNodeDistribution(runtimeRoot),
|
pruneNodeDistribution(runtimeRoot),
|
||||||
pruneOnnxRuntime(runtimeRoot),
|
pruneOnnxRuntime(runtimeRoot),
|
||||||
pruneNodePty(runtimeRoot),
|
pruneNodePty(runtimeRoot),
|
||||||
pruneClaudeAgentSdk(runtimeRoot),
|
pruneClaudeAgentSdk(runtimeRoot),
|
||||||
pruneCodexCli(runtimeRoot),
|
|
||||||
pruneOpenCodeCli(runtimeRoot),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+1
@@ -2637,6 +2637,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"log",
|
"log",
|
||||||
|
"mac-notification-sys",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ tauri-plugin-websocket = "2"
|
|||||||
tokio = { version = "1", features = ["net", "sync"] }
|
tokio = { version = "1", features = ["net", "sync"] }
|
||||||
tokio-tungstenite = "0.24"
|
tokio-tungstenite = "0.24"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
mac-notification-sys = "0.6"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use tauri::{AppHandle, Runtime};
|
||||||
|
|
||||||
|
pub const DESKTOP_NOTIFICATION_CLICK_EVENT: &str = "desktop-notification-click";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct DesktopNotificationInput {
|
||||||
|
pub title: String,
|
||||||
|
pub body: Option<String>,
|
||||||
|
pub data: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct DesktopNotificationClickPayload {
|
||||||
|
data: Option<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod platform {
|
||||||
|
use super::{
|
||||||
|
AppHandle, DesktopNotificationClickPayload, DesktopNotificationInput,
|
||||||
|
DESKTOP_NOTIFICATION_CLICK_EVENT,
|
||||||
|
};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use tauri::{async_runtime, Emitter, Runtime};
|
||||||
|
|
||||||
|
const MACOS_NOTIFICATION_SENDER_BUNDLE_ID: &str = "dev.paseo.desktop";
|
||||||
|
static MACOS_NOTIFICATION_SENDER_INIT: OnceLock<Result<(), String>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn ensure_notification_sender() -> Result<(), String> {
|
||||||
|
MACOS_NOTIFICATION_SENDER_INIT
|
||||||
|
.get_or_init(|| {
|
||||||
|
mac_notification_sys::set_application(MACOS_NOTIFICATION_SENDER_BUNDLE_ID)
|
||||||
|
.map_err(|error| {
|
||||||
|
format!(
|
||||||
|
"Failed to initialize macOS notification sender for {}: {error}",
|
||||||
|
MACOS_NOTIFICATION_SENDER_BUNDLE_ID
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_notification<R: Runtime>(
|
||||||
|
app: AppHandle<R>,
|
||||||
|
input: DesktopNotificationInput,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let _task = async_runtime::spawn_blocking(move || {
|
||||||
|
if let Err(error) = ensure_notification_sender() {
|
||||||
|
log::warn!("{error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut notification = mac_notification_sys::Notification::default();
|
||||||
|
notification
|
||||||
|
.title(&input.title)
|
||||||
|
.message(input.body.as_deref().unwrap_or(""))
|
||||||
|
.wait_for_click(true);
|
||||||
|
|
||||||
|
match notification.send() {
|
||||||
|
Ok(mac_notification_sys::NotificationResponse::Click) => {
|
||||||
|
let payload = DesktopNotificationClickPayload { data: input.data };
|
||||||
|
if let Err(error) = app.emit(DESKTOP_NOTIFICATION_CLICK_EVENT, payload) {
|
||||||
|
log::warn!("failed to emit desktop notification click event: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(error) => {
|
||||||
|
log::warn!("failed to send macOS notification: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
mod platform {
|
||||||
|
use super::{AppHandle, DesktopNotificationInput};
|
||||||
|
use tauri::Runtime;
|
||||||
|
use tauri_plugin_notification::NotificationExt;
|
||||||
|
|
||||||
|
pub fn send_notification<R: Runtime>(
|
||||||
|
app: AppHandle<R>,
|
||||||
|
input: DesktopNotificationInput,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let mut notification = app.notification().builder().title(&input.title);
|
||||||
|
if let Some(body) = &input.body {
|
||||||
|
notification = notification.body(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
notification
|
||||||
|
.show()
|
||||||
|
.map_err(|error| format!("Failed to send desktop notification: {error}"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn send_desktop_notification<R: Runtime>(
|
||||||
|
app: AppHandle<R>,
|
||||||
|
input: DesktopNotificationInput,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
platform::send_notification(app, input)
|
||||||
|
}
|
||||||
@@ -11,7 +11,9 @@ use tauri::menu::{Menu, MenuItemBuilder, MenuItemKind, PredefinedMenuItem, Subme
|
|||||||
use tauri::{AppHandle, Manager, WebviewWindow};
|
use tauri::{AppHandle, Manager, WebviewWindow};
|
||||||
use tauri_plugin_updater::UpdaterExt;
|
use tauri_plugin_updater::UpdaterExt;
|
||||||
|
|
||||||
|
mod desktop_notifications;
|
||||||
mod runtime_manager;
|
mod runtime_manager;
|
||||||
|
use desktop_notifications::send_desktop_notification;
|
||||||
use runtime_manager::{
|
use runtime_manager::{
|
||||||
cli_symlink_instructions, close_local_daemon_transport, managed_daemon_logs,
|
cli_symlink_instructions, close_local_daemon_transport, managed_daemon_logs,
|
||||||
managed_daemon_pairing, managed_daemon_status, managed_runtime_status,
|
managed_daemon_pairing, managed_daemon_status, managed_runtime_status,
|
||||||
@@ -544,6 +546,7 @@ pub fn run() {
|
|||||||
read_file_base64,
|
read_file_base64,
|
||||||
delete_attachment_file,
|
delete_attachment_file,
|
||||||
garbage_collect_attachment_files,
|
garbage_collect_attachment_files,
|
||||||
|
send_desktop_notification,
|
||||||
webview_log
|
webview_log
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/openai": "2.0.52",
|
"@ai-sdk/openai": "2.0.52",
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.2.11",
|
"@anthropic-ai/claude-agent-sdk": "^0.2.11",
|
||||||
"@openai/codex": "^0.114.0",
|
|
||||||
"@deepgram/sdk": "^3.4.0",
|
"@deepgram/sdk": "^3.4.0",
|
||||||
"@getpaseo/relay": "0.1.26",
|
"@getpaseo/relay": "0.1.26",
|
||||||
"@lezer/common": "^1.5.0",
|
"@lezer/common": "^1.5.0",
|
||||||
@@ -88,11 +87,11 @@
|
|||||||
"node-pty": "1.2.0-beta.11",
|
"node-pty": "1.2.0-beta.11",
|
||||||
"onnxruntime-node": "^1.23.0",
|
"onnxruntime-node": "^1.23.0",
|
||||||
"openai": "^4.20.0",
|
"openai": "^4.20.0",
|
||||||
"opencode-ai": "^1.2.24",
|
|
||||||
"pino": "^10.2.0",
|
"pino": "^10.2.0",
|
||||||
"pino-pretty": "^13.1.3",
|
"pino-pretty": "^13.1.3",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"rotating-file-stream": "^3.2.9",
|
"rotating-file-stream": "^3.2.9",
|
||||||
|
"shell-env": "^4.0.3",
|
||||||
"sherpa-onnx": "1.12.28",
|
"sherpa-onnx": "1.12.28",
|
||||||
"sherpa-onnx-node": "1.12.28",
|
"sherpa-onnx-node": "1.12.28",
|
||||||
"strip-ansi": "^7.1.2",
|
"strip-ansi": "^7.1.2",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { execFileSync, execSync } from "node:child_process";
|
import { execFileSync, execSync } from "node:child_process";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { platform } from "node:os";
|
import { platform } from "node:os";
|
||||||
|
import { shellEnvSync } from "shell-env";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import type { AgentProvider } from "./agent-sdk-types.js";
|
import type { AgentProvider } from "./agent-sdk-types.js";
|
||||||
@@ -104,11 +105,24 @@ export function resolveProviderCommandPrefix(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cachedShellEnv: Record<string, string> | null = null;
|
||||||
|
|
||||||
|
export function resolveShellEnv(): Record<string, string> {
|
||||||
|
if (cachedShellEnv) return cachedShellEnv;
|
||||||
|
try {
|
||||||
|
cachedShellEnv = shellEnvSync();
|
||||||
|
} catch {
|
||||||
|
cachedShellEnv = { ...process.env } as Record<string, string>;
|
||||||
|
}
|
||||||
|
return cachedShellEnv;
|
||||||
|
}
|
||||||
|
|
||||||
export function applyProviderEnv(
|
export function applyProviderEnv(
|
||||||
baseEnv: Record<string, string | undefined>,
|
baseEnv: Record<string, string | undefined>,
|
||||||
runtimeSettings?: ProviderRuntimeSettings
|
runtimeSettings?: ProviderRuntimeSettings
|
||||||
): Record<string, string | undefined> {
|
): Record<string, string | undefined> {
|
||||||
return {
|
return {
|
||||||
|
...resolveShellEnv(),
|
||||||
...baseEnv,
|
...baseEnv,
|
||||||
...(runtimeSettings?.env ?? {}),
|
...(runtimeSettings?.env ?? {}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,10 +27,8 @@ import type { Logger } from "pino";
|
|||||||
import { execSync, spawn } from "node:child_process";
|
import { execSync, spawn } from "node:child_process";
|
||||||
import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync, Dirent } from "node:fs";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { Dirent } from "node:fs";
|
|
||||||
import { createRequire } from "node:module";
|
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
@@ -42,6 +40,7 @@ import {
|
|||||||
} from "./codex/tool-call-mapper.js";
|
} from "./codex/tool-call-mapper.js";
|
||||||
import {
|
import {
|
||||||
applyProviderEnv,
|
applyProviderEnv,
|
||||||
|
findExecutable,
|
||||||
resolveProviderCommandPrefix,
|
resolveProviderCommandPrefix,
|
||||||
type ProviderRuntimeSettings,
|
type ProviderRuntimeSettings,
|
||||||
} from "../provider-launch-config.js";
|
} from "../provider-launch-config.js";
|
||||||
@@ -150,52 +149,13 @@ function mergeCodexConfiguredDefaults(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const CODEX_TARGET_TRIPLE: Record<string, Record<string, string>> = {
|
|
||||||
linux: { x64: "x86_64-unknown-linux-musl", arm64: "aarch64-unknown-linux-musl" },
|
|
||||||
darwin: { x64: "x86_64-apple-darwin", arm64: "aarch64-apple-darwin" },
|
|
||||||
win32: { x64: "x86_64-pc-windows-msvc", arm64: "aarch64-pc-windows-msvc" },
|
|
||||||
};
|
|
||||||
|
|
||||||
const CODEX_PLATFORM_PACKAGE: Record<string, string> = {
|
|
||||||
"x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
|
|
||||||
"aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
|
|
||||||
"x86_64-apple-darwin": "@openai/codex-darwin-x64",
|
|
||||||
"aarch64-apple-darwin": "@openai/codex-darwin-arm64",
|
|
||||||
"x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
|
|
||||||
"aarch64-pc-windows-msvc": "@openai/codex-win32-arm64",
|
|
||||||
};
|
|
||||||
|
|
||||||
function resolveBundledCodexBinary(): string | null {
|
|
||||||
const targetTriple = CODEX_TARGET_TRIPLE[process.platform]?.[process.arch];
|
|
||||||
if (!targetTriple) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const platformPackage = CODEX_PLATFORM_PACKAGE[targetTriple];
|
|
||||||
if (!platformPackage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const binaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
||||||
try {
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
|
||||||
const vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
|
|
||||||
const binaryPath = path.join(vendorRoot, targetTriple, "codex", binaryName);
|
|
||||||
if (existsSync(binaryPath)) {
|
|
||||||
return binaryPath;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Platform package not installed
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveCodexBinary(): string {
|
function resolveCodexBinary(): string {
|
||||||
const bundled = resolveBundledCodexBinary();
|
const found = findExecutable("codex");
|
||||||
if (bundled) {
|
if (found) {
|
||||||
return bundled;
|
return found;
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Bundled Codex binary not found for platform ${process.platform}-${process.arch}. Ensure the @openai/codex package and its platform-specific optional dependency are installed.`
|
"Codex binary not found. Install the Codex CLI (https://github.com/openai/codex) and ensure it is available in your shell PATH."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { spawn, type ChildProcess } from "node:child_process";
|
import { spawn, type ChildProcess } from "node:child_process";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
|
||||||
import path from "node:path";
|
|
||||||
import os from "node:os";
|
|
||||||
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2/client";
|
import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2/client";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import type { Logger } from "pino";
|
import type { Logger } from "pino";
|
||||||
@@ -33,6 +30,7 @@ import type {
|
|||||||
} from "../agent-sdk-types.js";
|
} from "../agent-sdk-types.js";
|
||||||
import {
|
import {
|
||||||
applyProviderEnv,
|
applyProviderEnv,
|
||||||
|
findExecutable,
|
||||||
resolveProviderCommandPrefix,
|
resolveProviderCommandPrefix,
|
||||||
type ProviderRuntimeSettings,
|
type ProviderRuntimeSettings,
|
||||||
} from "../provider-launch-config.js";
|
} from "../provider-launch-config.js";
|
||||||
@@ -152,40 +150,13 @@ const OpencodeToolPartToTimelineItemSchema = OpencodeToolPartTimelineEnvelopeSch
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const OPENCODE_PLATFORM_MAP: Record<string, string> = {
|
|
||||||
darwin: "darwin",
|
|
||||||
linux: "linux",
|
|
||||||
win32: "windows",
|
|
||||||
};
|
|
||||||
|
|
||||||
function resolveBundledOpenCodeBinary(): string | null {
|
|
||||||
const platform = OPENCODE_PLATFORM_MAP[os.platform()];
|
|
||||||
if (!platform) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const arch = os.arch();
|
|
||||||
const packageName = `opencode-${platform}-${arch}`;
|
|
||||||
const binaryName = platform === "windows" ? "opencode.exe" : "opencode";
|
|
||||||
try {
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
||||||
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", binaryName);
|
|
||||||
if (existsSync(binaryPath)) {
|
|
||||||
return binaryPath;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Platform package not installed
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveOpenCodeBinary(): string {
|
function resolveOpenCodeBinary(): string {
|
||||||
const bundled = resolveBundledOpenCodeBinary();
|
const found = findExecutable("opencode");
|
||||||
if (bundled) {
|
if (found) {
|
||||||
return bundled;
|
return found;
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Bundled OpenCode binary not found for platform ${os.platform()}-${os.arch()}. Ensure the opencode-ai package and its platform-specific optional dependency are installed.`
|
"OpenCode binary not found. Install OpenCode (https://github.com/opencode-ai/opencode) and ensure it is available in your shell PATH."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7379,7 +7379,7 @@ export class Session {
|
|||||||
const requestedResumeOffset =
|
const requestedResumeOffset =
|
||||||
typeof msg.resumeOffset === 'number'
|
typeof msg.resumeOffset === 'number'
|
||||||
? msg.resumeOffset
|
? msg.resumeOffset
|
||||||
: session.getOutputOffset()
|
: 0
|
||||||
const initialOffset = Math.max(0, Math.floor(requestedResumeOffset))
|
const initialOffset = Math.max(0, Math.floor(requestedResumeOffset))
|
||||||
const binding: {
|
const binding: {
|
||||||
terminalId: string
|
terminalId: string
|
||||||
|
|||||||
Reference in New Issue
Block a user