afucontext 0.0.0-stage → 0.1.18
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/LICENSE +201 -0
- package/README.md +182 -2
- package/README.zh-CN.md +183 -0
- package/bin/afu.mjs +127 -0
- package/bin/cache-lease.mjs +16 -0
- package/bin/host-autoload.py +546 -0
- package/bin/install-guide.mjs +56 -0
- package/bin/install-process.mjs +19 -0
- package/bin/platform.mjs +74 -0
- package/bin/prebuilt.mjs +64 -0
- package/bin/runtime.mjs +115 -0
- package/package.json +29 -4
- package/platform-dependencies.json +15 -0
- package/platform-locks/dsh/package-lock.json +425 -0
- package/platform-locks/dsh/package.json +16 -0
- package/platform-locks/pi/package-lock.json +1789 -0
- package/platform-locks/pi/package.json +9 -0
- package/python/afu_context_local-0.1.18-py3-none-any.whl +0 -0
- package/python/constraints.txt +25 -0
- package/release.json +7 -0
- package/scripts/dsh-overlay.mjs +105 -0
- package/scripts/dsh-presets.mjs +49 -0
- package/scripts/dsh-web-probe.mjs +14 -0
- package/src/afu_host_integrations/dsh/dist/commands.js +28 -0
- package/src/afu_host_integrations/dsh/dist/index.js +548 -0
- package/src/afu_host_integrations/pi/dist/index.js +565 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/commands.js +40 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/host-worker.js +170 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/index.js +243 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/prefix.js +12 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/protocol.js +79 -0
- package/src/afu_host_integrations/shared/runtime-client/dist/telemetry.js +78 -0
package/bin/platform.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {obtainPack} from './prebuilt.mjs';
|
|
2
|
+
import {runInstall} from './install-process.mjs';
|
|
3
|
+
/** Host-only dependencies live outside the installed npm package. */
|
|
4
|
+
import {mkdirSync,readFileSync,writeFileSync,existsSync,rmSync,cpSync,renameSync,realpathSync} from 'node:fs';
|
|
5
|
+
import {join,dirname,delimiter,basename} from 'node:path';
|
|
6
|
+
import {homedir} from 'node:os';
|
|
7
|
+
|
|
8
|
+
import {spawnSync} from 'node:child_process';
|
|
9
|
+
import {pathToFileURL} from 'node:url';
|
|
10
|
+
import {randomUUID} from 'node:crypto';
|
|
11
|
+
import {recoverLock,importProbeSucceeded} from './runtime.mjs';
|
|
12
|
+
const read=p=>{try{return JSON.parse(readFileSync(p,'utf8'));}catch{return null;}};
|
|
13
|
+
export function npmInvocation(env=process.env,platform=process.platform){
|
|
14
|
+
if(platform!=='win32')return ['npm',[]];
|
|
15
|
+
const candidates=[join(dirname(process.execPath),'node_modules/npm/bin/npm-cli.js'),
|
|
16
|
+
...String(env.PATH??env.Path??'').split(delimiter).map(p=>join(p,'node_modules/npm/bin/npm-cli.js'))];
|
|
17
|
+
const cli=candidates.find(p=>existsSync(p));
|
|
18
|
+
if(!cli)throw Error('Cannot locate npm-cli.js; install Node.js with npm');
|
|
19
|
+
return [process.execPath,[cli]];
|
|
20
|
+
}
|
|
21
|
+
export async function preparePlatform(root,manifest,args,env=process.env){
|
|
22
|
+
const host=['install','upgrade'].includes(args[0])?args[1]:args[0];
|
|
23
|
+
if(!['pi','dsh'].includes(host))return root;
|
|
24
|
+
const dependencies=read(join(root,'platform-dependencies.json'))?.[host];
|
|
25
|
+
if(!dependencies)throw Error('Missing platform dependency manifest');
|
|
26
|
+
const base=env.AFU_RUNTIME_DIR??join(homedir(),'.local/share/afu-context/runtimes');
|
|
27
|
+
const canonical=join(base,manifest.build+'-'+host),pointer=canonical+'.location.json',lock=canonical+'.lock',token=randomUUID();
|
|
28
|
+
let target=canonical;
|
|
29
|
+
const refresh=()=>{const name=read(pointer)?.name;target=typeof name==='string'&&name.startsWith(manifest.build+'-'+host)&&!/[\\/]/.test(name)?join(base,name):canonical;};
|
|
30
|
+
refresh();
|
|
31
|
+
const ready=()=>read(join(target,'platform-ready.json'))?.build===manifest.build && Object.keys(dependencies).every(name=>existsSync(join(target,'node_modules',name,'package.json'))) && importProbeSucceeded(spawnSync(process.execPath,['--input-type=module','-e','await import(process.argv[1])',pathToFileURL(join(target,'src/afu_host_integrations',host,'dist/index.js')).href],{env,stdio:'ignore',timeout:process.platform==='win32'?30000:10000}));
|
|
32
|
+
mkdirSync(base,{recursive:true,mode:0o700});
|
|
33
|
+
const deadline=Date.now()+180000;
|
|
34
|
+
while(true){
|
|
35
|
+
refresh();if(ready())return target;
|
|
36
|
+
try{mkdirSync(lock);writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,token}));break;}
|
|
37
|
+
catch(e){if(e.code!=='EEXIST')throw e;}
|
|
38
|
+
recoverLock(lock);if(Date.now()>deadline)throw Error('Platform installation still pending');
|
|
39
|
+
await new Promise(r=>setTimeout(r,300));
|
|
40
|
+
}
|
|
41
|
+
const owns=()=>read(join(lock,'owner.json'))?.token===token;
|
|
42
|
+
if(!owns())return target;
|
|
43
|
+
let createdTarget;
|
|
44
|
+
try{
|
|
45
|
+
refresh();if(ready())return target;
|
|
46
|
+
// Never repair in place: an older host may still import files from this tree.
|
|
47
|
+
target=canonical+'-generation-'+randomUUID();
|
|
48
|
+
createdTarget=target;
|
|
49
|
+
mkdirSync(target,{recursive:true,mode:0o700});
|
|
50
|
+
for(const name of ['src/afu_host_integrations','scripts'])cpSync(join(root,name),join(target,name),{recursive:true});
|
|
51
|
+
cpSync(join(root,'release.json'),join(target,'release.json'));
|
|
52
|
+
cpSync(join(root,'platform-locks',host,'package.json'),join(target,'package.json'));
|
|
53
|
+
cpSync(join(root,'platform-locks',host,'package-lock.json'),join(target,'package-lock.json'));
|
|
54
|
+
// COSEC: fixed package names/versions from the release, no shell interpolation.
|
|
55
|
+
const [executable,prefix]=npmInvocation(env);
|
|
56
|
+
if(manifest.prebuilt && process.platform!=='win32'){
|
|
57
|
+
const payload=await obtainPack(root,manifest,host,env,pid=>writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,childPid:pid,token}),{mode:0o600}));
|
|
58
|
+
cpSync(join(payload,'node_modules'),join(target,'node_modules'),{recursive:true,verbatimSymlinks:true});
|
|
59
|
+
}else {
|
|
60
|
+
// npm treats a Windows junction alias as a linked package. Use its real
|
|
61
|
+
// prefix so npm ci validates the shipped lock against the actual root.
|
|
62
|
+
const installTarget=process.platform==='win32'?realpathSync(target):target;
|
|
63
|
+
await runInstall(executable,[...prefix,'ci','--registry=https://registry.npmjs.org','--prefix',installTarget,'--ignore-scripts','--no-audit','--no-fund'],env,pid=>writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,childPid:pid,token})));
|
|
64
|
+
}
|
|
65
|
+
// Import the real adapters: a package.json marker alone misses broken files
|
|
66
|
+
// and corrupt transitive dependencies. No model request is made by imports.
|
|
67
|
+
await runInstall(process.execPath,['--input-type=module','-e','await import(process.argv[1])',pathToFileURL(join(target,'src/afu_host_integrations',host,'dist/index.js')).href],env);
|
|
68
|
+
writeFileSync(join(target,'platform-ready.json'),JSON.stringify({build:manifest.build,cache_protocol:1}),{mode:0o600});
|
|
69
|
+
const temp=pointer+'.'+token;writeFileSync(temp,JSON.stringify({name:process.platform==='win32'?basename(target):target.slice(base.length+1)}),{mode:0o600});renameSync(temp,pointer);return target;
|
|
70
|
+
}catch(error){
|
|
71
|
+
if(owns()&&createdTarget)rmSync(createdTarget,{recursive:true,force:true});
|
|
72
|
+
throw error;
|
|
73
|
+
}finally{if(owns())rmSync(lock,{recursive:true,force:true});}
|
|
74
|
+
}
|
package/bin/prebuilt.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Release-owned platform packs, copied only after manifest verification. */
|
|
2
|
+
import {createRequire} from 'node:module';
|
|
3
|
+
import {readFileSync,existsSync,lstatSync,realpathSync,readdirSync,mkdirSync,mkdtempSync,renameSync,rmSync,writeFileSync} from 'node:fs';
|
|
4
|
+
import {join,resolve,sep} from 'node:path';
|
|
5
|
+
import {createHash} from 'node:crypto';
|
|
6
|
+
import {homedir} from 'node:os';
|
|
7
|
+
import {runInstall} from './install-process.mjs';
|
|
8
|
+
export function packName(kind){
|
|
9
|
+
if(!['runtime','pi','dsh'].includes(kind))throw Error('INVALID_PACK_KIND');
|
|
10
|
+
if(!['darwin','linux','win32'].includes(process.platform)||!['arm64','x64'].includes(process.arch))throw Error('UNSUPPORTED_PLATFORM');
|
|
11
|
+
return `afu-context-${kind}-${process.platform}-${process.arch}`;
|
|
12
|
+
}
|
|
13
|
+
export function verifyPack(folder,manifest,kind){
|
|
14
|
+
const info=JSON.parse(readFileSync(join(folder,'pack-manifest.json'),'utf8'));
|
|
15
|
+
if(info.build!==manifest.build||info.kind!==kind||info.platform!==process.platform||info.arch!==process.arch)throw Error('PLATFORM_PACK_MISMATCH');
|
|
16
|
+
const root=realpathSync(folder);
|
|
17
|
+
const listed=new Set(Object.keys(info.files));
|
|
18
|
+
function walk(dir){for(const entry of readdirSync(dir,{withFileTypes:true})){const file=join(dir,entry.name);if(entry.isDirectory())walk(file);else {const name=file.slice(root.length+1).split(sep).join('/');if(!listed.has(name))throw Error('PACK_UNLISTED_FILE');if(lstatSync(file).isSymbolicLink()&&lstatSync(realpathSync(file)).isDirectory())throw Error('PACK_DIRECTORY_LINK');}}}
|
|
19
|
+
walk(join(root,'payload'));
|
|
20
|
+
// COSEC: signed npm integrity plus per-file release hashes; forbid escaping
|
|
21
|
+
// paths/symlinks before copying any executable into the private runtime.
|
|
22
|
+
for(const [name,hash] of Object.entries(info.files)){
|
|
23
|
+
const file=resolve(root,name);
|
|
24
|
+
if(!file.startsWith(root+sep)||!realpathSync(file).startsWith(root+sep))throw Error('PACK_PATH_ESCAPE');
|
|
25
|
+
const stat=lstatSync(file);
|
|
26
|
+
const actual=createHash('sha256').update(readFileSync(file)).digest('hex');
|
|
27
|
+
if(!stat.isFile()&&!stat.isSymbolicLink()||actual!==hash)throw Error('PACK_INTEGRITY_FAILED');
|
|
28
|
+
}
|
|
29
|
+
if(!info.files['payload/'+(kind==='runtime'?'bin/python3':'package.json')])throw Error('PACK_ENTRY_MISSING');
|
|
30
|
+
return join(folder,'payload');
|
|
31
|
+
}
|
|
32
|
+
export async function obtainPack(root,manifest,kind,env=process.env,onChild=()=>{}){
|
|
33
|
+
const name=packName(kind);let folder;
|
|
34
|
+
if(env.AFU_BUNDLE_DIR)folder=join(resolve(env.AFU_BUNDLE_DIR),name);
|
|
35
|
+
else{
|
|
36
|
+
try{folder=createRequire(join(root,'package.json')).resolve(name+'/package.json').replace(/[/\\]package.json$/,'');}catch{}
|
|
37
|
+
if(!folder){
|
|
38
|
+
const base=resolve(env.AFU_RUNTIME_DIR??join(homedir(),'.local/share/afu-context/runtimes'),'downloads',manifest.build+'-'+kind);
|
|
39
|
+
// COSEC: only release-owned sibling generations can be selected by this pointer.
|
|
40
|
+
let cached=base;
|
|
41
|
+
try{const selected=JSON.parse(readFileSync(base+'.json','utf8')).generation;
|
|
42
|
+
if(typeof selected==='string'&&selected.startsWith(manifest.build+'-'+kind+'-')&&!/[\\/]/.test(selected))cached=join(resolve(base,'..'),selected);
|
|
43
|
+
}catch{}
|
|
44
|
+
try{return verifyPack(join(cached,'node_modules',name),manifest,kind);}catch{}
|
|
45
|
+
mkdirSync(resolve(base,'..'),{recursive:true,mode:0o700});
|
|
46
|
+
const stage=mkdtempSync(base+'-');let committed=false;
|
|
47
|
+
try{
|
|
48
|
+
console.error(`AFU: downloading ${kind} pack for ${process.platform}/${process.arch}`);
|
|
49
|
+
// npm retries transient fetch failures and validates its content-addressed cache.
|
|
50
|
+
await runInstall('npm',['install','--prefix',stage,'--registry=https://registry.npmjs.org','--ignore-scripts','--no-audit','--no-fund','--package-lock=false','--fetch-retries=2','--fetch-retry-mintimeout=1000','--fetch-retry-maxtimeout=5000','--fetch-timeout=30000',name+'@'+manifest.version],env,onChild);
|
|
51
|
+
folder=join(stage,'node_modules',name);
|
|
52
|
+
const payload=verifyPack(folder,manifest,kind);
|
|
53
|
+
const temporary=stage+'.json';
|
|
54
|
+
writeFileSync(temporary,JSON.stringify({generation:stage.slice(resolve(base,'..').length+1)}),{mode:0o600});
|
|
55
|
+
renameSync(temporary,base+'.json');committed=true;
|
|
56
|
+
return payload;
|
|
57
|
+
}finally{
|
|
58
|
+
// Never delete a previously usable pack; only this attempt's unpublished files.
|
|
59
|
+
if(!committed){rmSync(stage,{recursive:true,force:true});rmSync(stage+'.json',{force:true});}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return verifyPack(folder,manifest,kind);
|
|
64
|
+
}
|
package/bin/runtime.mjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {obtainPack} from './prebuilt.mjs';
|
|
2
|
+
import {cpSync} from 'node:fs';
|
|
3
|
+
import {runInstall} from './install-process.mjs';
|
|
4
|
+
/** Isolated, recoverable bootstrap. Never touches host integrations. */
|
|
5
|
+
import {spawnSync} from 'node:child_process';
|
|
6
|
+
import {existsSync,mkdirSync,readFileSync,writeFileSync,rmSync,statSync,renameSync} from 'node:fs';
|
|
7
|
+
import {join,resolve,dirname,basename} from 'node:path';
|
|
8
|
+
import {homedir} from 'node:os';
|
|
9
|
+
import {randomUUID} from 'node:crypto';
|
|
10
|
+
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
|
|
11
|
+
const read=p=>{try{return JSON.parse(readFileSync(p,'utf8'));}catch{return null;}};
|
|
12
|
+
const alive=pid=>{if(!Number.isSafeInteger(pid)||pid<=0)return false;try{process.kill(pid,0);return true;}catch(e){return e.code!=='ESRCH';}};
|
|
13
|
+
export function recoverLock(lock, now=Date.now()) {
|
|
14
|
+
// Serialize recovery contenders inside the old lock. Never steal a living owner.
|
|
15
|
+
const guard=join(lock,'recover');
|
|
16
|
+
let age;
|
|
17
|
+
try{age=now-(statSync(lock).birthtimeMs || statSync(lock).mtimeMs);}catch{return false;}
|
|
18
|
+
try{mkdirSync(guard);writeFileSync(join(guard,'owner.json'),JSON.stringify({pid:process.pid}));}
|
|
19
|
+
catch{
|
|
20
|
+
try {
|
|
21
|
+
const reaper=read(join(guard,'owner.json'));
|
|
22
|
+
if(reaper ? !alive(reaper.pid) : now-statSync(guard).mtimeMs>180000)rmSync(guard,{recursive:true,force:true});
|
|
23
|
+
} catch {}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
let moved=false;
|
|
27
|
+
try{
|
|
28
|
+
const owner=read(join(lock,'owner.json'));
|
|
29
|
+
if(owner && (alive(owner.pid)||alive(owner.childPid)))return false;
|
|
30
|
+
// Missing/partial ownership may be a just-created lock, not a crashed install.
|
|
31
|
+
if(!owner && age<180000)return false;
|
|
32
|
+
const tomb=lock+'.abandoned-'+randomUUID();
|
|
33
|
+
renameSync(lock,tomb);moved=true;rmSync(tomb,{recursive:true,force:true});return true;
|
|
34
|
+
}catch{return false;}finally{if(!moved)rmSync(guard,{recursive:true,force:true});}
|
|
35
|
+
}
|
|
36
|
+
export function importProbeSucceeded(result,platform=process.platform){
|
|
37
|
+
if(platform==='win32'&&result.error?.code==='ETIMEDOUT')throw Error('IMPORT_CHECK_TIMED_OUT: existing cache preserved; retry when the machine is less busy');
|
|
38
|
+
return result.status===0;
|
|
39
|
+
}
|
|
40
|
+
export function runtimeReady(location, build) {
|
|
41
|
+
const marker=read(join(location,'afu-ready.json'));
|
|
42
|
+
const python=join(location,process.platform==='win32'?'Scripts/python.exe':'bin/python');
|
|
43
|
+
if(marker?.build!==build || !existsSync(python))return false;
|
|
44
|
+
// A surviving marker is not proof that imports and the interpreter still work.
|
|
45
|
+
return importProbeSucceeded(spawnSync(python,['-c','import afu_context.local_cli, aiohttp, tiktoken'],{stdio:'ignore',timeout:process.platform==='win32'?30000:10000}));
|
|
46
|
+
}
|
|
47
|
+
export async function prepareRuntime(root,manifest,env=process.env) {
|
|
48
|
+
const base=env.AFU_RUNTIME_DIR ?? join(homedir(),'.local/share/afu-context/runtimes');
|
|
49
|
+
const location=join(base,manifest.build), lock=location+'.lock';
|
|
50
|
+
const token=randomUUID();
|
|
51
|
+
const current=()=>{
|
|
52
|
+
const pointer=read(join(base,manifest.build+'.location.json'))?.location;
|
|
53
|
+
// COSEC: a cache pointer may select only this build's sibling repair directories.
|
|
54
|
+
if(typeof pointer==='string' && dirname(resolve(pointer))===resolve(base) && basename(pointer).startsWith(manifest.build+'-repair-'))return pointer;
|
|
55
|
+
return location;
|
|
56
|
+
};
|
|
57
|
+
mkdirSync(base,{recursive:true,mode:0o700});
|
|
58
|
+
if(runtimeReady(current(),manifest.build))return current();
|
|
59
|
+
const deadline=Date.now()+180000;
|
|
60
|
+
while(true){
|
|
61
|
+
try{mkdirSync(lock,{mode:0o700});writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,token}),{mode:0o600});break;}
|
|
62
|
+
catch(e){if(e.code!=='EEXIST')throw e;}
|
|
63
|
+
recoverLock(lock);
|
|
64
|
+
if(runtimeReady(current(),manifest.build))return current();
|
|
65
|
+
if(Date.now()>deadline)throw Error('Runtime installation is still owned by another process: '+lock);
|
|
66
|
+
await sleep(300);
|
|
67
|
+
}
|
|
68
|
+
const owns=()=>read(join(lock,'owner.json'))?.token===token;
|
|
69
|
+
const run=async(command,args)=>{
|
|
70
|
+
if(!owns())throw Error('Runtime installation ownership changed');
|
|
71
|
+
try{await runInstall(command,args,env,pid=>writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,childPid:pid,token}),{mode:0o600}));}
|
|
72
|
+
finally{if(owns())writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,token}),{mode:0o600});}
|
|
73
|
+
};
|
|
74
|
+
let createdTarget;
|
|
75
|
+
try{
|
|
76
|
+
if(runtimeReady(current(),manifest.build))return current();
|
|
77
|
+
if(manifest.prebuilt && process.platform!=='win32'){
|
|
78
|
+
const payload=await obtainPack(root,manifest,'runtime',env,pid=>writeFileSync(join(lock,'owner.json'),JSON.stringify({pid:process.pid,childPid:pid,token}),{mode:0o600}));
|
|
79
|
+
const target=existsSync(location)?location+'-repair-'+randomUUID():location;
|
|
80
|
+
createdTarget=target;
|
|
81
|
+
cpSync(payload,target,{recursive:true,verbatimSymlinks:true});
|
|
82
|
+
const python=join(target,'bin/python');
|
|
83
|
+
await run(python,['-c','import afu_context.local_cli, aiohttp, tiktoken']);
|
|
84
|
+
writeFileSync(join(target,'afu-release.json'),JSON.stringify(manifest),{mode:0o600});
|
|
85
|
+
writeFileSync(join(target,'afu-ready.json'),JSON.stringify({build:manifest.build,cache_protocol:1}),{mode:0o600});
|
|
86
|
+
if(target!==location){const pointer=join(base,manifest.build+'.location.json'),temp=pointer+'.'+token;writeFileSync(temp,JSON.stringify({location:target}),{mode:0o600});renameSync(temp,pointer);}
|
|
87
|
+
return target;
|
|
88
|
+
}
|
|
89
|
+
const choices=env.AFU_PYTHON ? [[env.AFU_PYTHON,[]]] : process.platform==='win32' ? [['py',['-3']],['python',[]]] : [['python3',[]],['python',[]]];
|
|
90
|
+
const selected=choices.find(([cmd,args])=>spawnSync(cmd,[...args,'-c','import sys;sys.exit(sys.version_info<(3,11))'],{stdio:'ignore',timeout:10000}).status===0);
|
|
91
|
+
if(!selected)throw Error('Python 3.11+ is required. Install it or set AFU_PYTHON.');
|
|
92
|
+
// Keep a damaged environment at its old path so existing processes/files are not removed.
|
|
93
|
+
// New preparation uses a fresh directory; the venv must be created at its final path.
|
|
94
|
+
let target=location;
|
|
95
|
+
if(existsSync(location))target=location+'-repair-'+randomUUID();
|
|
96
|
+
const python=join(target,process.platform==='win32'?'Scripts/python.exe':'bin/python');
|
|
97
|
+
createdTarget=target;
|
|
98
|
+
await run(selected[0],[...selected[1],'-m','venv',target]);
|
|
99
|
+
const inputs=['-c',join(root,'python/constraints.txt'),join(root,'python',manifest.wheel)];
|
|
100
|
+
if(spawnSync('uv',['--version'],{stdio:'ignore',timeout:10000}).status===0)await run('uv',['pip','install','--python',python,...inputs]);
|
|
101
|
+
else await run(python,['-m','pip','install','--disable-pip-version-check',...inputs]);
|
|
102
|
+
await run(python,['-c','import afu_context.local_cli, aiohttp, tiktoken']);
|
|
103
|
+
writeFileSync(join(target,'afu-release.json'),JSON.stringify(manifest),{mode:0o600});
|
|
104
|
+
writeFileSync(join(target,'afu-ready.json'),JSON.stringify({build:manifest.build,cache_protocol:1}),{mode:0o600});
|
|
105
|
+
if(target!==location){
|
|
106
|
+
const pointer=join(base,manifest.build+'.location.json');
|
|
107
|
+
const temporary=pointer+'.'+token;
|
|
108
|
+
writeFileSync(temporary,JSON.stringify({location:target}),{mode:0o600});renameSync(temporary,pointer);
|
|
109
|
+
}
|
|
110
|
+
return target;
|
|
111
|
+
}catch(error){
|
|
112
|
+
if(owns()&&createdTarget)rmSync(createdTarget,{recursive:true,force:true});
|
|
113
|
+
throw error;
|
|
114
|
+
}finally{if(owns())rmSync(lock,{recursive:true,force:true});}
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "afucontext",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.18",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"description": "Local asynchronous context compression and diagnostics",
|
|
7
|
+
"bin": {
|
|
8
|
+
"afucontext": "bin/afu.mjs",
|
|
9
|
+
"afuctx": "bin/afu.mjs",
|
|
10
|
+
"afu": "bin/afu.mjs"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {},
|
|
19
|
+
"files": [
|
|
20
|
+
"platform-dependencies.json",
|
|
21
|
+
"platform-locks",
|
|
22
|
+
"bin",
|
|
23
|
+
"python",
|
|
24
|
+
"src/afu_host_integrations",
|
|
25
|
+
"scripts",
|
|
26
|
+
"release.json",
|
|
27
|
+
"README.md",
|
|
28
|
+
"README.zh-CN.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pi": {
|
|
3
|
+
"@earendil-works/pi-coding-agent": "0.84.4"
|
|
4
|
+
},
|
|
5
|
+
"dsh": {
|
|
6
|
+
"@deepseek-ai/cordis": "4.1.0",
|
|
7
|
+
"@deepseek-ai/dsh-agent": "0.2.2-rc.1",
|
|
8
|
+
"@deepseek-ai/dsh-compaction": "0.2.2-rc.1",
|
|
9
|
+
"@deepseek-ai/dsh-compaction-basic": "0.2.2-rc.1",
|
|
10
|
+
"@deepseek-ai/dsh-llm": "0.2.2-rc.1",
|
|
11
|
+
"@deepseek-ai/cordis-plugin-include": "1.1.0",
|
|
12
|
+
"js-yaml": "4.3.2",
|
|
13
|
+
"@deepseek-ai/cordis-plugin-loader": "1.1.0"
|
|
14
|
+
}
|
|
15
|
+
}
|