@sleep2agi/agent-network 2.3.0-preview.37 → 2.3.0-preview.39
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/dist/bin/cli.js +20 -1
- package/dist/src/batch-workdir.d.ts +9 -0
- package/dist/src/bootstrap-password-db.d.ts +13 -0
- package/dist/src/channel-attachments.d.ts +29 -0
- package/dist/src/channel-task-trace.d.ts +10 -0
- package/dist/src/claude-vendor-env.d.ts +28 -0
- package/dist/src/cli-args.d.ts +8 -0
- package/dist/src/client-task-trace.d.ts +9 -0
- package/dist/src/client.js +1 -1
- package/dist/src/codex-model-default.d.ts +6 -0
- package/dist/src/controlled-upload.d.ts +83 -0
- package/dist/src/dashboard-managed-process.d.ts +35 -0
- package/dist/src/grok-attach-client.d.ts +115 -0
- package/dist/src/grok-copresence-disclosure.d.ts +11 -0
- package/dist/src/grok-copresence-profile.d.ts +49 -0
- package/dist/src/im/feishu/adapter.d.ts +43 -0
- package/dist/src/im/feishu/bridge.d.ts +2 -0
- package/dist/src/im/feishu/worker-lifecycle.d.ts +13 -0
- package/dist/src/im/feishu/worker.js +120 -119
- package/dist/src/locale-diagnostic.d.ts +12 -0
- package/dist/src/mock-llm.d.ts +12 -0
- package/dist/src/node-server.js +7 -1
- package/dist/src/normalize-runtime.d.ts +1 -1
- package/dist/src/opencode-agent-node-pair.d.ts +2 -2
- package/dist/src/owner-env-file.d.ts +2 -0
- package/dist/src/primary-network.d.ts +23 -0
- package/dist/src/private-state.d.ts +10 -0
- package/dist/src/secret-shell-guidance.d.ts +3 -0
- package/dist/src/task-trace.d.ts +33 -0
- package/dist/src/tmux-attach.d.ts +8 -0
- package/dist/src/token-cli.d.ts +13 -0
- package/package.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve one batch workdir before any mkdir/chdir loop starts.
|
|
3
|
+
*
|
|
4
|
+
* Node does not expand shell tildes. Leaving `~/team` relative means every
|
|
5
|
+
* iteration can resolve it from the previous node's cwd and create paths such
|
|
6
|
+
* as `/work/~/team/~/team`. Only the current user's `~` form is supported;
|
|
7
|
+
* `~other` is rejected instead of being written as a literal directory.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeBatchWorkdir(input: string, cwd?: string, home?: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface BootstrapPasswordUpdateInvocation {
|
|
2
|
+
argv: string[];
|
|
3
|
+
env: NodeJS.ProcessEnv;
|
|
4
|
+
script: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the database selected for the local Hub into an explicit absolute
|
|
8
|
+
* path before launching the bootstrap helper. The helper must never infer a
|
|
9
|
+
* database from its own HOME/cwd: those can differ from the parent process.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveBootstrapDatabasePath(env: NodeJS.ProcessEnv, home: string, cwd: string): string;
|
|
12
|
+
export declare const BOOTSTRAP_PASSWORD_UPDATE_SCRIPT: string;
|
|
13
|
+
export declare function buildBootstrapPasswordUpdateInvocation(userId: string, dbPath: string, baseEnv?: NodeJS.ProcessEnv): BootstrapPasswordUpdateInvocation;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface AttachmentDescriptor {
|
|
2
|
+
type?: unknown;
|
|
3
|
+
file_id?: unknown;
|
|
4
|
+
path?: unknown;
|
|
5
|
+
mime?: unknown;
|
|
6
|
+
name?: unknown;
|
|
7
|
+
size?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface ChannelAttachmentDeps {
|
|
10
|
+
hubUrl: string;
|
|
11
|
+
authToken: string;
|
|
12
|
+
cacheDir: string;
|
|
13
|
+
fetch?: typeof fetch;
|
|
14
|
+
maxBytes?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ChannelAttachmentResult {
|
|
17
|
+
paths: string[];
|
|
18
|
+
failures: Array<{
|
|
19
|
+
fileId: string;
|
|
20
|
+
code: string;
|
|
21
|
+
message: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare function readableAttachmentsFromInbox(message: any): AttachmentDescriptor[];
|
|
25
|
+
export declare function channelAttachmentCacheDir(home: string, alias: string): string;
|
|
26
|
+
export declare function downloadChannelAttachments(message: any, deps: ChannelAttachmentDeps): Promise<ChannelAttachmentResult>;
|
|
27
|
+
export declare const downloadChannelImageAttachments: typeof downloadChannelAttachments;
|
|
28
|
+
export declare function appendChannelAttachmentPaths(content: string, paths: readonly string[]): string;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function sendChannelTaskWithTrace(input: {
|
|
2
|
+
alias: string;
|
|
3
|
+
task: string;
|
|
4
|
+
priority?: string;
|
|
5
|
+
fromAlias: string;
|
|
6
|
+
networkId?: string | null;
|
|
7
|
+
}, deps: {
|
|
8
|
+
send: (args: Record<string, unknown>) => Promise<any>;
|
|
9
|
+
log: (line: string) => void;
|
|
10
|
+
}): Promise<any>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const CLAUDE_VENDOR_ENV_KEYS: readonly ["ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_API_KEY"];
|
|
2
|
+
export type PlainSecretEnvRewrite = {
|
|
3
|
+
key: string;
|
|
4
|
+
refName: string;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Plan the values that the node-create writer will move into its line-oriented
|
|
9
|
+
* `.env` file. This is deliberately side-effect free: callers can run it as a
|
|
10
|
+
* preflight before creating a node directory, changing process.env, or writing
|
|
11
|
+
* config.json. Keeping the line-break rejection here makes it cover every
|
|
12
|
+
* caller of the dotenv writer, including the no-name interactive wizard.
|
|
13
|
+
*/
|
|
14
|
+
export declare function planPlainSecretEnvRewrites(input: {
|
|
15
|
+
env: unknown;
|
|
16
|
+
nodeId: string;
|
|
17
|
+
}): PlainSecretEnvRewrite[];
|
|
18
|
+
/**
|
|
19
|
+
* Persist the exact Anthropic-compatible vendor environment used by an
|
|
20
|
+
* explicit claude-agent-sdk create. Explicit --env values retain precedence;
|
|
21
|
+
* ambient shell values only fill missing known keys. Other environment values
|
|
22
|
+
* are intentionally ignored so node create never snapshots the whole shell.
|
|
23
|
+
*/
|
|
24
|
+
export declare function collectClaudeVendorEnvForCreate(input: {
|
|
25
|
+
runtime: string;
|
|
26
|
+
explicitEnv: readonly string[];
|
|
27
|
+
shellEnv: Readonly<Record<string, string | undefined>>;
|
|
28
|
+
}): string[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ParsedCliOptions = {
|
|
2
|
+
[key: string]: string | string[];
|
|
3
|
+
_channels: string[];
|
|
4
|
+
_envs: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare const BOOLEAN_FLAGS: Set<string>;
|
|
7
|
+
export declare function parseCliOptions(argv: string[]): ParsedCliOptions;
|
|
8
|
+
export declare function positionalArgs(argv: string[]): string[];
|
package/dist/src/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function a0_0x6de1(_0x4e3e59,_0x1b9c4f){_0x4e3e59=_0x4e3e59-0xea;const _0x40fe82=a0_0x40fe();let _0x6de1e1=_0x40fe82[_0x4e3e59];return _0x6de1e1;}const a0_0x230d08=a0_0x6de1;function a0_0x40fe(){const _0x14968a=['url','slice','parse','new_task','sseAbort','text/event-stream','report_status','replace','emit','type','4091458iZWsQm','toTimeString','144765bkuQHm','/events/','catch','heartbeatInterval','getAllStatus','24726GPhIpb','token','connectSSE','520RwIKTP','running','10BPzsZx','signal','reconnectDelay','15568289UrBTPk','SSE\x20failed:\x20','new_message','disconnect','startsWith','text','5490CuQXkh','error','decode','Bearer\x20','get_inbox','s...','695eQhJwC','stringify','content','log','connected',']\x20[commhub:','now','task','sdk','normal','processInbox','sdk-','cwd','Authorization','min','agent','heartbeat\x20failed:\x20','message','get_all_status','status','pop','inbox\x20error:\x20','autoConnect','2.0','application/json,\x20text/event-stream','SSE\x20connected','connect','514lHxAnX','send_message','resumeId','call','disconnected','3430986Elwrzi','5313740UULazF','/mcp','send','getReader','reply','tools/call','alias','body','messages','ack_inbox','name','data:\x20','heartbeatTimer','read','broadcast','server','sleep','send_reply'];a0_0x40fe=function(){return _0x14968a;};return a0_0x40fe();}(function(_0x6bcbe2,_0x394dd0){const _0x28270e=a0_0x6de1,_0x11eb5c=_0x6bcbe2();while(!![]){try{const _0x3862fd=parseInt(_0x28270e(0x131))/0x1*(-parseInt(_0x28270e(0xfa))/0x2)+parseInt(_0x28270e(0xff))/0x3+-parseInt(_0x28270e(0x100))/0x4+parseInt(_0x28270e(0x137))/0x5*(-parseInt(_0x28270e(0x123))/0x6)+parseInt(_0x28270e(0x11c))/0x7+-parseInt(_0x28270e(0x126))/0x8*(-parseInt(_0x28270e(0x11e))/0x9)+parseInt(_0x28270e(0x128))/0xa*(parseInt(_0x28270e(0x12b))/0xb);if(_0x3862fd===_0x394dd0)break;else _0x11eb5c['push'](_0x11eb5c['shift']());}catch(_0x2fac3a){_0x11eb5c['push'](_0x11eb5c['shift']());}}}(a0_0x40fe,0xd60fc));import{EventEmitter as a0_0x5ce8b4}from'events';import{hostname as a0_0x37fdd8}from'os';class T extends a0_0x5ce8b4{[a0_0x230d08(0x112)];[a0_0x230d08(0x106)];[a0_0x230d08(0x124)];[a0_0x230d08(0xee)];['resumeId'];[a0_0x230d08(0x121)];[a0_0x230d08(0x12a)];[a0_0x230d08(0x10c)];['sseAbort'];[a0_0x230d08(0x127)]=!0x1;constructor(_0x5bc09d){const _0x5c0a7b=a0_0x230d08;super();if(this[_0x5c0a7b(0x112)]=_0x5bc09d[_0x5c0a7b(0x112)][_0x5c0a7b(0x119)](/\/$/,''),this[_0x5c0a7b(0x106)]=_0x5bc09d[_0x5c0a7b(0x106)],this[_0x5c0a7b(0x124)]=_0x5bc09d['token'],this[_0x5c0a7b(0xee)]=_0x5bc09d['agent']||_0x5c0a7b(0x13f),this[_0x5c0a7b(0xfc)]=_0x5c0a7b(0xea)+_0x5bc09d[_0x5c0a7b(0x106)]+'-'+Date[_0x5c0a7b(0x13d)]()['toString'](0x24),this[_0x5c0a7b(0x121)]=_0x5bc09d[_0x5c0a7b(0x121)]??0x2bf20,this[_0x5c0a7b(0x12a)]=_0x5bc09d[_0x5c0a7b(0x12a)]??0xbb8,_0x5bc09d[_0x5c0a7b(0xf5)]!==!0x1)this[_0x5c0a7b(0xf9)]();}[a0_0x230d08(0x13a)](_0x1f9b0e){const _0x275549=a0_0x230d08;console[_0x275549(0x13a)]('['+new Date()[_0x275549(0x11d)]()[_0x275549(0x113)](0x0,0x8)+_0x275549(0x13c)+this['alias']+']\x20'+_0x1f9b0e);}async['call'](_0x29f9c4,_0x201e70){const _0x411ac6=a0_0x230d08;let _0x220afa={'Content-Type':'application/json','Accept':_0x411ac6(0xf7)};if(this['token'])_0x220afa[_0x411ac6(0xec)]='Bearer\x20'+this['token'];let _0x4b27f6=await(await fetch(this[_0x411ac6(0x112)]+_0x411ac6(0x101),{'method':'POST','headers':_0x220afa,'body':JSON[_0x411ac6(0x138)]({'jsonrpc':_0x411ac6(0xf6),'id':Date[_0x411ac6(0x13d)](),'method':_0x411ac6(0x105),'params':{'name':_0x29f9c4,'arguments':_0x201e70}})}))[_0x411ac6(0x130)](),_0x5acd89=_0x4b27f6['match'](/data: (.+)/),_0x372246=_0x5acd89?JSON[_0x411ac6(0x114)](_0x5acd89[0x1]):JSON[_0x411ac6(0x114)](_0x4b27f6),_0x92053b=_0x372246?.['result']?.[_0x411ac6(0x139)]?.[0x0]?.['text'];return _0x92053b?JSON[_0x411ac6(0x114)](_0x92053b):_0x372246;}async[a0_0x230d08(0xf9)](){const _0x2ebdc6=a0_0x230d08;if(this[_0x2ebdc6(0x127)])return;this[_0x2ebdc6(0x127)]=!0x0,await this['status']('idle'),this[_0x2ebdc6(0x13a)]('registered'),this['heartbeatTimer']=setInterval(()=>{const _0x2f5259=_0x2ebdc6;this[_0x2f5259(0xf2)]('idle')[_0x2f5259(0x120)](_0x57cd61=>this[_0x2f5259(0x13a)](_0x2f5259(0xef)+_0x57cd61[_0x2f5259(0xf0)]));},this['heartbeatInterval']),this[_0x2ebdc6(0x125)]();}async[a0_0x230d08(0x12e)](){const _0x183f5f=a0_0x230d08;if(this[_0x183f5f(0x127)]=!0x1,this[_0x183f5f(0x116)]?.['abort'](),this[_0x183f5f(0x10c)])clearInterval(this['heartbeatTimer']);await this['status']('offline')[_0x183f5f(0x120)](()=>{}),this['log'](_0x183f5f(0xfe));}async[a0_0x230d08(0x102)](_0x3a7d95,_0x47a952,_0x36398e=a0_0x230d08(0x140)){const _0x2a160d=a0_0x230d08;return this[_0x2a160d(0xfd)]('send_task',{'alias':_0x3a7d95,'task':_0x47a952,'priority':_0x36398e,'from_session':this[_0x2a160d(0x106)]});}async[a0_0x230d08(0xf0)](_0x484042,_0xf535cf){const _0x24802d=a0_0x230d08;return this[_0x24802d(0xfd)](_0x24802d(0xfb),{'alias':_0x484042,'message':_0xf535cf,'from_session':this[_0x24802d(0x106)]});}async[a0_0x230d08(0x104)](_0x394ec4,_0x1abafa,_0x1408f2='completed'){const _0x144317=a0_0x230d08;return this[_0x144317(0xfd)](_0x144317(0x111),{'in_reply_to':_0x394ec4,'text':_0x1abafa,'status':_0x1408f2});}async[a0_0x230d08(0xf2)](_0x56e212,_0x2c4ef9){const _0x226ed9=a0_0x230d08;return this['call'](_0x226ed9(0x118),{'resume_id':this['resumeId'],'alias':this[_0x226ed9(0x106)],'status':_0x56e212,'server':a0_0x37fdd8(),'hostname':a0_0x37fdd8(),'agent':this[_0x226ed9(0xee)],'project_dir':process[_0x226ed9(0xeb)](),..._0x2c4ef9});}async[a0_0x230d08(0x122)](){const _0x4103bf=a0_0x230d08;return this[_0x4103bf(0xfd)](_0x4103bf(0xf1),{});}async[a0_0x230d08(0x10e)](_0x94aed9,_0x54955a){const _0x343276=a0_0x230d08;return this[_0x343276(0xfd)](_0x343276(0x10e),{'message':_0x94aed9,'filter_server':_0x54955a?.[_0x343276(0x10f)],'filter_status':_0x54955a?.['status']});}async[a0_0x230d08(0x125)](){const _0x3a741a=a0_0x230d08;let _0x38bfda=encodeURIComponent(this[_0x3a741a(0x106)]),_0x62700f=this[_0x3a741a(0x112)]+_0x3a741a(0x11f)+_0x38bfda,_0x18d07f=this[_0x3a741a(0x12a)];while(this[_0x3a741a(0x127)]){try{this['sseAbort']=new AbortController();let _0x386080={'Accept':_0x3a741a(0x117)};if(this['token'])_0x386080[_0x3a741a(0xec)]=_0x3a741a(0x134)+this['token'];let _0x25c195=await fetch(_0x62700f,{'headers':_0x386080,'signal':this[_0x3a741a(0x116)][_0x3a741a(0x129)]});if(!_0x25c195['ok']||!_0x25c195[_0x3a741a(0x107)]){this[_0x3a741a(0x13a)](_0x3a741a(0x12c)+_0x25c195[_0x3a741a(0xf2)]),await this['sleep'](_0x18d07f),_0x18d07f=Math[_0x3a741a(0xed)](_0x18d07f*1.5,0xea60);continue;}_0x18d07f=this['reconnectDelay'];let _0x223947=_0x25c195[_0x3a741a(0x107)][_0x3a741a(0x103)](),_0x47e6d3=new TextDecoder(),_0x291c17='';while(this[_0x3a741a(0x127)]){let {done:_0x7a321d,value:_0x5719eb}=await _0x223947[_0x3a741a(0x10d)]();if(_0x7a321d)break;_0x291c17+=_0x47e6d3[_0x3a741a(0x133)](_0x5719eb,{'stream':!0x0});let _0x544ff5=_0x291c17['split']('\x0a');_0x291c17=_0x544ff5[_0x3a741a(0xf3)]()||'';for(let _0xa90ef6 of _0x544ff5){if(!_0xa90ef6[_0x3a741a(0x12f)](_0x3a741a(0x10b)))continue;try{let _0x4003a3=JSON[_0x3a741a(0x114)](_0xa90ef6[_0x3a741a(0x113)](0x6));if(_0x4003a3[_0x3a741a(0x11b)]===_0x3a741a(0x13b)){this[_0x3a741a(0x13a)](_0x3a741a(0xf8)),this[_0x3a741a(0x11a)](_0x3a741a(0x13b));continue;}if(_0x4003a3['type']===_0x3a741a(0x115)||_0x4003a3['type']===_0x3a741a(0x12d)||_0x4003a3[_0x3a741a(0x11b)]===_0x3a741a(0x10e))await this[_0x3a741a(0x141)]();}catch{}}}}catch(_0x56ecd7){if(_0x56ecd7[_0x3a741a(0x10a)]==='AbortError')break;this[_0x3a741a(0x11a)](_0x3a741a(0x132),_0x56ecd7),this[_0x3a741a(0x13a)]('SSE\x20error:\x20'+_0x56ecd7['message']);}if(this[_0x3a741a(0x127)])this[_0x3a741a(0x11a)](_0x3a741a(0xfe)),this[_0x3a741a(0x13a)]('SSE\x20reconnecting\x20in\x20'+_0x18d07f/0x3e8+_0x3a741a(0x136)),await this['sleep'](_0x18d07f),_0x18d07f=Math[_0x3a741a(0xed)](_0x18d07f*1.5,0xea60);}}async[a0_0x230d08(0x141)](){const _0x549c3d=a0_0x230d08;try{let _0x4348a1=(await this[_0x549c3d(0xfd)](_0x549c3d(0x135),{'alias':this[_0x549c3d(0x106)],'limit':0xa}))?.[_0x549c3d(0x108)]||[];for(let _0x212c2b of _0x4348a1)await this[_0x549c3d(0xfd)](_0x549c3d(0x109),{'alias':this[_0x549c3d(0x106)],'message_id':_0x212c2b['id']}),this[_0x549c3d(0x13a)]('←\x20'+_0x212c2b['from_session']+':\x20'+_0x212c2b[_0x549c3d(0x139)][_0x549c3d(0x113)](0x0,0x3c)),this['emit'](_0x549c3d(0x13e),_0x212c2b),this[_0x549c3d(0x11a)](_0x549c3d(0xf0),_0x212c2b);}catch(_0x39cfba){this[_0x549c3d(0x13a)](_0x549c3d(0xf4)+_0x39cfba['message']);}}[a0_0x230d08(0x110)](_0x3aad9b){return new Promise(_0x44066d=>setTimeout(_0x44066d,_0x3aad9b));}}var $=T;export{$ as default,T as CommHub};
|
|
1
|
+
const a0_0x275b2c=a0_0x5183;(function(_0x1899e4,_0x2c3e24){const _0x389bbc=a0_0x5183,_0x541eb9=_0x1899e4();while(!![]){try{const _0x3ae317=-parseInt(_0x389bbc(0x103))/0x1*(-parseInt(_0x389bbc(0x10e))/0x2)+-parseInt(_0x389bbc(0x14a))/0x3+parseInt(_0x389bbc(0x130))/0x4*(-parseInt(_0x389bbc(0x164))/0x5)+-parseInt(_0x389bbc(0x176))/0x6*(-parseInt(_0x389bbc(0x10c))/0x7)+parseInt(_0x389bbc(0x138))/0x8*(-parseInt(_0x389bbc(0x159))/0x9)+-parseInt(_0x389bbc(0x11a))/0xa+parseInt(_0x389bbc(0xf9))/0xb*(parseInt(_0x389bbc(0x139))/0xc);if(_0x3ae317===_0x2c3e24)break;else _0x541eb9['push'](_0x541eb9['shift']());}catch(_0x2beda3){_0x541eb9['push'](_0x541eb9['shift']());}}}(a0_0x25fe,0x5569c));import{EventEmitter as a0_0x5ae740}from'events';import{hostname as a0_0x4a8173}from'os';var K=/(?:atok|ntok|utok|ghp|github_pat)_[A-Za-z0-9_-]+|Bearer\s+\S+/gi,L=(_0x4743b1,_0x387712)=>(_0x4743b1??_0x387712)['replace'](/[\r\n\x00-\x1f\x7f]/g,'\x20')['slice'](0x0,0xf0);function N(_0x34379d){const _0x48c8ff=a0_0x5183;return(_0x34379d instanceof Error?_0x34379d[_0x48c8ff(0x165)]:String(_0x34379d??'unknown\x20error'))[_0x48c8ff(0xfb)](K,_0x48c8ff(0x132))[_0x48c8ff(0xfb)](/[\r\n\x00-\x1f\x7f]/g,'\x20')[_0x48c8ff(0x142)](0x0,0xf0);}function O(_0x64e4ee,_0x475a73=process.env.ANET_TASK_TRACE_FORMAT===a0_0x275b2c(0xfe)){const _0xe76c26=a0_0x275b2c;if(_0x475a73)return JSON[_0xe76c26(0xfc)](_0x64e4ee);let _0x5a7033=L(_0x64e4ee[_0xe76c26(0x118)],_0xe76c26(0x149)),_0x2a3bbf=L(_0x64e4ee[_0xe76c26(0x15f)],_0xe76c26(0x17d)),_0x1eb80c=_0x64e4ee[_0xe76c26(0x106)]?'\x20error='+_0x64e4ee['error_code']+':'+(_0x64e4ee[_0xe76c26(0x154)]??''):'';return _0xe76c26(0x121)+_0x64e4ee[_0xe76c26(0x178)]+_0xe76c26(0x10f)+L(_0x64e4ee[_0xe76c26(0x155)],_0xe76c26(0x124))+_0xe76c26(0x136)+L(_0x64e4ee['to_alias'],'<unknown>')+_0xe76c26(0x166)+_0x2a3bbf+'\x20parent_task_id='+_0x5a7033+_0xe76c26(0x146)+L(_0x64e4ee[_0xe76c26(0x137)],'<unknown>')+_0xe76c26(0x10b)+_0x64e4ee[_0xe76c26(0x115)]+'\x20duration_ms='+_0x64e4ee[_0xe76c26(0x13d)]+'\x20lifecycle='+_0x64e4ee[_0xe76c26(0x141)]+_0x1eb80c;}function U(_0x2eaa25){const _0x4a10df=a0_0x275b2c;return{'event':{'sending':_0x4a10df(0x15b),'delivered':_0x4a10df(0x123),'failed':'task.send.failed','acked':_0x4a10df(0x152),'started':_0x4a10df(0x11d),'replied':_0x4a10df(0x108),'expired':'task.expired'}[_0x2eaa25[_0x4a10df(0x178)]],..._0x2eaa25};}function j(_0x15e281){const _0x47f926=a0_0x275b2c;let _0x77ac62=_0x15e281?.[_0x47f926(0x15f)]||_0x15e281?.[_0x47f926(0x169)]||_0x15e281?.['id'];if(_0x77ac62)return String(_0x77ac62);let _0x35281d=_0x15e281?.[_0x47f926(0x150)]?.[0x0]?.[_0x47f926(0x17b)];if(typeof _0x35281d!==_0x47f926(0x101))return null;try{let _0x43be0f=JSON[_0x47f926(0x135)](_0x35281d);return _0x43be0f?.[_0x47f926(0x15f)]||_0x43be0f?.[_0x47f926(0x169)]||_0x43be0f?.['id']||null;}catch{return null;}}async function X(_0x5ecc93,_0x1155af){const _0x5c5188=a0_0x275b2c;let _0x9b96b5=_0x1155af['now']??Date[_0x5c5188(0x13a)],_0x3f9c06=_0x9b96b5(),_0x467551=(_0x62eec,_0x352098,_0x1d0c38,_0xfe19e3)=>{const _0x250fd7=_0x5c5188;_0x1155af[_0x250fd7(0x112)](O(U({'from_alias':_0x5ecc93['fromAlias'],'to_alias':_0x5ecc93[_0x250fd7(0xff)],'task_id':_0x352098,'parent_task_id':_0x5ecc93['parentTaskId'],'network_id':_0x5ecc93[_0x250fd7(0x161)],'transport':_0x5ecc93[_0x250fd7(0x115)],'status':_0x62eec,'duration_ms':_0x9b96b5()-_0x3f9c06,'lifecycle_tracking':_0x5ecc93[_0x250fd7(0x15d)],..._0x1d0c38!==void 0x0?{'error_code':_0xfe19e3||_0x250fd7(0x11f),'error_message':N(_0x1d0c38)}:{}})));};_0x467551('sending',null);try{let _0x221bd3=await _0x1155af[_0x5c5188(0x11c)](),_0x1e21d7=j(_0x221bd3);if(_0x1e21d7)_0x467551(_0x5c5188(0x126),_0x1e21d7);else{if(_0x221bd3?.['ok']===!0x1||_0x221bd3?.[_0x5c5188(0x13b)])_0x467551(_0x5c5188(0x133),_0x1e21d7,_0x221bd3?.[_0x5c5188(0x13b)]||_0x5c5188(0x156),_0x5c5188(0x153));else _0x467551(_0x5c5188(0x133),null,_0x5c5188(0x167),_0x5c5188(0x15e));}return _0x221bd3;}catch(_0x265dd6){throw _0x467551(_0x5c5188(0x133),null,_0x265dd6,_0x5c5188(0x11f)),_0x265dd6;}}function a0_0x25fe(){const _0xb9dfae=['error_code','type','task.replied','application/json','Authorization','\x20transport=','810859gFhvDy','alias','2QtQwAI','\x20from=','SSE\x20connected','send_reply','log','get_all_status','normal','transport','messages','toTimeString','parent_task_id','toString','2797230WXXUnF','min','send','task.started','connect','send_failed','Bearer\x20','[commhub:task]\x20','from_session','task.send.delivered','<unknown>','catch','delivered','tools/call','startsWith','new_message','cwd','report_status','agent','broadcast','offline','token','2328412dtpHim','/events/','[REDACTED]','failed','name','parse','\x20to=','network_id','8PwpKDP','16622268xsqAyf','now','error','call','duration_ms','abort','emit','not_tracked','lifecycle_tracking','slice','SSE\x20error:\x20','server','disconnected','\x20network_id=','POST','/mcp','<missing>','1706547nWmBRh','get_inbox','resumeId','sleep','decode','heartbeat\x20failed:\x20','content','sdk-','task.ack','send_rejected','error_message','from_alias','CommHub\x20rejected\x20task','heartbeatTimer','url','2211723zhhLSV','body','task.send.start','running','lifecycleTracking','missing_task_id','task_id','sseAbort','networkId','application/json,\x20text/event-stream','heartbeatInterval','5jCJfIG','message','\x20task_id=','CommHub\x20response\x20omitted\x20task_id','match','message_id','split','idle','connectSSE','disconnect','parentTaskId','reply','reconnectDelay','getAllStatus','completed','autoConnect',']\x20[commhub:','sdk','24vfqScU','pop','status','SSE\x20reconnecting\x20in\x20','registered','text','fromAlias','<pending>','AbortError','11hYJWWR','send_task','replace','stringify','SSE\x20failed:\x20','json','toAlias','connected','string','s...','177737YcXhsF','processInbox','new_task'];a0_0x25fe=function(){return _0xb9dfae;};return a0_0x25fe();}async function Y(_0x18f5b7,_0x34f68a){const _0x370848=a0_0x275b2c;return X({'fromAlias':_0x18f5b7[_0x370848(0x17c)],'toAlias':_0x18f5b7[_0x370848(0x10d)],'parentTaskId':_0x18f5b7[_0x370848(0x16e)]||null,'networkId':_0x18f5b7[_0x370848(0x161)]||null,'transport':'mcp_http','lifecycleTracking':_0x370848(0x140)},_0x34f68a);}class $ extends a0_0x5ae740{[a0_0x275b2c(0x158)];[a0_0x275b2c(0x10d)];[a0_0x275b2c(0x12f)];[a0_0x275b2c(0x12c)];['resumeId'];[a0_0x275b2c(0x163)];[a0_0x275b2c(0x170)];[a0_0x275b2c(0x157)];[a0_0x275b2c(0x160)];[a0_0x275b2c(0x15c)]=!0x1;constructor(_0xe91faf){const _0x3a18a8=a0_0x275b2c;super();if(this[_0x3a18a8(0x158)]=_0xe91faf['url'][_0x3a18a8(0xfb)](/\/$/,''),this[_0x3a18a8(0x10d)]=_0xe91faf[_0x3a18a8(0x10d)],this['token']=_0xe91faf[_0x3a18a8(0x12f)],this[_0x3a18a8(0x12c)]=_0xe91faf['agent']||_0x3a18a8(0x175),this[_0x3a18a8(0x14c)]=_0x3a18a8(0x151)+_0xe91faf[_0x3a18a8(0x10d)]+'-'+Date[_0x3a18a8(0x13a)]()[_0x3a18a8(0x119)](0x24),this[_0x3a18a8(0x163)]=_0xe91faf['heartbeatInterval']??0x2bf20,this[_0x3a18a8(0x170)]=_0xe91faf[_0x3a18a8(0x170)]??0xbb8,_0xe91faf[_0x3a18a8(0x173)]!==!0x1)this[_0x3a18a8(0x11e)]();}[a0_0x275b2c(0x112)](_0x50b378){const _0x149a5c=a0_0x275b2c;console[_0x149a5c(0x112)]('['+new Date()[_0x149a5c(0x117)]()[_0x149a5c(0x142)](0x0,0x8)+_0x149a5c(0x174)+this['alias']+']\x20'+_0x50b378);}async[a0_0x275b2c(0x13c)](_0xdcca6f,_0x554b6d){const _0x2b3ea0=a0_0x275b2c;let _0x273539={'Content-Type':_0x2b3ea0(0x109),'Accept':_0x2b3ea0(0x162)};if(this[_0x2b3ea0(0x12f)])_0x273539[_0x2b3ea0(0x10a)]=_0x2b3ea0(0x120)+this[_0x2b3ea0(0x12f)];let _0x3d6607=await(await fetch(this[_0x2b3ea0(0x158)]+_0x2b3ea0(0x148),{'method':_0x2b3ea0(0x147),'headers':_0x273539,'body':JSON['stringify']({'jsonrpc':'2.0','id':Date[_0x2b3ea0(0x13a)](),'method':_0x2b3ea0(0x127),'params':{'name':_0xdcca6f,'arguments':_0x554b6d}})}))[_0x2b3ea0(0x17b)](),_0x674607=_0x3d6607[_0x2b3ea0(0x168)](/data: (.+)/),_0x4ead71=_0x674607?JSON[_0x2b3ea0(0x135)](_0x674607[0x1]):JSON[_0x2b3ea0(0x135)](_0x3d6607),_0x8743ad=_0x4ead71?.['result']?.[_0x2b3ea0(0x150)]?.[0x0]?.[_0x2b3ea0(0x17b)];return _0x8743ad?JSON[_0x2b3ea0(0x135)](_0x8743ad):_0x4ead71;}async['connect'](){const _0x1c19af=a0_0x275b2c;if(this[_0x1c19af(0x15c)])return;this[_0x1c19af(0x15c)]=!0x0,await this[_0x1c19af(0x178)](_0x1c19af(0x16b)),this[_0x1c19af(0x112)](_0x1c19af(0x17a)),this[_0x1c19af(0x157)]=setInterval(()=>{const _0x3cc4de=_0x1c19af;this[_0x3cc4de(0x178)]('idle')[_0x3cc4de(0x125)](_0x3373a7=>this[_0x3cc4de(0x112)](_0x3cc4de(0x14f)+_0x3373a7[_0x3cc4de(0x165)]));},this[_0x1c19af(0x163)]),this[_0x1c19af(0x16c)]();}async[a0_0x275b2c(0x16d)](){const _0x1687cd=a0_0x275b2c;if(this['running']=!0x1,this['sseAbort']?.[_0x1687cd(0x13e)](),this[_0x1687cd(0x157)])clearInterval(this[_0x1687cd(0x157)]);await this[_0x1687cd(0x178)](_0x1687cd(0x12e))[_0x1687cd(0x125)](()=>{}),this['log'](_0x1687cd(0x145));}async[a0_0x275b2c(0x11c)](_0x24a943,_0x33edf7,_0x134b0b=a0_0x275b2c(0x114)){const _0x317fa0=a0_0x275b2c;return Y({'alias':_0x24a943,'fromAlias':this[_0x317fa0(0x10d)]},{'log':_0x239d0b=>console[_0x317fa0(0x112)](_0x239d0b),'send':()=>this[_0x317fa0(0x13c)](_0x317fa0(0xfa),{'alias':_0x24a943,'task':_0x33edf7,'priority':_0x134b0b,'from_session':this[_0x317fa0(0x10d)]})});}async[a0_0x275b2c(0x165)](_0x907eb4,_0x1fc553){const _0x4d46c4=a0_0x275b2c;return this['call']('send_message',{'alias':_0x907eb4,'message':_0x1fc553,'from_session':this[_0x4d46c4(0x10d)]});}async[a0_0x275b2c(0x16f)](_0xc96fc,_0x52b5bb,_0x4f6d42=a0_0x275b2c(0x172)){const _0x840d4d=a0_0x275b2c;return this[_0x840d4d(0x13c)](_0x840d4d(0x111),{'in_reply_to':_0xc96fc,'text':_0x52b5bb,'status':_0x4f6d42});}async[a0_0x275b2c(0x178)](_0xd11d92,_0x3a87e7){const _0xbecf5f=a0_0x275b2c;return this[_0xbecf5f(0x13c)](_0xbecf5f(0x12b),{'resume_id':this['resumeId'],'alias':this[_0xbecf5f(0x10d)],'status':_0xd11d92,'server':a0_0x4a8173(),'hostname':a0_0x4a8173(),'agent':this[_0xbecf5f(0x12c)],'project_dir':process[_0xbecf5f(0x12a)](),..._0x3a87e7});}async[a0_0x275b2c(0x171)](){const _0x114689=a0_0x275b2c;return this[_0x114689(0x13c)](_0x114689(0x113),{});}async[a0_0x275b2c(0x12d)](_0x1ff19e,_0x121f13){const _0x3ffceb=a0_0x275b2c;return this[_0x3ffceb(0x13c)]('broadcast',{'message':_0x1ff19e,'filter_server':_0x121f13?.[_0x3ffceb(0x144)],'filter_status':_0x121f13?.[_0x3ffceb(0x178)]});}async[a0_0x275b2c(0x16c)](){const _0x173812=a0_0x275b2c;let _0x223ecb=encodeURIComponent(this['alias']),_0x335935=this[_0x173812(0x158)]+_0x173812(0x131)+_0x223ecb,_0x293a88=this['reconnectDelay'];while(this[_0x173812(0x15c)]){try{this[_0x173812(0x160)]=new AbortController();let _0xd9b782={'Accept':'text/event-stream'};if(this[_0x173812(0x12f)])_0xd9b782[_0x173812(0x10a)]=_0x173812(0x120)+this[_0x173812(0x12f)];let _0x55a5a2=await fetch(_0x335935,{'headers':_0xd9b782,'signal':this[_0x173812(0x160)]['signal']});if(!_0x55a5a2['ok']||!_0x55a5a2[_0x173812(0x15a)]){this['log'](_0x173812(0xfd)+_0x55a5a2[_0x173812(0x178)]),await this['sleep'](_0x293a88),_0x293a88=Math[_0x173812(0x11b)](_0x293a88*1.5,0xea60);continue;}_0x293a88=this[_0x173812(0x170)];let _0x237050=_0x55a5a2[_0x173812(0x15a)]['getReader'](),_0x2a4625=new TextDecoder(),_0x1ea83b='';while(this[_0x173812(0x15c)]){let {done:_0x202ca0,value:_0x272c4c}=await _0x237050['read']();if(_0x202ca0)break;_0x1ea83b+=_0x2a4625[_0x173812(0x14e)](_0x272c4c,{'stream':!0x0});let _0x1efefc=_0x1ea83b[_0x173812(0x16a)]('\x0a');_0x1ea83b=_0x1efefc[_0x173812(0x177)]()||'';for(let _0x2df130 of _0x1efefc){if(!_0x2df130[_0x173812(0x128)]('data:\x20'))continue;try{let _0xd64f37=JSON['parse'](_0x2df130[_0x173812(0x142)](0x6));if(_0xd64f37[_0x173812(0x107)]===_0x173812(0x100)){this[_0x173812(0x112)](_0x173812(0x110)),this[_0x173812(0x13f)](_0x173812(0x100));continue;}if(_0xd64f37[_0x173812(0x107)]===_0x173812(0x105)||_0xd64f37[_0x173812(0x107)]===_0x173812(0x129)||_0xd64f37[_0x173812(0x107)]===_0x173812(0x12d))await this['processInbox']();}catch{}}}}catch(_0x102b5d){if(_0x102b5d[_0x173812(0x134)]===_0x173812(0x17e))break;this['emit'](_0x173812(0x13b),_0x102b5d),this[_0x173812(0x112)](_0x173812(0x143)+_0x102b5d['message']);}if(this['running'])this['emit']('disconnected'),this[_0x173812(0x112)](_0x173812(0x179)+_0x293a88/0x3e8+_0x173812(0x102)),await this[_0x173812(0x14d)](_0x293a88),_0x293a88=Math[_0x173812(0x11b)](_0x293a88*1.5,0xea60);}}async[a0_0x275b2c(0x104)](){const _0x2b8f9d=a0_0x275b2c;try{let _0x4ff533=(await this[_0x2b8f9d(0x13c)](_0x2b8f9d(0x14b),{'alias':this[_0x2b8f9d(0x10d)],'limit':0xa}))?.[_0x2b8f9d(0x116)]||[];for(let _0xd8ad70 of _0x4ff533)await this['call']('ack_inbox',{'alias':this[_0x2b8f9d(0x10d)],'message_id':_0xd8ad70['id']}),this[_0x2b8f9d(0x112)]('←\x20'+_0xd8ad70[_0x2b8f9d(0x122)]+':\x20'+_0xd8ad70[_0x2b8f9d(0x150)][_0x2b8f9d(0x142)](0x0,0x3c)),this[_0x2b8f9d(0x13f)]('task',_0xd8ad70),this[_0x2b8f9d(0x13f)](_0x2b8f9d(0x165),_0xd8ad70);}catch(_0x56c577){this[_0x2b8f9d(0x112)]('inbox\x20error:\x20'+_0x56c577['message']);}}[a0_0x275b2c(0x14d)](_0x8c993e){return new Promise(_0xc522b3=>setTimeout(_0xc522b3,_0x8c993e));}}var F=$;function a0_0x5183(_0x4903e5,_0x33c9ae){_0x4903e5=_0x4903e5-0xf9;const _0x25fe6e=a0_0x25fe();let _0x5183bc=_0x25fe6e[_0x4903e5];return _0x5183bc;}export{F as default,$ as CommHub};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #693 — token-bound node controlled local file → Hub /api/upload bridge.
|
|
3
|
+
*
|
|
4
|
+
* Security boundary (hard gates):
|
|
5
|
+
* 1. Cross-host: bytes travel over HTTP multipart; Hub never reads agent FS.
|
|
6
|
+
* 2. Identity: caller supplies the node's bearer token; ntok binds network.
|
|
7
|
+
* 3. Path safety: realpath + allowlisted roots; reject NUL, symlink, traversal.
|
|
8
|
+
* 4. Size ≤ 12 MiB enforced with same-fd fstat + bounded read (no full slurp).
|
|
9
|
+
* 5. Hub /api/upload is the sole write surface (atomic on hub side).
|
|
10
|
+
*
|
|
11
|
+
* Adversarial add-only (#694 DO-NOT-MERGE fixes):
|
|
12
|
+
* - TOCTOU: open O_NOFOLLOW → fstat(fd) → read(fd) only; no re-open by path.
|
|
13
|
+
* - Bounded read: never allocate/read past CONTROLLED_UPLOAD_MAX_BYTES.
|
|
14
|
+
* - NUL guard is live and reachable before any fs call.
|
|
15
|
+
*/
|
|
16
|
+
/** Mirrors server/src/uploads.ts MAX_UPLOAD_BYTES. */
|
|
17
|
+
export declare const CONTROLLED_UPLOAD_MAX_BYTES: number;
|
|
18
|
+
/** Chunk size for bounded streaming read from a single fd. */
|
|
19
|
+
export declare const CONTROLLED_UPLOAD_READ_CHUNK: number;
|
|
20
|
+
export type ControlledUploadErrorCode = "path_required" | "path_nul" | "path_not_found" | "path_untrusted" | "path_symlink" | "path_not_regular" | "path_empty" | "payload_too_large" | "read_failed" | "auth_required" | "hub_url_required" | "upload_failed" | "bad_response" | "missing_file_id";
|
|
21
|
+
export type ControlledUploadFail = {
|
|
22
|
+
ok: false;
|
|
23
|
+
error: ControlledUploadErrorCode;
|
|
24
|
+
message: string;
|
|
25
|
+
};
|
|
26
|
+
export type ControlledUploadOk = {
|
|
27
|
+
ok: true;
|
|
28
|
+
file_id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
mime: string;
|
|
31
|
+
size: number;
|
|
32
|
+
url?: string;
|
|
33
|
+
};
|
|
34
|
+
export type ControlledUploadResult = ControlledUploadOk | ControlledUploadFail;
|
|
35
|
+
export interface ControlledUploadDeps {
|
|
36
|
+
hubUrl: string;
|
|
37
|
+
authToken: string;
|
|
38
|
+
allowedRoots?: string[];
|
|
39
|
+
alias?: string;
|
|
40
|
+
nodeDir?: string;
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
home?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Normalize a client filename: basename only, strip controls, cap 255. */
|
|
45
|
+
export declare function normalizeUploadName(input: string | undefined | null, fallback?: string): string;
|
|
46
|
+
export declare function defaultControlledUploadRoots(opts?: {
|
|
47
|
+
home?: string;
|
|
48
|
+
alias?: string;
|
|
49
|
+
nodeDir?: string;
|
|
50
|
+
cwd?: string;
|
|
51
|
+
env?: NodeJS.ProcessEnv;
|
|
52
|
+
}): string[];
|
|
53
|
+
/** True iff canonicalPath is strictly inside one of the allowlisted roots. */
|
|
54
|
+
export declare function isPathInsideAllowedRoots(canonicalPath: string, allowedRoots: string[]): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a user-supplied path to a canonical path under an allowlisted root.
|
|
57
|
+
* Does not open the file for reading — open+fstat+read is a separate same-fd step.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveControlledUploadPath(rawPath: string, allowedRoots: string[]): {
|
|
60
|
+
ok: true;
|
|
61
|
+
canonicalPath: string;
|
|
62
|
+
} | ControlledUploadFail;
|
|
63
|
+
export type ControlledFileBytes = {
|
|
64
|
+
ok: true;
|
|
65
|
+
bytes: Buffer;
|
|
66
|
+
size: number;
|
|
67
|
+
canonicalPath: string;
|
|
68
|
+
} | ControlledUploadFail;
|
|
69
|
+
/**
|
|
70
|
+
* Open → fstat(fd) → bounded read(fd) on the **same fd** (TOCTOU hard gate).
|
|
71
|
+
* Uses O_NOFOLLOW when available so a symlink swap at open is rejected.
|
|
72
|
+
* Never uses path-based readFileSync after the initial open.
|
|
73
|
+
*/
|
|
74
|
+
export declare function openFstatBoundedReadControlledFile(canonicalPath: string, allowedRoots: string[], maxBytes?: number): ControlledFileBytes;
|
|
75
|
+
/**
|
|
76
|
+
* Upload a controlled local file to Hub POST /api/upload.
|
|
77
|
+
* Returns file_id + metadata for send_reply attachments.
|
|
78
|
+
* Never falls back to sending a raw path to the Hub.
|
|
79
|
+
*/
|
|
80
|
+
export declare function uploadControlledLocalFile(rawPath: string, deps: ControlledUploadDeps, opts?: {
|
|
81
|
+
name?: string;
|
|
82
|
+
mime?: string;
|
|
83
|
+
}): Promise<ControlledUploadResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type DashboardLaunchSource = "npx" | "global";
|
|
2
|
+
export interface DashboardLaunchRecord {
|
|
3
|
+
schema: 1;
|
|
4
|
+
port: number;
|
|
5
|
+
listener_pid: number;
|
|
6
|
+
listener_birth: string;
|
|
7
|
+
source: DashboardLaunchSource;
|
|
8
|
+
source_key: string;
|
|
9
|
+
recorded_at: string;
|
|
10
|
+
}
|
|
11
|
+
export type DashboardListenerDecision = {
|
|
12
|
+
action: "start";
|
|
13
|
+
} | {
|
|
14
|
+
action: "already_running";
|
|
15
|
+
pid: number;
|
|
16
|
+
} | {
|
|
17
|
+
action: "terminate_owned_stale";
|
|
18
|
+
pid: number;
|
|
19
|
+
reason: "unhealthy" | "version_changed";
|
|
20
|
+
} | {
|
|
21
|
+
action: "refuse";
|
|
22
|
+
reason: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function isDashboardProcessCommand(command: string): boolean;
|
|
25
|
+
export declare function decideDashboardListener(input: {
|
|
26
|
+
port: number;
|
|
27
|
+
listenerPids: number[];
|
|
28
|
+
record: DashboardLaunchRecord | null;
|
|
29
|
+
listenerBirth: string | null;
|
|
30
|
+
listenerCommand: string | null;
|
|
31
|
+
desiredSource: DashboardLaunchSource;
|
|
32
|
+
desiredSourceKey: string;
|
|
33
|
+
healthy: boolean;
|
|
34
|
+
}): DashboardListenerDecision;
|
|
35
|
+
export declare function parseDashboardLaunchRecord(value: unknown): DashboardLaunchRecord | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Duplex } from "node:stream";
|
|
2
|
+
export declare const GROK_ATTACH_PROTOCOL = "anet-grok-copresence-attach";
|
|
3
|
+
export declare const GROK_ATTACH_PROTOCOL_VERSION = 1;
|
|
4
|
+
export declare const GROK_ATTACH_DEFAULT_MAX_FRAME_BYTES: number;
|
|
5
|
+
export declare const GROK_ATTACH_DEFAULT_MAX_BUFFER_BYTES: number;
|
|
6
|
+
export declare const GROK_ATTACH_DEFAULT_HANDSHAKE_TIMEOUT_MS = 5000;
|
|
7
|
+
export declare const GROK_ATTACH_DEFAULT_CLOSE_TIMEOUT_MS = 1000;
|
|
8
|
+
export type GrokAttachJsonValue = null | boolean | number | string | GrokAttachJsonValue[] | {
|
|
9
|
+
[key: string]: GrokAttachJsonValue;
|
|
10
|
+
};
|
|
11
|
+
export interface GrokAttachHelloFrame {
|
|
12
|
+
type: "hello";
|
|
13
|
+
protocol: typeof GROK_ATTACH_PROTOCOL;
|
|
14
|
+
version: typeof GROK_ATTACH_PROTOCOL_VERSION;
|
|
15
|
+
alias: string;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface GrokAttachOutputFrame {
|
|
20
|
+
type: "output";
|
|
21
|
+
data: string;
|
|
22
|
+
encoding: "base64";
|
|
23
|
+
}
|
|
24
|
+
export interface GrokAttachStatusFrame {
|
|
25
|
+
type: "status";
|
|
26
|
+
status: GrokAttachJsonValue;
|
|
27
|
+
}
|
|
28
|
+
export interface GrokAttachErrorFrame {
|
|
29
|
+
type: "error";
|
|
30
|
+
code: string;
|
|
31
|
+
message: string;
|
|
32
|
+
fatal: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface GrokAttachDetachFrame {
|
|
35
|
+
type: "detach";
|
|
36
|
+
}
|
|
37
|
+
export type GrokAttachServerFrame = GrokAttachHelloFrame | GrokAttachOutputFrame | GrokAttachStatusFrame | GrokAttachErrorFrame | GrokAttachDetachFrame;
|
|
38
|
+
export interface GrokAttachInputFrame {
|
|
39
|
+
type: "input";
|
|
40
|
+
data: string;
|
|
41
|
+
encoding: "base64";
|
|
42
|
+
}
|
|
43
|
+
export interface GrokAttachResizeFrame {
|
|
44
|
+
type: "resize";
|
|
45
|
+
cols: number;
|
|
46
|
+
rows: number;
|
|
47
|
+
}
|
|
48
|
+
export type GrokAttachClientFrame = GrokAttachInputFrame | GrokAttachResizeFrame | GrokAttachDetachFrame;
|
|
49
|
+
export interface GrokAttachInputSource {
|
|
50
|
+
on(event: "data", listener: (chunk: unknown) => void): unknown;
|
|
51
|
+
on(event: "end", listener: () => void): unknown;
|
|
52
|
+
off?(event: "data" | "end", listener: (...args: any[]) => void): unknown;
|
|
53
|
+
removeListener?(event: "data" | "end", listener: (...args: any[]) => void): unknown;
|
|
54
|
+
pause?(): unknown;
|
|
55
|
+
resume?(): unknown;
|
|
56
|
+
}
|
|
57
|
+
export interface GrokAttachOutputSink {
|
|
58
|
+
write(chunk: Uint8Array): boolean | void;
|
|
59
|
+
columns?: number;
|
|
60
|
+
rows?: number;
|
|
61
|
+
once?(event: "drain", listener: () => void): unknown;
|
|
62
|
+
}
|
|
63
|
+
export interface GrokAttachSignalSource {
|
|
64
|
+
on(event: "SIGWINCH", listener: () => void): unknown;
|
|
65
|
+
off?(event: "SIGWINCH", listener: () => void): unknown;
|
|
66
|
+
removeListener?(event: "SIGWINCH", listener: () => void): unknown;
|
|
67
|
+
}
|
|
68
|
+
export interface GrokAttachSocketStat {
|
|
69
|
+
uid: number;
|
|
70
|
+
isSocket(): boolean;
|
|
71
|
+
isSymbolicLink(): boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface GrokAttachDependencies {
|
|
74
|
+
lstat?: (socketPath: string) => Promise<GrokAttachSocketStat>;
|
|
75
|
+
getuid?: () => number | undefined;
|
|
76
|
+
connect?: (socketPath: string) => Duplex;
|
|
77
|
+
}
|
|
78
|
+
export interface GrokAttachClientOptions {
|
|
79
|
+
socketPath: string;
|
|
80
|
+
input: GrokAttachInputSource;
|
|
81
|
+
output: GrokAttachOutputSink;
|
|
82
|
+
signalSource?: GrokAttachSignalSource;
|
|
83
|
+
terminalSize?: () => {
|
|
84
|
+
cols: number | undefined;
|
|
85
|
+
rows: number | undefined;
|
|
86
|
+
};
|
|
87
|
+
maxFrameBytes?: number;
|
|
88
|
+
maxBufferBytes?: number;
|
|
89
|
+
handshakeTimeoutMs?: number;
|
|
90
|
+
closeTimeoutMs?: number;
|
|
91
|
+
detachOnInputEnd?: boolean;
|
|
92
|
+
onHello?: (frame: GrokAttachHelloFrame) => void;
|
|
93
|
+
onStatus?: (frame: GrokAttachStatusFrame) => void;
|
|
94
|
+
onError?: (error: Error, frame?: GrokAttachErrorFrame) => void;
|
|
95
|
+
onDetach?: (frame: GrokAttachDetachFrame) => void;
|
|
96
|
+
dependencies?: GrokAttachDependencies;
|
|
97
|
+
}
|
|
98
|
+
export type GrokAttachCloseReason = "local-detach" | "input-end" | "remote-detach" | "socket-close" | "socket-error" | "protocol-error";
|
|
99
|
+
export interface GrokAttachCloseInfo {
|
|
100
|
+
reason: GrokAttachCloseReason;
|
|
101
|
+
error?: Error;
|
|
102
|
+
}
|
|
103
|
+
export interface GrokAttachSession {
|
|
104
|
+
readonly socketPath: string;
|
|
105
|
+
readonly closed: Promise<GrokAttachCloseInfo>;
|
|
106
|
+
detach(): void;
|
|
107
|
+
resize(cols?: number, rows?: number): void;
|
|
108
|
+
}
|
|
109
|
+
export declare class GrokAttachRemoteError extends Error {
|
|
110
|
+
readonly code: string;
|
|
111
|
+
readonly fatal: boolean;
|
|
112
|
+
constructor(frame: GrokAttachErrorFrame);
|
|
113
|
+
}
|
|
114
|
+
export declare function validateGrokAttachSocket(socketPath: string, dependencies?: GrokAttachDependencies): Promise<void>;
|
|
115
|
+
export declare function connectGrokAttach(options: GrokAttachClientOptions): Promise<GrokAttachSession>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type GrokCopresenceSessionDisclosure = "configured" | "new" | "resume";
|
|
2
|
+
export type GrokCopresenceDisclosure = {
|
|
3
|
+
profile: "commhub-only" | "x-search" | "invalid";
|
|
4
|
+
lines: readonly string[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Describe only the two exact tool profiles accepted by the pinned Grok TUI
|
|
8
|
+
* runtime. This is deliberately exact: a near-miss must never be presented as
|
|
9
|
+
* either reviewed capability set.
|
|
10
|
+
*/
|
|
11
|
+
export declare function grokCopresenceDisclosure(tools: unknown, session?: GrokCopresenceSessionDisclosure): GrokCopresenceDisclosure;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const GROK_UNIX_SOCKET_PATH_MAX_BYTES = 100;
|
|
2
|
+
export declare const GROK_COPRESENCE_CAPABILITY_MARKER = "ANET_CAPABILITY_GROK_COPRESENCE_V2";
|
|
3
|
+
export declare const GROK_PREVIEW_RESOLVER_INHERITED_ENV_KEYS: readonly ["PATH", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "LC_CTYPE", "TZ"];
|
|
4
|
+
export declare const GROK_AGENT_NODE_INHERITED_ENV_KEYS: readonly ["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "LC_CTYPE", "TZ", "SHELL", "USER", "LOGNAME", "TERM", "COLORTERM", "NO_COLOR"];
|
|
5
|
+
export declare const GROK_AGENT_NODE_OPTIONAL_ENV_KEYS: readonly ["GROK_BINARY", "GROK_HOME", "FLOCK_BINARY", "SETPRIV_BINARY", "UNSHARE_BINARY", "GROK_CLI_TIMEOUT_MS", "GROK_HANDSHAKE_TIMEOUT_MS", "LOG_LEVEL", "ANET_GOAL_TICK_MS", "COMMHUB_MAX_GOALS_PER_NODE"];
|
|
6
|
+
/** Exact environment for the long-lived agent-node parent of the Grok TUI. */
|
|
7
|
+
export declare function buildGrokAgentNodeEnv(parentEnv: NodeJS.ProcessEnv): Record<string, string>;
|
|
8
|
+
export declare function grokPreviewResolverConfigPaths(home: string): {
|
|
9
|
+
directory: string;
|
|
10
|
+
userConfig: string;
|
|
11
|
+
globalConfig: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* npm rejects loading the same file as both user and global config. Prepare
|
|
15
|
+
* two distinct, empty, owner-only files without following a final symlink so
|
|
16
|
+
* the resolver cannot inherit a user's ordinary npmrc credentials.
|
|
17
|
+
*/
|
|
18
|
+
export declare function prepareGrokPreviewResolverConfigs(home: string): void;
|
|
19
|
+
/** Exact environment for the short-lived npm resolver and capability probe. */
|
|
20
|
+
export declare function buildGrokPreviewResolverEnv(parentEnv: NodeJS.ProcessEnv, home: string): Record<string, string>;
|
|
21
|
+
/** Old headless-only agent-node builds already advertised grok-build-cli. */
|
|
22
|
+
export declare function agentNodeHelpSupportsGrokCopresence(help: string): boolean;
|
|
23
|
+
export interface GrokCopresenceProfileFields {
|
|
24
|
+
grokCopresence: boolean;
|
|
25
|
+
grokLeaderSocket?: string;
|
|
26
|
+
grokAttachSocket?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface GrokSocketPathOptions {
|
|
29
|
+
cwd?: string;
|
|
30
|
+
home?: string;
|
|
31
|
+
xdgRuntimeDir?: string;
|
|
32
|
+
uid?: number;
|
|
33
|
+
platform?: NodeJS.Platform;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Allocate deterministic Unix socket paths without creating anything.
|
|
37
|
+
*
|
|
38
|
+
* Grok's workspace sandbox does not admit an otherwise owner-controlled
|
|
39
|
+
* XDG_RUNTIME_DIR such as /run/user/<uid>. Keep the primary sockets under the
|
|
40
|
+
* node's owner-bound state home, which the runtime already admits, and use a
|
|
41
|
+
* short private tmp path only when the Unix socket length limit requires it.
|
|
42
|
+
* The runtime owns directory creation and permissions; `anet node create`
|
|
43
|
+
* only persists the identity of the two sockets.
|
|
44
|
+
*/
|
|
45
|
+
export declare function grokCopresenceSocketPaths(nodeId: string, options?: GrokSocketPathOptions): {
|
|
46
|
+
leaderSocket: string;
|
|
47
|
+
attachSocket: string;
|
|
48
|
+
};
|
|
49
|
+
export declare function grokBuildCliCreationFields(runtime: string, nodeId: string, headless?: boolean, options?: GrokSocketPathOptions): GrokCopresenceProfileFields | Record<string, never>;
|
|
@@ -1,5 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-020 §3.1 — Feishu (Lark) adapter for the IM compatibility layer.
|
|
3
|
+
*
|
|
4
|
+
* Uses `@larksuiteoapi/node-sdk` in WebSocket long-connection mode (WSClient).
|
|
5
|
+
* No public IP / no domain verification / no webhook signature decryption —
|
|
6
|
+
* the three biggest 飞书 接入 risks all live in the HTTP event-callback path,
|
|
7
|
+
* not in WSClient mode.
|
|
8
|
+
*
|
|
9
|
+
* Milestones:
|
|
10
|
+
* M1: contract scaffold.
|
|
11
|
+
* M2 (this file): WSClient init + EventDispatcher for `im.message.receive_v1`
|
|
12
|
+
* + event normalization + access whitelist gate + audit log.
|
|
13
|
+
* M3: outbound `im.message.create` (text), edit support (≤20/msg).
|
|
14
|
+
* M5: image upload / download (`im.image.create` / `im.messageResource.get`)
|
|
15
|
+
* + group @bot detection refined to match the bot's own open_id.
|
|
16
|
+
*/
|
|
17
|
+
import * as lark from "@larksuiteoapi/node-sdk";
|
|
1
18
|
import type { IMAdapter, IMAdapterHealth, IMChannelConfig, IMConversationRef, IMIngressMode, NormalizedIMEvent, NormalizedIMMessage } from "../types.js";
|
|
2
19
|
type OnEventHandler = (event: NormalizedIMEvent) => Promise<void>;
|
|
20
|
+
export type FeishuWsClientLike = Pick<lark.WSClient, "start" | "close">;
|
|
21
|
+
export type FeishuWsClientFactory = (params: ConstructorParameters<typeof lark.WSClient>[0]) => FeishuWsClientLike;
|
|
22
|
+
type FeishuInboundHandler = (rawEvent: unknown) => Promise<unknown>;
|
|
23
|
+
export interface FeishuEventDispatcherLike {
|
|
24
|
+
register(handlers: Record<string, FeishuInboundHandler>): unknown;
|
|
25
|
+
}
|
|
26
|
+
export interface FeishuAdapterOptions {
|
|
27
|
+
/** @internal Avoids real bot-info HTTP calls in lifecycle tests. */
|
|
28
|
+
createClient?: (params: ConstructorParameters<typeof lark.Client>[0]) => lark.Client;
|
|
29
|
+
/** @internal Test seam; production uses the pinned Lark SDK WSClient. */
|
|
30
|
+
createWsClient?: FeishuWsClientFactory;
|
|
31
|
+
/** @internal Test seam; production uses the pinned Lark SDK dispatcher. */
|
|
32
|
+
createEventDispatcher?: () => FeishuEventDispatcherLike;
|
|
33
|
+
/** Independent outer bound in case the SDK promise/callback path stalls. */
|
|
34
|
+
wsReadyTimeoutMs?: number;
|
|
35
|
+
/** Called once when an already-ready socket exhausts reconnect attempts. */
|
|
36
|
+
onTerminalError?: (error: Error) => void;
|
|
37
|
+
}
|
|
3
38
|
export declare class FeishuAdapter implements IMAdapter {
|
|
4
39
|
readonly platform = "feishu";
|
|
5
40
|
readonly ingressMode: IMIngressMode;
|
|
@@ -7,6 +42,8 @@ export declare class FeishuAdapter implements IMAdapter {
|
|
|
7
42
|
private connectionName_;
|
|
8
43
|
private client;
|
|
9
44
|
private wsClient;
|
|
45
|
+
private lifecycleGeneration;
|
|
46
|
+
private readonly options;
|
|
10
47
|
/**
|
|
11
48
|
* The bot's own open_id, resolved at init() via /open-apis/bot/v3/info.
|
|
12
49
|
* Used to detect real @bot mentions (vs any mention) in group messages.
|
|
@@ -17,6 +54,7 @@ export declare class FeishuAdapter implements IMAdapter {
|
|
|
17
54
|
/** Where to persist downloaded inbound media (M5c). */
|
|
18
55
|
private mediaDir;
|
|
19
56
|
private health_;
|
|
57
|
+
constructor(options?: FeishuAdapterOptions);
|
|
20
58
|
/**
|
|
21
59
|
* Snapshot of the current `access.allowFrom` list (from access.json).
|
|
22
60
|
* Used by the bridge's rate-limiter to exempt operator-vouched explicit
|
|
@@ -131,4 +169,9 @@ export declare const FEISHU_TEXT_SINGLE_LIMIT = 4000;
|
|
|
131
169
|
* If the input is already short enough, returns a single-element array.
|
|
132
170
|
*/
|
|
133
171
|
export declare function splitTextForFeishu(text: string, maxChars: number): string[];
|
|
172
|
+
/**
|
|
173
|
+
* Lark errors may echo request/config values. Keep worker logs actionable while
|
|
174
|
+
* ensuring credentials and multiline payloads never cross the process boundary.
|
|
175
|
+
*/
|
|
176
|
+
export declare function sanitizeFeishuWsError(rawError: unknown, appId: string, appSecret: string): Error;
|
|
134
177
|
export {};
|
|
@@ -44,6 +44,8 @@ export interface FeishuBridgeOptions {
|
|
|
44
44
|
* - stderr logger otherwise (standalone smoke debugging).
|
|
45
45
|
*/
|
|
46
46
|
onEvent?: (event: NormalizedIMEvent) => Promise<void>;
|
|
47
|
+
/** Fatal WS failure after initial readiness (for worker lifecycle ownership). */
|
|
48
|
+
onTerminalError?: (error: Error) => void;
|
|
47
49
|
}
|
|
48
50
|
/** Bridge → parent: inbound IM event ready for think(). */
|
|
49
51
|
export interface BridgeIncomingEnvelope {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface WorkerProcessBoundary {
|
|
2
|
+
stderr: {
|
|
3
|
+
write(message: string): unknown;
|
|
4
|
+
};
|
|
5
|
+
exit(code: number): unknown;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A terminal WS error means this worker no longer owns a live ingress path.
|
|
9
|
+
* Exit non-zero so the parent/supervisor cannot continue advertising it as
|
|
10
|
+
* healthy. Kept separate from worker.ts so the real exit contract is testable.
|
|
11
|
+
*/
|
|
12
|
+
export declare function exitFeishuWorker(error: Error, boundary?: WorkerProcessBoundary): void;
|
|
13
|
+
export {};
|