@aliyun-es/agentic-context 0.0.0-stage → 0.1.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/src/payload.js ADDED
@@ -0,0 +1,116 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { NAME, LEGACY_NAME, MARKETPLACE, VERSION, platformPath } from './config.js';
5
+ import { writePrivate } from './files.js';
6
+ import { contextText, requestHeaders } from '../runtime/client.js';
7
+
8
+ export const PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url));
9
+ export function shellQuote(value) {
10
+ return `'${value.replaceAll("'", "'\\''")}'`;
11
+ }
12
+ export function hookConfig(paths, agent, { platform = process.platform, execPath = process.execPath } = {}) {
13
+ const args = [platformPath(platform).join(paths.runtime, 'hook.js'), paths.config];
14
+ let invocation;
15
+ if (platform === 'win32' && agent === 'claude-code') {
16
+ invocation = { command: execPath, args };
17
+ } else if (platform === 'win32') {
18
+ // A fixed PowerShell launcher works from cmd, PowerShell and Git Bash without re-parsing paths.
19
+ const script = `[Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.UTF8Encoding]::new($false); & ${[execPath, ...args].map(value => `'${value.replaceAll("'", "''")}'`).join(' ')}; exit $LASTEXITCODE`;
20
+ invocation = { command: `powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script, 'utf16le').toString('base64')}` };
21
+ } else {
22
+ invocation = { command: [execPath, ...args].map(shellQuote).join(' ') };
23
+ }
24
+ const events = agent === 'codex' ? ['SessionStart', 'UserPromptSubmit', 'Stop'] : ['SessionStart', 'UserPromptSubmit', 'Stop', 'PreCompact'];
25
+ return { hooks: Object.fromEntries(events.map(event => [event, [{ hooks: [{ type: 'command', ...invocation, timeout: 15 }] }]])) };
26
+ }
27
+ export function mcpServer(config, agent) {
28
+ const base = { url: `${config.endpoint}/v1/agentic-memory/mcp` };
29
+ const headers = { ...requestHeaders(config), Accept: 'application/json, text/event-stream' };
30
+ if (agent === 'codex') return { ...base, http_headers: headers };
31
+ if (agent === 'hermes') return { ...base, headers };
32
+ if (agent === 'openclaw') return { ...base, transport: 'streamable-http', headers };
33
+ return { ...base, type: 'http', headers };
34
+ }
35
+
36
+ async function copyRuntime(destination) {
37
+ await fs.cp(path.join(PACKAGE_ROOT, 'runtime'), destination, { recursive: true });
38
+ await writePrivate(path.join(destination, 'package.json'), { type: 'module' });
39
+ const parserRoot = path.dirname(fileURLToPath(import.meta.resolve('eventsource-parser/package.json')));
40
+ await fs.cp(parserRoot, path.join(destination, 'node_modules/eventsource-parser'), { recursive: true });
41
+ }
42
+
43
+ export function requiredPayloadFiles(agent, paths) {
44
+ const skillName = paths.skillName || NAME;
45
+ const runtime = agent === 'openclaw' ? path.join(paths.plugin, 'runtime') : paths.runtime;
46
+ // Legacy releases predate automatic recall and the submission helper. Check
47
+ // their original entry points, not files introduced by a newer release.
48
+ const shared = paths.legacy ? (agent === 'hermes' ? [] : agent === 'openclaw' ? ['client.js'] : ['package.json', 'client.js']) : [
49
+ 'package.json', 'client.js', 'mcp.js', 'recall.js', 'version.js',
50
+ 'node_modules/eventsource-parser/package.json', 'node_modules/eventsource-parser/dist/index.js'];
51
+ const entrypoints = agent === 'hermes' ? (paths.legacy ? [] : ['recall-cli.js'])
52
+ : ['capture.js', ...(agent === 'openclaw' ? (paths.legacy ? [] : ['submission.js']) : ['hook.js'])];
53
+ const files = [...shared, ...entrypoints].map(file => path.join(runtime, file));
54
+ files.push(path.join(paths.plugin, 'skills', skillName, 'SKILL.md'));
55
+ if (['codex', 'claude-code'].includes(agent)) {
56
+ const host = agent === 'codex' ? '.codex-plugin' : '.claude-plugin';
57
+ files.push(...[`${host}/plugin.json`, '.mcp.json', 'hooks/hooks.json'].map(file => path.join(paths.plugin, file)),
58
+ path.join(paths.marketplace, agent === 'codex' ? '.agents/plugins/marketplace.json' : '.claude-plugin/marketplace.json'));
59
+ } else if (agent === 'openclaw') {
60
+ files.push(...['index.js', 'package.json', 'openclaw.plugin.json'].map(file => path.join(paths.plugin, file)));
61
+ } else if (agent === 'hermes') {
62
+ if (!paths.legacy) files.push(path.join(paths.state, 'recall-command.json'));
63
+ files.push(...['__init__.py', 'plugin.yaml'].map(file => path.join(paths.plugin, 'provider', file)));
64
+ }
65
+ return files;
66
+ }
67
+
68
+ export async function preparePayload(agent, paths, config) {
69
+ const name = paths.name || NAME;
70
+ const marketplaceName = paths.marketplaceName || MARKETPLACE;
71
+ const skillName = paths.skillName || NAME;
72
+ await fs.mkdir(paths.state, { recursive: true, mode: 0o700 });
73
+ await fs.chmod(paths.state, 0o700);
74
+ if (agent !== 'openclaw') await copyRuntime(paths.runtime);
75
+ const skill = (await fs.readFile(path.join(PACKAGE_ROOT, 'skills/agentic-context/SKILL.md'), 'utf8'))
76
+ + `\n## Connection\n\n${contextText(config)}\n`;
77
+ await writePrivate(path.join(paths.plugin, 'skills', skillName, 'SKILL.md'), skill);
78
+ const manifest = { name, version: VERSION, description: 'Agentic Context: memory, knowledge bases and skills', author: { name: 'Alibaba Cloud' } };
79
+ if (['claude-code', 'codex'].includes(agent)) {
80
+ const host = agent === 'codex' ? '.codex-plugin' : '.claude-plugin';
81
+ await writePrivate(path.join(paths.plugin, host, 'plugin.json'), {
82
+ ...manifest, ...(agent === 'codex' ? {
83
+ skills: './skills/', mcpServers: './.mcp.json',
84
+ interface: {
85
+ displayName: 'Agentic Context', shortDescription: 'Memory, knowledge bases and skills',
86
+ longDescription: 'Connect an existing Agentic Context workspace to recall memory, search enterprise documents and retrieve reusable skills.',
87
+ developerName: 'Alibaba Cloud', category: 'Productivity', capabilities: ['Read', 'Write'],
88
+ defaultPrompt: 'Use Agentic Context to find relevant context for my task.',
89
+ },
90
+ } : {}),
91
+ });
92
+ await writePrivate(path.join(paths.plugin, '.mcp.json'), { mcpServers: { [name]: mcpServer(config, agent) } });
93
+ await writePrivate(path.join(paths.plugin, 'hooks/hooks.json'), hookConfig(paths, agent));
94
+ const catalog = agent === 'codex'
95
+ ? { name: marketplaceName, interface: { displayName: 'Alibaba Cloud Agentic Context' }, plugins: [{ name, source: { source: 'local', path: `./plugins/${name}` }, policy: { installation: 'AVAILABLE', authentication: 'ON_INSTALL' }, category: 'Productivity' }] }
96
+ : { name: marketplaceName, owner: { name: 'Alibaba Cloud' }, plugins: [{ ...manifest, source: `./plugins/${name}` }] };
97
+ await writePrivate(path.join(paths.marketplace, agent === 'codex' ? '.agents/plugins/marketplace.json' : '.claude-plugin/marketplace.json'), catalog);
98
+ }
99
+ if (agent === 'openclaw') {
100
+ await copyRuntime(path.join(paths.plugin, 'runtime'));
101
+ await fs.copyFile(path.join(PACKAGE_ROOT, 'adapters/openclaw/index.js'), path.join(paths.plugin, 'index.js'));
102
+ await writePrivate(path.join(paths.plugin, 'package.json'), { name: '@aliyun-es/agentic-context', version: VERSION, type: 'module', openclaw: { extensions: ['./index.js'] } });
103
+ await writePrivate(path.join(paths.plugin, 'openclaw.plugin.json'), {
104
+ id: name, name: 'Agentic Context', version: VERSION, skills: ['./skills'],
105
+ activation: { onStartup: true },
106
+ cliCommands: [name, LEGACY_NAME].map(command => ({ name: command, description: 'Agentic Context memory and skill operations', hasSubcommands: true })),
107
+ // OpenClaw validates config during install, before the explicit plugin entry is written.
108
+ configSchema: { type: 'object', properties: { configPath: { type: 'string', default: paths.config } }, required: ['configPath'], additionalProperties: false },
109
+ });
110
+ }
111
+ if (agent === 'hermes') {
112
+ await fs.cp(path.join(PACKAGE_ROOT, 'adapters/hermes'), path.join(paths.plugin, 'provider'), { recursive: true });
113
+ await writePrivate(path.join(paths.state, 'recall-command.json'), [process.execPath, path.join(paths.runtime, 'recall-cli.js'), paths.config]);
114
+ }
115
+ return skill;
116
+ }
package/src/redact.js ADDED
@@ -0,0 +1,37 @@
1
+ export function createRedactor(secrets, write) {
2
+ const values = secrets.filter(Boolean).sort((a, b) => b.length - a.length);
3
+ let pending = '', bearer = false;
4
+ return (text, final = false) => {
5
+ pending += text;
6
+ let output = '';
7
+ while (pending) {
8
+ if (bearer) {
9
+ const end = pending.search(/[\s"']/);
10
+ if (end === -1) { pending = ''; break; }
11
+ pending = pending.slice(end);
12
+ bearer = false;
13
+ }
14
+ // Retain partial secrets between chunks; emit ordinary prompts immediately.
15
+ if (!final && (values.some(value => value.startsWith(pending))
16
+ || (pending.length <= 7 && 'bearer '.startsWith(pending.toLowerCase())))) break;
17
+ const secret = values.find(value => pending.startsWith(value));
18
+ if (secret) { output += '[REDACTED]'; pending = pending.slice(secret.length); continue; }
19
+ const authorization = /^Bearer\s+/i.exec(pending);
20
+ if (authorization) {
21
+ output += 'Bearer [REDACTED]';
22
+ pending = pending.slice(authorization[0].length);
23
+ bearer = true;
24
+ continue;
25
+ }
26
+ output += pending[0];
27
+ pending = pending.slice(1);
28
+ }
29
+ if (output) write(output);
30
+ };
31
+ }
32
+
33
+ export function redact(text, secrets) {
34
+ let result = '';
35
+ createRedactor(secrets, part => { result += part; })(text, true);
36
+ return result;
37
+ }