@mogragsigma/dsh-dual-host-plugin 0.33.3 → 2.0.0
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 +68 -97
- package/bundle/README.md +68 -97
- package/bundle/SETUP.md +87 -0
- package/bundle/approval-chat.js +2 -2
- package/bundle/approval-feishu.js +4 -1
- package/bundle/approval-ledger.js +30 -13
- package/bundle/approval-workbench.js +2 -0
- package/bundle/artifact-media.js +2 -2
- package/bundle/compatibility.json +47 -1
- package/bundle/configuration-v2.json +15 -0
- package/bundle/delivery-state.js +2 -0
- package/bundle/development-gate.js +2 -0
- package/bundle/distribution-lock.json +18 -18
- package/bundle/gateway-maintenance.js +1 -0
- package/bundle/global-rules/AGENTS.md +24 -20
- package/bundle/index.js +135 -25
- package/bundle/intervention-state.js +1 -1
- package/bundle/link-contract.js +9 -4
- package/bundle/managed-closeout.js +45 -0
- package/bundle/managed-cloud-policy.js +19 -0
- package/bundle/managed-command.js +71 -0
- package/bundle/managed-development-contract.js +43 -0
- package/bundle/managed-development.js +86 -0
- package/bundle/managed-endpoint.js +282 -0
- package/bundle/managed-execution.js +207 -0
- package/bundle/managed-policy.js +20 -0
- package/bundle/managed-runtime.js +186 -0
- package/bundle/managed-search.js +57 -0
- package/bundle/managed-tools.js +17 -0
- package/bundle/managed-triggers.js +85 -0
- package/bundle/native-context.js +4 -2
- package/bundle/native-entry.js +6 -2
- package/bundle/native-human-context.js +29 -0
- package/bundle/native-model-choice.js +12 -0
- package/bundle/native-rules/continuity.md +13 -0
- package/bundle/native-rules/execution.md +36 -0
- package/bundle/native-tools.js +52 -19
- package/bundle/notification-artifacts.js +26 -10
- package/bundle/notification-endpoint.js +1 -1
- package/bundle/notification-feishu.js +2 -2
- package/bundle/notification-media-endpoint.js +4 -3
- package/bundle/notification-observations.js +45 -0
- package/bundle/onboarding-cloud-package.js +1 -1
- package/bundle/onboarding-cloud.js +1 -1
- package/bundle/onboarding-file-contract.js +3 -2
- package/bundle/onboarding-file-transfer.js +4 -4
- package/bundle/onboarding-native-probe.js +1 -1
- package/bundle/onboarding-runtime-activation.js +1 -1
- package/bundle/onboarding-runtime-extension.js +2 -2
- package/bundle/package-lock.json +18 -18
- package/bundle/package.json +16 -14
- package/bundle/peer-file-review.js +1 -1
- package/bundle/peer-files.js +8 -4
- package/bundle/recovery.js +1 -1
- package/bundle/repository-gitee.js +12 -0
- package/bundle/repository-managed.js +1 -0
- package/bundle/runtime-im-lock.json +3409 -2849
- package/bundle/runtime-im-package.json +5 -5
- package/bundle/runtime-lock.json +3409 -2849
- package/bundle/runtime-package.json +5 -5
- package/bundle/schedule-ledger.js +35 -0
- package/bundle/search-contract.js +11 -1
- package/bundle/skills/task-approval/SKILL.md +20 -1
- package/bundle/skills/task-development/SKILL.md +30 -13
- package/bundle/skills/task-direct-link/SKILL.md +39 -13
- package/bundle/skills/task-file-transfer/SKILL.md +2 -2
- package/bundle/skills/task-notify/SKILL.md +2 -0
- package/bundle/skills/task-repository/SKILL.md +10 -1
- package/bundle/skills/task-research/SKILL.md +17 -2
- package/bundle/source-assessment.js +38 -0
- package/bundle/source-gitee.js +52 -0
- package/bundle/source-intake.js +137 -0
- package/bundle/source-policy-envelope.js +29 -0
- package/bundle/source-routing-tools.js +12 -0
- package/bundle/source-routing.js +218 -0
- package/bundle/source-search.js +2 -1
- package/bundle/store.js +6 -5
- package/bundle/task-artifacts.js +51 -12
- package/bundle/task-board.js +8 -4
- package/bundle/task-catalog.js +4 -3
- package/bundle/task-context.js +2 -2
- package/bundle/task-continuity-tools.js +14 -0
- package/bundle/task-continuity.js +248 -0
- package/bundle/task-envelope.js +31 -20
- package/bundle/task-notifications.js +6 -2
- package/bundle/task-observer-model.js +25 -0
- package/bundle/task-observer.js +120 -0
- package/bundle/task-results.js +1 -1
- package/bundle/task-scheduler.js +3 -3
- package/bundle/task-trigger-time.js +17 -12
- package/bundle/task-triggers.js +5 -30
- package/bundle/task-worker.js +10 -2
- package/bundle/task-workflows.js +2 -1
- package/bundle/workbench-client.js +165 -30
- package/bundle/workbench-decisions.js +22 -5
- package/bundle/worker-tools.js +8 -1
- package/compatibility.json +47 -1
- package/package.json +13 -13
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// F17: stable event IDs and existing recipient grants/receipts, not a new sender.
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {tx} from './recovery.js';
|
|
4
|
+
const digest=x=>createHash('sha256').update(JSON.stringify(x)).digest('hex');
|
|
5
|
+
export function observeNotices(n,events){
|
|
6
|
+
if(!events.length)return;
|
|
7
|
+
n.db.exec('CREATE TABLE IF NOT EXISTS notification_observation_members(notification_id TEXT NOT NULL,task_id TEXT NOT NULL,binding_revision INTEGER NOT NULL,PRIMARY KEY(notification_id,task_id));');n.observationGroups=true;
|
|
8
|
+
const groups=new Map();
|
|
9
|
+
for(const e of events){
|
|
10
|
+
if(n.db.prepare('SELECT 1 FROM task_notification_events WHERE id=?').get(e.id))continue;
|
|
11
|
+
const b=n.db.prepare('SELECT * FROM notification_bindings WHERE task_id=?').get(e.task_id);
|
|
12
|
+
if(!n.valid(b)||!JSON.parse(b.policy).events.includes(e.category)){
|
|
13
|
+
n.db.prepare('INSERT OR IGNORE INTO task_notification_events VALUES (?,?,?,?,?,NULL)').run(e.id,e.task_id,e.category,n.g.now(),'no_current_notification_grant');continue;
|
|
14
|
+
}
|
|
15
|
+
// Only proved provider-level common cause, identical destination + category.
|
|
16
|
+
const key=JSON.stringify([e.common??e.id,b.target_key,b.route_hash,e.category]);
|
|
17
|
+
const group=groups.get(key)??[];group.push({...e,b});groups.set(key,group);
|
|
18
|
+
}
|
|
19
|
+
for(const group of groups.values())for(let i=0;i<group.length;i+=10){const members=group.slice(i,i+10),first=members[0],id='observation-notice-'+digest(members.map(e=>e.id).sort());
|
|
20
|
+
tx(n.db,()=>{
|
|
21
|
+
if(n.db.prepare('SELECT 1 FROM task_notification_events WHERE id=?').get(first.id))return;
|
|
22
|
+
const count=n.db.prepare("SELECT count(*) n FROM task_notifications WHERE task_id=? AND state IN ('queued','leased','dispatching','needs_review')").get(first.task_id).n;
|
|
23
|
+
if(count>=200){for(const e of members)n.db.prepare('INSERT OR IGNORE INTO task_notification_events VALUES (?,?,?,?,?,NULL)').run(e.id,e.task_id,e.category,n.g.now(),'queue_limit');return;}
|
|
24
|
+
const names=members.map(e=>n.g.catalog.head(e.task_id).title.slice(0,120)),text=(members.length>1?'本次核对曾记录同一服务问题(最新状态以任务页为准):'+names.join('、')+'。':'任务「'+names[0]+'」核对记录:')+first.text+'\n可在任务中查看依据、成果或追加指示。';
|
|
25
|
+
const target=n.config.get(first.b.target_key);
|
|
26
|
+
n.db.prepare("INSERT INTO task_notifications(id,task_id,binding_revision,target_key,host,route_hash,category,text,state,due,created) VALUES (?,?,?,?,?,?,?,?,'queued',?,?)").run(id,first.task_id,first.b.revision,target.key,target.host,target.route_hash,first.category,text.slice(0,8000),n.g.now(),n.g.now());
|
|
27
|
+
for(const e of members){n.db.prepare('INSERT OR IGNORE INTO notification_observation_members VALUES (?,?,?)').run(id,e.task_id,e.b.revision);n.db.prepare('INSERT INTO task_notification_events VALUES (?,?,?,?,?,?)').run(e.id,e.task_id,e.category,n.g.now(),'queued',id);}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function observationGrant(n,id){
|
|
32
|
+
if(!n.observationGroups)return true;
|
|
33
|
+
const members=n.db.prepare('SELECT * FROM notification_observation_members WHERE notification_id=?').all(id);
|
|
34
|
+
if(!members.length)return true;
|
|
35
|
+
let relevant=false;
|
|
36
|
+
for(const m of members){
|
|
37
|
+
const b=n.db.prepare('SELECT * FROM notification_bindings WHERE task_id=?').get(m.task_id);
|
|
38
|
+
if(!n.valid(b)||b.revision!==m.binding_revision)return false;
|
|
39
|
+
const e=n.db.prepare('SELECT id,category FROM task_notification_events WHERE delivery_id=? AND task_id=?').get(id,m.task_id);
|
|
40
|
+
if(e?.category!=='attention'||n.g.observer?.noticeCurrent(m.task_id,e.id)===true)relevant=true;
|
|
41
|
+
}
|
|
42
|
+
// Group text is explicitly a historical snapshot. One still-current issue
|
|
43
|
+
// warrants the batch; all recovered/paused issues suppress it. Never resend.
|
|
44
|
+
return relevant;
|
|
45
|
+
}
|
|
@@ -34,5 +34,5 @@ export async function cloudPackage(source=path.dirname(fileURLToPath(import.meta
|
|
|
34
34
|
const pkg=JSON.parse(Buffer.from(files.find(x=>x.path==='plugin/package.json').data,'base64'));
|
|
35
35
|
const compat=JSON.parse(Buffer.from(files.find(x=>x.path==='plugin/compatibility.json').data,'base64'));
|
|
36
36
|
if(pkg.name!=='@mogragsigma/dsh-dual-host-plugin'||pkg.version!==compat.version)fail();
|
|
37
|
-
return {files,manifest,manifestHash,bytes:total,pluginVersion:pkg.version,hostVersion:compat.hostBaseline};
|
|
37
|
+
return {files,manifest,manifestHash,bytes:total,pluginVersion:pkg.version,hostVersion:(compat.hostCompatibilityPolicy?.standalonePinnedHost??compat.hostBaseline)};
|
|
38
38
|
}
|
|
@@ -98,7 +98,7 @@ export function createOnboardingCloud(ctx,config={},runtime={}){
|
|
|
98
98
|
}
|
|
99
99
|
const compatibility=JSON.parse(await fs.readFile(new URL('./compatibility.json',import.meta.url),'utf8')),pkg=await cloudPackage(runtime.source);
|
|
100
100
|
const steps=['创建专用安装目录及归属账本','安装固定版本官方 DSH 与依赖','安装自有插件和独立 profile','保护性创建或追加全局规则与 Skill','启动仅 loopback 可访问的独立 Host','核验真实路径、能力与持久回执'];
|
|
101
|
-
const plan={owner:OWNER,schema:1,id:randomUUID(),createdAt:now(),expiresAt:now()+300000,bindingRevision:b.revision,bindingFingerprint:digest(b),snapshot,hostVersion:compatibility.hostBaseline,pluginVersion:compatibility.version,sourceManifestHash:pkg.manifestHash,sourceFiles:pkg.manifest.length,sourceBytes:pkg.bytes,canStage:preparationAvailable()&&!conflicts.length,preparationOnly:true,steps,conflicts,canApply:false,executor:'pending_implementation',modelEnabled:false,imEnabled:false,repositoryWrites:false};
|
|
101
|
+
const plan={owner:OWNER,schema:1,id:randomUUID(),createdAt:now(),expiresAt:now()+300000,bindingRevision:b.revision,bindingFingerprint:digest(b),snapshot,hostVersion:(compatibility.hostCompatibilityPolicy?.standalonePinnedHost??compatibility.hostBaseline),pluginVersion:compatibility.version,sourceManifestHash:pkg.manifestHash,sourceFiles:pkg.manifest.length,sourceBytes:pkg.bytes,canStage:preparationAvailable()&&!conflicts.length,preparationOnly:true,steps,conflicts,canApply:false,executor:'pending_implementation',modelEnabled:false,imEnabled:false,repositoryWrites:false};
|
|
102
102
|
plan.fingerprint=digest(plan);const c=credentials();await c.modifyRecord(PLAN,old=>{
|
|
103
103
|
if(old){const p=old.payload;if(old.kind!=='grant'||!p||p.owner!==OWNER||p.schema!==1||!uuid(p.id)||p.canApply!==false||p.executor!=='pending_implementation')fail('CLOUD_PLAN_REVIEW');const {fingerprint,...body}=p;if(digest(body)!==fingerprint)fail('CLOUD_PLAN_REVIEW');}
|
|
104
104
|
return {kind:'grant',payload:plan};
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
// Preserve it for immutable controllers; actual scope is sendRoots (plan.scope.sendRoots).
|
|
4
4
|
// Consumers must not use it to infer inclusion/exclusion of an explicitly selected IM directory.
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import {FILE_CONFIG_MAX} from './peer-files.js';
|
|
6
7
|
import {digest} from './onboarding-ssh.js';
|
|
7
8
|
const exact=(v,ks)=>v&&typeof v==='object'&&!Array.isArray(v)&&Object.keys(v).sort().join()===ks.slice().sort().join();
|
|
8
9
|
const uuid=v=>typeof v==='string'&&/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(v),hex=v=>typeof v==='string'&&/^[a-f0-9]{64}$/.test(v),rev=v=>Number.isSafeInteger(v)&&v>=0&&v<Number.MAX_SAFE_INTEGER-1;
|
|
9
10
|
const fail=()=>{throw Error('ONBOARDING_FILES_RESPONSE_REVIEW');};
|
|
10
11
|
const abs=v=>typeof v==='string'&&v.length<=4096&&v.startsWith('/')&&path.posix.normalize(v)===v&&!/[\x00-\x1f\x7f]/u.test(v);
|
|
11
|
-
function limits(v){if(!exact(v,['maxBytes','queueEntries','chunkBytes','automaticDeletion'])
|
|
12
|
-
export function fileConfigurationInput(v){if(!exact(v,['enabled','sendRoots','maxBytes'])||typeof v.enabled!=='boolean'||!Array.isArray(v.sendRoots)||v.sendRoots.length>8||!v.sendRoots.every(abs)||new Set(v.sendRoots).size!==v.sendRoots.length||!Number.isSafeInteger(v.maxBytes)||v.maxBytes<1||v.maxBytes>
|
|
12
|
+
function limits(v){if(!exact(v,['maxBytes','queueEntries','chunkBytes','automaticDeletion'])||![2097152,FILE_CONFIG_MAX].includes(v.maxBytes)||v.queueEntries!==64||v.chunkBytes!==49152||v.automaticDeletion!==false)fail();}
|
|
13
|
+
export function fileConfigurationInput(v){if(!exact(v,['enabled','sendRoots','maxBytes'])||typeof v.enabled!=='boolean'||!Array.isArray(v.sendRoots)||v.sendRoots.length>8||!v.sendRoots.every(abs)||new Set(v.sendRoots).size!==v.sendRoots.length||!Number.isSafeInteger(v.maxBytes)||v.maxBytes<1||v.maxBytes>FILE_CONFIG_MAX)fail();return v;}
|
|
13
14
|
function scope(s){if(!exact(s,['runtimeId','role','namespace','root','rootIdentity','sendRoots','receiveRoot','maxBytes'])||!uuid(s.runtimeId)||s.role!=='cloud'||s.namespace!=='managed-'+s.runtimeId||!abs(s.root)||path.posix.basename(s.root)!==s.runtimeId||!/^\d+:\d+$/.test(s.rootIdentity)||s.receiveRoot!==s.root+'/files/inbox'||!Array.isArray(s.sendRoots)||!s.sendRoots.length||s.sendRoots.some(x=>!exact(x,['path','identity'])||!abs(x.path)||typeof x.identity!=='string'||!(x.path===s.root+'/files/outbox'?x.identity==='owned-outbox':/^\d+:\d+$/.test(x.identity))))fail();fileConfigurationInput({enabled:true,sendRoots:s.sendRoots.map(x=>x.path),maxBytes:s.maxBytes});}
|
|
14
15
|
export function cloudFilePlan(v){if(v?.kind==='scope_migration')return cloudFileMigrationPlan(v);if(!exact(v,['id','expectedRevision','enabled','scope','expiresAt','fingerprint','contract','limits','writes','modelRequests','imTemporaryRootsIncluded','repositoryWriteAuthorized','executionAuthorized'])||v.contract!==2||!uuid(v.id)||!rev(v.expectedRevision)||typeof v.enabled!=='boolean'||!Number.isSafeInteger(v.expiresAt)||!hex(v.fingerprint)||v.writes!==0||v.modelRequests!==0||v.imTemporaryRootsIncluded!==false||v.repositoryWriteAuthorized!==false||v.executionAuthorized!==false)fail();scope(v.scope);limits(v.limits);return v;}
|
|
15
16
|
export function fileMigrationInput(v){if(!exact(v,['sendRoots','maxBytes']))fail();fileConfigurationInput({...v,enabled:false});return v;}
|
|
@@ -3,7 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {randomUUID} from 'node:crypto';
|
|
5
5
|
import {credentialKey} from '@deepseek-ai/dsh-credentials';
|
|
6
|
-
import {PeerFiles,FILE_MAX} from './peer-files.js';
|
|
6
|
+
import {PeerFiles,FILE_MAX,FILE_CONFIG_MAX} from './peer-files.js';
|
|
7
7
|
import {FileRoot} from './peer-file-fs.js';
|
|
8
8
|
import {migrationSnapshot,migrateFileScope} from './peer-file-migration.js';
|
|
9
9
|
import {fileQueueList,fileQueuePlan,fileQueueReceipt,fileQueueClosed,closeFileQueuePlan,applyFileQueuePlan} from './peer-file-review.js';
|
|
@@ -11,7 +11,7 @@ import {digest} from './onboarding-ssh.js';
|
|
|
11
11
|
const owner='dsh-dual-host-file-transfer',exact=(v,ks)=>v&&typeof v==='object'&&!Array.isArray(v)&&Object.keys(v).sort().join()===ks.slice().sort().join();
|
|
12
12
|
const uuid=v=>typeof v==='string'&&/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(v),hex=v=>typeof v==='string'&&/^[a-f0-9]{64}$/.test(v);
|
|
13
13
|
const fail=c=>{throw Error('ONBOARDING_FILES_'+c);},within=(a,b)=>a===b||a.startsWith(b+path.sep);
|
|
14
|
-
const limits={maxBytes:
|
|
14
|
+
const limits={maxBytes:FILE_CONFIG_MAX,queueEntries:64,chunkBytes:48*1024,automaticDeletion:false};
|
|
15
15
|
// The outbox contains user input, not executable runtime state. Recognize only our
|
|
16
16
|
// exact private marker; callers must still validate the directory itself. Never
|
|
17
17
|
// traverse, chmod or adopt its contents here: PeerFiles validates each send.
|
|
@@ -29,7 +29,7 @@ export function createOnboardingFileTransfer({g,root,credentials,protectedRoots=
|
|
|
29
29
|
function inspectOwned(){base.check();if(!fs.existsSync(folder))return;owned(folder,true);owned(path.join(folder,'owner.json'),false);if(fs.readFileSync(path.join(folder,'owner.json'),'utf8')!==marker)fail('PATH_REVIEW');for(const p of [outbox,inbox])if(fs.existsSync(p))owned(p,true);}
|
|
30
30
|
function makeOwned(){inspectOwned();if(!fs.existsSync(folder)){fs.mkdirSync(folder,{mode:0o700});fs.writeFileSync(path.join(folder,'owner.json'),marker,{flag:'wx',mode:0o600});}for(const p of [outbox,inbox]){try{fs.mkdirSync(p,{mode:0o700});}catch(e){if(e.code!=='EEXIST')throw e;}owned(p,true);}}
|
|
31
31
|
function scope(roots,maxBytes){
|
|
32
|
-
ready();inspectOwned();if(!Array.isArray(roots)||roots.length>8||!Number.isSafeInteger(maxBytes)||maxBytes<1||maxBytes>
|
|
32
|
+
ready();inspectOwned();if(!Array.isArray(roots)||roots.length>8||!Number.isSafeInteger(maxBytes)||maxBytes<1||maxBytes>FILE_CONFIG_MAX)fail('ARGUMENTS');
|
|
33
33
|
const selected=(roots.length?roots:[outbox]).map(p=>{
|
|
34
34
|
if(typeof p!=='string'||p.length>4096||!path.isAbsolute(p)||path.resolve(p)!==p||/[\x00-\x1f\x7f]/u.test(p))fail('SOURCE_REVIEW');
|
|
35
35
|
if(p===outbox)return {path:p,identity:'owned-outbox'};
|
|
@@ -39,7 +39,7 @@ export function createOnboardingFileTransfer({g,root,credentials,protectedRoots=
|
|
|
39
39
|
if(new Set(selected.map(x=>x.path)).size!==selected.length||selected.some((a,i)=>selected.some((b,j)=>i!==j&&within(a.path,b.path))))fail('SOURCE_OVERLAP');
|
|
40
40
|
return {runtimeId,role,namespace,root,rootIdentity:base.id,sendRoots:selected,receiveRoot:inbox,maxBytes};
|
|
41
41
|
}
|
|
42
|
-
function checkedScope(s){if(!exact(s,['runtimeId','role','namespace','root','rootIdentity','sendRoots','receiveRoot','maxBytes'])||s.runtimeId!==runtimeId||s.role!==role||s.namespace!==namespace||s.root!==root||s.rootIdentity!==base.id||s.receiveRoot!==inbox||!Array.isArray(s.sendRoots)||!s.sendRoots.length||s.sendRoots.length>8||s.sendRoots.some(x=>!exact(x,['path','identity'])||typeof x.path!=='string'||!path.isAbsolute(x.path)||path.resolve(x.path)!==x.path||typeof x.identity!=='string'||!(x.path===outbox?x.identity==='owned-outbox':/^\d+:\d+$/.test(x.identity)))||!Number.isSafeInteger(s.maxBytes)||s.maxBytes<1||s.maxBytes>
|
|
42
|
+
function checkedScope(s){if(!exact(s,['runtimeId','role','namespace','root','rootIdentity','sendRoots','receiveRoot','maxBytes'])||s.runtimeId!==runtimeId||s.role!==role||s.namespace!==namespace||s.root!==root||s.rootIdentity!==base.id||s.receiveRoot!==inbox||!Array.isArray(s.sendRoots)||!s.sendRoots.length||s.sendRoots.length>8||s.sendRoots.some(x=>!exact(x,['path','identity'])||typeof x.path!=='string'||!path.isAbsolute(x.path)||path.resolve(x.path)!==x.path||typeof x.identity!=='string'||!(x.path===outbox?x.identity==='owned-outbox':/^\d+:\d+$/.test(x.identity)))||!Number.isSafeInteger(s.maxBytes)||s.maxBytes<1||s.maxBytes>FILE_CONFIG_MAX)fail('LEDGER_REVIEW');return s;}
|
|
43
43
|
function record(r){if(!r)return null;const p=r.payload;
|
|
44
44
|
if(r.kind!=='grant'||!exact(p,['owner','schema','revision','operation','planHash','enabled','scope','updatedAt',...(p?.schema===2?['transition']:[])])||p.owner!==owner||![1,2].includes(p.schema)||!Number.isSafeInteger(p.revision)||p.revision<1||p.revision>=Number.MAX_SAFE_INTEGER-1||!uuid(p.operation)||!hex(p.planHash)||typeof p.enabled!=='boolean'||!Number.isSafeInteger(p.updatedAt))fail('LEDGER_REVIEW');
|
|
45
45
|
if(p.scope===null){if(p.enabled||p.schema===2)fail('LEDGER_REVIEW');return p;}checkedScope(p.scope);
|
|
@@ -15,7 +15,7 @@ export function apply(ctx,c){
|
|
|
15
15
|
try{
|
|
16
16
|
const roster=await ctx.agentPresets.list(),compat=JSON.parse(await fs.readFile(new URL('./compatibility.json',import.meta.url)));
|
|
17
17
|
const require=createRequire(import.meta.url),host=JSON.parse(await fs.readFile(require.resolve('@deepseek-ai/dsh/package.json')));
|
|
18
|
-
if(host.version!==compat.hostBaseline)throw Error('NATIVE_HOST_VERSION_REVIEW');
|
|
18
|
+
if(host.version!==(compat.hostCompatibilityPolicy?.standalonePinnedHost??compat.hostBaseline))throw Error('NATIVE_HOST_VERSION_REVIEW');
|
|
19
19
|
for(const preset of ['standard','ptc','cordis','minimal']){
|
|
20
20
|
if(!roster.some(x=>x.id===preset))throw Error('NATIVE_PRESET_MISSING');
|
|
21
21
|
stage=preset+':create';const h=await ctx.agents.withoutInitiator(()=>ctx.agents.create({sessionId:'onboarding-check-'+c.nonce+'-'+preset,meta:{cwd:c.workspace,agentPreset:preset},agentOptions:ctx.agentDefaultModel.currentSelection(),setup:async ac=>{await ctx.agentPresets.mount(ac,preset);}}));
|
|
@@ -49,7 +49,7 @@ export function createRuntimeActivation(api,q,j,{repair,inventory,source,compat,
|
|
|
49
49
|
}
|
|
50
50
|
function admission(){
|
|
51
51
|
const r=j.restart,n=j.nativeReceipt,rows=n?.rows;
|
|
52
|
-
if((normal?!['committed','rolled_back'].includes(versionSubject.state):!repair||repair.state!=='committed')||!uuid(r?.id)||r.state!=='ready'||r.nativeResumeAcknowledged!==true||r.consentContract!==1||!Number.isSafeInteger(r.createdAt)||!normal&&r.createdAt<repair.updatedAt||!Number.isSafeInteger(r.completedAt)||r.completedAt<r.createdAt||hash(r.host??null)!==hash(j.host??null)||r.nonce!==j.nativeNonce||j.state!=='host_ready'||n?.ok!==true||n.pid!==j.host?.pid||n.nonce!==j.nativeNonce||n.hostVersion!==compat.hostBaseline||n.modelAttempts!==0||n.modelAttemptScope!=='inspection_agents_only')fail('RUNTIME_ACTIVATION_RESTART_REQUIRED');
|
|
52
|
+
if((normal?!['committed','rolled_back'].includes(versionSubject.state):!repair||repair.state!=='committed')||!uuid(r?.id)||r.state!=='ready'||r.nativeResumeAcknowledged!==true||r.consentContract!==1||!Number.isSafeInteger(r.createdAt)||!normal&&r.createdAt<repair.updatedAt||!Number.isSafeInteger(r.completedAt)||r.completedAt<r.createdAt||hash(r.host??null)!==hash(j.host??null)||r.nonce!==j.nativeNonce||j.state!=='host_ready'||n?.ok!==true||n.pid!==j.host?.pid||n.nonce!==j.nativeNonce||n.hostVersion!==(compat.hostCompatibilityPolicy?.standalonePinnedHost??compat.hostBaseline)||n.modelAttempts!==0||n.modelAttemptScope!=='inspection_agents_only')fail('RUNTIME_ACTIVATION_RESTART_REQUIRED');
|
|
53
53
|
if(!Array.isArray(rows)||rows.length!==4||new Set(rows.map(x=>x.preset)).size!==4)fail('RUNTIME_ACTIVATION_ADMISSION_REVIEW');
|
|
54
54
|
if(versionContext&&n.pluginVersion!==versionContext.version)fail('RUNTIME_ACTIVATION_VERSION_REVIEW');
|
|
55
55
|
for(const preset of ['standard','ptc','cordis','minimal']){
|
|
@@ -47,12 +47,12 @@ async function runtimePlan(q){
|
|
|
47
47
|
const im=q.im===true,prefix=im?'runtime-im':'runtime';
|
|
48
48
|
const pkg=JSON.parse((await regular(path.join(q.root,'plugin',prefix+'-package.json')))?.data??'null'),lock=JSON.parse((await regular(path.join(q.root,'plugin',prefix+'-lock.json')))?.data??'null');
|
|
49
49
|
const compat=JSON.parse((await regular(path.join(q.root,'plugin/compatibility.json'))).data);
|
|
50
|
-
if(pkg?.private!==true||pkg.name!=='dsh-dual-host-isolated-runtime'||pkg.dependencies?.['@deepseek-ai/dsh']!==compat.hostBaseline||lock?.lockfileVersion!==3||hash(lock.packages?.['']?.dependencies)!==hash(pkg.dependencies)||pkg.scripts)fail('RUNTIME_LOCK_REVIEW');
|
|
50
|
+
if(pkg?.private!==true||pkg.name!=='dsh-dual-host-isolated-runtime'||pkg.dependencies?.['@deepseek-ai/dsh']!==(compat.hostCompatibilityPolicy?.standalonePinnedHost??compat.hostBaseline)||lock?.lockfileVersion!==3||hash(lock.packages?.['']?.dependencies)!==hash(pkg.dependencies)||pkg.scripts)fail('RUNTIME_LOCK_REVIEW');
|
|
51
51
|
for(const [name,p] of Object.entries(lock.packages)){if(!name)continue;if(!name.startsWith('node_modules/')||p.link||p.resolved&&!/^https:\/\/registry\.npmjs\.org\/[^?#\s]+$/.test(p.resolved)||!p.integrity||!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(p.integrity))fail('RUNTIME_LOCK_REVIEW');}
|
|
52
52
|
if(im&&(compat.onboardingOptionalIm!==1||pkg.dependencies?.['@xmanrui/dsh-im']!==(compat.imOptional?.installationVersion??compat.imOptional?.testedVersion))||!im&&pkg.dependencies?.['@xmanrui/dsh-im'])fail('RUNTIME_OPTIONAL_IM_REVIEW');
|
|
53
53
|
const systemLock=await runtimeTool('/usr/bin/flock').catch(()=>null);if(!systemLock||systemLock.uid!==0||(systemLock.mode&0o022)!==0)fail('RUNTIME_LOCK_UNAVAILABLE');
|
|
54
54
|
const d=await fs.statfs(q.root);if(Number(d.bavail)*Number(d.bsize)<2147483648)fail('RUNTIME_DISK_LOW');
|
|
55
|
-
const plan={schema:1,operationId:q.operationId,sourceManifestHash:s.owner.manifestHash,root:q.root,hostVersion:compat.hostBaseline,node:{path:process.execPath,version:process.versions.node,...identity(await fs.stat(process.execPath))},npm,systemLock,packageHash:hash(pkg),lockHash:hash(lock),registry:'https://registry.npmjs.org',optionalIm:im?{package:'@xmanrui/dsh-im',version:pkg.dependencies['@xmanrui/dsh-im'],loaded:true,botConnection:false,credentialTransfer:false}:null,nativeHost:{listen:'127.0.0.1',port:0,rules:true,skills:true,officialSupportScript:'@deepseek-ai/dsh-subprocess-local/scripts/ensure-spawn-helper.mjs',gatewayBound:false},lifecycleScripts:false,modelEnabled:false,imEnabled:false,repositoryWrites:false,hostStarted:false};
|
|
55
|
+
const plan={schema:1,operationId:q.operationId,sourceManifestHash:s.owner.manifestHash,root:q.root,hostVersion:(compat.hostCompatibilityPolicy?.standalonePinnedHost??compat.hostBaseline),node:{path:process.execPath,version:process.versions.node,...identity(await fs.stat(process.execPath))},npm,systemLock,packageHash:hash(pkg),lockHash:hash(lock),registry:'https://registry.npmjs.org',optionalIm:im?{package:'@xmanrui/dsh-im',version:pkg.dependencies['@xmanrui/dsh-im'],loaded:true,botConnection:false,credentialTransfer:false}:null,nativeHost:{listen:'127.0.0.1',port:0,rules:true,skills:true,officialSupportScript:'@deepseek-ai/dsh-subprocess-local/scripts/ensure-spawn-helper.mjs',gatewayBound:false},lifecycleScripts:false,modelEnabled:false,imEnabled:false,repositoryWrites:false,hostStarted:false};
|
|
56
56
|
return {...plan,fingerprint:hash(plan)};
|
|
57
57
|
}
|
|
58
58
|
function runtimeEnv(q){const r=runtimeRoot(q),home=path.join(r,'.bootstrap-home');return {HOME:home,PATH:path.dirname(process.execPath)+':/usr/bin:/bin',SHELL:'/bin/sh',LANG:'C.UTF-8',XDG_CONFIG_HOME:path.join(home,'config'),XDG_CACHE_HOME:path.join(home,'cache'),XDG_DATA_HOME:path.join(home,'data'),NPM_CONFIG_USERCONFIG:path.join(r,'npm-user.ini'),NPM_CONFIG_GLOBALCONFIG:path.join(r,'npm-global.ini'),NPM_CONFIG_CACHE:path.join(r,'.npm-cache'),NPM_CONFIG_UPDATE_NOTIFIER:'false'};}
|
package/bundle/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mogragsigma/dsh-dual-host-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@mogragsigma/dsh-dual-host-plugin",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "2.0.0",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@js-temporal/polyfill": "0.5.1",
|
|
@@ -14,24 +14,24 @@
|
|
|
14
14
|
"koffi": "3.1.1"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
|
-
"dsh": "0.2.1-rc.5"
|
|
17
|
+
"dsh": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@deepseek-ai/dsh-
|
|
21
|
-
"@deepseek-ai/dsh-
|
|
22
|
-
"@deepseek-ai/dsh-
|
|
23
|
-
"@
|
|
24
|
-
"@deepseek-ai/dsh-
|
|
25
|
-
"@deepseek-ai/dsh-
|
|
26
|
-
"@deepseek-ai/dsh-
|
|
27
|
-
"@deepseek-ai/dsh-web-
|
|
28
|
-
"@
|
|
20
|
+
"@deepseek-ai/dsh-llm": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
21
|
+
"@deepseek-ai/dsh-llm-deepseek": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
22
|
+
"@deepseek-ai/dsh-llm-pi-ai": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
23
|
+
"@earendil-works/pi-ai": ">=1.3.0 <1.6.0",
|
|
24
|
+
"@deepseek-ai/dsh-agent": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
25
|
+
"@deepseek-ai/dsh-credentials": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
26
|
+
"@deepseek-ai/dsh-http-proxy": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
27
|
+
"@deepseek-ai/dsh-web-fetch-http": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
28
|
+
"@deepseek-ai/dsh-web-search-deepseek": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependenciesMeta": {
|
|
31
|
-
"@deepseek-ai/dsh-
|
|
31
|
+
"@deepseek-ai/dsh-llm-pi-ai": {
|
|
32
32
|
"optional": true
|
|
33
33
|
},
|
|
34
|
-
"@
|
|
34
|
+
"@earendil-works/pi-ai": {
|
|
35
35
|
"optional": true
|
|
36
36
|
},
|
|
37
37
|
"@deepseek-ai/dsh-http-proxy": {
|
|
@@ -40,19 +40,19 @@
|
|
|
40
40
|
"@deepseek-ai/dsh-llm": {
|
|
41
41
|
"optional": true
|
|
42
42
|
},
|
|
43
|
-
"@deepseek-ai/dsh-
|
|
43
|
+
"@deepseek-ai/dsh-web-search-deepseek": {
|
|
44
44
|
"optional": true
|
|
45
45
|
},
|
|
46
|
-
"@deepseek-ai/dsh-
|
|
46
|
+
"@deepseek-ai/dsh-agent": {
|
|
47
47
|
"optional": true
|
|
48
48
|
},
|
|
49
49
|
"@deepseek-ai/dsh-web-fetch-http": {
|
|
50
50
|
"optional": true
|
|
51
51
|
},
|
|
52
|
-
"@deepseek-ai/dsh-
|
|
52
|
+
"@deepseek-ai/dsh-credentials": {
|
|
53
53
|
"optional": true
|
|
54
54
|
},
|
|
55
|
-
"@
|
|
55
|
+
"@deepseek-ai/dsh-llm-deepseek": {
|
|
56
56
|
"optional": true
|
|
57
57
|
}
|
|
58
58
|
}
|
package/bundle/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mogragsigma/dsh-dual-host-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./native-entry.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"./native-tools": "./native-tools.js",
|
|
9
9
|
"./client": "./workbench-client.js"
|
|
10
10
|
},
|
|
11
|
-
"description": "
|
|
11
|
+
"description": "Single-owner native cloud tasks for DeepSeek Harness, with local and IM entry, continuous follow-up and verifiable artifacts",
|
|
12
12
|
"files": [
|
|
13
13
|
"*.js",
|
|
14
14
|
"cordis.patch.yml",
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
"LICENSE",
|
|
34
34
|
"LICENSE.en.md",
|
|
35
35
|
"THIRD_PARTY_NOTICES.md",
|
|
36
|
-
"ASSET_NOTICE.md"
|
|
36
|
+
"ASSET_NOTICE.md",
|
|
37
|
+
"SETUP.md",
|
|
38
|
+
"configuration-v2.json"
|
|
37
39
|
],
|
|
38
40
|
"dsh": {
|
|
39
41
|
"bundle": {
|
|
@@ -56,15 +58,15 @@
|
|
|
56
58
|
"koffi": "3.1.1"
|
|
57
59
|
},
|
|
58
60
|
"peerDependencies": {
|
|
59
|
-
"@deepseek-ai/dsh-llm": "0.2.1-rc.5",
|
|
60
|
-
"@deepseek-ai/dsh-llm-deepseek": "0.2.1-rc.5",
|
|
61
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.2.1-rc.5",
|
|
62
|
-
"@earendil-works/pi-ai": "1.3.0",
|
|
63
|
-
"@deepseek-ai/dsh-agent": "0.2.1-rc.5",
|
|
64
|
-
"@deepseek-ai/dsh-credentials": "0.2.1-rc.5",
|
|
65
|
-
"@deepseek-ai/dsh-http-proxy": "0.2.1-rc.5",
|
|
66
|
-
"@deepseek-ai/dsh-web-fetch-http": "0.2.1-rc.5",
|
|
67
|
-
"@deepseek-ai/dsh-web-search-deepseek": "0.2.1-rc.5"
|
|
61
|
+
"@deepseek-ai/dsh-llm": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
62
|
+
"@deepseek-ai/dsh-llm-deepseek": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
63
|
+
"@deepseek-ai/dsh-llm-pi-ai": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
64
|
+
"@earendil-works/pi-ai": ">=1.3.0 <1.6.0",
|
|
65
|
+
"@deepseek-ai/dsh-agent": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
66
|
+
"@deepseek-ai/dsh-credentials": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
67
|
+
"@deepseek-ai/dsh-http-proxy": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
68
|
+
"@deepseek-ai/dsh-web-fetch-http": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0",
|
|
69
|
+
"@deepseek-ai/dsh-web-search-deepseek": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0"
|
|
68
70
|
},
|
|
69
71
|
"peerDependenciesMeta": {
|
|
70
72
|
"@deepseek-ai/dsh-llm-pi-ai": {
|
|
@@ -113,9 +115,9 @@
|
|
|
113
115
|
"dsh-plugin",
|
|
114
116
|
"deepseek-harness",
|
|
115
117
|
"dual-host",
|
|
116
|
-
"
|
|
118
|
+
"cloud-tasks"
|
|
117
119
|
],
|
|
118
120
|
"engines": {
|
|
119
|
-
"dsh": "0.2.1-rc.5"
|
|
121
|
+
"dsh": ">=0.2.1-rc.5 <0.2.2-0 || >=0.2.2-rc.1 <0.2.3-0"
|
|
120
122
|
}
|
|
121
123
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import {FileRoot,fileHash} from './peer-file-fs.js';
|
|
7
|
-
import {FILE_MAX,FILE_CHUNK,fileMetadata} from './peer-files.js';
|
|
7
|
+
import {FILE_CONFIG_MAX as FILE_MAX,FILE_CHUNK,fileMetadata} from './peer-files.js';
|
|
8
8
|
import {fileScopeConfig} from './peer-file-migration.js';
|
|
9
9
|
import {digest} from './onboarding-ssh.js';
|
|
10
10
|
const json=JSON.stringify,fail=c=>{throw Error('ONBOARDING_FILES_'+c);};
|
package/bundle/peer-files.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import {join} from 'node:path';
|
|
3
3
|
import {FileRoot,fileHash,fileName,sourceFile} from './peer-file-fs.js';
|
|
4
|
-
export const FILE_CHUNK=48*1024,FILE_MAX=
|
|
4
|
+
export const FILE_CHUNK=48*1024,FILE_MAX=32*1024*1024,FILE_CONFIG_MAX=512*1024*1024;
|
|
5
|
+
// FILE_MAX is the fresh-install default, not a universal transfer-size promise.
|
|
6
|
+
// Preserve an existing implicit limit; expanding a persisted scope remains explicit.
|
|
5
7
|
const other=r=>r==='local'?'cloud':'local',jid=x=>typeof x==='string'&&/^file-[a-f0-9]{64}$/.test(x),task=x=>typeof x==='string'&&/^task-[a-f0-9-]{36}$/.test(x);
|
|
6
8
|
const json=JSON.stringify,fail=c=>{throw Error('PEER_FILE_'+c);};
|
|
7
9
|
const same=(a,b)=>json(a)===json(b);
|
|
8
10
|
export function fileMetadata(a,namespace,origin,max){
|
|
11
|
+
if(Number.isSafeInteger(a?.size)&&a.size>max)fail('TOO_LARGE');
|
|
9
12
|
const keys=['v','namespace','id','task_id','from','to','name','size','sha256','created_at'];
|
|
10
13
|
if(!a||typeof a!=='object'||Object.keys(a).some(k=>!keys.includes(k))||a.v!==1||a.namespace!==namespace||!jid(a.id)||!task(a.task_id)||a.from!==origin||a.to!==other(origin)||!Number.isSafeInteger(a.size)||a.size<0||a.size>max||!(/^[a-f0-9]{64}$/.test(a.sha256??''))||!Number.isSafeInteger(a.created_at)||a.created_at<0)fail('METADATA_INVALID');
|
|
11
14
|
fileName(a.name);return Object.fromEntries(keys.map(k=>[k,a[k]]));
|
|
@@ -15,9 +18,10 @@ function decode(s){if(typeof s!=='string'||s.length>FILE_CHUNK*4/3||!s.length)fa
|
|
|
15
18
|
export class PeerFiles {
|
|
16
19
|
constructor(g,c){
|
|
17
20
|
if(!c||c.enabled!==true||Object.keys(c).some(k=>!['enabled','sendRoots','receiveRoot','maxBytes'].includes(k))||!Array.isArray(c.sendRoots)||!c.sendRoots.length||c.sendRoots.length>8)fail('CONFIG_INVALID');
|
|
18
|
-
this.g=g;this.db=g.db;this.role=g.c.role;this.namespace=g.c.namespace;this.
|
|
21
|
+
this.g=g;this.db=g.db;this.role=g.c.role;this.namespace=g.c.namespace;const retained=this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='peer_file_owner'").get()?this.db.prepare('SELECT config FROM peer_file_owner WHERE id=1').get():null;
|
|
22
|
+
this.max=c.maxBytes??(retained?JSON.parse(retained.config).max:FILE_MAX);
|
|
19
23
|
if(!['local','cloud'].includes(this.role)||typeof this.namespace!=='string'||!this.namespace||this.namespace.length>128)fail('CONFIG_INVALID');
|
|
20
|
-
if(!Number.isSafeInteger(this.max)||this.max<1||this.max>
|
|
24
|
+
if(!Number.isSafeInteger(this.max)||this.max<1||this.max>FILE_CONFIG_MAX)fail('CONFIG_INVALID');
|
|
21
25
|
this.roots=c.sendRoots.map(p=>new FileRoot(p));this.receive=new FileRoot(c.receiveRoot);
|
|
22
26
|
const dir=join(g.c.stateDir,'peer-files');fs.mkdirSync(dir,{recursive:true,mode:0o700});this.spool=new FileRoot(dir);
|
|
23
27
|
this.db.exec(`CREATE TABLE IF NOT EXISTS peer_file_owner(id INTEGER PRIMARY KEY,config TEXT NOT NULL);
|
|
@@ -149,7 +153,7 @@ export class PeerFiles {
|
|
|
149
153
|
const out=this.db.prepare('SELECT * FROM peer_file_out').all().filter(r=>JSON.parse(r.meta).task_id===task_id).map(r=>this.receipt(r));
|
|
150
154
|
const incoming=this.db.prepare('SELECT * FROM peer_file_in').all().filter(r=>JSON.parse(r.meta).task_id===task_id).map(r=>{
|
|
151
155
|
const m=JSON.parse(r.meta);let availability='not_received';
|
|
152
|
-
if(r.state==='received'){try{const b=this.receive.read([m.task_id,m.id,m.name],
|
|
156
|
+
if(r.state==='received'){try{const b=this.receive.read([m.task_id,m.id,m.name],m.size);availability=b.length===m.size&&fileHash(b)===m.sha256?'available':'changed';}catch(e){availability=e.message==='PEER_FILE_TOO_LARGE'?'changed':'missing_or_unsafe';}}
|
|
153
157
|
return {...this.receipt(r),path:r.path,availability,checked_at:this.g.now()};
|
|
154
158
|
});return {host:this.role,sent:out,received:incoming,grants_execution:false};
|
|
155
159
|
}
|
package/bundle/recovery.js
CHANGED
|
@@ -3,7 +3,7 @@ import {randomUUID} from 'node:crypto';
|
|
|
3
3
|
export const permanentErrors=new Set(['INGRESS_ID_CONFLICT','TASK_CONTENT_CONFLICT','INVALID_TASK_ENVELOPE','INVALID_TASK_TITLE','INVALID_CONTEXT_SOURCE','CONTEXT_ORIGIN_MISMATCH','REFERENCE_NOT_IN_USER_CONTEXT','UNSUPPORTED_REFERENCE','CONTEXT_LIMIT','INVALID_INGRESS','INVALID_GOAL','INVALID_SUBMISSION','AUTH_DENIED','METHOD_DENIED']);
|
|
4
4
|
export function safeError(e){const s=e?.message;return permanentErrors.has(s)?s:['DELIVERY_TIMEOUT','SEND_TIMEOUT'].includes(s)?s:'TRANSIENT_FAILURE';}
|
|
5
5
|
export function bounded(p,ms,code){let t;return Promise.race([p,new Promise((_,reject)=>{t=setTimeout(()=>reject(Error(code)),ms);})]).finally(()=>clearTimeout(t));}
|
|
6
|
-
export function tx(db,fn){db.exec('BEGIN IMMEDIATE');try{const r=fn();db.exec('COMMIT');return r;}catch(e){db.exec('ROLLBACK');throw e;}}
|
|
6
|
+
export function tx(db,fn){db.exec('BEGIN IMMEDIATE');try{const r=fn();db.exec('COMMIT');return r;}catch(e){try{db.exec('ROLLBACK');}catch{}throw e;}}
|
|
7
7
|
export function migrateRecovery(db){
|
|
8
8
|
tx(db,()=>{
|
|
9
9
|
for(const table of ['submissions','receipts','notices']){
|
|
@@ -54,6 +54,18 @@ export class GiteeReadOnly {
|
|
|
54
54
|
if(!Number.isSafeInteger(meta.id)||meta.id<=0||typeof meta.full_name!=='string'||typeof meta.private!=='boolean'||repositoryURL('https://gitee.com/'+meta.full_name).url!==r.url)throw Error('REPOSITORY_IDENTITY_MISMATCH');
|
|
55
55
|
return {provider:'gitee',repository_id:meta.id,url:r.url,private:meta.private};
|
|
56
56
|
}
|
|
57
|
+
async issue(snapshot,number){
|
|
58
|
+
const r=repositoryURL(snapshot.url);
|
|
59
|
+
if(!Number.isSafeInteger(snapshot.repository_id)||snapshot.repository_id<=0||typeof number!=='string'||!/^I[A-Z0-9]{1,20}$/.test(number))throw Error('REPOSITORY_ISSUE_INVALID');
|
|
60
|
+
if((await this.identity(r.url)).repository_id!==snapshot.repository_id)throw Error('REPOSITORY_IDENTITY_MISMATCH');
|
|
61
|
+
const item=await this.get(`/repos/${encodeURIComponent(r.owner)}/${encodeURIComponent(r.repo)}/issues/${number}`,131072);
|
|
62
|
+
const at=Date.parse(item.updated_at),url=r.url+'/issues/'+number;item.body??='';
|
|
63
|
+
if(!Number.isSafeInteger(item.id)||item.id<=0||item.number!==number||item.html_url!==url||!Number.isSafeInteger(at)||at<0||
|
|
64
|
+
!['open','progressing','closed','rejected'].includes(item.state)||typeof item.title!=='string'||typeof item.body!=='string'||
|
|
65
|
+
Buffer.byteLength(item.title+'\n'+item.body)>65536||!Number.isSafeInteger(item.user?.id)||item.user.id<=0)throw Error('REPOSITORY_ISSUE_RESPONSE_INVALID');
|
|
66
|
+
return {id:item.id,number,state:item.state,updated_at:at,title:item.title,body:item.body,url,
|
|
67
|
+
actor:{id:String(item.user.id),kind:item.user.type==='User'?'human':item.user.type==='Bot'?'bot':'unknown'}};
|
|
68
|
+
}
|
|
57
69
|
async commit(snapshot,expected){
|
|
58
70
|
const r=repositoryURL(snapshot.url),path=`/repos/${encodeURIComponent(r.owner)}/${encodeURIComponent(r.repo)}`;
|
|
59
71
|
if(!/^[a-f0-9]{40}$/.test(snapshot.base_sha??'')||![expected?.tree_oid,expected?.parent_oid].every(x=>/^[a-f0-9]{40}$/.test(x??'')))throw Error('ARTIFACT_PROVIDER_COMMIT_INVALID');
|