dsh-plugin-live2d-stage 0.5.2 → 0.5.3
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/README.md +76 -60
- package/assets/floating.html +1 -1
- package/assets/floating.js +321 -285
- package/character/deepseek/AdvancedARKitSettings.vbridger +0 -0
- package/character/deepseek/Animation/IDLE.motion3.json +3606 -0
- package/character/deepseek/Animation//344/270/276/346/211/213/345/210/207/351/273/230/350/256/244/346/211/213.motion3.json +151 -0
- package/character/deepseek/Animation//347/213/227/345/260/276/350/215/211/345/210/207/345/233/236/351/273/230/350/256/244/346/211/213.motion3.json +205 -0
- package/character/deepseek/Animation//347/213/227/345/260/276/350/215/211/345/210/207/346/215/242/345/207/272/346/235/245.motion3.json +240 -0
- package/character/deepseek/Animation//347/253/226/346/211/213/346/214/207/345/210/207/345/207/272/346/235/245/345/212/250/347/224/273.motion3.json +251 -0
- package/character/deepseek/Animation//347/253/226/346/211/213/346/214/207/345/210/207/345/233/236/351/273/230/350/256/244.motion3.json +219 -0
- package/character/deepseek/Animation//350/204/261/346/215/225.motion3.json +1793 -0
- package/character/deepseek/Animation//351/273/230/350/256/244/346/211/213/345/210/207/344/270/276/346/211/213.motion3.json +180 -0
- package/character/deepseek/DeepSeek/345/205/215/350/264/271 Live2D /346/250/241/345/236/213/344/275/277/347/224/250/351/241/273/347/237/245.txt" +138 -0
- package/character/deepseek/Exp3//344/275/223/345/236/2131.exp3.json +11 -0
- package/character/deepseek/Exp3//344/275/223/345/236/2132.exp3.json +11 -0
- package/character/deepseek/Exp3//345/205/224/347/211/231.exp3.json +11 -0
- package/character/deepseek/Exp3//345/211/215/345/200/276.exp3.json +11 -0
- package/character/deepseek/Exp3//345/212/240/350/275/275.exp3.json +11 -0
- package/character/deepseek/Exp3//345/215/225/347/211/207/347/234/274/351/225/234.exp3.json +11 -0
- package/character/deepseek/Exp3//345/234/210/345/234/210/347/234/274.exp3.json +26 -0
- package/character/deepseek/Exp3//345/260/276/345/267/264/345/274/200/345/205/263.exp3.json +11 -0
- package/character/deepseek/Exp3//345/261/221/345/261/221/347/234/274.exp3.json +21 -0
- package/character/deepseek/Exp3//346/265/201/345/206/267/346/261/227.exp3.json +11 -0
- package/character/deepseek/Exp3//346/265/201/346/263/252.exp3.json +11 -0
- package/character/deepseek/Exp3//347/210/261/345/277/203/347/234/274.exp3.json +11 -0
- package/character/deepseek/Exp3//347/253/226/351/243/237/346/214/207/345/205/263.exp3.json +11 -0
- package/character/deepseek/Exp3//347/253/226/351/243/237/346/214/207/345/274/200.exp3.json +16 -0
- package/character/deepseek/Exp3//350/203/270/345/217/243/350/241/243/346/234/215.exp3.json +11 -0
- package/character/deepseek/Exp3//350/204/261/351/236/213.exp3.json +11 -0
- package/character/deepseek/Exp3//350/204/270/347/272/242.exp3.json +11 -0
- package/character/deepseek/Exp3//350/243/231/345/255/220/345/210/207/346/215/242.exp3.json +11 -0
- package/character/deepseek/Exp3//351/222/242/347/233/206.exp3.json +11 -0
- package/character/deepseek/Exp3//351/273/221/346/241/206/347/234/274/351/225/234.exp3.json +11 -0
- package/character/deepseek/Exp3//351/273/221/350/204/270.exp3.json +11 -0
- package/character/deepseek/Exp3//351/273/230/350/256/244/344/275/223/345/236/213.exp3.json +11 -0
- package/character/deepseek/Exp3//357/274/236/357/274/234.exp3.json +26 -0
- package/character/deepseek/items_pinned_to_model.json +15 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.4096/texture_00.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.4096/texture_01.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.4096/texture_02.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.4096/texture_03.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.4096/texture_04.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.cdi3.json +3251 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.moc3 +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.model3.json +30 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.physics3.json +13512 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.png +0 -0
- package/character/deepseek//345/244/247/350/202/245/351/261/274.vtube.json +4086 -0
- package/lib/chat.mjs +16 -2
- package/lib/client.js +21158 -20849
- package/lib/floating.mjs +1 -1
- package/lib/index.mjs +21 -9
- package/lib/native/Live2DFloating.exe +0 -0
- package/lib/store.cjs +11173 -11064
- package/package.json +7 -5
- package/character/hiyori/hiyori_free_t08.2048/texture_00.png +0 -0
- package/character/hiyori/hiyori_free_t08.cdi3.json +0 -276
- package/character/hiyori/hiyori_free_t08.moc3 +0 -0
- package/character/hiyori/hiyori_free_t08.model3.json +0 -72
- package/character/hiyori/hiyori_free_t08.physics3.json +0 -631
- package/character/hiyori/motion/hiyori_m01.motion3.json +0 -951
- package/character/hiyori/motion/hiyori_m02.motion3.json +0 -1238
- package/character/hiyori/motion/hiyori_m03.motion3.json +0 -1035
- package/character/hiyori/motion/hiyori_m04.motion3.json +0 -776
- package/character/hiyori/motion/hiyori_m05.motion3.json +0 -1462
- package/character/hiyori/motion/hiyori_m06.motion3.json +0 -727
- package/character/hiyori/motion/hiyori_m07.motion3.json +0 -874
- package/character/hiyori/motion/hiyori_m08.motion3.json +0 -755
package/lib/floating.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {resolve} from 'node:path';
|
|
|
5
5
|
|
|
6
6
|
export class FloatingController {
|
|
7
7
|
constructor({directory,profile,origin,onClose=()=>{}}){this.directory=directory;this.profile=profile;this.origin=origin;this.onClose=onClose;this.child=null;this.token='';this.status='closed';this.error='';this.pending=null;}
|
|
8
|
-
admits(req){const token=req.headers['x-live2d-float'];if(!this.token||typeof token!=='string'||Buffer.byteLength(token)!==Buffer.byteLength(this.token)||!timingSafeEqual(Buffer.from(token),Buffer.from(this.token)))return false;const path=new URL(req.url,'http://localhost').pathname;return req.method==='GET'&&(path==='/live2d-stage/state'||path==='/live2d-stage/
|
|
8
|
+
admits(req){const token=req.headers['x-live2d-float'];if(!this.token||typeof token!=='string'||Buffer.byteLength(token)!==Buffer.byteLength(this.token)||!timingSafeEqual(Buffer.from(token),Buffer.from(this.token)))return false;const path=new URL(req.url,'http://localhost').pathname;return req.method==='GET'&&(path==='/live2d-stage/state'||path==='/live2d-stage/stream'||path==='/live2d-stage/chat/sessions'||path==='/live2d-stage/chat/history'||path.startsWith('/live2d-stage/assets/')||path.startsWith('/live2d-stage/models/'))||req.method==='POST'&&['/live2d-stage/diagnostics','/live2d-stage/play','/live2d-stage/close','/live2d-stage/dock','/live2d-stage/chat/send','/live2d-stage/chat/upload'].includes(path);}
|
|
9
9
|
start(testOptions={}) {
|
|
10
10
|
if(this.pending)return this.pending;if(this.child&&this.status==='ready')return Promise.resolve();
|
|
11
11
|
if(process.platform!=='win32')return Promise.reject(new Error('桌面悬浮目前支持 Windows'));
|
package/lib/index.mjs
CHANGED
|
@@ -13,7 +13,7 @@ const base='/live2d-stage/';
|
|
|
13
13
|
function toolDefinition(definition,output) {
|
|
14
14
|
const properties={},required=[];
|
|
15
15
|
for(const [key,value]of Object.entries(definition.parameters)){const {required:isRequired,...schema}=value;properties[key]=schema;if(isRequired)required.push(key);}
|
|
16
|
-
return {...definition,parameters:{type:'object',properties,required,additionalProperties:false},output,execute:async(args,exec)=>{if(!args||typeof args!=='object'||Array.isArray(args))throw new Error('工具参数须为对象');for(const key of Object.keys(args))if(!Object.hasOwn(properties,key))throw new Error('未知参数:'+key);for(const key of required)if(typeof args[key]!=='string'||!args[key])throw new Error('缺少参数:'+key);return definition.execute(args,exec);}};
|
|
16
|
+
return {...definition,parameters:{type:'object',properties,required,additionalProperties:false},output,execute:async(args,exec={})=>{if(!args||typeof args!=='object'||Array.isArray(args))throw new Error('工具参数须为对象');for(const key of Object.keys(args))if(!Object.hasOwn(properties,key))throw new Error('未知参数:'+key);for(const key of required){if(args[key]===null&&Array.isArray(properties[key].type)&&properties[key].type.includes('null'))continue;if(typeof args[key]!=='string'||!args[key])throw new Error('缺少参数:'+key);}return definition.execute(args,exec);}};
|
|
17
17
|
}
|
|
18
18
|
async function body(req,max) {const chunks=[];let size=0;for await(const chunk of req){size+=chunk.length;if(size>max)throw new Error('上传体量超限');chunks.push(chunk);}return Buffer.concat(chunks);}
|
|
19
19
|
const types={'.html':'text/html; charset=utf-8','.json':'application/json','.moc3':'application/octet-stream','.png':'image/png','.jpg':'image/jpeg','.wav':'audio/wav','.mp3':'audio/mpeg','.js':'text/javascript','.vert':'text/plain','.frag':'text/plain'};
|
|
@@ -35,12 +35,23 @@ export function handler(store,assets,admit,preferences=value=>store.updatePrefer
|
|
|
35
35
|
else if(req.method==='GET'&&url.pathname===base+'chat/sessions')result=await chat.list();
|
|
36
36
|
else if(req.method==='GET'&&url.pathname===base+'chat/history')result=await chat.history(url.searchParams.get('session'));
|
|
37
37
|
else if(req.method==='POST'&&url.pathname===base+'chat/send')result=await chat.send(JSON.parse((await body(req,131072)).toString()));
|
|
38
|
+
else if(req.method==='POST'&&url.pathname===base+'chat/upload')result=await chat.openUpload(JSON.parse((await body(req,131072)).toString()));
|
|
39
|
+
else if(req.method==='GET'&&url.pathname===base+'chat/upload-request')result=chat.uploadRequest();
|
|
40
|
+
else if(req.method==='POST'&&url.pathname===base+'chat/upload-complete')result=chat.completeUpload(JSON.parse((await body(req,4096)).toString()));
|
|
38
41
|
else if(req.method==='POST'&&url.pathname===base+'preferences')result=await preferences(JSON.parse((await body(req,4096)).toString()));
|
|
39
42
|
else if(req.method==='POST'&&url.pathname===base+'import')result=await store.importZip(await body(req,MAX_ZIP),url.searchParams.get('name'));
|
|
40
43
|
else if(req.method==='POST'&&url.pathname===base+'update')result=await store.update(JSON.parse((await body(req,65536)).toString()));
|
|
41
44
|
else if(req.method==='POST'&&url.pathname===base+'play') {const data=JSON.parse((await body(req,65536)).toString());result=store.play(data.characterId,data.actionId);}
|
|
42
|
-
else if(req.method==='
|
|
43
|
-
|
|
45
|
+
else if(req.method==='GET'&&url.pathname===base+'stream') {
|
|
46
|
+
const unsubscribe=store.subscribe(url.searchParams.get('client'),url.searchParams.get('character'),event=>{
|
|
47
|
+
if(res.destroyed||res.writableEnded)throw new Error('播放连接已关闭');
|
|
48
|
+
if(!res.write('data: '+JSON.stringify(event)+'\n\n'))res.destroy();
|
|
49
|
+
});
|
|
50
|
+
res.writeHead(200,{'Content-Type':'text/event-stream','Cache-Control':'no-store','X-Accel-Buffering':'no'});
|
|
51
|
+
res.write(': connected\n\n');
|
|
52
|
+
const heartbeat=setInterval(()=>res.write(': keepalive\n\n'),15000);
|
|
53
|
+
res.on('close',()=>{clearInterval(heartbeat);unsubscribe();});return;
|
|
54
|
+
}
|
|
44
55
|
else if(req.method==='GET'&&(url.pathname.startsWith(base+'models/')||url.pathname.startsWith(base+'assets/'))) {
|
|
45
56
|
let file;
|
|
46
57
|
if(url.pathname.startsWith(base+'models/')) {
|
|
@@ -49,7 +60,8 @@ export function handler(store,assets,admit,preferences=value=>store.updatePrefer
|
|
|
49
60
|
const character=store.character(id);if(!character.files.includes(p))throw new Error('模型资源不存在');
|
|
50
61
|
file=resolve(store.root,'models',id,p);
|
|
51
62
|
} else {const p=decodeURIComponent(url.pathname.slice((base+'assets/').length));safePath(p);file=resolve(assets,p);}
|
|
52
|
-
|
|
63
|
+
// Large moc3 files can exceed the browser's cache entry limit.
|
|
64
|
+
const bytes=await readFile(file);res.writeHead(200,{'Content-Type':types[extname(file)]??'application/octet-stream','X-Content-Type-Options':'nosniff','Cache-Control':'private, no-store'});res.end(bytes);return;
|
|
53
65
|
} else {res.writeHead(404);res.end();return;}
|
|
54
66
|
res.writeHead(200,{'Content-Type':'application/json','Cache-Control':'no-store'});res.end(JSON.stringify(result));
|
|
55
67
|
} catch(e) {res.writeHead(400,{'Content-Type':'application/json','Cache-Control':'no-store'});res.end(JSON.stringify({message:e.message}));}
|
|
@@ -58,7 +70,7 @@ export function handler(store,assets,admit,preferences=value=>store.updatePrefer
|
|
|
58
70
|
export async function apply(ctx,config={}) {
|
|
59
71
|
const root=config.dataDir??resolve(process.env.DSH_HOME||resolve(homedir(),'.dsh'),'live2d-stage');
|
|
60
72
|
const store=new StageStore(root);await store.initialize();
|
|
61
|
-
await store.ensureDefaultCharacter(fileURLToPath(new URL('../character/
|
|
73
|
+
await store.ensureDefaultCharacter(fileURLToPath(new URL('../character/deepseek/',import.meta.url)));
|
|
62
74
|
const assets=fileURLToPath(new URL('../assets/',import.meta.url));
|
|
63
75
|
const floating=new FloatingController({directory:fileURLToPath(new URL('./native/',import.meta.url)),profile:resolve(root,'webview2'),origin:'',onClose:()=>{void preferences({desktopFloating:false,visible:false,actionControl:false}).catch(()=>{});}});
|
|
64
76
|
let activeTools=[];
|
|
@@ -74,12 +86,12 @@ export async function apply(ctx,config={}) {
|
|
|
74
86
|
ctx.effect(()=>()=>{for(const dispose of activeTools)if(typeof dispose==='function')dispose();void floating.stop();},'live2d-stage.floating');
|
|
75
87
|
const output={schema:{},render:(_args,value)=>[{type:'text',text:JSON.stringify(value)}]};
|
|
76
88
|
const definitions=[
|
|
77
|
-
{name:'
|
|
78
|
-
{name:'
|
|
79
|
-
{name:'
|
|
89
|
+
{name:'live2d_list_motions',description:'Read the cached active-character motions.json catalog only on first use or when the character or motions catalog version changes. Reuse it across replies. Each entry is a motion3.json action with a stable ID and editable name.',parameters:{},execute:async()=>{const version=store.catalogVersion('motions');return {...await store.readCatalog('motions'),version};}},
|
|
90
|
+
{name:'live2d_list_expressions',description:'Read the cached active-character expressions.json catalog only on first use or when the character or expressions catalog version changes. Reuse it across replies. Each entry is an exp3.json expression with a stable ID and editable name.',parameters:{},execute:async()=>{const version=store.catalogVersion('expressions');return {...await store.readCatalog('expressions'),version};}},
|
|
91
|
+
{name:'live2d_present',description:'Submit one motion and one expression together to the active Live2D player before each final reply. Select their IDs independently from motions.json and expressions.json returned by the list tools. Repeated selections are allowed. Use null only when that category is empty. This returns immediately without a playback receipt.',parameters:{characterId:{type:'string',required:true},motionId:{type:['string','null'],required:true},expressionId:{type:['string','null'],required:true}},execute:async(args,exec)=>{exec.signal?.throwIfAborted();return store.present(args.characterId,args.motionId,args.expressionId);}}
|
|
80
92
|
];
|
|
81
93
|
syncTools();
|
|
82
94
|
if(store.preferences().desktopFloating){const timer=setTimeout(()=>{void preferences({desktopFloating:true}).catch(async()=>{await store.updatePreferences({desktopFloating:false});});},1000);ctx.effect(()=>()=>clearTimeout(timer),'live2d-stage.restore');}
|
|
83
|
-
ctx.systemPrompt.section({name:'live2d-stage:capability',order:3200,interpolate:false,text:()=>store.
|
|
95
|
+
ctx.systemPrompt.section({name:'live2d-stage:capability',order:3200,interpolate:false,text:()=>store.presentationPrompt()});
|
|
84
96
|
}
|
|
85
97
|
|
|
Binary file
|