mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-08-31 01:37:47 +08:00
Fix macOS notarization timestamps
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ Simplified Chinese release notes are the primary version.
|
||||
- Added Rust, Vitest, contract, and real Electron Gate B coverage for Plugin v2 App Center, typed gateway, mention/activation, MCP resources, Right Surface, history restoration, and uninstall semantics.
|
||||
- Added regression guards for Electron packaged assets, embedded Browser HTML, GUI smoke, Skills watcher behavior, and the tool lifecycle.
|
||||
- Added canonical reasoning linear-persistence and 1,200-command history-import performance regressions covering repeated deltas, final snapshot replacement, and background progress completion.
|
||||
- Added a macOS release-signing policy that disables timestamps for nested resources and retries when Apple's timestamp service is unavailable.
|
||||
- Added a macOS release-signing policy that disables timestamps only for non-code resources, retries when Apple's timestamp service is unavailable, and preserves secure timestamps for nested Mach-O binaries required by notarization.
|
||||
|
||||
### Documentation
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
- 新增 Plugin v2 App Center、typed gateway、mention/activation、MCP resource、Right Surface、历史恢复与卸载语义的 Rust、Vitest、contract 和真实 Electron Gate B 覆盖。
|
||||
- 补充 Electron 打包资产、内嵌 Browser HTML、GUI smoke、Skills watcher 和 tool lifecycle 的回归守卫。
|
||||
- 新增 canonical reasoning 线性持久化与 1,200 条命令历史导入性能回归,覆盖重复 delta、final 快照替换和后台进度完成。
|
||||
- 增加 macOS 正式签名嵌套资源的 timestamp 关闭策略与时间戳服务不可用重试守卫。
|
||||
- 增加 macOS 正式签名非代码资源的 timestamp 关闭策略与时间戳服务不可用重试守卫,同时确保嵌套 Mach-O 保留安全时间戳并可通过 notarization。
|
||||
|
||||
### 文档
|
||||
|
||||
|
||||
+17
-1
@@ -61,6 +61,19 @@ function isTopLevelAppBundle(filePath) {
|
||||
return normalized.endsWith(".app") && !normalized.includes(".app/");
|
||||
}
|
||||
|
||||
const MACOS_NON_CODE_RESOURCE_EXTENSIONS = new Set([
|
||||
".asar",
|
||||
".bin",
|
||||
".dat",
|
||||
".pak",
|
||||
]);
|
||||
|
||||
function isMacOSNonCodeResource(filePath) {
|
||||
const normalized = String(filePath || "").replace(/\\/g, "/");
|
||||
const extension = path.extname(normalized).toLowerCase();
|
||||
return MACOS_NON_CODE_RESOURCE_EXTENSIONS.has(extension);
|
||||
}
|
||||
|
||||
function macSignOptions({
|
||||
env = process.env,
|
||||
platform = process.platform,
|
||||
@@ -79,8 +92,11 @@ function macSignOptions({
|
||||
preEmbedProvisioningProfile: false,
|
||||
optionsForFile: (filePath) => {
|
||||
if (!isTopLevelAppBundle(filePath)) {
|
||||
if (releaseSigning && isMacOSNonCodeResource(filePath)) {
|
||||
return { timestamp: "none" };
|
||||
}
|
||||
return releaseSigning
|
||||
? { timestamp: "none" }
|
||||
? {}
|
||||
: { hardenedRuntime: false, timestamp: "none" };
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
- `cargo fmt --manifest-path "lime-rs/Cargo.toml" --all -- --check`:通过。
|
||||
- `git diff --check`:通过。
|
||||
- GUI 构建只输出已知非阻断告警:`oem-runtime-config.js` 非 module script、Browserslist 数据过期与 Electron `console-message` API 废弃提示;本次未更新核心依赖。
|
||||
- 发布后 CI `30997182812` 首轮失败于 macOS `codesign --timestamp` 处理 `locale.pak`,错误为 `The timestamp service is not available.`;已修复嵌套资源 timestamp 策略,并将该错误纳入 Forge package 重试分类。
|
||||
- 发布后 CI `30997182812` 首轮失败于 macOS `codesign --timestamp` 处理 `locale.pak`,错误为 `The timestamp service is not available.`;已修复非代码资源 timestamp 策略,并将该错误纳入 Forge package 重试分类。重跑 `31000622796` 暴露此前对全部嵌套文件关闭 timestamp 会导致 notarization 拒绝嵌套 Mach-O;现已仅对 `.pak`、`.bin`、`.dat`、`.asar` 资源关闭 timestamp,嵌套代码恢复安全 timestamp。
|
||||
- 发布后补充 Agent chat terminal session-detail refresh 迁移及 contract guard;Forge 配置、发布工作流、相关守卫与 Agent chat 定向测试均通过。
|
||||
|
||||
## 架构确认
|
||||
|
||||
@@ -61,6 +61,11 @@ describe("Electron Forge config", () => {
|
||||
macSignOptions({ env, platform: "darwin" }).optionsForFile(
|
||||
"release-electron/Lime-darwin-arm64/Lime.app/Contents/MacOS/Lime",
|
||||
),
|
||||
).toEqual({});
|
||||
expect(
|
||||
macSignOptions({ env, platform: "darwin" }).optionsForFile(
|
||||
"release-electron/Lime-darwin-arm64/Lime.app/Contents/Resources/fa_FEMININE.lproj/locale.pak",
|
||||
),
|
||||
).toEqual({ timestamp: "none" });
|
||||
expect(macNotarizeOptions({ env, platform: "darwin" })).toEqual({
|
||||
appleId: "release@example.com",
|
||||
|
||||
@@ -633,6 +633,8 @@ function assertForgeConfig(forgeConfigPath = DEFAULT_FORGE_CONFIG_PATH) {
|
||||
"optionsForFile",
|
||||
"MACOS_APP_ENTITLEMENTS",
|
||||
"isTopLevelAppBundle",
|
||||
"MACOS_NON_CODE_RESOURCE_EXTENSIONS",
|
||||
"isMacOSNonCodeResource",
|
||||
'!normalized.includes(".app/")',
|
||||
"const releaseSigning =",
|
||||
'env.LIME_ELECTRON_SIGN === "1"',
|
||||
@@ -640,6 +642,7 @@ function assertForgeConfig(forgeConfigPath = DEFAULT_FORGE_CONFIG_PATH) {
|
||||
"identityValidation: releaseSigning",
|
||||
"return releaseSigning",
|
||||
': { hardenedRuntime: false, timestamp: "none" }',
|
||||
'return { timestamp: "none" }',
|
||||
"if (!releaseSigning)",
|
||||
'options.identity = "-"',
|
||||
"lime-rs/entitlements.plist",
|
||||
|
||||
@@ -487,6 +487,137 @@ describe("agentSessionState runtimeSync detail refresh", () => {
|
||||
expect(result.snapshot.threadRead?.status).toBe("completed");
|
||||
});
|
||||
|
||||
it("历史回合的已停止标记不能污染后续已完成回合", () => {
|
||||
const canceledTurnId = "turn-runtime-sync-canceled-history";
|
||||
const completedTurnId = "turn-runtime-sync-continued-completed";
|
||||
const continuedText = "继续输出已恢复:正文已经正常收口";
|
||||
const detail = {
|
||||
id: "topic-runtime-sync-cancel-then-continue",
|
||||
created_at: 1782800000,
|
||||
updated_at: 1782800004,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
timestamp: 1782800000,
|
||||
content: [{ type: "text", text: "整理今天的国际新闻" }],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
timestamp: 1782800001,
|
||||
content: [{ type: "text", text: "(已停止)" }],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
timestamp: 1782800002,
|
||||
content: [{ type: "text", text: "继续输出" }],
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
timestamp: 1782800003,
|
||||
content: [{ type: "text", text: continuedText }],
|
||||
},
|
||||
],
|
||||
turns: [
|
||||
{
|
||||
id: canceledTurnId,
|
||||
thread_id: "topic-runtime-sync-cancel-then-continue-thread",
|
||||
prompt_text: "整理今天的国际新闻",
|
||||
status: "canceled",
|
||||
started_at: "2026-07-10T00:00:00.000Z",
|
||||
completed_at: "2026-07-10T00:00:01.000Z",
|
||||
created_at: "2026-07-10T00:00:00.000Z",
|
||||
updated_at: "2026-07-10T00:00:01.000Z",
|
||||
},
|
||||
{
|
||||
id: completedTurnId,
|
||||
thread_id: "topic-runtime-sync-cancel-then-continue-thread",
|
||||
prompt_text: "继续输出",
|
||||
status: "completed",
|
||||
started_at: "2026-07-10T00:00:02.000Z",
|
||||
completed_at: "2026-07-10T00:00:04.000Z",
|
||||
created_at: "2026-07-10T00:00:02.000Z",
|
||||
updated_at: "2026-07-10T00:00:04.000Z",
|
||||
},
|
||||
],
|
||||
items: [
|
||||
createAgentMessageItem({
|
||||
id: "item-runtime-sync-canceled-history",
|
||||
thread_id: "topic-runtime-sync-cancel-then-continue-thread",
|
||||
turn_id: canceledTurnId,
|
||||
text: "(已停止)",
|
||||
sequence: 1,
|
||||
}),
|
||||
createAgentMessageItem({
|
||||
id: "item-runtime-sync-continued-completed",
|
||||
thread_id: "topic-runtime-sync-cancel-then-continue-thread",
|
||||
turn_id: completedTurnId,
|
||||
text: continuedText,
|
||||
sequence: 2,
|
||||
}),
|
||||
],
|
||||
thread_read: {
|
||||
thread_id: "topic-runtime-sync-cancel-then-continue-thread",
|
||||
status: "completed",
|
||||
active_turn_id: completedTurnId,
|
||||
turns: [
|
||||
{ turn_id: canceledTurnId, status: "canceled" },
|
||||
{ turn_id: completedTurnId, status: "completed" },
|
||||
],
|
||||
},
|
||||
} satisfies AgentSessionDetail;
|
||||
|
||||
const result = buildHydratedAgentSessionSnapshot({
|
||||
topicId: "topic-runtime-sync-cancel-then-continue",
|
||||
detail,
|
||||
currentSessionId: "topic-runtime-sync-cancel-then-continue",
|
||||
currentMessages: [
|
||||
createMessage({
|
||||
id: "local-canceled-user",
|
||||
role: "user",
|
||||
content: "整理今天的国际新闻",
|
||||
runtimeTurnId: canceledTurnId,
|
||||
}),
|
||||
createMessage({
|
||||
id: "local-canceled-assistant",
|
||||
role: "assistant",
|
||||
content: "(已停止)",
|
||||
contentParts: [{ type: "text", text: "(已停止)" }],
|
||||
isThinking: false,
|
||||
runtimeTurnId: canceledTurnId,
|
||||
}),
|
||||
createMessage({
|
||||
id: "local-continued-user",
|
||||
role: "user",
|
||||
content: "继续输出",
|
||||
runtimeTurnId: completedTurnId,
|
||||
}),
|
||||
createMessage({
|
||||
id: "local-continued-assistant",
|
||||
role: "assistant",
|
||||
content: continuedText,
|
||||
isThinking: false,
|
||||
runtimeTurnId: completedTurnId,
|
||||
}),
|
||||
],
|
||||
currentThreadTurns: [],
|
||||
currentThreadItems: [],
|
||||
currentExecutionRuntime: null,
|
||||
currentExecutionStrategy: "react",
|
||||
topics: [],
|
||||
detailMergeMode: "terminal_reconcile",
|
||||
});
|
||||
|
||||
const continuedAssistant = result.snapshot.messages.find(
|
||||
(message) => message.runtimeTurnId === completedTurnId,
|
||||
);
|
||||
expect(continuedAssistant).toMatchObject({
|
||||
role: "assistant",
|
||||
content: continuedText,
|
||||
isThinking: false,
|
||||
});
|
||||
expect(continuedAssistant?.content).not.toContain("(已停止)");
|
||||
});
|
||||
|
||||
it("terminalReconcile detached canceled detail 应迁移 pending-turn 已停止终态", () => {
|
||||
const realTurnId = "turn-runtime-sync-detached-canceled-real";
|
||||
const promptText = "整理今天的国际新闻";
|
||||
|
||||
@@ -271,13 +271,16 @@ function collectLocalInterruptedMarkerTurnIds(options: {
|
||||
}
|
||||
}
|
||||
|
||||
pushTurnId(options.fallbackTurnId);
|
||||
for (
|
||||
let index = (options.incomingItems?.length ?? 0) - 1;
|
||||
index >= 0;
|
||||
index -= 1
|
||||
) {
|
||||
pushTurnId(readTimelineItemTurnId(options.incomingItems?.[index]));
|
||||
// 没有具体运行回合归属时,才从当前/入站时间线推断停止回合。
|
||||
if (interruptedTurnIds.size === 0) {
|
||||
pushTurnId(options.fallbackTurnId);
|
||||
for (
|
||||
let index = (options.incomingItems?.length ?? 0) - 1;
|
||||
index >= 0;
|
||||
index -= 1
|
||||
) {
|
||||
pushTurnId(readTimelineItemTurnId(options.incomingItems?.[index]));
|
||||
}
|
||||
}
|
||||
|
||||
return interruptedTurnIds;
|
||||
|
||||
Reference in New Issue
Block a user