Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0388fa0912 |
@@ -32,7 +32,7 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
|
|||||||
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
|
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
|
||||||
- Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either.
|
- Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either.
|
||||||
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
|
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
|
||||||
- Icon spritesheet generation requires an explicit `sliceMode` and has no default. Use `sliceMode="grid"` with the `gridX` and `gridY` the requirement actually names (1-32 each) only for equal grid cells or fixed slots; use `sliceMode="connected-components"` for free-form sheets or an open number of subjects, and constrain the count with `sliceCount` instead of inventing grid dimensions. `connected-components` must not carry `gridX`/`gridY`; an omitted, contradictory, or misapplied declaration returns 400 before billing.
|
- Icon spritesheet generation accepts `sliceMode="connected-components"` (default alpha-connectivity detection) or `sliceMode="grid"`. Grid mode requires `gridX` and `gridY` (1-32); use `sliceCount` only to constrain connected-component output.
|
||||||
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
|
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
|
||||||
- Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record.
|
- Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record.
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ For image edit/redraw, confirming an upload is not sufficient: create a project
|
|||||||
|
|
||||||
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
|
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
|
||||||
|
|
||||||
`sliceMode` is required and has no default, so every request must state it. Use `"connected-components"` to detect independent opaque regions by alpha connectivity, or `"grid"` with positive `gridX` and `gridY` values (maximum 32 each) only when the requirement names equal grid cells or fixed slots; the dimensions must come from that requirement. `connected-components` must not carry `gridX`/`gridY`, and `sliceCount` constrains the connected-component result instead of expressing a grid. Omitting `sliceMode`, or contradicting the declared mode with grid dimensions, returns 400 before pricing, enqueueing, or any provider call.
|
`sliceMode` controls atlas splitting. Use `"connected-components"` (default) to detect independent opaque regions by alpha connectivity, or `"grid"` with positive `gridX` and `gridY` values (maximum 32 each). `sliceCount` optionally constrains the connected-component result.
|
||||||
|
|
||||||
## Common Values
|
## Common Values
|
||||||
|
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ Keep the existing autonomous-build task graph. Do not add a parallel task system
|
|||||||
|
|
||||||
1. `art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context.
|
1. `art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context.
|
||||||
2. `design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`.
|
2. `design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`.
|
||||||
3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. `sliceMode` is required and has no default: send `sliceMode: "grid"` with `gridX`/`gridY` only when the requirement itself fixes the slots or names the column/row count, and otherwise send `sliceMode: "connected-components"` (with `sliceCount` when a subject count must be constrained); never invent a grid to express "kinds of assets", and never send `gridX`/`gridY` with `connected-components`.
|
3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. For a fixed four-category game contract it may send `sliceMode: "grid"`; for free-form assets use `sliceMode: "connected-components"` (the default).
|
||||||
|
|
||||||
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. When the requirement fixes grid slots, require the response `sliceMode` to match the declared `grid` request and exactly `gridX × gridY` slices before registering the local runtime sheet; a connected-components request is instead judged by its own `sliceCount` or by the requirement, and both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
|
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. When using the fixed four-category contract, require response `sliceMode: "grid"` and exactly four slices before registering the local runtime sheet; both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
|
||||||
|
|
||||||
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
|
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
|
||||||
|
|
||||||
|
|||||||
@@ -617,7 +617,6 @@ class GenarrativeExternalClient:
|
|||||||
self,
|
self,
|
||||||
reference_id: str,
|
reference_id: str,
|
||||||
icon_descriptions: list[str],
|
icon_descriptions: list[str],
|
||||||
slice_mode: str,
|
|
||||||
**fields: Any,
|
**fields: Any,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
reference_id = normalize_optional_text(reference_id)
|
reference_id = normalize_optional_text(reference_id)
|
||||||
@@ -626,20 +625,6 @@ class GenarrativeExternalClient:
|
|||||||
descriptions = [item.strip() for item in icon_descriptions if item.strip()]
|
descriptions = [item.strip() for item in icon_descriptions if item.strip()]
|
||||||
if not descriptions:
|
if not descriptions:
|
||||||
raise GenarrativeApiError("icon_descriptions must contain at least one non-empty item")
|
raise GenarrativeApiError("icon_descriptions must contain at least one non-empty item")
|
||||||
slice_mode = normalize_optional_text(slice_mode)
|
|
||||||
if slice_mode not in ("connected-components", "grid"):
|
|
||||||
raise GenarrativeApiError(
|
|
||||||
"slice_mode must be declared explicitly as 'connected-components' or 'grid'; the API has no default"
|
|
||||||
)
|
|
||||||
grid_x = fields.get("gridX")
|
|
||||||
grid_y = fields.get("gridY")
|
|
||||||
if slice_mode == "grid":
|
|
||||||
if grid_x is None or grid_y is None:
|
|
||||||
raise GenarrativeApiError("slice_mode='grid' requires both gridX and gridY")
|
|
||||||
elif grid_x is not None or grid_y is not None:
|
|
||||||
raise GenarrativeApiError(
|
|
||||||
"slice_mode='connected-components' must not carry gridX/gridY"
|
|
||||||
)
|
|
||||||
label = fields.get("assetLabel", "图标图集")
|
label = fields.get("assetLabel", "图标图集")
|
||||||
self._apply_canvas_session_fields(fields, label, 1024, 1024)
|
self._apply_canvas_session_fields(fields, label, 1024, 1024)
|
||||||
fields.setdefault("screenColor", "auto")
|
fields.setdefault("screenColor", "auto")
|
||||||
@@ -650,7 +635,6 @@ class GenarrativeExternalClient:
|
|||||||
**fields,
|
**fields,
|
||||||
"referenceId": reference_id,
|
"referenceId": reference_id,
|
||||||
"iconDescriptions": descriptions,
|
"iconDescriptions": descriptions,
|
||||||
"sliceMode": slice_mode,
|
|
||||||
},
|
},
|
||||||
idempotency_key=idempotency_key,
|
idempotency_key=idempotency_key,
|
||||||
)
|
)
|
||||||
@@ -895,9 +879,9 @@ def _self_test() -> None:
|
|||||||
client.generate_icon_spritesheet(
|
client.generate_icon_spritesheet(
|
||||||
"editor-resource-spec",
|
"editor-resource-spec",
|
||||||
["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"],
|
["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"],
|
||||||
"connected-components",
|
|
||||||
canvasSession=session,
|
canvasSession=session,
|
||||||
assetLabel="贪吃蛇透明图集",
|
assetLabel="贪吃蛇透明图集",
|
||||||
|
sliceMode="connected-components",
|
||||||
referenceId="must-not-override-explicit-reference",
|
referenceId="must-not-override-explicit-reference",
|
||||||
iconDescriptions=["不得覆盖显式图标描述"],
|
iconDescriptions=["不得覆盖显式图标描述"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -158,15 +158,6 @@ ALIYUN_OSS_POST_EXPIRE_SECONDS="600"
|
|||||||
ALIYUN_OSS_POST_MAX_SIZE_BYTES="20971520"
|
ALIYUN_OSS_POST_MAX_SIZE_BYTES="20971520"
|
||||||
ALIYUN_OSS_SUCCESS_ACTION_STATUS="200"
|
ALIYUN_OSS_SUCCESS_ACTION_STATUS="200"
|
||||||
|
|
||||||
# AGC 项目定时快照上传目标。对象只落在服务端私有前缀
|
|
||||||
# `agc/project-snapshots/v1/{user}/{project}/` 下,客户端直传票据不覆盖该前缀。
|
|
||||||
# bucket 与凭据可以与资源 bucket 分离;凭据未设置时回退使用 ALIYUN_OSS_ACCESS_KEY_*,
|
|
||||||
# 但 bucket / endpoint 默认指向 AGC 发行 bucket,需要该凭据具备目标 bucket 的 PutObject 权限。
|
|
||||||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_BUCKET="agc-dev"
|
|
||||||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ENDPOINT="oss-rg-china-mainland.aliyuncs.com"
|
|
||||||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_ID=""
|
|
||||||
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_SECRET=""
|
|
||||||
|
|
||||||
# BgFilter 受限资源 worker。父 api-server / external-generation-worker 与唯一的
|
# BgFilter 受限资源 worker。父 api-server / external-generation-worker 与唯一的
|
||||||
# `GENARRATIVE_PROCESS_ROLE=bgfilter-worker` 进程必须使用同一个内部 Token。
|
# `GENARRATIVE_PROCESS_ROLE=bgfilter-worker` 进程必须使用同一个内部 Token。
|
||||||
# `npm run dev` 与 `npm run dev:api-server` 都会自动带起并验活唯一 worker,不要再开第二个终端重复启动。
|
# `npm run dev` 与 `npm run dev:api-server` 都会自动带起并验活唯一 worker,不要再开第二个终端重复启动。
|
||||||
|
|||||||
@@ -41,14 +41,6 @@ temp*build*/
|
|||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-resources/
|
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-resources/
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-package.json
|
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-package.json
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/plugins/
|
/apps/ai-game-creator-shell/src-tauri/resources/plugins/
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/bin/
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/codex-path/
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/codex-resources/
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/codex-package.json
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/manifest.json
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/NOTICE.md
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/darwin-arm64/
|
|
||||||
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/darwin-x64/
|
|
||||||
/plugins/agc-cocos-editor/native/payload/
|
/plugins/agc-cocos-editor/native/payload/
|
||||||
/apps/ai-game-creator-shell/logs/
|
/apps/ai-game-creator-shell/logs/
|
||||||
/apps/ai-game-creator-shell/.llm-drafts/
|
/apps/ai-game-creator-shell/.llm-drafts/
|
||||||
|
|||||||
-16
@@ -172,20 +172,6 @@ _Avoid_: 多步骤向导、完整规则编辑器、拖拽编辑器
|
|||||||
Bark Battle 平台作品闭环按契约与领域规则、后端存储/API、最小前端纵切、投影体验、收口验证的顺序推进。
|
Bark Battle 平台作品闭环按契约与领域规则、后端存储/API、最小前端纵切、投影体验、收口验证的顺序推进。
|
||||||
_Avoid_: mock 先行堆积、前后端各自发散、先做排行榜 UI
|
_Avoid_: mock 先行堆积、前后端各自发散、先做排行榜 UI
|
||||||
|
|
||||||
## 项目开发对话(DirectProject)
|
|
||||||
|
|
||||||
**项目对话历史**:
|
|
||||||
AGC 本地项目内 Codex 原始对话条目的持久集合,是聊天展示、工具卡片和线程恢复注入的唯一持久事实源。
|
|
||||||
_Avoid_: 会话缓存、展示态历史、按 UI 需要另存的对话副本
|
|
||||||
|
|
||||||
**运行态事件**:
|
|
||||||
Thread Manager 向订阅者推送的当前回合原始事件流,只服务运行期间与短期断线恢复,不替代项目对话历史。
|
|
||||||
_Avoid_: 进度通知、快照轮询、第二套历史
|
|
||||||
|
|
||||||
**聊天投影**:
|
|
||||||
把项目对话历史条目与运行态事件转换成消息气泡和工具卡片的读取期转换;不持久化,也不构成事实源。
|
|
||||||
_Avoid_: 投影缓存文件、已脱敏卡片库、第二套 reducer
|
|
||||||
|
|
||||||
## Relationships
|
## Relationships
|
||||||
|
|
||||||
- 一个 **汪汪声浪大作战** 单局包含多个 **有效声浪触发**。
|
- 一个 **汪汪声浪大作战** 单局包含多个 **有效声浪触发**。
|
||||||
@@ -220,5 +206,3 @@ _Avoid_: 投影缓存文件、已脱敏卡片库、第二套 reducer
|
|||||||
- “入口闭环”曾可能只指内部 demo 或单个详情 CTA;已解析为 **正式作品入口闭环**,不新增独立专区或活动页。
|
- “入口闭环”曾可能只指内部 demo 或单个详情 CTA;已解析为 **正式作品入口闭环**,不新增独立专区或活动页。
|
||||||
- “创作编辑”曾可能指多步骤向导或完整编辑器;已解析为 **轻配置编辑流程**,使用单页表单 + 预览卡片完成保存草稿、发布和发布后跳转作品详情。
|
- “创作编辑”曾可能指多步骤向导或完整编辑器;已解析为 **轻配置编辑流程**,使用单页表单 + 预览卡片完成保存草稿、发布和发布后跳转作品详情。
|
||||||
- “实施顺序”曾可能按 UI 或功能并行发散;已解析为契约/领域规则先行,再做后端存储/API,随后打通最小前端纵切,最后补投影体验与收口验证。
|
- “实施顺序”曾可能按 UI 或功能并行发散;已解析为契约/领域规则先行,再做后端存储/API,随后打通最小前端纵切,最后补投影体验与收口验证。
|
||||||
- “回合进度事件”曾同时指 Direct turn update 与 Thread Manager 运行态事件;已解析为 AGC 项目开发对话只保留 **运行态事件**。
|
|
||||||
- “哪些消息可显示”曾可能由后端历史分页判断;已解析为可见性判断属于 **聊天投影**,后端只按原始条目分页,前端负责跳过不可显示条目并推进分页锚点。
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"autoCompactTokenLimit": 64000,
|
"autoCompactTokenLimit": 64000,
|
||||||
"toolOutputTokenLimit": 12000,
|
"toolOutputTokenLimit": 12000,
|
||||||
"requestTimeoutMs": 180000,
|
"requestTimeoutMs": 180000,
|
||||||
"maxRetries": 10,
|
"maxRetries": 2,
|
||||||
"retryBackoffMs": 500
|
"retryBackoffMs": 500
|
||||||
},
|
},
|
||||||
"agentLlm": {}
|
"agentLlm": {}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@genarrative/ai-game-creator-shell",
|
"name": "@genarrative/ai-game-creator-shell",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.67",
|
"version": "0.1.47",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/start-tauri-dev.mjs",
|
"dev": "node scripts/start-tauri-dev.mjs",
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"@tauri-apps/plugin-dialog": "^2.7.2",
|
"@tauri-apps/plugin-dialog": "^2.7.2",
|
||||||
"@tauri-apps/plugin-http": "^2.5.9",
|
"@tauri-apps/plugin-http": "^2.5.9",
|
||||||
"@tauri-apps/plugin-opener": "~2",
|
"@tauri-apps/plugin-opener": "~2",
|
||||||
"@tauri-apps/plugin-updater": "2.11.0",
|
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
"focus-trap-react": "^12.0.3",
|
"focus-trap-react": "^12.0.3",
|
||||||
"lexical": "^0.47.0",
|
"lexical": "^0.47.0",
|
||||||
@@ -58,10 +57,8 @@
|
|||||||
"react-colorful": "^5.8.0",
|
"react-colorful": "^5.8.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-window": "^1.8.11",
|
|
||||||
"rehype-highlight": "^7.0.2",
|
"rehype-highlight": "^7.0.2",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"three": "^0.184.0",
|
|
||||||
"vite": "^6.2.0",
|
"vite": "^6.2.0",
|
||||||
"zustand": "^5.0.14"
|
"zustand": "^5.0.14"
|
||||||
},
|
},
|
||||||
@@ -73,8 +70,6 @@
|
|||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/three": "^0.184.1",
|
|
||||||
"@types/react-window": "^1.8.8",
|
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.1.14",
|
||||||
"typescript": "~5.8.2",
|
"typescript": "~5.8.2",
|
||||||
"vitest": "^0.34.6"
|
"vitest": "^0.34.6"
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { execFileSync } from 'node:child_process';
|
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import os from 'node:os';
|
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
import { resolveReleaseContext, runTauriBuild } from './build-release.mjs';
|
|
||||||
|
|
||||||
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
||||||
const repoRoot = path.resolve(appRoot, '../..');
|
|
||||||
assert.equal(process.platform, 'darwin', '只能在 macOS Agent 执行');
|
|
||||||
assert.equal(
|
|
||||||
process.env.JENKINS_URL?.length > 0,
|
|
||||||
true,
|
|
||||||
'此入口仅用于 Jenkins 独立工作区',
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
fs.realpathSync(process.env.WORKSPACE || '.'),
|
|
||||||
fs.realpathSync(repoRoot),
|
|
||||||
'必须在 Jenkins workspace 根目录执行',
|
|
||||||
);
|
|
||||||
const space = fs.statfsSync(repoRoot);
|
|
||||||
assert.ok(
|
|
||||||
space.bavail * space.bsize >= 8 * 1024 ** 3,
|
|
||||||
'构建前至少需要 8 GiB 可用空间;禁止自动清理开发缓存',
|
|
||||||
);
|
|
||||||
|
|
||||||
// 本入口永不发布,不使用 Agent 用户可能持有的发布或 Apple 认证环境。
|
|
||||||
for (const key of Object.keys(process.env)) {
|
|
||||||
if (/^(TAURI_SIGNING_|APPLE_|AGC_OSS_)/u.test(key)) delete process.env[key];
|
|
||||||
}
|
|
||||||
process.env.RUSTC_WRAPPER = '';
|
|
||||||
process.env.CARGO_TARGET_DIR = path.join(appRoot, 'src-tauri/target');
|
|
||||||
const context = resolveReleaseContext(['--target=universal-apple-darwin']);
|
|
||||||
const args = [
|
|
||||||
'--target=universal-apple-darwin',
|
|
||||||
'--bundles',
|
|
||||||
'app',
|
|
||||||
'--ci',
|
|
||||||
'--no-sign',
|
|
||||||
'--config',
|
|
||||||
'{"bundle":{"createUpdaterArtifacts":false}}',
|
|
||||||
];
|
|
||||||
const command = (binary, argv, options = {}) =>
|
|
||||||
execFileSync(binary, argv, { cwd: repoRoot, stdio: 'inherit', ...options });
|
|
||||||
runTauriBuild(args, context);
|
|
||||||
const app = path.join(context.bundleRoot, 'macos/陶泥儿.app');
|
|
||||||
for (const architecture of ['arm64', 'x86_64']) {
|
|
||||||
command(process.execPath, [
|
|
||||||
path.join(appRoot, 'scripts/check-macos-bundle.mjs'),
|
|
||||||
app,
|
|
||||||
architecture,
|
|
||||||
'--universal',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
const version = JSON.parse(
|
|
||||||
fs.readFileSync(path.join(appRoot, 'package.json'), 'utf8'),
|
|
||||||
).version;
|
|
||||||
assert.match(version, /^\d+\.\d+\.\d+$/u);
|
|
||||||
const artifacts = path.join(repoRoot, 'artifacts');
|
|
||||||
// 只清理本 Job 的归档输出,不能把上次 DMG 当成本次成功产物。
|
|
||||||
fs.rmSync(artifacts, { recursive: true, force: true });
|
|
||||||
fs.mkdirSync(artifacts, { recursive: true });
|
|
||||||
const dmg = path.join(artifacts, `陶泥儿_${version}_universal.dmg`);
|
|
||||||
const stage = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-ci-dmg-'));
|
|
||||||
try {
|
|
||||||
command('ditto', [app, path.join(stage, '陶泥儿.app')]);
|
|
||||||
fs.symlinkSync('/Applications', path.join(stage, 'Applications'));
|
|
||||||
command('hdiutil', [
|
|
||||||
'create',
|
|
||||||
'-volname',
|
|
||||||
'陶泥儿',
|
|
||||||
'-srcfolder',
|
|
||||||
stage,
|
|
||||||
'-format',
|
|
||||||
'UDZO',
|
|
||||||
dmg,
|
|
||||||
]);
|
|
||||||
command('hdiutil', ['verify', dmg]);
|
|
||||||
} finally {
|
|
||||||
fs.rmSync(stage, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
const hash = createHash('sha256');
|
|
||||||
for await (const chunk of fs.createReadStream(dmg)) hash.update(chunk);
|
|
||||||
fs.writeFileSync(
|
|
||||||
`${dmg}.sha256`,
|
|
||||||
`${hash.digest('hex')} ${path.basename(dmg)}\n`,
|
|
||||||
);
|
|
||||||
const commit = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
||||||
cwd: repoRoot,
|
|
||||||
encoding: 'utf8',
|
|
||||||
}).trim();
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(artifacts, 'build-manifest.json'),
|
|
||||||
`${JSON.stringify(
|
|
||||||
{
|
|
||||||
version,
|
|
||||||
commit,
|
|
||||||
target: context.target,
|
|
||||||
channel: context.channel,
|
|
||||||
signed: false,
|
|
||||||
notarized: false,
|
|
||||||
uploaded: false,
|
|
||||||
smoke: ['arm64', 'x86_64'],
|
|
||||||
intelSmoke: process.arch === 'arm64' ? 'Rosetta' : 'native',
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
)}\n`,
|
|
||||||
);
|
|
||||||
console.log('[macOS CI] universal 包与校验文件已生成;未发布、未签名或公证');
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -35,12 +35,6 @@ const windowsTauriConfig = JSON.parse(
|
|||||||
'utf8',
|
'utf8',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const macosTauriConfig = JSON.parse(
|
|
||||||
fs.readFileSync(
|
|
||||||
new URL('../src-tauri/tauri.macos.conf.json', import.meta.url),
|
|
||||||
'utf8',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
const cargoManifestSource = fs.readFileSync(
|
const cargoManifestSource = fs.readFileSync(
|
||||||
new URL('../src-tauri/Cargo.toml', import.meta.url),
|
new URL('../src-tauri/Cargo.toml', import.meta.url),
|
||||||
'utf8',
|
'utf8',
|
||||||
@@ -127,10 +121,6 @@ const allowedUncalledTauriCommands = [
|
|||||||
'open_game_creator_launcher_window',
|
'open_game_creator_launcher_window',
|
||||||
'open_game_creator_workspace_window',
|
'open_game_creator_workspace_window',
|
||||||
'read_direct_project_conversation',
|
'read_direct_project_conversation',
|
||||||
// 项目定时快照上传只在 Rust 侧触发(周期定时器 / 工作区窗口关闭)与排障调用;
|
|
||||||
// 按产品口径不做客户端可见界面,因此同 `open_game_creator_*_window` 一样按 native-only 登记。
|
|
||||||
'read_local_project_snapshot_state',
|
|
||||||
'sync_local_project_snapshot',
|
|
||||||
'reset_design_agent_session',
|
'reset_design_agent_session',
|
||||||
'stop_local_game_preview_if_matches',
|
'stop_local_game_preview_if_matches',
|
||||||
'start_game_creator_external_mcp',
|
'start_game_creator_external_mcp',
|
||||||
@@ -1364,39 +1354,6 @@ if (windowsTauriConfig.bundle?.useLocalToolsDir !== true) {
|
|||||||
'AI game creator shell Windows Tauri config must cache bundling tools in the project target directory',
|
'AI game creator shell Windows Tauri config must cache bundling tools in the project target directory',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
assert.deepEqual(
|
|
||||||
macosTauriConfig.bundle?.resources,
|
|
||||||
Object.fromEntries([
|
|
||||||
...['darwin-arm64', 'darwin-x64'].flatMap((arch) =>
|
|
||||||
[
|
|
||||||
'bin/codex',
|
|
||||||
'bin/codex-code-mode-host',
|
|
||||||
'codex-path/rg',
|
|
||||||
'codex-resources/zsh/bin/zsh',
|
|
||||||
'codex-package.json',
|
|
||||||
'NOTICE.md',
|
|
||||||
'manifest.json',
|
|
||||||
].map((file) => [
|
|
||||||
`resources/codex/mac-native/${arch}/${file}`,
|
|
||||||
`coding-agent/mac-native/${arch}/${file}`,
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
['resources/plugins', 'plugins'],
|
|
||||||
]),
|
|
||||||
'macOS must bundle the complete native Codex layout and plugin workspace',
|
|
||||||
);
|
|
||||||
assert.deepEqual(
|
|
||||||
macosTauriConfig.plugins?.updater?.endpoints,
|
|
||||||
[
|
|
||||||
'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-mac/latest.json',
|
|
||||||
],
|
|
||||||
'macOS local builds must not use the Windows update channel',
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
macosTauriConfig.bundle?.macOS?.minimumSystemVersion,
|
|
||||||
'15.0',
|
|
||||||
'macOS deployment baseline must cover the bundled native zsh requirement',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tauriConfig.app?.withGlobalTauri !== true) {
|
if (tauriConfig.app?.withGlobalTauri !== true) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -1761,7 +1718,7 @@ for (const snippet of [
|
|||||||
'fn append_local_permission_log_at(',
|
'fn append_local_permission_log_at(',
|
||||||
'"command.auto"',
|
'"command.auto"',
|
||||||
'GameCreationAppPermission::Auto',
|
'GameCreationAppPermission::Auto',
|
||||||
'fn game_creator_bundled_codex_cli_path',
|
'GAME_CREATOR_BUNDLED_CODEX_CLI_RELATIVE_PATH',
|
||||||
'validate_game_creator_bundled_codex_cli',
|
'validate_game_creator_bundled_codex_cli',
|
||||||
'内置 Codex CLI 完整性校验失败',
|
'内置 Codex CLI 完整性校验失败',
|
||||||
]) {
|
]) {
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { spawn, spawnSync } from 'node:child_process';
|
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import os from 'node:os';
|
|
||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
// 只操作临时复制品;不启动 GUI、不读取开发机凭据、不访问 Provider。
|
|
||||||
assert.equal(process.platform, 'darwin', '此验证必须在 macOS 执行');
|
|
||||||
const source = path.resolve(process.argv[2] || '');
|
|
||||||
const architecture =
|
|
||||||
process.argv[3] || (process.arch === 'arm64' ? 'arm64' : 'x86_64');
|
|
||||||
assert.ok(
|
|
||||||
['arm64', 'x86_64'].includes(architecture),
|
|
||||||
'架构只接受 arm64 / x86_64',
|
|
||||||
);
|
|
||||||
const requireUniversal = process.argv.includes('--universal');
|
|
||||||
assert.ok(
|
|
||||||
source.endsWith('.app') && fs.statSync(source).isDirectory(),
|
|
||||||
'请传入 .app 绝对路径',
|
|
||||||
);
|
|
||||||
const root = fs.realpathSync(
|
|
||||||
fs.mkdtempSync(path.join(os.tmpdir(), 'agc-macos-bundle-')),
|
|
||||||
);
|
|
||||||
const app = path.join(root, '陶泥儿 隔离测试.app');
|
|
||||||
const home = path.join(root, 'home');
|
|
||||||
const config = path.join(root, 'config');
|
|
||||||
const tmp = path.join(root, 'tmp');
|
|
||||||
const codexHome = path.join(root, 'codex-home');
|
|
||||||
for (const directory of [home, config, tmp, codexHome]) {
|
|
||||||
fs.mkdirSync(directory, { mode: 0o700 });
|
|
||||||
}
|
|
||||||
const env = {
|
|
||||||
HOME: home,
|
|
||||||
PATH: '/usr/bin:/bin',
|
|
||||||
TMPDIR: tmp,
|
|
||||||
CODEX_HOME: codexHome,
|
|
||||||
};
|
|
||||||
|
|
||||||
function run(command, args) {
|
|
||||||
// 只强制被测应用切片;本机 Xcode 检查工具可能仅提供宿主架构。
|
|
||||||
const useSlice = command.startsWith(`${app}${path.sep}`);
|
|
||||||
const result = spawnSync(
|
|
||||||
useSlice ? '/usr/bin/arch' : command,
|
|
||||||
useSlice ? [`-${architecture}`, command, ...args] : args,
|
|
||||||
{
|
|
||||||
cwd: root,
|
|
||||||
env,
|
|
||||||
encoding: 'utf8',
|
|
||||||
timeout: 120_000,
|
|
||||||
maxBuffer: 1024 * 1024,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert.ifError(result.error);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function hashFile(file) {
|
|
||||||
const hash = createHash('sha256');
|
|
||||||
for await (const chunk of fs.createReadStream(file)) hash.update(chunk);
|
|
||||||
return hash.digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handshake(executable) {
|
|
||||||
const child = spawn(executable, ['app-server'], {
|
|
||||||
cwd: root,
|
|
||||||
env,
|
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
|
||||||
});
|
|
||||||
let buffered = '';
|
|
||||||
let stderrBytes = 0;
|
|
||||||
try {
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const timer = setTimeout(
|
|
||||||
() => reject(new Error('app-server 初始化超时')),
|
|
||||||
120_000,
|
|
||||||
);
|
|
||||||
const finish = (error) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
if (error) reject(error);
|
|
||||||
else resolve();
|
|
||||||
};
|
|
||||||
child.on('error', finish);
|
|
||||||
child.on('exit', (code) =>
|
|
||||||
finish(new Error(`app-server 提前退出 ${code}`)),
|
|
||||||
);
|
|
||||||
child.stderr.on('data', (chunk) => {
|
|
||||||
stderrBytes += chunk.length;
|
|
||||||
if (stderrBytes > 1024 * 1024)
|
|
||||||
finish(new Error('app-server stderr 超限'));
|
|
||||||
});
|
|
||||||
child.stdout.on('data', (chunk) => {
|
|
||||||
buffered += chunk.toString('utf8');
|
|
||||||
if (buffered.length > 1024 * 1024)
|
|
||||||
return finish(new Error('app-server stdout 超限'));
|
|
||||||
let end;
|
|
||||||
while ((end = buffered.indexOf('\n')) >= 0) {
|
|
||||||
const line = buffered.slice(0, end);
|
|
||||||
buffered = buffered.slice(end + 1);
|
|
||||||
try {
|
|
||||||
const message = JSON.parse(line);
|
|
||||||
if (message.id !== 1) continue;
|
|
||||||
assert.ok(message.result?.userAgent, '初始化必须返回真实服务身份');
|
|
||||||
assert.equal(message.error, undefined);
|
|
||||||
child.stdin.write(`${JSON.stringify({ method: 'initialized' })}\n`);
|
|
||||||
finish();
|
|
||||||
} catch (error) {
|
|
||||||
finish(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
child.stdin.on('error', finish);
|
|
||||||
child.stdin.write(
|
|
||||||
`${JSON.stringify({
|
|
||||||
id: 1,
|
|
||||||
method: 'initialize',
|
|
||||||
params: {
|
|
||||||
clientInfo: {
|
|
||||||
name: 'agc_bundle_smoke',
|
|
||||||
title: 'AGC bundle smoke',
|
|
||||||
version: '1',
|
|
||||||
},
|
|
||||||
capabilities: { experimentalApi: true },
|
|
||||||
},
|
|
||||||
})}\n`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
if (child.exitCode === null && child.signalCode === null) {
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
const timer = setTimeout(() => child.kill('SIGKILL'), 3000);
|
|
||||||
child.once('exit', () => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
child.kill('SIGTERM');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.cpSync(source, app, { recursive: true });
|
|
||||||
const resources = path.join(app, 'Contents/Resources');
|
|
||||||
const platform = architecture === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
|
|
||||||
const bundle = path.join(resources, 'coding-agent/mac-native', platform);
|
|
||||||
const executable = path.join(bundle, 'bin/codex');
|
|
||||||
const main = path.join(
|
|
||||||
app,
|
|
||||||
'Contents/MacOS/genarrative-ai-game-creator-shell',
|
|
||||||
);
|
|
||||||
const mainArchitectures = run('/usr/bin/lipo', ['-archs', main]);
|
|
||||||
assert.equal(mainArchitectures.status, 0);
|
|
||||||
assert.ok(mainArchitectures.stdout.split(/\s+/).includes(architecture));
|
|
||||||
if (requireUniversal) {
|
|
||||||
assert.deepEqual(mainArchitectures.stdout.trim().split(/\s+/).sort(), [
|
|
||||||
'arm64',
|
|
||||||
'x86_64',
|
|
||||||
]);
|
|
||||||
for (const platform of ['darwin-arm64', 'darwin-x64']) {
|
|
||||||
assert.ok(
|
|
||||||
fs.existsSync(
|
|
||||||
path.join(
|
|
||||||
resources,
|
|
||||||
'coding-agent/mac-native',
|
|
||||||
platform,
|
|
||||||
'manifest.json',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const manifest = JSON.parse(
|
|
||||||
fs.readFileSync(path.join(bundle, 'manifest.json'), 'utf8'),
|
|
||||||
);
|
|
||||||
assert.equal(manifest.schemaVersion, 'genarrative-codex-sidecar.v2');
|
|
||||||
assert.equal(manifest.platform, platform);
|
|
||||||
assert.equal(manifest.version, 'codex-cli 0.147.0');
|
|
||||||
const components = [
|
|
||||||
'bin/codex',
|
|
||||||
'bin/codex-code-mode-host',
|
|
||||||
'codex-path/rg',
|
|
||||||
'codex-resources/zsh/bin/zsh',
|
|
||||||
'codex-package.json',
|
|
||||||
];
|
|
||||||
assert.deepEqual(Object.keys(manifest.files).sort(), [...components].sort());
|
|
||||||
for (const component of components) {
|
|
||||||
const file = path.join(bundle, component);
|
|
||||||
assert.equal(await hashFile(file), manifest.files[component], component);
|
|
||||||
if (component !== 'codex-package.json') {
|
|
||||||
fs.accessSync(file, fs.constants.X_OK);
|
|
||||||
const arch = run('/usr/bin/lipo', ['-archs', file]);
|
|
||||||
assert.equal(arch.status, 0, component);
|
|
||||||
assert.equal(arch.stdout.trim(), architecture, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.ok(fs.existsSync(path.join(bundle, 'NOTICE.md')));
|
|
||||||
const plugin = path.join(resources, 'plugins/agc-cocos-editor');
|
|
||||||
for (const file of [
|
|
||||||
'plugin.json',
|
|
||||||
'src/entry.mjs',
|
|
||||||
'panels/cocos-editor.html',
|
|
||||||
]) {
|
|
||||||
assert.ok(fs.existsSync(path.join(plugin, file)), file);
|
|
||||||
}
|
|
||||||
const packageFiles = fs.readdirSync(resources, { recursive: true });
|
|
||||||
assert.ok(
|
|
||||||
!packageFiles.some((file) =>
|
|
||||||
/(^|\/)(\.env[^/]*|auth\.json|node_modules|target|\.git)(\/|$)|\.(exe|dll)$/.test(
|
|
||||||
file,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
assert.equal(run(executable, ['--version']).stdout.trim(), manifest.version);
|
|
||||||
assert.equal(
|
|
||||||
run(path.join(bundle, 'codex-path/rg'), ['--version']).status,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
run(path.join(bundle, 'codex-resources/zsh/bin/zsh'), ['--version']).status,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 使用正式 AGC 查找/校验入口,而非只证明 sidecar 可以独立执行。
|
|
||||||
const status = run(main, ['--config-dir', config, '--llm-status']);
|
|
||||||
const statusText = `${status.stdout}\n${status.stderr}`;
|
|
||||||
assert.ok(!statusText.includes('Codex CLI 未安装'), statusText);
|
|
||||||
assert.ok(
|
|
||||||
statusText.includes('authentication-required'),
|
|
||||||
'隔离账号应仅被登录门禁拒绝',
|
|
||||||
);
|
|
||||||
await handshake(executable);
|
|
||||||
|
|
||||||
// 临时复制品缺少辅助程序时,正式入口必须拒绝内置程序;PATH 无全局 Codex 可兜底。
|
|
||||||
fs.renameSync(
|
|
||||||
path.join(bundle, 'bin/codex-code-mode-host'),
|
|
||||||
path.join(root, 'saved-code-mode-host'),
|
|
||||||
);
|
|
||||||
const broken = run(main, ['--config-dir', config, '--llm-status']);
|
|
||||||
assert.notEqual(broken.status, 0);
|
|
||||||
assert.match(`${broken.stdout}\n${broken.stderr}`, /Codex CLI 未安装/);
|
|
||||||
console.log(
|
|
||||||
`PASS (${architecture}): 隔离安装包资源、架构、摘要、权限、正式 Codex 查找、app-server 握手及缺组件拒绝`,
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'未验证:GUI、真实登录/Provider 对话、Cocos macOS 原生桥接;插件 Node 仍为外部前提',
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
fs.rmSync(root, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
@@ -724,7 +724,6 @@ function canvasAssetCall(agentId) {
|
|||||||
assetKind: 'art-spritesheet',
|
assetKind: 'art-spritesheet',
|
||||||
assetLabel: '游戏首版核心美术素材',
|
assetLabel: '游戏首版核心美术素材',
|
||||||
replaceExisting: false,
|
replaceExisting: false,
|
||||||
sliceMode: 'connected-components',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2692,7 +2691,7 @@ function createDeterministicCanvasFixture(apiKey) {
|
|||||||
'deterministic spritesheet fixture',
|
'deterministic spritesheet fixture',
|
||||||
model: 'deterministic-canvas-v1',
|
model: 'deterministic-canvas-v1',
|
||||||
provider: 'deterministic-loopback',
|
provider: 'deterministic-loopback',
|
||||||
sliceMode: 'connected-components',
|
sliceLayout: 'grid-2x2',
|
||||||
spritesheetResource: {
|
spritesheetResource: {
|
||||||
resourceId,
|
resourceId,
|
||||||
projectId,
|
projectId,
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* `agc` 开发启动下发给 Vite 的客户端特性开关默认值。
|
|
||||||
*
|
|
||||||
* 开发态默认关闭客户端更新检查:`npm run agc` / `agc:serve` 启动的客户端不请求 OSS
|
|
||||||
* 更新清单,也不显示更新入口。需要联调更新流程时显式传
|
|
||||||
* `VITE_AGC_ENABLE_APP_UPDATE_CHECK=1`;此处不覆盖已经显式配置的取值。
|
|
||||||
*/
|
|
||||||
const agcAppUpdateCheckEnvKey = 'VITE_AGC_ENABLE_APP_UPDATE_CHECK';
|
|
||||||
|
|
||||||
function withAgcDevFeatureFlags(env = process.env) {
|
|
||||||
if (String(env[agcAppUpdateCheckEnvKey] ?? '').trim()) {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...env,
|
|
||||||
[agcAppUpdateCheckEnvKey]: '0',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { agcAppUpdateCheckEnvKey, withAgcDevFeatureFlags };
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { execFileSync } from 'node:child_process';
|
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
||||||
const repoRoot = path.resolve(appRoot, '../..');
|
|
||||||
const platforms = {
|
|
||||||
arm64: 'aarch64-apple-darwin',
|
|
||||||
x64: 'x86_64-apple-darwin',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function lockedMacPackage(lock, arch, version) {
|
|
||||||
assert.ok(Object.hasOwn(platforms, arch), '未知 macOS 架构');
|
|
||||||
const alias = `@openai/codex-darwin-${arch}`;
|
|
||||||
const entry = lock.packages?.[`node_modules/${alias}`];
|
|
||||||
assert.equal(
|
|
||||||
entry?.version,
|
|
||||||
`${version}-darwin-${arch}`,
|
|
||||||
'原生依赖必须与应用锁定版本一致',
|
|
||||||
);
|
|
||||||
assert.deepEqual(entry.os, ['darwin']);
|
|
||||||
assert.deepEqual(entry.cpu, [arch]);
|
|
||||||
const url = new URL(entry.resolved);
|
|
||||||
assert.equal(url.protocol, 'https:');
|
|
||||||
assert.equal(
|
|
||||||
url.hostname,
|
|
||||||
'registry.npmjs.org',
|
|
||||||
'只下载锁定的官方 npm 原生包',
|
|
||||||
);
|
|
||||||
assert.equal(url.username + url.password + url.search + url.hash, '');
|
|
||||||
assert.match(entry.integrity, /^sha512-[A-Za-z0-9+/]+={0,2}$/);
|
|
||||||
return { alias, target: platforms[arch], ...entry };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function verifyPackageIntegrity(bytes, expected) {
|
|
||||||
const actual = `sha512-${createHash('sha512').update(bytes).digest('base64')}`;
|
|
||||||
assert.equal(actual, expected, 'Codex 下载包 lockfile integrity 不匹配');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateArchiveListing(listing) {
|
|
||||||
const files = listing.trim().split(/\r?\n/u);
|
|
||||||
assert.ok(files.length > 0);
|
|
||||||
for (const file of files) {
|
|
||||||
assert.ok(file.startsWith('package/'), '原生包必须只有 package 根目录');
|
|
||||||
assert.ok(
|
|
||||||
!file.split('/').includes('..') && !file.includes('\\'),
|
|
||||||
'压缩包路径不安全',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function prepareMacosCodex() {
|
|
||||||
assert.equal(process.platform, 'darwin', '该入口仅用于 macOS 构建机');
|
|
||||||
const lock = JSON.parse(
|
|
||||||
fs.readFileSync(path.join(repoRoot, 'package-lock.json'), 'utf8'),
|
|
||||||
);
|
|
||||||
const app = JSON.parse(
|
|
||||||
fs.readFileSync(path.join(appRoot, 'package.json'), 'utf8'),
|
|
||||||
);
|
|
||||||
const version = app.devDependencies['@openai/codex'];
|
|
||||||
assert.match(version, /^\d+\.\d+\.\d+$/u, 'Codex 必须锁定精确版本');
|
|
||||||
const cache = path.join(appRoot, 'src-tauri/target/.macos-native-cache');
|
|
||||||
fs.mkdirSync(cache, { recursive: true });
|
|
||||||
for (const arch of Object.keys(platforms)) {
|
|
||||||
const entry = lockedMacPackage(lock, arch, version);
|
|
||||||
const archive = path.join(cache, `codex-${entry.version}.tgz`);
|
|
||||||
if (!fs.existsSync(archive)) {
|
|
||||||
const response = await fetch(entry.resolved, {
|
|
||||||
signal: AbortSignal.timeout(300_000),
|
|
||||||
});
|
|
||||||
assert.ok(response.ok, `原生包下载失败 HTTP ${response.status}`);
|
|
||||||
const bytes = Buffer.from(await response.arrayBuffer());
|
|
||||||
verifyPackageIntegrity(bytes, entry.integrity);
|
|
||||||
const partial = `${archive}.${process.pid}.tmp`;
|
|
||||||
fs.writeFileSync(partial, bytes);
|
|
||||||
fs.renameSync(partial, archive);
|
|
||||||
}
|
|
||||||
verifyPackageIntegrity(fs.readFileSync(archive), entry.integrity);
|
|
||||||
validateArchiveListing(
|
|
||||||
execFileSync('tar', ['-tzf', archive], { encoding: 'utf8' }),
|
|
||||||
);
|
|
||||||
// 拒绝链接、设备及其它特殊条目,不能让 tar 在包目录之外写入。
|
|
||||||
const entries = execFileSync('tar', ['-tvzf', archive], {
|
|
||||||
encoding: 'utf8',
|
|
||||||
});
|
|
||||||
assert.ok(
|
|
||||||
entries
|
|
||||||
.trim()
|
|
||||||
.split(/\r?\n/u)
|
|
||||||
.every((line) => /^[-d]/u.test(line)),
|
|
||||||
'原生包禁止链接或特殊文件',
|
|
||||||
);
|
|
||||||
const parent = path.join(repoRoot, 'node_modules/@openai');
|
|
||||||
fs.mkdirSync(parent, { recursive: true });
|
|
||||||
const stage = fs.mkdtempSync(path.join(parent, '.mac-native-'));
|
|
||||||
try {
|
|
||||||
execFileSync(
|
|
||||||
'tar',
|
|
||||||
['-xzf', archive, '-C', stage, '--strip-components=1'],
|
|
||||||
{ stdio: 'pipe' },
|
|
||||||
);
|
|
||||||
const metadata = JSON.parse(
|
|
||||||
fs.readFileSync(
|
|
||||||
path.join(stage, 'vendor', entry.target, 'codex-package.json'),
|
|
||||||
'utf8',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
assert.equal(metadata.version, version);
|
|
||||||
assert.equal(metadata.target, entry.target);
|
|
||||||
assert.equal(metadata.entrypoint, 'bin/codex');
|
|
||||||
const destination = path.join(repoRoot, 'node_modules', entry.alias);
|
|
||||||
assert.ok(
|
|
||||||
!fs.existsSync(destination) ||
|
|
||||||
!fs.lstatSync(destination).isSymbolicLink(),
|
|
||||||
'拒绝覆盖链接依赖',
|
|
||||||
);
|
|
||||||
fs.rmSync(destination, { recursive: true, force: true });
|
|
||||||
fs.renameSync(stage, destination);
|
|
||||||
} finally {
|
|
||||||
fs.rmSync(stage, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
console.log(`[macOS Codex] ${entry.version}: lockfile integrity 已验证`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
process.argv[1] &&
|
|
||||||
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
||||||
) {
|
|
||||||
await prepareMacosCodex();
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { createHash } from 'node:crypto';
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import { test } from 'node:test';
|
|
||||||
|
|
||||||
import {
|
|
||||||
lockedMacPackage,
|
|
||||||
validateArchiveListing,
|
|
||||||
verifyPackageIntegrity,
|
|
||||||
} from './prepare-macos-codex.mjs';
|
|
||||||
|
|
||||||
const lock = JSON.parse(
|
|
||||||
fs.readFileSync(new URL('../../../package-lock.json', import.meta.url)),
|
|
||||||
);
|
|
||||||
const version = JSON.parse(
|
|
||||||
fs.readFileSync(new URL('../package.json', import.meta.url)),
|
|
||||||
).devDependencies['@openai/codex'];
|
|
||||||
|
|
||||||
test('both macOS dependencies resolve from the lockfile without floating versions', () => {
|
|
||||||
assert.equal(
|
|
||||||
lockedMacPackage(lock, 'arm64', version).target,
|
|
||||||
'aarch64-apple-darwin',
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
lockedMacPackage(lock, 'x64', version).target,
|
|
||||||
'x86_64-apple-darwin',
|
|
||||||
);
|
|
||||||
assert.throws(() => lockedMacPackage(lock, 'other', version));
|
|
||||||
assert.throws(() => lockedMacPackage(lock, 'x64', '0.0.0'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('native package integrity rejects tampering', () => {
|
|
||||||
const bytes = Buffer.from('pinned package');
|
|
||||||
const integrity = `sha512-${createHash('sha512').update(bytes).digest('base64')}`;
|
|
||||||
verifyPackageIntegrity(bytes, integrity);
|
|
||||||
assert.throws(() =>
|
|
||||||
verifyPackageIntegrity(Buffer.from('modified'), integrity),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('archive traversal and non-package entries fail closed', () => {
|
|
||||||
validateArchiveListing(
|
|
||||||
'package/package.json\npackage/vendor/target/bin/codex\n',
|
|
||||||
);
|
|
||||||
for (const listing of [
|
|
||||||
'',
|
|
||||||
'/tmp/payload',
|
|
||||||
'package/../private',
|
|
||||||
'other/file',
|
|
||||||
'package/..\\file',
|
|
||||||
]) {
|
|
||||||
assert.throws(() => validateArchiveListing(listing));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('CI pipeline is manual archive-only and does not reuse a developer workspace', () => {
|
|
||||||
const pipeline = fs.readFileSync(
|
|
||||||
new URL(
|
|
||||||
'../../../jenkins/Jenkinsfile.ai-game-creator-shell-macos-build',
|
|
||||||
import.meta.url,
|
|
||||||
),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
for (const required of [
|
|
||||||
'genarrative-agc-macos',
|
|
||||||
'disableConcurrentBuilds()',
|
|
||||||
'$AGC_AGENT_ROOT',
|
|
||||||
'StrictHostKeyChecking=yes',
|
|
||||||
'git merge-base --is-ancestor',
|
|
||||||
'allowEmptyArchive: false',
|
|
||||||
]) {
|
|
||||||
assert.ok(pipeline.includes(required), required);
|
|
||||||
}
|
|
||||||
for (const forbidden of [
|
|
||||||
'triggers {',
|
|
||||||
'cron(',
|
|
||||||
'pollSCM(',
|
|
||||||
'release:upload',
|
|
||||||
'AgcUpdaterSigningKey',
|
|
||||||
'AliyunAccessKeyId',
|
|
||||||
'git clean -fdx',
|
|
||||||
]) {
|
|
||||||
assert.ok(!pipeline.includes(forbidden), forbidden);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* 发布上传的 OSS 命令行整理:把 ossutil 参数与凭据整理成可执行或可打印的形式,
|
|
||||||
* 便于在 dry-run 下核对将要执行的上传,同时保证任何输出都不回显凭据明文。
|
|
||||||
*/
|
|
||||||
const redactedCredential = '<redacted>';
|
|
||||||
|
|
||||||
export function readReleaseDryRun(env = process.env) {
|
|
||||||
const value = env.AGC_RELEASE_DRY_RUN?.trim().toLowerCase();
|
|
||||||
return value === '1' || value === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
function quoteArgument(value) {
|
|
||||||
return /[\s"']/u.test(value) ? JSON.stringify(value) : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatOssutilCommand({ binary, args, endpoint, credentials }) {
|
|
||||||
const parts = [binary, ...args, '--endpoint', endpoint];
|
|
||||||
if (credentials) {
|
|
||||||
parts.push(
|
|
||||||
'--access-key-id',
|
|
||||||
redactedCredential,
|
|
||||||
'--access-key-secret',
|
|
||||||
redactedCredential,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return parts.map(quoteArgument).join(' ');
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { readFileSync } from 'node:fs';
|
|
||||||
import { test } from 'node:test';
|
|
||||||
|
|
||||||
import { formatOssutilCommand, readReleaseDryRun } from './release-oss.mjs';
|
|
||||||
|
|
||||||
test('dry run only accepts explicit truthy values', () => {
|
|
||||||
assert.equal(readReleaseDryRun({}), false);
|
|
||||||
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '1' }), true);
|
|
||||||
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: ' true ' }), true);
|
|
||||||
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '0' }), false);
|
|
||||||
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '' }), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('printed upload command keeps arguments and hides credentials', () => {
|
|
||||||
const command = formatOssutilCommand({
|
|
||||||
binary: 'ossutil',
|
|
||||||
args: [
|
|
||||||
'cp',
|
|
||||||
'--force',
|
|
||||||
'陶泥儿 0.1.48.exe',
|
|
||||||
'oss://agc-dev/agc/dev-win/x.exe',
|
|
||||||
],
|
|
||||||
endpoint: 'oss-rg-china-mainland.aliyuncs.com',
|
|
||||||
credentials: true,
|
|
||||||
});
|
|
||||||
assert.match(command, /^ossutil cp --force /u);
|
|
||||||
assert.match(command, /"陶泥儿 0\.1\.48\.exe"/u);
|
|
||||||
assert.match(command, /oss:\/\/agc-dev\/agc\/dev-win\/x\.exe/u);
|
|
||||||
assert.match(
|
|
||||||
command,
|
|
||||||
/--access-key-id <redacted> --access-key-secret <redacted>/u,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('uploader gates every ossutil call behind the dry run switch', () => {
|
|
||||||
const source = readFileSync(
|
|
||||||
new URL('./release-upload.mjs', import.meta.url),
|
|
||||||
'utf8',
|
|
||||||
);
|
|
||||||
assert.match(source, /const dryRun = readReleaseDryRun\(\);/u);
|
|
||||||
assert.match(source, /if \(dryRun\) \{/u);
|
|
||||||
assert.match(source, /dry-run:未写入任何 OSS 对象/u);
|
|
||||||
});
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import { formatOssutilCommand, readReleaseDryRun } from './release-oss.mjs';
|
|
||||||
|
|
||||||
const bucket = process.env.AGC_OSS_BUCKET?.trim() || 'agc-dev';
|
const bucket = process.env.AGC_OSS_BUCKET?.trim() || 'agc-dev';
|
||||||
const endpoint =
|
const endpoint =
|
||||||
process.env.AGC_OSS_ENDPOINT?.trim() || 'oss-rg-china-mainland.aliyuncs.com';
|
process.env.AGC_OSS_ENDPOINT?.trim() || 'oss-rg-china-mainland.aliyuncs.com';
|
||||||
@@ -10,9 +8,9 @@ if (!/^[a-z0-9][a-z0-9.-]{1,62}$/u.test(bucket) || /[\r\n\0]/u.test(endpoint)) {
|
|||||||
throw new Error('OSS bucket 或 endpoint 配置无效');
|
throw new Error('OSS bucket 或 endpoint 配置无效');
|
||||||
}
|
}
|
||||||
process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`;
|
process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`;
|
||||||
const dryRun = readReleaseDryRun();
|
|
||||||
|
|
||||||
const { buildRelease } = await import('./build-release.mjs');
|
const { generateUpdateManifest, prepareReleaseVersion, runTauriBuild } =
|
||||||
|
await import('./build-release.mjs');
|
||||||
|
|
||||||
function runOssutil(args) {
|
function runOssutil(args) {
|
||||||
const binary = process.env.OSSUTIL_BIN?.trim() || 'ossutil';
|
const binary = process.env.OSSUTIL_BIN?.trim() || 'ossutil';
|
||||||
@@ -21,18 +19,6 @@ function runOssutil(args) {
|
|||||||
if (Boolean(accessKeyId) !== Boolean(accessKeySecret)) {
|
if (Boolean(accessKeyId) !== Boolean(accessKeySecret)) {
|
||||||
throw new Error('OSS AccessKey ID 和 Secret 必须同时提供');
|
throw new Error('OSS AccessKey ID 和 Secret 必须同时提供');
|
||||||
}
|
}
|
||||||
if (dryRun) {
|
|
||||||
// 演练:只打印将要执行的上传,凭据以占位符呈现,不写入 OSS。
|
|
||||||
console.log(
|
|
||||||
`[dry-run] ${formatOssutilCommand({
|
|
||||||
binary,
|
|
||||||
args,
|
|
||||||
endpoint,
|
|
||||||
credentials: Boolean(accessKeyId),
|
|
||||||
})}`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const credentialArgs = accessKeyId
|
const credentialArgs = accessKeyId
|
||||||
? ['--access-key-id', accessKeyId, '--access-key-secret', accessKeySecret]
|
? ['--access-key-id', accessKeyId, '--access-key-secret', accessKeySecret]
|
||||||
: [];
|
: [];
|
||||||
@@ -50,40 +36,13 @@ function runOssutil(args) {
|
|||||||
if (result.status !== 0) process.exit(result.status ?? 1);
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { artifact, channel, legacyManifestPath, manifest, manifestPath } =
|
await prepareReleaseVersion();
|
||||||
await buildRelease(process.argv.slice(2));
|
runTauriBuild([]);
|
||||||
const artifactKey = `agc/${channel}/${manifest.version}/${path.basename(artifact)}`;
|
const { artifact, manifestPath, manifest } = generateUpdateManifest();
|
||||||
|
const artifactKey = `agc/${manifest.version}/${path.basename(artifact)}`;
|
||||||
// Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过;
|
// Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过;
|
||||||
// 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。
|
// 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。
|
||||||
runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]);
|
runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]);
|
||||||
runOssutil([
|
runOssutil(['cp', '--force', manifestPath, `oss://${bucket}/agc/latest.json`]);
|
||||||
'cp',
|
|
||||||
'--force',
|
|
||||||
`${artifact}.sig`,
|
|
||||||
`oss://${bucket}/${artifactKey}.sig`,
|
|
||||||
]);
|
|
||||||
runOssutil([
|
|
||||||
'cp',
|
|
||||||
'--force',
|
|
||||||
manifestPath,
|
|
||||||
`oss://${bucket}/agc/${channel}/latest.json`,
|
|
||||||
]);
|
|
||||||
console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`);
|
console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`);
|
||||||
console.log(
|
console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/agc/latest.json`);
|
||||||
`[ai-game-creator-shell] 已上传 oss://${bucket}/agc/${channel}/latest.json`,
|
|
||||||
);
|
|
||||||
if (legacyManifestPath) {
|
|
||||||
// 迁移桥:让仍走旧 sha256 清单的已发布客户端升级到新协议,一个版本周期后删除。
|
|
||||||
runOssutil([
|
|
||||||
'cp',
|
|
||||||
'--force',
|
|
||||||
legacyManifestPath,
|
|
||||||
`oss://${bucket}/agc/latest.json`,
|
|
||||||
]);
|
|
||||||
console.log(
|
|
||||||
`[ai-game-creator-shell] 已上传迁移指针 oss://${bucket}/agc/latest.json`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (dryRun) {
|
|
||||||
console.log('[ai-game-creator-shell] dry-run:未写入任何 OSS 对象');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import http from 'node:http';
|
|||||||
import net from 'node:net';
|
import net from 'node:net';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
import { withAgcDevFeatureFlags } from './dev-feature-flags.mjs';
|
|
||||||
import { resolveAgcDevEndpoint, withAgcDevEndpointEnv } from './dev-port.mjs';
|
import { resolveAgcDevEndpoint, withAgcDevEndpointEnv } from './dev-port.mjs';
|
||||||
|
|
||||||
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
const appRoot = fileURLToPath(new URL('..', import.meta.url));
|
||||||
@@ -105,7 +104,7 @@ const child = spawn(
|
|||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: appRoot,
|
cwd: appRoot,
|
||||||
env: withAgcDevFeatureFlags(withAgcDevEndpointEnv(endpoint)),
|
env: withAgcDevEndpointEnv(endpoint),
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
// Node 18.20+/20+/24 on Windows rejects spawning .cmd (npm.cmd) without a shell (EINVAL).
|
// Node 18.20+/20+/24 on Windows rejects spawning .cmd (npm.cmd) without a shell (EINVAL).
|
||||||
shell: true,
|
shell: true,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
stopWindowsProcessTree,
|
stopWindowsProcessTree,
|
||||||
stopWindowsWorktreeProcesses,
|
stopWindowsWorktreeProcesses,
|
||||||
} from '../../../scripts/dev-windows-process.mjs';
|
} from '../../../scripts/dev-windows-process.mjs';
|
||||||
import { withAgcDevFeatureFlags } from './dev-feature-flags.mjs';
|
|
||||||
import {
|
import {
|
||||||
agcVitePortEnvKey,
|
agcVitePortEnvKey,
|
||||||
readAgcDevEndpoint,
|
readAgcDevEndpoint,
|
||||||
@@ -979,10 +978,7 @@ async function startVite(apiTarget, endpoint = readAgcDevEndpoint()) {
|
|||||||
'--port',
|
'--port',
|
||||||
String(endpoint.port),
|
String(endpoint.port),
|
||||||
],
|
],
|
||||||
{
|
{ cwd: appRoot, env: withAgcDevEndpointEnv(endpoint) },
|
||||||
cwd: appRoot,
|
|
||||||
env: withAgcDevFeatureFlags(withAgcDevEndpointEnv(endpoint)),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
import assert from 'node:assert/strict';
|
|
||||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
||||||
import { tmpdir } from 'node:os';
|
|
||||||
import { dirname, join } from 'node:path';
|
|
||||||
import { test } from 'node:test';
|
|
||||||
import { setTimeout as delay } from 'node:timers/promises';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
import { createServer, loadConfigFromFile, normalizePath } from 'vite';
|
|
||||||
|
|
||||||
test(
|
|
||||||
'AGC 排除 Rust 构建目录且保留源码与共享组件监听',
|
|
||||||
{ timeout: 30_000 },
|
|
||||||
async () => {
|
|
||||||
const loaded = await loadConfigFromFile(
|
|
||||||
{ command: 'serve', mode: 'development' },
|
|
||||||
fileURLToPath(new URL('../vite.config.ts', import.meta.url)),
|
|
||||||
);
|
|
||||||
assert.ok(loaded);
|
|
||||||
assert.notEqual(loaded.config.server?.watch, null);
|
|
||||||
assert.notEqual(loaded.config.server?.hmr, false);
|
|
||||||
assert.ok(
|
|
||||||
[loaded.config.server?.watch?.ignored]
|
|
||||||
.flat()
|
|
||||||
.includes('**/src-tauri/target/**'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const fixture = await mkdtemp(join(tmpdir(), 'agc-vite-watch-'));
|
|
||||||
const root = join(fixture, 'apps', 'ai-game-creator-shell');
|
|
||||||
const source = join(root, 'src', 'main.js');
|
|
||||||
const css = join(root, 'src', 'styles.css');
|
|
||||||
const shared = join(fixture, 'packages', 'shared', 'src', 'component.js');
|
|
||||||
const target = join(root, 'src-tauri', 'target');
|
|
||||||
const artifact = join(target, 'debug', 'incremental', 'cache.bin');
|
|
||||||
let server;
|
|
||||||
try {
|
|
||||||
for (const file of [source, css, shared, artifact]) {
|
|
||||||
await mkdir(dirname(file), { recursive: true });
|
|
||||||
await writeFile(
|
|
||||||
file,
|
|
||||||
file === css ? 'body { color: red; }' : 'export default 1;',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 使用真实 Vite watcher 和实际配置,仅将扫描根替换为小型夹具;
|
|
||||||
// 不加载业务插件、后端或原生窗口,也不扫描开发机上的大型 target。
|
|
||||||
server = await createServer({
|
|
||||||
configFile: false,
|
|
||||||
envFile: false,
|
|
||||||
root,
|
|
||||||
logLevel: 'silent',
|
|
||||||
server: {
|
|
||||||
watch: loaded.config.server?.watch,
|
|
||||||
middlewareMode: true,
|
|
||||||
hmr: false,
|
|
||||||
fs: { allow: [fixture] },
|
|
||||||
},
|
|
||||||
optimizeDeps: { noDiscovery: true, include: [] },
|
|
||||||
});
|
|
||||||
const waitForWatchedFile = async (file) => {
|
|
||||||
const normalized = normalizePath(file);
|
|
||||||
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
||||||
if (
|
|
||||||
Object.entries(server.watcher.getWatched()).some(
|
|
||||||
([directory, names]) =>
|
|
||||||
names.some(
|
|
||||||
(name) => normalizePath(join(directory, name)) === normalized,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
await delay(50);
|
|
||||||
}
|
|
||||||
assert.fail(`源码必须仍被监听:${normalized}`);
|
|
||||||
};
|
|
||||||
await waitForWatchedFile(source);
|
|
||||||
|
|
||||||
// 真实模块转换应将 root 外的共享源码加入监听。
|
|
||||||
await server.transformRequest(`/@fs/${normalizePath(shared)}`);
|
|
||||||
for (const file of [source, css, shared]) {
|
|
||||||
const normalized = normalizePath(file);
|
|
||||||
await waitForWatchedFile(file);
|
|
||||||
const changed = new Promise((resolve, reject) => {
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
server.watcher.off('change', onChange);
|
|
||||||
reject(new Error(`未收到源码变更:${normalized}`));
|
|
||||||
}, 5_000);
|
|
||||||
function onChange(path) {
|
|
||||||
if (normalizePath(path) !== normalized) return;
|
|
||||||
clearTimeout(timer);
|
|
||||||
server.watcher.off('change', onChange);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
server.watcher.on('change', onChange);
|
|
||||||
});
|
|
||||||
await writeFile(
|
|
||||||
file,
|
|
||||||
file === css ? 'body { color: blue; }' : 'export default 2;',
|
|
||||||
);
|
|
||||||
await changed;
|
|
||||||
}
|
|
||||||
const targetPath = normalizePath(target);
|
|
||||||
const targetDirectories = Object.keys(server.watcher.getWatched())
|
|
||||||
.map(normalizePath)
|
|
||||||
.filter(
|
|
||||||
(path) => path === targetPath || path.startsWith(`${targetPath}/`),
|
|
||||||
);
|
|
||||||
assert.deepEqual(targetDirectories, [], 'Rust target 不应创建目录监听器');
|
|
||||||
} finally {
|
|
||||||
await server?.close();
|
|
||||||
await rm(fixture, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
+12
-240
@@ -814,16 +814,6 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@@ -847,7 +837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types 0.5.0",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -860,7 +850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1415,16 +1405,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filetime"
|
|
||||||
version = "0.2.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -1745,7 +1725,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "genarrative-ai-game-creator-shell"
|
name = "genarrative-ai-game-creator-shell"
|
||||||
version = "0.1.67"
|
version = "0.1.47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"agent-runtime-core",
|
"agent-runtime-core",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1767,6 +1747,7 @@ dependencies = [
|
|||||||
"oxc_parser",
|
"oxc_parser",
|
||||||
"oxc_semantic",
|
"oxc_semantic",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
|
"percent-encoding",
|
||||||
"platform-agent",
|
"platform-agent",
|
||||||
"platform-llm",
|
"platform-llm",
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
@@ -1785,7 +1766,6 @@ dependencies = [
|
|||||||
"tauri-plugin-dialog",
|
"tauri-plugin-dialog",
|
||||||
"tauri-plugin-http",
|
"tauri-plugin-http",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
"tauri-plugin-updater",
|
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.2",
|
"toml 0.8.2",
|
||||||
@@ -1796,7 +1776,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
"zip 2.4.2",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2240,11 +2220,9 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"system-configuration",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-registry",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2528,36 +2506,6 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jni"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"combine",
|
|
||||||
"jni-macros",
|
|
||||||
"jni-sys 0.4.1",
|
|
||||||
"log",
|
|
||||||
"simd_cesu8",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"walkdir",
|
|
||||||
"windows-link 0.2.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jni-macros"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustc_version",
|
|
||||||
"simd_cesu8",
|
|
||||||
"syn 2.0.118",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2871,12 +2819,6 @@ dependencies = [
|
|||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "minisign-verify"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -3292,18 +3234,6 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-osa-kit"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.13.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-app-kit",
|
|
||||||
"objc2-foundation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-quartz-core"
|
name = "objc2-quartz-core"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -3448,20 +3378,6 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "osakit"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
|
||||||
dependencies = [
|
|
||||||
"objc2",
|
|
||||||
"objc2-foundation",
|
|
||||||
"objc2-osa-kit",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "outref"
|
name = "outref"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -4444,20 +4360,15 @@ dependencies = [
|
|||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-rustls",
|
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustls",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"rustls-platform-verifier",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -4571,33 +4482,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-platform-verifier"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation 0.10.1",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"jni 0.22.4",
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"rustls",
|
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-platform-verifier-android",
|
|
||||||
"rustls-webpki",
|
|
||||||
"security-framework",
|
|
||||||
"security-framework-sys",
|
|
||||||
"webpki-root-certs",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-platform-verifier-android"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.13"
|
version = "0.103.13"
|
||||||
@@ -4725,7 +4609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
@@ -5068,22 +4952,6 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simd_cesu8"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
|
|
||||||
dependencies = [
|
|
||||||
"rustc_version",
|
|
||||||
"simdutf8",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simdutf8"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "similar"
|
name = "similar"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
@@ -5307,27 +5175,6 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "system-configuration"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.13.0",
|
|
||||||
"core-foundation 0.9.4",
|
|
||||||
"system-configuration-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "system-configuration-sys"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-deps"
|
name = "system-deps"
|
||||||
version = "6.2.2"
|
version = "6.2.2"
|
||||||
@@ -5349,7 +5196,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.0",
|
||||||
"block2",
|
"block2",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dbus",
|
"dbus",
|
||||||
@@ -5359,7 +5206,7 @@ dependencies = [
|
|||||||
"gdkwayland-sys",
|
"gdkwayland-sys",
|
||||||
"gdkx11-sys",
|
"gdkx11-sys",
|
||||||
"gtk",
|
"gtk",
|
||||||
"jni 0.21.1",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk",
|
"ndk",
|
||||||
@@ -5392,17 +5239,6 @@ dependencies = [
|
|||||||
"syn 2.0.118",
|
"syn 2.0.118",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tar"
|
|
||||||
version = "0.4.46"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
|
|
||||||
dependencies = [
|
|
||||||
"filetime",
|
|
||||||
"libc",
|
|
||||||
"xattr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
@@ -5426,7 +5262,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"http",
|
"http",
|
||||||
"jni 0.21.1",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
@@ -5641,39 +5477,6 @@ dependencies = [
|
|||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tauri-plugin-updater"
|
|
||||||
version = "2.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b28d8cabdeb0564f03ae261963de4bc3d98321cd3d213e76a81b7d344e5df606"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"dirs",
|
|
||||||
"flate2",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"infer",
|
|
||||||
"log",
|
|
||||||
"minisign-verify",
|
|
||||||
"osakit",
|
|
||||||
"percent-encoding",
|
|
||||||
"reqwest 0.13.4",
|
|
||||||
"rustls",
|
|
||||||
"semver",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tar",
|
|
||||||
"tauri",
|
|
||||||
"tauri-plugin",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"url",
|
|
||||||
"windows-sys 0.60.2",
|
|
||||||
"zip 4.6.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.11.3"
|
version = "2.11.3"
|
||||||
@@ -5684,7 +5487,7 @@ dependencies = [
|
|||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni 0.21.1",
|
"jni",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-ui-kit",
|
"objc2-ui-kit",
|
||||||
"objc2-web-kit",
|
"objc2-web-kit",
|
||||||
@@ -5707,7 +5510,7 @@ checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni 0.21.1",
|
"jni",
|
||||||
"log",
|
"log",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
@@ -6783,15 +6586,6 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-root-certs"
|
|
||||||
version = "1.0.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
|
|
||||||
dependencies = [
|
|
||||||
"rustls-pki-types",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
@@ -7398,7 +7192,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"javascriptcore-rs",
|
"javascriptcore-rs",
|
||||||
"jni 0.21.1",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
"ndk",
|
"ndk",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -7462,16 +7256,6 @@ version = "0.13.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xattr"
|
|
||||||
version = "1.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rustix",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -7653,18 +7437,6 @@ dependencies = [
|
|||||||
"zopfli",
|
"zopfli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zip"
|
|
||||||
version = "4.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"crc32fast",
|
|
||||||
"indexmap 2.14.0",
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user