人格脑默认留在用户本地,远程服务仅显性选择

This commit is contained in:
铸渊 ICE-P-ZY001 2026-10-19 21:50:03 +08:00
commit a12ebd1bc0
13 changed files with 136 additions and 60 deletions

13
runtime/client.test.mjs Normal file
View file

@ -0,0 +1,13 @@
import test from 'node:test';import assert from 'node:assert/strict';import {questionTarget} from '../scripts/question-target.mjs';
test('no configuration never implies a public persona endpoint',()=>{assert.throws(()=>questionTarget(),/LOCAL_PERSONA_SERVICE_NOT_CONFIGURED/);});
test('local service requires literal loopback and refuses credentials in URL',()=>{
assert.equal(questionTarget({localUrl:'http://127.0.0.1:31313/v1/ask'}).remote,false);
assert.equal(questionTarget({localUrl:'http://[::1]:31313/v1/ask'}).remote,false);
for(const localUrl of ['https://guanghulab.com/api/ai/v1/ask','http://192.168.1.1/v1/ask','http://user:secret@127.0.0.1/v1/ask','http://127.0.0.1/v1/ask?token=secret'])assert.throws(()=>questionTarget({localUrl}));
});
test('remote service must be explicit, secure, and cannot silently replace local',()=>{
assert.equal(questionTarget({remoteUrl:'https://owner.example/v1/ask'}).remote,true);
assert.equal(questionTarget({remoteUrl:'https://guanghulab.com/api/ai/v1/ask'}).remote,true);
assert.throws(()=>questionTarget({remoteUrl:'http://owner.example/v1/ask'}),/EXPLICIT_REMOTE_HTTPS_REQUIRED/);
assert.throws(()=>questionTarget({remoteUrl:'https://owner.example/v1/ask',localUrl:'http://127.0.0.1/v1/ask'}),/QUESTION_TARGET_CONFLICT/);
});