@harness-mix/cli 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/NOTICE +12 -0
- package/README.md +74 -71
- package/docs/harness-management.md +1 -1
- package/docs/native-acp.md +7 -2
- package/docs/task-handoff-design.md +65 -0
- package/licenses/codex-styler-Apache-2.0.txt +193 -0
- package/licenses/heige-codex-skin-studio-MIT.txt +22 -0
- package/output/native-build/desktop-controller.mjs +3 -2
- package/output/native-build/renderer-extension.js +1350 -46
- package/package.json +189 -188
- package/scripts/acp-capabilities-probe.cjs +1 -0
- package/scripts/build-native.cjs +1 -1
- package/scripts/codex-adapter-test.cjs +44 -0
- package/scripts/collaboration-recovery-test.cjs +3 -0
- package/scripts/e2e-codex.cjs +3 -1
- package/scripts/e2e-native-acp.cjs +1 -1
- package/scripts/error-kind-test.cjs +107 -0
- package/scripts/handoff-checkpoint-test.cjs +38 -0
- package/scripts/integrations-test.cjs +5 -2
- package/scripts/kiro-cursor-adapters-test.cjs +18 -0
- package/scripts/native-acp-depth-test.cjs +1 -1
- package/scripts/native-config-test.cjs +32 -2
- package/scripts/native-protocol-test.cjs +60 -4
- package/scripts/native-ui-smoke.cjs +127 -2
- package/scripts/pets-test.cjs +146 -0
- package/scripts/switch-harness-test.cjs +79 -12
- package/scripts/tool-concurrency-regression-test.cjs +8 -6
- package/src/assets/icons/cline-color.svg +1 -0
- package/src/assets/skins/codex-styler/ASSET_LICENSES.md +36 -0
- package/src/assets/skins/codex-styler/NOTICE +11 -0
- package/src/assets/skins/codex-styler/themes/gilded-grandeur/hero.webp +0 -0
- package/src/assets/skins/codex-styler/themes/merry-big-top/hero.webp +0 -0
- package/src/assets/skins/codex-styler/themes/nocturne-studio/hero.webp +0 -0
- package/src/assets/skins/codex-styler/themes/quiet-garden/hero.webp +0 -0
- package/src/assets/skins/heige/ASSET_PROVENANCE.md +56 -0
- package/src/assets/skins/heige/NOTICE.md +13 -0
- package/src/assets/skins/heige/themes/dalao-dianyan/hero.webp +0 -0
- package/src/assets/skins/heige/themes/dalao-dianyan/theme.json +14 -0
- package/src/assets/skins/heige/themes/deepspace-dawn/hero.webp +0 -0
- package/src/assets/skins/heige/themes/deepspace-dawn/theme.json +14 -0
- package/src/assets/skins/heige/themes/deepspace-star/hero.webp +0 -0
- package/src/assets/skins/heige/themes/deepspace-star/theme.json +14 -0
- package/src/assets/skins/heige/themes/dragonball-nimbus/hero.webp +0 -0
- package/src/assets/skins/heige/themes/dragonball-nimbus/theme.json +16 -0
- package/src/assets/skins/heige/themes/dragonball-super-saiyan/hero.webp +0 -0
- package/src/assets/skins/heige/themes/dragonball-super-saiyan/theme.json +16 -0
- package/src/assets/skins/heige/themes/genshin-dawn/hero.webp +0 -0
- package/src/assets/skins/heige/themes/genshin-dawn/theme.json +14 -0
- package/src/assets/skins/heige/themes/genshin-night/hero.webp +0 -0
- package/src/assets/skins/heige/themes/genshin-night/theme.json +14 -0
- package/src/assets/skins/heige/themes/miku-488137/hero.webp +0 -0
- package/src/assets/skins/heige/themes/miku-488137/logo.webp +0 -0
- package/src/assets/skins/heige/themes/miku-488137/polaroid.webp +0 -0
- package/src/assets/skins/heige/themes/miku-488137/theme.json +16 -0
- package/src/assets/skins/heige/themes/naruto-hokage/hero.webp +0 -0
- package/src/assets/skins/heige/themes/naruto-hokage/theme.json +14 -0
- package/src/assets/skins/heige/themes/naruto-sasuke/hero.webp +0 -0
- package/src/assets/skins/heige/themes/naruto-sasuke/theme.json +14 -0
- package/src/assets/skins/heige/themes/wuthering-echo/hero.webp +0 -0
- package/src/assets/skins/heige/themes/wuthering-echo/theme.json +14 -0
- package/src/assets/skins/heige/themes/wuthering-tide/hero.webp +0 -0
- package/src/assets/skins/heige/themes/wuthering-tide/theme.json +14 -0
- package/src/main/adapters/cline.js +22 -0
- package/src/main/adapters/codex.js +78 -8
- package/src/main/adapters/index.js +2 -1
- package/src/main/adapters/native-acp-command.js +1 -0
- package/src/main/harness-adapter/error-kind.js +82 -0
- package/src/main/harness-adapter/event-normalizer.js +7 -1
- package/src/main/host/core-session.js +1 -1
- package/src/main/host/handoff-access.js +23 -4
- package/src/main/host/handoff-checkpoints.js +9 -2
- package/src/main/host/handoff-tools.js +3 -0
- package/src/main/host/handoff.js +11 -2
- package/src/main/host/runtime.js +272 -163
- package/src/main/host/session-history.js +54 -15
- package/src/main/native/icons.js +3 -2
- package/src/main/native/launcher.js +49 -2
- package/src/main/native/pets.js +395 -0
- package/src/main/native/protocol.js +197 -55
- package/src/main/protocol-core/projector.js +44 -41
- package/src/native-ui/desktop-control/dist/production-controller.d.ts.map +1 -1
- package/src/native-ui/desktop-control/dist/production-controller.js +1 -0
- package/src/native-ui/desktop-control/dist/production-controller.js.map +1 -1
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js +4 -4
- package/src/native-ui/desktop-control/dist/renderer-draft-prewarm-runtime.js.map +1 -1
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/desktop-control/src/production-controller.ts +1 -0
- package/src/native-ui/desktop-control/src/renderer-draft-prewarm-runtime.ts +4 -4
- package/src/native-ui/desktop-control/test/production-controller.test.ts +1 -0
- package/src/native-ui/desktop-control/test/renderer-draft-prewarm-policy.test.ts +7 -0
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts +5 -2
- package/src/native-ui/renderer-extension/dist/types/agent-selection-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-icon.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +2 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-sidebar-agent-icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/connections-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +5 -3
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts +5 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pet-market.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts +57 -0
- package/src/native-ui/renderer-extension/dist/types/settings/pets-client.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/shell.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/settings/skin-market.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts +31 -0
- package/src/native-ui/renderer-extension/dist/types/settings/skin-runtime.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/versioned-renderer-adapter.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/package.json +1 -1
- package/src/native-ui/renderer-extension/src/agent-selection-state.ts +61 -14
- package/src/native-ui/renderer-extension/src/assets.d.ts +5 -0
- package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +5 -4
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +1 -0
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +78 -33
- package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +2 -2
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +2 -0
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +6 -0
- package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +5 -0
- package/src/native-ui/renderer-extension/src/renderer-sidebar-agent-icons.ts +1 -0
- package/src/native-ui/renderer-extension/src/settings/connections-page.ts +2 -0
- package/src/native-ui/renderer-extension/src/settings/icons.ts +15 -6
- package/src/native-ui/renderer-extension/src/settings/localization.ts +6 -2
- package/src/native-ui/renderer-extension/src/settings/pages.ts +72 -61
- package/src/native-ui/renderer-extension/src/settings/pet-market.css +310 -0
- package/src/native-ui/renderer-extension/src/settings/pet-market.ts +447 -0
- package/src/native-ui/renderer-extension/src/settings/pets-client.ts +74 -0
- package/src/native-ui/renderer-extension/src/settings/shell.css +27 -8
- package/src/native-ui/renderer-extension/src/settings/shell.ts +3 -1
- package/src/native-ui/renderer-extension/src/settings/skin-market.css +213 -0
- package/src/native-ui/renderer-extension/src/settings/skin-market.ts +174 -0
- package/src/native-ui/renderer-extension/src/settings/skin-runtime.ts +630 -0
- package/src/native-ui/renderer-extension/src/versioned-renderer-adapter.ts +2 -1
- package/src/native-ui/renderer-extension/test/agent-selection-state.test.ts +58 -0
- package/src/native-ui/renderer-extension/test/renderer-binding-probe.test.ts +12 -0
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +1 -0
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +12 -1
- package/src/native-ui/renderer-extension/test/settings/shell.test.ts +6 -0
- package/src/native-ui/renderer-extension/test/settings/skin-runtime.test.ts +162 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +7 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +3 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +3 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { createPetMarket, readAsarHeader } = require('../src/main/native/pets');
|
|
8
|
+
|
|
9
|
+
function buildSyntheticAsar(targetPath, petName, blobContent) {
|
|
10
|
+
const tree = {
|
|
11
|
+
files: {
|
|
12
|
+
webview: {
|
|
13
|
+
files: {
|
|
14
|
+
assets: {
|
|
15
|
+
files: {
|
|
16
|
+
[`${petName}-spritesheet-v1-0123456789abcdef.webp`]: {
|
|
17
|
+
size: blobContent.length,
|
|
18
|
+
offset: '0',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const jsonBuf = Buffer.from(JSON.stringify(tree), 'utf8');
|
|
27
|
+
const jsonLen = jsonBuf.length;
|
|
28
|
+
const padLen = (4 - (jsonLen % 4)) % 4;
|
|
29
|
+
const pickleSize = 8 + jsonLen + padLen;
|
|
30
|
+
const head = Buffer.alloc(16);
|
|
31
|
+
head.writeUInt32LE(4, 0);
|
|
32
|
+
head.writeUInt32LE(pickleSize, 4);
|
|
33
|
+
head.writeUInt32LE(jsonLen + 4, 8);
|
|
34
|
+
head.writeUInt32LE(jsonLen, 12);
|
|
35
|
+
const asarBuf = Buffer.concat([head, jsonBuf, Buffer.alloc(padLen), blobContent]);
|
|
36
|
+
fs.writeFileSync(targetPath, asarBuf);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
(async () => {
|
|
40
|
+
const testRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-mix-pets-test-'));
|
|
41
|
+
const fakeAsarDir = path.join(testRoot, 'resources');
|
|
42
|
+
fs.mkdirSync(fakeAsarDir, { recursive: true });
|
|
43
|
+
const fakeAsarPath = path.join(fakeAsarDir, 'app.asar');
|
|
44
|
+
const fakeCodexBin = path.join(fakeAsarDir, 'codex.exe');
|
|
45
|
+
fs.writeFileSync(fakeCodexBin, '');
|
|
46
|
+
|
|
47
|
+
const fakeSpriteBlob = Buffer.from('RIFF....WEBPVP8 ...fake-sprite-data...', 'utf8');
|
|
48
|
+
buildSyntheticAsar(fakeAsarPath, 'synthetic-buddy', fakeSpriteBlob);
|
|
49
|
+
|
|
50
|
+
const petsDir = path.join(testRoot, 'installed-pets');
|
|
51
|
+
fs.mkdirSync(petsDir, { recursive: true });
|
|
52
|
+
|
|
53
|
+
const env = {
|
|
54
|
+
CODEXHOST_STOCK_CODEX_PATH: fakeCodexBin,
|
|
55
|
+
HARNESS_MIX_PETS_DIR: petsDir,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// 1. Verify readAsarHeader
|
|
60
|
+
const asarInfo = readAsarHeader(fakeAsarPath);
|
|
61
|
+
assert.ok(asarInfo.blobBase > 16, 'asar blobBase should be past header');
|
|
62
|
+
const assetFiles = asarInfo.tree?.files?.webview?.files?.assets?.files;
|
|
63
|
+
assert.ok(assetFiles && typeof assetFiles === 'object', 'asset tree found');
|
|
64
|
+
|
|
65
|
+
// 2. Test createPetMarket catalog()
|
|
66
|
+
const market = createPetMarket({ env });
|
|
67
|
+
const catalog = market.catalog();
|
|
68
|
+
assert.equal(catalog.officialAvailable, true, 'synthetic asar should be available');
|
|
69
|
+
assert.equal(catalog.petsDir, petsDir, 'petsDir matches env override');
|
|
70
|
+
|
|
71
|
+
const officialPet = catalog.data.find(p => p.id === 'synthetic-buddy');
|
|
72
|
+
assert.ok(officialPet, 'synthetic-buddy should be present in catalog');
|
|
73
|
+
assert.equal(officialPet.source, 'official');
|
|
74
|
+
assert.equal(officialPet.installed, false);
|
|
75
|
+
assert.equal(officialPet.displayName, 'Synthetic Buddy');
|
|
76
|
+
|
|
77
|
+
// Curated community pets should also be listed
|
|
78
|
+
const curatedSample = catalog.data.find(p => p.id === 'komi-shouko-pixel');
|
|
79
|
+
assert.ok(curatedSample, 'curated community pet komi-shouko-pixel should be present');
|
|
80
|
+
assert.equal(curatedSample.installed, false);
|
|
81
|
+
|
|
82
|
+
// 3. Test preview() for official pet
|
|
83
|
+
const previewRes = market.preview({ id: 'synthetic-buddy' });
|
|
84
|
+
assert.equal(previewRes.id, 'synthetic-buddy');
|
|
85
|
+
assert.equal(previewRes.mime, 'image/webp');
|
|
86
|
+
const previewBuf = Buffer.from(previewRes.dataBase64, 'base64');
|
|
87
|
+
assert.equal(previewBuf.toString('utf8'), fakeSpriteBlob.toString('utf8'), 'preview base64 should match sprite blob');
|
|
88
|
+
|
|
89
|
+
// Invalid pet id checks
|
|
90
|
+
assert.throws(() => market.preview({ id: '../bad-pet' }), /Invalid pet id/);
|
|
91
|
+
assert.throws(() => market.preview({ id: 'non-existent-pet' }), /Unknown pet/);
|
|
92
|
+
|
|
93
|
+
// 4. Test install() of official pet
|
|
94
|
+
const installRes = await market.install({ id: 'synthetic-buddy' });
|
|
95
|
+
assert.equal(installRes.id, 'synthetic-buddy');
|
|
96
|
+
assert.equal(installRes.installed, true);
|
|
97
|
+
assert.equal(installRes.alreadyInstalled, false);
|
|
98
|
+
|
|
99
|
+
const installedDir = path.join(petsDir, 'synthetic-buddy');
|
|
100
|
+
assert.ok(fs.existsSync(installedDir), 'pet directory created');
|
|
101
|
+
const petJsonPath = path.join(installedDir, 'pet.json');
|
|
102
|
+
const petSpritePath = path.join(installedDir, 'spritesheet.webp');
|
|
103
|
+
assert.ok(fs.existsSync(petJsonPath), 'pet.json exists');
|
|
104
|
+
assert.ok(fs.existsSync(petSpritePath), 'spritesheet.webp exists');
|
|
105
|
+
|
|
106
|
+
const meta = JSON.parse(fs.readFileSync(petJsonPath, 'utf8'));
|
|
107
|
+
assert.equal(meta.id, 'synthetic-buddy');
|
|
108
|
+
assert.equal(meta.displayName, 'Synthetic Buddy');
|
|
109
|
+
assert.equal(meta.spritesheetPath, 'spritesheet.webp');
|
|
110
|
+
|
|
111
|
+
// Installing again should be a no-op returning alreadyInstalled: true
|
|
112
|
+
const reinstallRes = await market.install({ id: 'synthetic-buddy' });
|
|
113
|
+
assert.equal(reinstallRes.alreadyInstalled, true);
|
|
114
|
+
|
|
115
|
+
// Catalog should now reflect installed: true
|
|
116
|
+
const updatedCatalog = market.catalog();
|
|
117
|
+
const installedEntry = updatedCatalog.data.find(p => p.id === 'synthetic-buddy');
|
|
118
|
+
assert.ok(installedEntry, 'installed pet present');
|
|
119
|
+
assert.equal(installedEntry.installed, true);
|
|
120
|
+
assert.equal(installedEntry.source, 'installed');
|
|
121
|
+
|
|
122
|
+
// 5. Test community() fallback and search
|
|
123
|
+
const communityRes = await market.community({ search: 'komi' });
|
|
124
|
+
assert.ok(communityRes.total >= 1);
|
|
125
|
+
assert.equal(communityRes.data[0].id, 'komi-shouko-pixel');
|
|
126
|
+
|
|
127
|
+
// 6. Test uninstall()
|
|
128
|
+
const uninstallRes = market.uninstall({ id: 'synthetic-buddy' });
|
|
129
|
+
assert.equal(uninstallRes.id, 'synthetic-buddy');
|
|
130
|
+
assert.equal(uninstallRes.removed, true);
|
|
131
|
+
assert.ok(!fs.existsSync(installedDir), 'pet directory should be deleted');
|
|
132
|
+
|
|
133
|
+
// Calling uninstall again should return removed: false
|
|
134
|
+
const uninstallAgain = market.uninstall({ id: 'synthetic-buddy' });
|
|
135
|
+
assert.equal(uninstallAgain.removed, false);
|
|
136
|
+
|
|
137
|
+
// Catalog after uninstall reflects installed: false
|
|
138
|
+
const catalogAfterUninstall = market.catalog();
|
|
139
|
+
const uninstalledEntry = catalogAfterUninstall.data.find(p => p.id === 'synthetic-buddy');
|
|
140
|
+
assert.equal(uninstalledEntry.installed, false);
|
|
141
|
+
|
|
142
|
+
console.log('Pets market native tests passed successfully!');
|
|
143
|
+
} finally {
|
|
144
|
+
fs.rmSync(testRoot, { recursive: true, force: true });
|
|
145
|
+
}
|
|
146
|
+
})();
|
|
@@ -2,7 +2,7 @@ const assert = require('node:assert/strict');
|
|
|
2
2
|
const fs = require('node:fs/promises');
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { HostRuntime } = require('../src/main/host/runtime');
|
|
5
|
-
const { CAP, buildHandoffContext } = require('../src/main/host/handoff');
|
|
5
|
+
const { CAP, buildHandoffContext, composeHandoffEnvelope } = require('../src/main/host/handoff');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 跨 Harness 原地切换(switchHarness)脚本级测试:
|
|
@@ -13,15 +13,18 @@ const { CAP, buildHandoffContext } = require('../src/main/host/handoff');
|
|
|
13
13
|
const wait = async fn => { for (let i = 0; i < 300; i++) { if (fn()) return; await new Promise(r => setTimeout(r, 10)); } throw new Error('Timed out'); };
|
|
14
14
|
|
|
15
15
|
function fakeHarness(id, name) {
|
|
16
|
-
const state = { sent: [], opens: [], closed: 0, hold: null, withFileChange: false };
|
|
16
|
+
const state = { sent: [], opens: [], closed: 0, hold: null, withFileChange: false, openError: null, openWait: null, sendError: null };
|
|
17
17
|
const adapter = {
|
|
18
18
|
manifest: { id, name, aliases: [id], capabilities: {} },
|
|
19
19
|
async open(input) {
|
|
20
20
|
state.opens.push({ restore: input.thread.restore === true, nativeSessionId: input.thread.nativeSessionId, nativeSessionFile: input.thread.nativeSessionFile });
|
|
21
|
+
if (state.openWait) await state.openWait;
|
|
22
|
+
if (state.openError) throw new Error(state.openError);
|
|
21
23
|
return { nativeSessionId: input.thread.nativeSessionId };
|
|
22
24
|
},
|
|
23
25
|
async send(session, text, hooks) {
|
|
24
26
|
state.sent.push(text);
|
|
27
|
+
if (state.sendError) throw new Error(state.sendError);
|
|
25
28
|
if (state.hold) { state.hold.hooks = hooks; return; } // 挂起:Turn 保持 running,等待测试手动 settle
|
|
26
29
|
if (state.withFileChange) hooks.emit({ kind: 'file-change', changes: [{ path: 'src/login.ts', before: '', after: 'x', complete: true, changeType: 'added' }] });
|
|
27
30
|
hooks.emit({ kind: 'text-delta', text: `${name} 回复` });
|
|
@@ -46,6 +49,8 @@ async function main() {
|
|
|
46
49
|
assert.deepEqual(optimized.conversationTail.map(message => message.role), ['user', 'assistant', 'user', 'assistant']);
|
|
47
50
|
assert.equal(optimized.conversationTail.at(-1).text, longSolution, '最新方案应获得更大的交接预算');
|
|
48
51
|
assert.ok(optimized.conversationTail.reduce((sum, message) => sum + message.text.length, 0) <= CAP.totalChars);
|
|
52
|
+
assert.match(composeHandoffEnvelope({ fromHarnessId: 'a', context: {}, intent: 'review' }), /Do not modify files/);
|
|
53
|
+
assert.match(composeHandoffEnvelope({ fromHarnessId: 'a', context: {}, intent: 'reanalyze' }), /Reanalyze the task independently/);
|
|
49
54
|
|
|
50
55
|
await fs.mkdir('output', { recursive: true });
|
|
51
56
|
const root = await fs.mkdtemp(path.resolve('output/switch-harness-'));
|
|
@@ -90,17 +95,21 @@ async function main() {
|
|
|
90
95
|
const checkpointBeforeSend = rt.handoffs.get(thread.id, firstCheckpointId);
|
|
91
96
|
assert.equal(checkpointBeforeSend.intent, 'continue');
|
|
92
97
|
assert.equal(checkpointBeforeSend.fileState.files.some(file => file.path === 'src/login.ts' && file.changeType === 'added'), true);
|
|
93
|
-
assert.equal(rt.handoffs.owned(thread.id, firstCheckpointId).status, '
|
|
94
|
-
assert.equal(
|
|
95
|
-
assert.equal(
|
|
98
|
+
assert.equal(rt.handoffs.owned(thread.id, firstCheckpointId).status, 'ready');
|
|
99
|
+
assert.equal(b.state.opens[0].restore, false, '新 Harness 上是全新原生会话');
|
|
100
|
+
assert.equal(thread.restore, undefined, '目标 Session 打开后清理瞬态 restore 标记');
|
|
101
|
+
assert.equal(thread.pendingHandoff.phase, 'ready');
|
|
102
|
+
assert.equal(rt.sessions.has(thread.id), true, '目标 Harness 连接成功后才完成切换');
|
|
96
103
|
assert.equal(a.state.closed, 1);
|
|
97
|
-
assert.equal(b.state.opens.length,
|
|
104
|
+
assert.equal(b.state.opens.length, 1, '切换阶段预先连接目标 Harness');
|
|
98
105
|
assert.equal(thread.messages.filter(m => m.role === 'user').length, 1, '/switch 指令本身不落在会话里');
|
|
106
|
+
await assert.rejects(rt.switchHarness(thread.id, 'a'), /上一次 Harness 接力尚未完成/);
|
|
107
|
+
assert.ok((await rt.listCommands({ threadId: thread.id })).some(command => command.id === 'switch-cancel'));
|
|
99
108
|
|
|
100
109
|
// 4) 切换后首轮:信封注入 prompt,可见消息保持干净,注入后清除标记
|
|
101
110
|
await rt.send(thread.id, '继续');
|
|
102
111
|
await settle(thread.id);
|
|
103
|
-
assert.equal(b.state.opens.length, 1);
|
|
112
|
+
assert.equal(b.state.opens.length, 1, '首轮发送复用切换阶段已连接的目标 Session');
|
|
104
113
|
assert.equal(b.state.opens[0].restore, false);
|
|
105
114
|
assert.notEqual(thread.nativeSessionId, aNativeId);
|
|
106
115
|
assert.match(b.state.sent[0], /\[Harness Mix handoff\]/);
|
|
@@ -119,18 +128,18 @@ async function main() {
|
|
|
119
128
|
await settle(thread.id);
|
|
120
129
|
assert.ok(!b.state.sent[1].includes('[Harness Mix handoff]'), '信封只注入一次');
|
|
121
130
|
|
|
122
|
-
// 6) 切回 A
|
|
131
|
+
// 6) 切回 A:原生会话引用恢复并在提交切换前验证连接,chain 压栈两条
|
|
123
132
|
await rt.send(thread.id, '/switch a');
|
|
124
133
|
assert.equal(thread.harnessId, 'a');
|
|
125
134
|
assert.equal(thread.harnessChain.length, 2);
|
|
126
135
|
assert.equal(thread.harnessChain[1].harnessId, 'b');
|
|
127
136
|
assert.equal(thread.nativeSessionId, aNativeId, '切回恢复原原生会话标识');
|
|
128
|
-
assert.equal(thread.restore,
|
|
129
|
-
await rt.send(thread.id, '我回来了');
|
|
130
|
-
await settle(thread.id);
|
|
137
|
+
assert.equal(thread.restore, undefined);
|
|
131
138
|
assert.equal(a.state.opens.length, 2);
|
|
132
139
|
assert.equal(a.state.opens[1].restore, true, '切回走原生恢复路径');
|
|
133
140
|
assert.equal(a.state.opens[1].nativeSessionId, aNativeId);
|
|
141
|
+
await rt.send(thread.id, '我回来了');
|
|
142
|
+
await settle(thread.id);
|
|
134
143
|
assert.match(a.state.sent[1], /\[Harness Mix handoff\]/, '切回首轮同样注入信封');
|
|
135
144
|
|
|
136
145
|
// 7) 守卫:执行中 / 同 Harness / 未知 Harness / 草稿 / 协作子任务
|
|
@@ -152,7 +161,65 @@ async function main() {
|
|
|
152
161
|
const child = await rt.createThread({ harnessId: 'a', cwd: root, parentThreadId: thread.id });
|
|
153
162
|
await assert.rejects(rt.switchHarness(child.id, 'b'), /协作子任务/);
|
|
154
163
|
|
|
155
|
-
// 8)
|
|
164
|
+
// 8) 待投递接力可显式取消:关闭目标 Session,恢复源 Harness,不污染既有链
|
|
165
|
+
const chainBeforeCancel = thread.harnessChain.length;
|
|
166
|
+
const cancelCandidate = await rt.switchHarness(thread.id, 'b', { intent: 'review' });
|
|
167
|
+
assert.equal(thread.pendingHandoff.phase, 'ready');
|
|
168
|
+
await rt.send(thread.id, '/switch cancel');
|
|
169
|
+
assert.equal(thread.harnessId, 'a');
|
|
170
|
+
assert.equal(thread.pendingHandoff, undefined);
|
|
171
|
+
assert.equal(thread.harnessChain.length, chainBeforeCancel);
|
|
172
|
+
assert.equal(rt.handoffs.owned(thread.id, cancelCandidate.checkpointId).status, 'cancelled');
|
|
173
|
+
assert.equal(rt.sessions.get(thread.id)?.adapter, a.adapter);
|
|
174
|
+
|
|
175
|
+
// 9) 目标连接失败时事务回滚:线程归属、原生源 Session 与 chain 都恢复
|
|
176
|
+
const c = fakeHarness('c', 'Gamma');
|
|
177
|
+
c.state.openError = 'Gamma auth expired';
|
|
178
|
+
rt.adapters.set('c', c.adapter); rt.status.c = { available: true };
|
|
179
|
+
await assert.rejects(rt.switchHarness(thread.id, 'c'), /已恢复原 Harness.*Gamma auth expired/);
|
|
180
|
+
assert.equal(thread.harnessId, 'a');
|
|
181
|
+
assert.equal(thread.pendingHandoff, undefined);
|
|
182
|
+
assert.equal(thread.harnessChain.length, chainBeforeCancel);
|
|
183
|
+
assert.equal(rt.sessions.get(thread.id)?.adapter, a.adapter);
|
|
184
|
+
const rolledBack = [...rt.handoffs.checkpoints.values()].find(row => row.targetHarnessId === 'c');
|
|
185
|
+
assert.equal(rolledBack.status, 'rolled-back');
|
|
186
|
+
|
|
187
|
+
// 10) 首轮投递失败保留检查点;下一次发送复用同一目标 Session 和同一检查点重试
|
|
188
|
+
a.state.withFileChange = false;
|
|
189
|
+
const retryThread = await rt.createThread({ harnessId: 'a', cwd: root });
|
|
190
|
+
await rt.send(retryThread.id, '准备接力重试');
|
|
191
|
+
await settle(retryThread.id);
|
|
192
|
+
const retrySwitch = await rt.switchHarness(retryThread.id, 'b', { intent: 'review' });
|
|
193
|
+
b.state.sendError = 'temporary transport failure';
|
|
194
|
+
await rt.send(retryThread.id, '先审查');
|
|
195
|
+
await settle(retryThread.id);
|
|
196
|
+
assert.equal(retryThread.pendingHandoff.phase, 'failed');
|
|
197
|
+
assert.equal(rt.handoffs.owned(retryThread.id, retrySwitch.checkpointId).status, 'failed');
|
|
198
|
+
b.state.sendError = null;
|
|
199
|
+
await rt.send(retryThread.id, '重试审查');
|
|
200
|
+
await settle(retryThread.id);
|
|
201
|
+
assert.equal(retryThread.pendingHandoff, undefined);
|
|
202
|
+
assert.equal(rt.handoffs.owned(retryThread.id, retrySwitch.checkpointId).status, 'active');
|
|
203
|
+
assert.match(b.state.sent.at(-1), /Handoff mode: review.*Do not modify files/s);
|
|
204
|
+
|
|
205
|
+
// 11) 并发切换按 Thread 串行化:第二个 RPC 不得在第一个连接期间再创建检查点
|
|
206
|
+
const concurrentThread = await rt.createThread({ harnessId: 'a', cwd: root });
|
|
207
|
+
await rt.send(concurrentThread.id, '准备并发切换');
|
|
208
|
+
await settle(concurrentThread.id);
|
|
209
|
+
const d = fakeHarness('d', 'Delta');
|
|
210
|
+
let releaseDelta;
|
|
211
|
+
d.state.openWait = new Promise(resolve => { releaseDelta = resolve; });
|
|
212
|
+
rt.adapters.set('d', d.adapter); rt.status.d = { available: true };
|
|
213
|
+
const firstSwitch = rt.switchHarness(concurrentThread.id, 'd');
|
|
214
|
+
await wait(() => rt.switching.has(concurrentThread.id));
|
|
215
|
+
await assert.rejects(rt.switchHarness(concurrentThread.id, 'b'), /正在切换 Harness/);
|
|
216
|
+
releaseDelta();
|
|
217
|
+
await firstSwitch;
|
|
218
|
+
assert.equal(concurrentThread.harnessId, 'd');
|
|
219
|
+
await rt.cancelHarnessSwitch(concurrentThread.id);
|
|
220
|
+
assert.equal(concurrentThread.harnessId, 'a');
|
|
221
|
+
|
|
222
|
+
// 12) 持久化:threads.json 里切换链完整(含原生会话引用,供重启后惰性恢复)
|
|
156
223
|
await rt.store.save(rt.threads);
|
|
157
224
|
const persisted = (await rt.store.load()).find(t => t.id === thread.id);
|
|
158
225
|
assert.equal(persisted.harnessId, 'a');
|
|
@@ -89,20 +89,22 @@ async function main() {
|
|
|
89
89
|
const call = { type: 'assistant', message: { content: [{ type: 'tool_use', id: 'same-id', name: 'Read', input: { file_path: 'a.txt' } }] } };
|
|
90
90
|
for (const event of projectEvent(call)) emitA(event);
|
|
91
91
|
emitB({ kind: 'tool', toolCallId: 'same-id', title: 'Read', state: 'running', input: 'b.txt' });
|
|
92
|
-
assert.equal(notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === '
|
|
92
|
+
assert.equal(notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === 'dynamicToolCall').length, 0);
|
|
93
93
|
for (const event of projectEvent({ type: 'user', message: { content: [{ type: 'tool_result', tool_use_id: 'same-id', content: [{ type: 'text', text: 'only A' }] }] } })) emitA(event);
|
|
94
94
|
emitB({ kind: 'tool', toolCallId: 'same-id', state: 'error', output: 'only B failed' });
|
|
95
|
-
const completed = notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === '
|
|
95
|
+
const completed = notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === 'dynamicToolCall');
|
|
96
96
|
assert.equal(completed.length, 2);
|
|
97
|
-
assert.equal(completed.find(n => n.params.threadId === a.id).params.item.
|
|
98
|
-
|
|
97
|
+
assert.equal(completed.find(n => n.params.threadId === a.id).params.item.contentItems[0].text, 'only A');
|
|
98
|
+
const failedB = completed.find(n => n.params.threadId === b.id).params.item;
|
|
99
|
+
assert.equal(failedB.status, 'failed');
|
|
100
|
+
assert.equal(failedB.contentItems[0].text, 'only B failed');
|
|
99
101
|
assert.equal(a.tools[0].input, '{"file_path":"a.txt"}');
|
|
100
102
|
assert.equal(a.tools[0].output, 'only A');
|
|
101
103
|
assert.equal(b.tools[0].output, 'only B failed');
|
|
102
104
|
emitB({ kind: 'tool', toolCallId: 'same-id', state: 'error', output: { error: 'late detail' } });
|
|
103
|
-
const late = notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === '
|
|
105
|
+
const late = notifications.filter(n => n.method === 'item/completed' && n.params?.item?.type === 'dynamicToolCall').at(-1);
|
|
104
106
|
assert.equal(late.params.threadId, b.id);
|
|
105
|
-
assert.equal(late.params.item.
|
|
107
|
+
assert.equal(late.params.item.contentItems[0].text, '{"error":"late detail"}');
|
|
106
108
|
emitA({ kind: 'completed', finalAnswer: true });
|
|
107
109
|
assert.equal(rt.execution.isRunning(b.id), true);
|
|
108
110
|
emitB({ kind: 'completed', finalAnswer: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cline</title><path d="M17.035 3.991c2.75 0 4.98 2.24 4.98 5.003v1.667l1.45 2.896a1.01 1.01 0 01-.002.909l-1.448 2.864v1.668c0 2.762-2.23 5.002-4.98 5.002H7.074c-2.751 0-4.98-2.24-4.98-5.002V17.33l-1.48-2.855a1.01 1.01 0 01-.003-.927l1.482-2.887V8.994c0-2.763 2.23-5.003 4.98-5.003h9.962zM8.265 9.6a2.274 2.274 0 00-2.274 2.274v4.042a2.274 2.274 0 004.547 0v-4.042A2.274 2.274 0 008.265 9.6zm7.326 0a2.274 2.274 0 00-2.274 2.274v4.042a2.274 2.274 0 104.548 0v-4.042A2.274 2.274 0 0015.59 9.6z"></path><path d="M12.054 5.558a2.779 2.779 0 100-5.558 2.779 2.779 0 000 5.558z"></path></svg>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Asset licenses
|
|
2
|
+
|
|
3
|
+
Original artwork shipped in the built-in Codex Styler themes is distributed
|
|
4
|
+
under Creative Commons Attribution 4.0 International (CC BY 4.0).
|
|
5
|
+
|
|
6
|
+
| Asset | Origin | Dimensions | License |
|
|
7
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -----------------------------------------------------: | ----------------------------------------- |
|
|
8
|
+
| Codex Styler CS orbit logo, dark and light appearances | Generated specifically for this project, 2026-07-16 | 1254×1254 sources; platform exports generated by Tauri | CC BY 4.0 |
|
|
9
|
+
| Nocturne Studio background | Generated specifically for this project, 2026-07-16 | 1600×1000 | CC BY 4.0 |
|
|
10
|
+
| Quiet Garden background | Generated specifically for this project, 2026-07-16 | 1600×1000 | CC BY 4.0 |
|
|
11
|
+
| Gilded Grandeur background | Generated specifically for this project with the built-in image generation workflow, 2026-07-17 | 1586×992 | CC BY 4.0 |
|
|
12
|
+
| Merry Big Top background | Generated specifically for this project with the built-in image generation workflow, 2026-07-17 | 1586×992 | CC BY 4.0 |
|
|
13
|
+
| Moss chameleon calibrated direction atlas | Generated specifically for this project; non-linear direction calibration, shared cropping, chroma-key removal, and exact-alpha WebP encoding, 2026-07-16 | 181 frames across 4 pages | CC BY 4.0 |
|
|
14
|
+
| Pico parrot calibrated direction atlas | Generated specifically for this project; natural pose frames retained, background and watermark excluded, 2026-07-17 | 189 frames across 4 pages | CC BY 4.0 |
|
|
15
|
+
| Puddle frog calibrated direction atlas | Generated specifically for this project; natural pose frames retained, non-linear direction calibration, background and watermark excluded, 2026-07-17 | 211 frames across 5 pages | CC BY 4.0 |
|
|
16
|
+
| Mochi cat calibrated direction atlas | Generated specifically for this project; adaptive matte, shared ground line, and seamless loop closure, 2026-07-17 | 175 frames across 4 pages | CC BY 4.0 |
|
|
17
|
+
| Token Thief calibrated direction atlas | User-provided generated video; dual-corner watermark exclusion, gray-speck cleanup, shared ground line, and non-linear direction calibration, 2026-07-17 | 101 frames across 3 pages | CC BY 4.0 |
|
|
18
|
+
| Reset God calibrated direction atlas | User-provided generated video; dual-corner watermark exclusion, gray-speck cleanup, shared ground line, and non-linear direction calibration, 2026-07-17 | 100 frames across 3 pages | CC BY 4.0 |
|
|
19
|
+
| README and desktop companion portraits | Representative frames derived from the six calibrated direction atlases; exact cell crop, normalized transparent canvas, and alpha cleanup, 2026-07-17 | 6 images at 480×480 | CC BY 4.0 |
|
|
20
|
+
| Codex Styler manager screenshots | Captured from the English manager UI in matching light and dark appearances, 2026-07-17 | 2 images at 1320×840 | CC BY 4.0 |
|
|
21
|
+
| Composer game object set | Generated specifically for Triple Drop, Capsule Crane, and Mission Map; chroma-key removal and lossless-alpha WebP encoding, 2026-07-20 | 4 transparent images at 160×160 | CC BY 4.0 |
|
|
22
|
+
| Composer Interactions documentation screenshots | Captured from the English manager and a live local Codex configuration session, 2026-07-20 | 1280×720 and 1474×538 WebP files | Documentation evidence; Codex UI excluded |
|
|
23
|
+
| Companion Creator H.264 test fixture | Synthetic half-second color-frame video generated specifically for local media decoding and E2E regression tests, 2026-07-17 | 160×90, 12 fps, 6 frames | CC BY 4.0 |
|
|
24
|
+
|
|
25
|
+
No source code, image, video, sprite, logo, or other asset from
|
|
26
|
+
Fei-Away/Codex-Dream-Skin or xplol/KaMenisACat is redistributed by this
|
|
27
|
+
repository. Those projects are implementation and style references only.
|
|
28
|
+
|
|
29
|
+
Documentation screenshots of themes applied to Codex Desktop are included to
|
|
30
|
+
demonstrate compatibility. Original Codex interface elements, product names,
|
|
31
|
+
and trademarks visible in those screenshots remain the property of their
|
|
32
|
+
respective owners and are not relicensed by this project. Codex Styler theme
|
|
33
|
+
artwork and companion artwork remain covered by the project asset license.
|
|
34
|
+
|
|
35
|
+
Asset dimensions, frame order, and replacement guidance are recorded in
|
|
36
|
+
[docs/assets-and-replacement.md](docs/assets-and-replacement.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Codex Styler
|
|
2
|
+
Copyright 2026 Codex Styler contributors
|
|
3
|
+
|
|
4
|
+
Codex Styler is an independent, unofficial project. It is not affiliated
|
|
5
|
+
with, endorsed by, or sponsored by OpenAI.
|
|
6
|
+
|
|
7
|
+
OpenAI and Codex are trademarks of OpenAI, L.L.C. Their names are used only
|
|
8
|
+
to describe compatibility with the OpenAI Codex Desktop application.
|
|
9
|
+
|
|
10
|
+
Third-party dependency notices and the release inventory policy are recorded
|
|
11
|
+
in THIRD_PARTY_NOTICES.md.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 视觉素材来源与再分发状态
|
|
2
|
+
|
|
3
|
+
本表记录当前仓库能够证明的事实与项目所有者作出的发布决定。Git 历史能证明文件何时进入仓库,不能单独证明原始作者、生成工具、授权链或再分发许可。
|
|
4
|
+
|
|
5
|
+
`node scripts/check-asset-provenance.mjs --check` 校验清单完整性。公开发布前必须运行 `node scripts/check-asset-provenance.mjs --release`;它接受「已验证可公开再分发」或「项目所有者确认公开发布」两种状态。后者表示项目所有者在知悉来源记录边界后明确接受公开发布风险,不等同于第三方法律意见或商业授权证明。
|
|
6
|
+
|
|
7
|
+
2026-07-17,项目所有者确认当时清单中的视觉素材可以随 `v5.2.1` 公开发布。2026-07-18,项目所有者提供两张当前 Mac 的 Codex 真机截图,并明确要求更新到 GitHub 项目介绍区;同日提供两张悟空主题生成图,明确要求制作成内置预设并公开发布。2026-08-11,项目所有者提供一张当前 Mac 的 WorkBuddy 真机截图(Miku 488137 主题),并明确要求补充到 GitHub 项目介绍区开头。2026-08-23,项目所有者提供一张 HeiGe 皮肤启动器 5.5.15 真机截图,并明确要求放入 GitHub 介绍页面作为更新声明。后续新增素材仍须逐项登记,未经验证或所有者确认的状态继续阻断 Release。
|
|
8
|
+
|
|
9
|
+
| 仓库路径 | 用途 | 已知创建或来源证据 | 已知许可 | 再分发状态 | 替换行动 |
|
|
10
|
+
| --- | --- | --- | --- | --- | --- |
|
|
11
|
+
| `assets/miku-character.png` | Miku 主题角色图层 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
12
|
+
| `assets/launcher/miku-launcher-icon.png` | macOS 启动器图标母版 | 2026-08-19 按项目所有者要求,以 `assets/miku-character.png` 为参考经图像生成工具编辑衍生 | 未知,继承角色源图未验证的授权状态 | 项目所有者确认公开发布 | 公开分发前补齐角色及源图授权,或替换为原创素材 |
|
|
13
|
+
| `assets/launcher/AppIcon.icns` | macOS 启动器 Bundle 图标 | 2026-08-19 由 `assets/launcher/miku-launcher-icon.png` 的标准尺寸 iconset 确定性衍生 | 未知,继承图标母版与角色源图未验证的授权状态 | 项目所有者确认公开发布 | 与图标母版同步替换或补齐权利记录 |
|
|
14
|
+
| `assets/launcher/LauncherLogo.png` | macOS 启动器窗口内置 Logo | 2026-08-21 按项目所有者要求,以 `assets/miku-character.png` 中的核心人物为参考,经图像生成工具重构为正方形上半身构图 | 未知,继承角色源图未验证的授权状态 | 项目所有者确认公开发布 | 公开分发前补齐角色及源图授权,或替换为原创素材 |
|
|
15
|
+
| `assets/miku-full-canvas.png` | Miku 主题完整画布 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
16
|
+
| `assets/miku-hero.png` | Miku 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
17
|
+
| `assets/miku-polaroid.png` | Miku 主题拍立得装饰 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
18
|
+
| `assets/miku-portrait-reference.png` | Miku 主题肖像参考 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐可核验来源或替换 |
|
|
19
|
+
| `assets/miku-reference.png` | Miku 主题构图参考 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐可核验来源或替换 |
|
|
20
|
+
| `assets/miku-sidebar-wash.png` | Miku 主题侧栏纹理 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
21
|
+
| `assets/previews/genshin-impact-codex-ui-1.webp` | Genshin 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
22
|
+
| `assets/previews/genshin-impact-codex-ui-2.webp` | Genshin 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
23
|
+
| `assets/previews/love-and-deepspace-codex-ui-1.webp` | Love and Deepspace 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
24
|
+
| `assets/previews/love-and-deepspace-codex-ui-2.webp` | Love and Deepspace 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
25
|
+
| `assets/previews/miku-studio.webp` | Miku Studio 预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
26
|
+
| `assets/previews/naruto-codex-ui-1.webp` | Naruto 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
27
|
+
| `assets/previews/naruto-codex-ui-2.webp` | Naruto 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
28
|
+
| `assets/previews/wuthering-waves-codex-ui-1.webp` | Wuthering Waves 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
29
|
+
| `assets/previews/wuthering-waves-codex-ui-2.webp` | Wuthering Waves 预设预览 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补齐来源记录或替换预览 |
|
|
30
|
+
| `custom-pet/miku-future/spritesheet.webp` | Miku 宠物精灵图 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的精灵图 |
|
|
31
|
+
| `docs/images/appearance-theme-contrast.jpg` | 外观主题配色设置说明截图 | 仓库标注为运行截图,但原始采集与画面权利记录缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充自有截图与来源记录 |
|
|
32
|
+
| `docs/images/dalao-live.jpg` | 大佬点烟主题运行截图 | 仓库标注为运行截图,但原始采集与画面权利记录缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充自有截图与来源记录 |
|
|
33
|
+
| `docs/images/genshin-dawn-live.jpg` | Genshin Dawn 运行截图 | 仓库标注为运行截图,但原始采集与画面权利记录缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充自有截图与来源记录 |
|
|
34
|
+
| `docs/images/genshin-night-live.jpg` | Genshin Night 运行截图 | 仓库标注为运行截图,但原始采集与画面权利记录缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充自有截图与来源记录 |
|
|
35
|
+
| `docs/images/macos-launcher-5.5.15-live.webp` | HeiGe 皮肤启动器 5.5.15 真机截图 | 项目所有者于 2026-08-23 提供当前 Mac 启动器 PNG,并明确要求发布到 GitHub 介绍页面作为更新声明;仓库保存压缩 WebP | 截图内第三方角色、标识及应用图标的权利不随截图转移 | 项目所有者确认公开发布 | 保留原始截图与本条提供记录 |
|
|
36
|
+
| `docs/images/theme-center-live.webp` | HeiGe 主题中心运行截图 | 项目所有者于 2026-07-18 提供当前 Mac 真机 PNG,并明确要求发布到 GitHub;仓库保存压缩 WebP | 截图内第三方角色、标识及界面元素的权利不随截图转移 | 项目所有者确认公开发布 | 保留原始截图与本条提供记录 |
|
|
37
|
+
| `docs/images/theme-switcher-live.webp` | Miku 主题及顶部切换入口运行截图 | 项目所有者于 2026-07-18 提供当前 Mac 真机 PNG,并明确要求发布到 GitHub;仓库保存压缩 WebP | 截图内第三方角色、标识及界面元素的权利不随截图转移 | 项目所有者确认公开发布 | 保留原始截图与本条提供记录 |
|
|
38
|
+
| `docs/images/wechat-group-qr.png` | 微信交流群二维码 | 项目所有者于 2026-07-18 提供当前个人微信二维码 PNG,并明确要求替换 GitHub 项目介绍图片 | 个人头像、昵称及二维码由项目所有者提供 | 项目所有者确认公开发布 | 保留本条提供记录;二维码失效时及时替换 |
|
|
39
|
+
| `docs/images/workbuddy-miku-live.webp` | WorkBuddy 换肤运行截图(Miku 488137 主题) | 项目所有者于 2026-08-11 提供当前 Mac WorkBuddy 真机 PNG,并明确要求补充到 GitHub 项目介绍区开头;仓库保存压缩 WebP | 截图内第三方角色、标识及界面元素的权利不随截图转移 | 项目所有者确认公开发布 | 保留原始截图与本条提供记录 |
|
|
40
|
+
| `docs/images/wuthering-live.jpg` | Wuthering Waves 主题运行截图 | 仓库标注为运行截图,但原始采集与画面权利记录缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充自有截图与来源记录 |
|
|
41
|
+
| `themes/dalao-dianyan/hero.webp` | Dalao Dianyan 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充人物及图像权利记录 |
|
|
42
|
+
| `themes/deepspace-dawn/hero.webp` | Deepspace Dawn 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
43
|
+
| `themes/deepspace-star/hero.webp` | Deepspace Star 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
44
|
+
| `themes/dragonball-nimbus/hero.webp` | 龙珠筋斗云主题主视觉 | 项目所有者于 2026-07-18 提供生成 PNG,并明确要求制作预设后公开发布;仓库保存压缩 WebP | 画面含第三方角色形象,项目未取得可独立核验的第三方授权文件 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的原创角色素材 |
|
|
45
|
+
| `themes/dragonball-super-saiyan/hero.webp` | 龙珠超级赛亚人主题主视觉 | 项目所有者于 2026-07-18 提供生成 PNG,并明确要求制作预设后公开发布;仓库保存压缩 WebP | 画面含第三方角色形象,项目未取得可独立核验的第三方授权文件 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的原创角色素材 |
|
|
46
|
+
| `themes/genshin-dawn/hero.webp` | Genshin Dawn 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
47
|
+
| `themes/genshin-night/hero.webp` | Genshin Night 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
48
|
+
| `themes/miku-488137/hero.webp` | Miku 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
49
|
+
| `themes/miku-488137/logo.webp` | Miku 主题 Logo | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续补充标识来源或替换 |
|
|
50
|
+
| `themes/miku-488137/polaroid.webp` | Miku 主题拍立得装饰 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
51
|
+
| `themes/naruto-hokage/hero.webp` | Naruto Hokage 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
52
|
+
| `themes/naruto-sasuke/hero.webp` | Naruto Sasuke 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
53
|
+
| `themes/wuthering-echo/hero.webp` | Wuthering Echo 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
54
|
+
| `themes/wuthering-tide/hero.webp` | Wuthering Tide 主题主视觉 | 来源证据缺失,授权未验证 | 未知 | 项目所有者确认公开发布 | 后续可替换为权利记录更完整的素材 |
|
|
55
|
+
|
|
56
|
+
MIT 许可证只适用于软件代码,不自动改变第三方可能拥有的素材权利。项目所有者确认发布表示接受当前发布决定及其风险。新增、替换或删除图片后,必须运行 `node scripts/check-asset-provenance.mjs --check`,并在同一变更中同步本表。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 软件许可、视觉素材与商标
|
|
2
|
+
|
|
3
|
+
本仓库是非官方项目,与 OpenAI、Crypton Future Media、miHoYo/HoYoverse、Kuro Games、Shueisha、Papergames 及其合作方不存在隶属、认可或背书关系。
|
|
4
|
+
|
|
5
|
+
MIT License 只覆盖本仓库的软件代码,不自动覆盖图片、角色设计、人物肖像、商标、截图或其他第三方内容。免责声明、「仅供学习」「非商业用途」「粉丝作品」「来自网络」或「AI 生成」等表述,不会产生授权,也不会替代原作者、肖像权人或商标权人的许可。
|
|
6
|
+
|
|
7
|
+
当前视觉素材的逐项证据与处置状态见 `ASSET_PROVENANCE.md`。部分素材缺少完整来源记录,公开打包、Release 和再分发仍有发布风险。项目所有者已在知悉该边界后确认随 `v5.2.1` 公开发布;该确认不构成对第三方权利的法律判断。使用者在复制、修改或再次分发这些素材前,应自行评估并取得所需权利。
|
|
8
|
+
|
|
9
|
+
Hatsune Miku(初音未来)、Genshin Impact(原神)、Wuthering Waves(鸣潮)、Naruto(火影忍者)、Love and Deepspace(恋与深空)及相关名称、角色设计和标识归各自权利人所有。提及这些名称只用于描述现有预设,不表示获得许可。
|
|
10
|
+
|
|
11
|
+
## English summary
|
|
12
|
+
|
|
13
|
+
The MIT License covers software code only. It does not grant permission to copy or redistribute bundled artwork, screenshots, likenesses, character designs, or trademarks. The project owner has explicitly accepted the documented provenance risk for the v5.2.1 public release. See `ASSET_PROVENANCE.md` before further redistribution.
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "dalao-dianyan",
|
|
4
|
+
"name": "大佬 · 点烟",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "dark",
|
|
7
|
+
"previewFocus": { "x": 50, "y": 8 },
|
|
8
|
+
"colors": {
|
|
9
|
+
"accent": "#e09a52",
|
|
10
|
+
"secondary": "#9aa3b0",
|
|
11
|
+
"surface": "#111111",
|
|
12
|
+
"text": "#f2e8da"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "deepspace-dawn",
|
|
4
|
+
"name": "恋与深空 · 晨曦",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "light",
|
|
7
|
+
"previewFocus": { "x": 50, "y": 8 },
|
|
8
|
+
"colors": {
|
|
9
|
+
"accent": "#8f7fe8",
|
|
10
|
+
"secondary": "#f097c8",
|
|
11
|
+
"surface": "#f6f2fb",
|
|
12
|
+
"text": "#4a4668"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "deepspace-star",
|
|
4
|
+
"name": "恋与深空 · 星辰",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "dark",
|
|
7
|
+
"previewFocus": { "x": 50, "y": 8 },
|
|
8
|
+
"colors": {
|
|
9
|
+
"accent": "#9d8bff",
|
|
10
|
+
"secondary": "#f097c8",
|
|
11
|
+
"surface": "#201a40",
|
|
12
|
+
"text": "#e8e2ff"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "dragonball-nimbus",
|
|
4
|
+
"name": "龙珠 · 筋斗云",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "light",
|
|
7
|
+
"previewFocus": { "x": 72, "y": 24 },
|
|
8
|
+
"thumbnailFocus": { "x": 66, "y": 31 },
|
|
9
|
+
"thumbnailZoom": 350,
|
|
10
|
+
"colors": {
|
|
11
|
+
"accent": "#4FC3F7",
|
|
12
|
+
"secondary": "#F6C445",
|
|
13
|
+
"surface": "#F3F7FF",
|
|
14
|
+
"text": "#14213D"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "dragonball-super-saiyan",
|
|
4
|
+
"name": "龙珠 · 超级赛亚人",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "light",
|
|
7
|
+
"previewFocus": { "x": 67, "y": 3 },
|
|
8
|
+
"thumbnailFocus": { "x": 67, "y": 15 },
|
|
9
|
+
"thumbnailZoom": 400,
|
|
10
|
+
"colors": {
|
|
11
|
+
"accent": "#F5C451",
|
|
12
|
+
"secondary": "#52C7F2",
|
|
13
|
+
"surface": "#FFF8E8",
|
|
14
|
+
"text": "#282033"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "genshin-dawn",
|
|
4
|
+
"name": "原神 · 晨曦",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "light",
|
|
7
|
+
"previewFocus": { "x": 50, "y": 24 },
|
|
8
|
+
"colors": {
|
|
9
|
+
"accent": "#5b7fd6",
|
|
10
|
+
"secondary": "#e0aa3e",
|
|
11
|
+
"surface": "#f2f1fb",
|
|
12
|
+
"text": "#2c3a6b"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "genshin-night",
|
|
4
|
+
"name": "原神 · 星夜",
|
|
5
|
+
"hero": "hero.webp",
|
|
6
|
+
"appearance": "dark",
|
|
7
|
+
"previewFocus": { "x": 50, "y": 17 },
|
|
8
|
+
"colors": {
|
|
9
|
+
"accent": "#e0b458",
|
|
10
|
+
"secondary": "#7a86d8",
|
|
11
|
+
"surface": "#171a2e",
|
|
12
|
+
"text": "#f0e6c8"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|