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
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
aiohappyeyeballs==2.7.1
|
|
2
|
+
aiohttp==3.14.5
|
|
3
|
+
aiohttp-socks==0.10.2
|
|
4
|
+
aiosignal==1.4.0
|
|
5
|
+
annotated-types==0.8.0
|
|
6
|
+
attrs==26.1.0
|
|
7
|
+
backports-zstd==1.8.0 ; python_full_version < '3.14'
|
|
8
|
+
certifi==2026.10.23
|
|
9
|
+
charset-normalizer==3.5.2
|
|
10
|
+
frozenlist==1.8.0
|
|
11
|
+
idna==3.20
|
|
12
|
+
multidict==7.2.1
|
|
13
|
+
propcache==0.5.4
|
|
14
|
+
pydantic==2.14.1
|
|
15
|
+
pydantic-core==2.50.1
|
|
16
|
+
python-socks==2.8.2
|
|
17
|
+
regex==2026.9.29
|
|
18
|
+
requests==2.34.2
|
|
19
|
+
tiktoken==0.12.0
|
|
20
|
+
tomlkit==0.15.2
|
|
21
|
+
truststore==0.10.5
|
|
22
|
+
typing-extensions==4.16.0
|
|
23
|
+
typing-inspection==0.4.4
|
|
24
|
+
urllib3==2.8.0
|
|
25
|
+
yarl==1.25.1
|
package/release.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Compose a disposable overlay using DSH's own YAML and patch semantics. */
|
|
2
|
+
import {readFileSync, writeFileSync, realpathSync} from 'node:fs';
|
|
3
|
+
import {createRequire} from 'node:module';
|
|
4
|
+
import {join,isAbsolute,dirname} from 'node:path';
|
|
5
|
+
import {pathToFileURL} from 'node:url';
|
|
6
|
+
import {load,dump} from 'js-yaml';
|
|
7
|
+
import {entryListSchema,applyEntryPatches} from '@deepseek-ai/cordis-plugin-include';
|
|
8
|
+
|
|
9
|
+
export function moduleReference(value) {
|
|
10
|
+
// COSEC: convert only local absolute paths, never reinterpret package names or URLs.
|
|
11
|
+
return process.platform === 'win32' && isAbsolute(value) ? pathToFileURL(value).href : value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Copy of a preset's plugin list with each active basic compaction entry
|
|
15
|
+
* disabled and an AFU entry inserted beside it; null when there is none. */
|
|
16
|
+
export function rewritePlugins(rows,module,commands) {
|
|
17
|
+
const ids=new Set();
|
|
18
|
+
(function collect(list){for(const r of list){if(r&&typeof r==='object'){if(r.id)ids.add(r.id);if(r.group&&Array.isArray(r.config))collect(r.config);}}})(rows);
|
|
19
|
+
let found=false;
|
|
20
|
+
const walk=list=>list.flatMap(row=>{
|
|
21
|
+
if (!row || typeof row!=='object' || row.disabled) return [row];
|
|
22
|
+
if (row.name==='@deepseek-ai/dsh-compaction-basic') {
|
|
23
|
+
if (!row.id) throw Error('Compaction scope must have stable entry ids');
|
|
24
|
+
const id='afu-'+row.id;
|
|
25
|
+
if (ids.has(id)) throw Error('AFU entry already exists');
|
|
26
|
+
found=true;
|
|
27
|
+
return [{...row,disabled:true},{...row,id,name:module},...(commands?[{id:'afu-commands',name:commands}]:[])];
|
|
28
|
+
}
|
|
29
|
+
if (row.group && Array.isArray(row.config)) return [{...row,config:walk(row.config)}];
|
|
30
|
+
return [row];
|
|
31
|
+
});
|
|
32
|
+
const plugins=walk(rows);
|
|
33
|
+
return found?plugins:null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function overlay(entries,module,presetModule,commands) {
|
|
37
|
+
if (!Array.isArray(entries)) throw Error('Expected a composed DSH entry list');
|
|
38
|
+
const ids=new Set(), matches=[], presets=[];
|
|
39
|
+
function visit(rows,parent=null) {
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
if (!row || typeof row!=='object') throw Error('Invalid DSH entry');
|
|
42
|
+
if (row.id) {if(ids.has(row.id))throw Error('Ambiguous duplicate DSH entry id');ids.add(row.id);}
|
|
43
|
+
if (row.disabled) continue;
|
|
44
|
+
if (row.name==='@deepseek-ai/dsh-agent-presets') presets.push({row,parent});
|
|
45
|
+
if (row.name==='@deepseek-ai/dsh-compaction-basic') matches.push({row,parent});
|
|
46
|
+
if(row.group && Array.isArray(row.config))visit(row.config,row);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
visit(entries);
|
|
50
|
+
if (presetModule) matches.push(...presets);
|
|
51
|
+
// DSH 0.2+: agents mount declarative presets (@deepseek-ai/dsh-agent-preset)
|
|
52
|
+
// whose config.plugins carry their own compaction group; the root provider
|
|
53
|
+
// is disabled. Rewrite each preset's plugin list the same way.
|
|
54
|
+
const presetPatches=[];
|
|
55
|
+
for (const row of entries) {
|
|
56
|
+
if (!row || row.disabled || row.name!=='@deepseek-ai/dsh-agent-preset' || !Array.isArray(row.config?.plugins)) continue;
|
|
57
|
+
const plugins=rewritePlugins(row.config.plugins,module,commands);
|
|
58
|
+
if (plugins===null) continue;
|
|
59
|
+
if (!row.id) throw Error('Compaction scope must have stable entry ids');
|
|
60
|
+
presetPatches.push({id:row.id,name:row.name,config:{...row.config,plugins}});
|
|
61
|
+
}
|
|
62
|
+
if (!matches.length && !presetPatches.length) throw Error('No active basic compaction provider in the composed profile');
|
|
63
|
+
const patches=[...presetPatches];
|
|
64
|
+
for (const {row,parent} of matches) {
|
|
65
|
+
if(!row.id || (parent && !parent.id))throw Error('Compaction scope must have stable entry ids');
|
|
66
|
+
const id='afu-'+row.id;
|
|
67
|
+
if(ids.has(id))throw Error('AFU entry already exists');
|
|
68
|
+
patches.push({id:row.id,name:row.name,disabled:true});
|
|
69
|
+
const isPresets=row.name==='@deepseek-ai/dsh-agent-presets';
|
|
70
|
+
patches.push({...parent && {id:parent.id},insert:[{...row,id,name:isPresets?presetModule:module},
|
|
71
|
+
...(commands && !isPresets && !ids.has('afu-commands')?[{id:'afu-commands',name:commands}]:[])]});
|
|
72
|
+
}
|
|
73
|
+
const warnings=[];applyEntryPatches(entries,patches,(x)=>warnings.push(x));
|
|
74
|
+
if(warnings.length)throw Error('DSH overlay did not match the composed tree');
|
|
75
|
+
return patches;
|
|
76
|
+
}
|
|
77
|
+
if(process.argv[1] && import.meta.url===pathToFileURL(process.argv[1]).href) {
|
|
78
|
+
try {
|
|
79
|
+
const tree=load(readFileSync(0,'utf8'),{schema:entryListSchema});
|
|
80
|
+
let presetModule;
|
|
81
|
+
if (process.argv[3] && process.argv[4]) {
|
|
82
|
+
const requireHost=createRequire(realpathSync(process.argv[4]));
|
|
83
|
+
try {
|
|
84
|
+
const native=requireHost.resolve('@deepseek-ai/dsh-agent-presets');
|
|
85
|
+
presetModule=join(process.argv[3],'presets.mjs');
|
|
86
|
+
writeFileSync(presetModule, `import Native from ${JSON.stringify(pathToFileURL(native).href)};\nimport {presetAdapter} from ${JSON.stringify(new URL('./dsh-presets.mjs',import.meta.url).href)};\nexport default presetAdapter(Native, ${JSON.stringify(moduleReference(process.argv[2]))}, ${JSON.stringify(process.argv[3])});\n`, {mode:0o600});
|
|
87
|
+
} catch (error) { if(error.code!=='MODULE_NOT_FOUND') throw error; }
|
|
88
|
+
}
|
|
89
|
+
// The commands module sits beside the provider module (commands.mjs).
|
|
90
|
+
const commands=process.argv[2] ? moduleReference(join(dirname(process.argv[2]),'commands.mjs')) : undefined;
|
|
91
|
+
const patches=overlay(tree,moduleReference(process.argv[2]),presetModule && moduleReference(presetModule),commands);
|
|
92
|
+
if (process.argv[5]==='web-compat') {
|
|
93
|
+
const picker=tree.find(row=>!row.disabled && row.name==='@deepseek-ai/dsh-host-directory-picker-auto');
|
|
94
|
+
if (picker) {
|
|
95
|
+
if(!picker.id) throw Error('Directory picker has no stable ID');
|
|
96
|
+
patches.push({id:picker.id,name:picker.name,disabled:true}, {insert:[
|
|
97
|
+
{id:'afu-directory-backend',name:'@deepseek-ai/dsh-host-directory-picker-browse'},
|
|
98
|
+
{id:'afu-directory-ui',name:'@deepseek-ai/dsh-client-ui-directory-picker-browse'}
|
|
99
|
+
]});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
process.stdout.write(dump(patches,{schema:entryListSchema}));
|
|
103
|
+
}
|
|
104
|
+
catch {process.stderr.write('AFU: cannot safely locate DSH compaction scopes\n');process.exitCode=2;}
|
|
105
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Adapt only mount-time preset snapshots; discovery and authoring stay native. */
|
|
2
|
+
import {cp, mkdtemp, readFile, writeFile, unlink} from 'node:fs/promises';
|
|
3
|
+
import {dirname, join} from 'node:path';
|
|
4
|
+
import {createHash} from 'node:crypto';
|
|
5
|
+
import {load, dump} from 'js-yaml';
|
|
6
|
+
import {entryListSchema, applyEntryPatches} from '@deepseek-ai/cordis-plugin-include';
|
|
7
|
+
import {overlay} from './dsh-overlay.mjs';
|
|
8
|
+
|
|
9
|
+
export function presetAdapter(Native, module, scratch) {
|
|
10
|
+
return class AfuAgentPresets extends Native {
|
|
11
|
+
snapshots = new Map();
|
|
12
|
+
async resolveMountable(id) {
|
|
13
|
+
const preset = await super.resolveMountable(id);
|
|
14
|
+
try {
|
|
15
|
+
const source = await readFile(preset.path, 'utf8');
|
|
16
|
+
// COSEC: only host-authorized local preset config; this schema preserves
|
|
17
|
+
// !!js expressions as data. AFU never evaluates config expressions.
|
|
18
|
+
const entries = load(source, {schema:entryListSchema});
|
|
19
|
+
let patches;
|
|
20
|
+
try { patches = overlay(entries, module); }
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.message === 'No active basic compaction provider in the composed profile') return preset;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
const key = createHash('sha256').update(preset.path).update(source).digest('hex');
|
|
26
|
+
let pending = this.snapshots.get(key);
|
|
27
|
+
if (!pending) {
|
|
28
|
+
pending = (async () => {
|
|
29
|
+
// Random directory under the launcher's private temporary directory;
|
|
30
|
+
// never use a preset ID as a writable path or modify installed files.
|
|
31
|
+
const folder = await mkdtemp(join(scratch, 'preset-'));
|
|
32
|
+
await cp(dirname(preset.path), folder, {recursive:true, verbatimSymlinks:false});
|
|
33
|
+
const path = join(folder, 'agent.cordis.yml');
|
|
34
|
+
// COSEC: detach any copied symlink before writing the replacement.
|
|
35
|
+
await unlink(path);
|
|
36
|
+
await writeFile(path, dump(applyEntryPatches(entries, patches), {schema:entryListSchema}), {mode:0o600});
|
|
37
|
+
return {...preset, path};
|
|
38
|
+
})();
|
|
39
|
+
this.snapshots.set(key, pending);
|
|
40
|
+
pending.catch(() => this.snapshots.delete(key));
|
|
41
|
+
}
|
|
42
|
+
return await pending;
|
|
43
|
+
} catch {
|
|
44
|
+
this.ctx.logger.warn('AFU preset overlay unavailable; retaining native compaction');
|
|
45
|
+
return preset;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Read host capabilities without starting plugins or changing its profile. */
|
|
2
|
+
import {createRequire} from 'node:module';
|
|
3
|
+
import {realpathSync,readFileSync} from 'node:fs';
|
|
4
|
+
import {pathToFileURL} from 'node:url';
|
|
5
|
+
const host=createRequire(realpathSync(process.argv[2]));
|
|
6
|
+
try {
|
|
7
|
+
const boot=await import(pathToFileURL(host.resolve('@deepseek-ai/dsh-app-boot')).href);
|
|
8
|
+
boot.loadLayeredEnv('afu');
|
|
9
|
+
const dir=boot.resolveProfileDir(process.argv[3]);
|
|
10
|
+
const manifest=JSON.parse(readFileSync(new URL('package.json',pathToFileURL(dir+'/')),'utf8'));
|
|
11
|
+
let compatible=false;
|
|
12
|
+
try { const {default:Hmr}=await import(pathToFileURL(host.resolve('@deepseek-ai/cordis-plugin-hmr')).href);compatible=typeof Hmr?.prototype?.registerConfig==='function'; } catch {}
|
|
13
|
+
process.stdout.write(JSON.stringify({profileDir:dir,patchReload:manifest.dsh?.profile?.patchReload??'live',hmrCompatible:compatible}));
|
|
14
|
+
} catch { process.exitCode=2; }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { afuStatus, afuDashboard, STATUS_COMMAND, DASHBOARD_COMMAND } from "../../shared/runtime-client/dist/index.js";
|
|
2
|
+
export const name = "afu-context-commands";
|
|
3
|
+
export const inject = ["commands"];
|
|
4
|
+
export function apply(ctx) {
|
|
5
|
+
const commands = ctx.commands;
|
|
6
|
+
const answer = async (work) => {
|
|
7
|
+
try {
|
|
8
|
+
return { kind: "success", text: await work };
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return { kind: "error", text: "AFU command failed: " + String(error?.message ?? error) };
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
ctx.effect(function* () {
|
|
15
|
+
yield commands.register({
|
|
16
|
+
name: STATUS_COMMAND,
|
|
17
|
+
description: "Show AFU context-compaction status for this session",
|
|
18
|
+
handler: ({ agent }) => answer(afuStatus("dsh", agent.session.id)),
|
|
19
|
+
});
|
|
20
|
+
yield commands.register({
|
|
21
|
+
name: DASHBOARD_COMMAND,
|
|
22
|
+
description: "Open the AFU dashboard in the browser",
|
|
23
|
+
handler: () => answer(afuDashboard()),
|
|
24
|
+
});
|
|
25
|
+
}, "afu-context commands");
|
|
26
|
+
}
|
|
27
|
+
export default { name, inject, apply };
|
|
28
|
+
//# sourceMappingURL=commands.js.map
|