@sleep2agi/agent-network 2.3.0-preview.8 → 2.3.0-preview.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -324
- package/dist/bin/anet.cjs +101 -0
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +20 -1
- package/dist/bin/goal-wake-log-render.d.ts +31 -0
- package/dist/src/active-network-task.d.ts +28 -0
- 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-meta.d.ts +10 -0
- package/dist/src/channel-task-trace.d.ts +14 -0
- package/dist/src/claude-vendor-env.d.ts +28 -0
- package/dist/src/cli-args.d.ts +8 -0
- package/dist/src/cli-version-display.d.ts +16 -0
- package/dist/src/client-task-trace.d.ts +9 -0
- package/dist/src/client.js +1 -1
- package/dist/src/codex-copresence-preflight.d.ts +65 -0
- package/dist/src/codex-copresence-profile.d.ts +125 -0
- package/dist/src/codex-copresence-recovery.d.ts +43 -0
- package/dist/src/codex-copresence-thread.d.ts +13 -0
- package/dist/src/codex-model-default.d.ts +6 -0
- package/dist/src/codex-tui-client-health.d.ts +17 -0
- package/dist/src/commhub-response.d.ts +1 -0
- package/dist/src/controlled-upload.d.ts +83 -0
- package/dist/src/copresence-deps.d.ts +22 -0
- package/dist/src/copresence-identity.d.ts +339 -0
- package/dist/src/copresence-startup-diagnosis.d.ts +17 -0
- package/dist/src/daemon-capability-display.d.ts +110 -0
- package/dist/src/daemon-capability-display.js +21 -0
- package/dist/src/daemon-runtime-path-preflight.d.ts +22 -0
- package/dist/src/daemon-runtime-staleness.d.ts +13 -0
- package/dist/src/dashboard-managed-process.d.ts +35 -0
- package/dist/src/display-width.d.ts +27 -0
- package/dist/src/doctor-node-count.d.ts +28 -0
- package/dist/src/environ-alias.d.ts +14 -0
- package/dist/src/grok-attach-client.d.ts +129 -0
- package/dist/src/grok-build-drift.d.ts +15 -0
- package/dist/src/grok-copresence-disclosure.d.ts +11 -0
- package/dist/src/grok-copresence-orchestration.d.ts +75 -0
- package/dist/src/grok-copresence-profile.d.ts +65 -0
- package/dist/src/host-telemetry.d.ts +14 -0
- package/dist/src/hub-time-display.d.ts +20 -0
- package/dist/src/hub-version-skew.d.ts +19 -0
- package/dist/src/im/correlation-store.d.ts +34 -0
- package/dist/src/im/feishu/adapter.d.ts +87 -1
- package/dist/src/im/feishu/bridge.d.ts +85 -14
- package/dist/src/im/feishu/config.d.ts +31 -0
- package/dist/src/im/feishu/hub-upload.d.ts +88 -0
- package/dist/src/im/feishu/outbound-marker.d.ts +140 -0
- package/dist/src/im/feishu/outbound-paths.d.ts +50 -0
- package/dist/src/im/feishu/outbound-route.d.ts +62 -0
- package/dist/src/im/feishu/worker-lifecycle.d.ts +13 -0
- package/dist/src/im/feishu/worker.js +190 -139
- package/dist/src/im/types.d.ts +38 -1
- package/dist/src/local-process-state.d.ts +12 -0
- package/dist/src/locale-diagnostic.d.ts +12 -0
- package/dist/src/ls-columns.d.ts +15 -0
- package/dist/src/mock-llm.d.ts +12 -0
- package/dist/src/node-activity-log.d.ts +4 -0
- package/dist/src/node-not-found.d.ts +1 -0
- package/dist/src/node-server-payload.d.ts +66 -0
- package/dist/src/node-server.js +7 -1
- package/dist/src/normalize-runtime.d.ts +29 -2
- package/dist/src/offline-age.d.ts +29 -0
- package/dist/src/one-line-cell.d.ts +20 -0
- package/dist/src/opencode-agent-node-pair.d.ts +25 -0
- package/dist/src/opencode-auth-login.d.ts +43 -0
- package/dist/src/opencode-launch-env.d.ts +13 -0
- package/dist/src/opencode-owner-mode.d.ts +6 -0
- package/dist/src/opencode-package-binary.d.ts +21 -0
- package/dist/src/opencode-pin.d.ts +44 -0
- package/dist/src/opencode-preset.d.ts +73 -0
- package/dist/src/opencode-runtime-binding.d.ts +30 -0
- package/dist/src/opencode-safe-root.d.ts +27 -0
- package/dist/src/opencode-smoke-env.d.ts +1 -0
- package/dist/src/outbound-attachments.d.ts +57 -0
- package/dist/src/outbound-tool-names.d.ts +1 -0
- package/dist/src/owned-roots.d.ts +58 -0
- package/dist/src/owner-env-file.d.ts +2 -0
- package/dist/src/package-mode-preflight.d.ts +41 -0
- package/dist/src/posix-codex-copresence.d.ts +8 -0
- package/dist/src/posix-modes.d.ts +36 -0
- package/dist/src/primary-network.d.ts +23 -0
- package/dist/src/private-state.d.ts +10 -0
- package/dist/src/process-telemetry.d.ts +12 -0
- package/dist/src/profile-serialize.d.ts +1 -0
- package/dist/src/project-key.d.ts +1 -0
- package/dist/src/reply-originator.d.ts +20 -0
- package/dist/src/resume-runtime-infer.d.ts +18 -0
- package/dist/src/secret-shell-guidance.d.ts +3 -0
- package/dist/src/session-status-class.d.ts +48 -0
- package/dist/src/stale-socket.d.ts +118 -0
- package/dist/src/subcommand-redirect.d.ts +26 -0
- package/dist/src/task-trace.d.ts +33 -0
- package/dist/src/tmux-attach.d.ts +8 -0
- package/dist/src/tmux-capability.d.ts +58 -0
- package/dist/src/tmux-exact-target.d.ts +34 -0
- package/dist/src/tmux-pane-prompt.d.ts +22 -0
- package/dist/src/token-cli.d.ts +13 -0
- package/dist/src/tool-allowlist.d.ts +28 -0
- package/dist/src/unsafe-package-path-reason.d.ts +17 -0
- package/dist/src/win-launcher.d.ts +20 -0
- package/dist/src/windows-codex-copresence.d.ts +60 -0
- package/package.json +10 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface WakeLogEntryShape {
|
|
2
|
+
ts?: string;
|
|
3
|
+
status?: string;
|
|
4
|
+
summary?: string;
|
|
5
|
+
task_id?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface WakeLogGoalShape {
|
|
8
|
+
goal_id: string;
|
|
9
|
+
progress_log?: WakeLogEntryShape[];
|
|
10
|
+
}
|
|
11
|
+
export interface WakeLogRenderOpts {
|
|
12
|
+
/** Trim to the last N entries. undefined = all. */
|
|
13
|
+
tail?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format progress_log as a JSON object suitable for `--json` output.
|
|
17
|
+
* Pure. Never reads/writes filesystem. Legacy goals without
|
|
18
|
+
* progress_log render as empty array (not error).
|
|
19
|
+
*/
|
|
20
|
+
export declare function renderWakeLogJson(goal: WakeLogGoalShape, opts?: WakeLogRenderOpts): {
|
|
21
|
+
goal_id: string;
|
|
22
|
+
total: number;
|
|
23
|
+
returned: number;
|
|
24
|
+
entries: WakeLogEntryShape[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Format progress_log as a table for human console output.
|
|
28
|
+
* Column layout mirrors printGoalShow so the two commands read
|
|
29
|
+
* consistently. Empty log renders a single "(none)" line, not an error.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderWakeLogText(goal: WakeLogGoalShape, opts?: WakeLogRenderOpts): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ActiveNetworkTask {
|
|
2
|
+
taskId: string;
|
|
3
|
+
from: string;
|
|
4
|
+
startedAt: number;
|
|
5
|
+
}
|
|
6
|
+
/** 运行时崩溃没来得及删标记时的兜底:超过这个年龄的标记当不存在。 */
|
|
7
|
+
export declare const ACTIVE_NETWORK_TASK_MAX_AGE_MS: number;
|
|
8
|
+
export declare function parseActiveNetworkTask(raw: string | null | undefined, now: number, maxAgeMs?: number): ActiveNetworkTask | null;
|
|
9
|
+
export type OutboundTool = "commhub_send_task" | "commhub_send_message";
|
|
10
|
+
export type OutboundRewrite = {
|
|
11
|
+
kind: "pass";
|
|
12
|
+
} | {
|
|
13
|
+
kind: "progress_of_active_task";
|
|
14
|
+
taskId: string;
|
|
15
|
+
from: string;
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* 只在「目标 alias 恰好是当前网络任务的发起方」时改写;发给别人的照常放行——
|
|
20
|
+
* 模型替任务派活给第三方是合法的协作,不能一刀切。
|
|
21
|
+
*/
|
|
22
|
+
export declare function decideOutboundRewrite(tool: OutboundTool, args: {
|
|
23
|
+
alias?: unknown;
|
|
24
|
+
task?: unknown;
|
|
25
|
+
message?: unknown;
|
|
26
|
+
} | null | undefined, active: ActiveNetworkTask | null): OutboundRewrite;
|
|
27
|
+
/** outbound-only 模式下模型不能自选 high(#1770 第三条):降为 normal,其余原样。 */
|
|
28
|
+
export declare function clampOutboundPriority(priority: unknown, outboundOnly: boolean): "high" | "normal" | "low" | undefined;
|
|
@@ -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
|
+
/** One inbound inbox row → the meta Claude Code renders as `<channel …>` attrs.
|
|
2
|
+
*
|
|
3
|
+
* Pure so the attribute set is assertable; it used to be inline in the SSE
|
|
4
|
+
* handler, where nothing could see it. */
|
|
5
|
+
export declare function inboundChannelMeta(msg: {
|
|
6
|
+
id: string;
|
|
7
|
+
from_session?: string | null;
|
|
8
|
+
priority?: string | null;
|
|
9
|
+
created_at?: string | null;
|
|
10
|
+
}): Record<string, string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function sendChannelTaskWithTrace(input: {
|
|
2
|
+
alias: string;
|
|
3
|
+
task: string;
|
|
4
|
+
priority?: string;
|
|
5
|
+
fromAlias: string;
|
|
6
|
+
networkId?: string | null;
|
|
7
|
+
/** Structured task metadata forwarded verbatim, e.g. { attachments: [...] }.
|
|
8
|
+
* Omitted from the outbound body when absent, so a call without it is
|
|
9
|
+
* unchanged. */
|
|
10
|
+
meta?: Record<string, unknown>;
|
|
11
|
+
}, deps: {
|
|
12
|
+
send: (args: Record<string, unknown>) => Promise<any>;
|
|
13
|
+
log: (line: string) => void;
|
|
14
|
+
}): 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[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1645 —— `anet doctor` 原先对三个 runtime CLI **只检查「在不在」**:
|
|
3
|
+
*
|
|
4
|
+
* try { execSync("codex --version", …); check("Codex CLI", true); } catch { … }
|
|
5
|
+
*
|
|
6
|
+
* 于是一个**装着、但太旧**的 CLI 会拿到一个 ✅。实测过一次:`codex-cli 0.149.1`
|
|
7
|
+
* 解不动上游 models 响应(`unknown variant \`max\``,它只认到 `xhigh`),
|
|
8
|
+
* rmcp worker 致命退出,用户看到的是 300s 超时 —— 而 doctor 说一切正常。
|
|
9
|
+
*
|
|
10
|
+
* 🔴 **这里只把版本摆出来,不做最低版本判定。**
|
|
11
|
+
* 「多少版本才够」由上游返回什么决定,不是我们能钉死的常量;猜一个下限,
|
|
12
|
+
* 会在别人升级 CLI、上游又改动之后变成误报。而一个会误报的检查,
|
|
13
|
+
* 第一周就会被人关掉。摆出实际版本,让读的人自己对得上号,是这一格
|
|
14
|
+
* 现在能诚实做到的全部。
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatCliVersion(raw: unknown): string;
|
package/dist/src/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function a0_0x1a4b(_0x5ed33c,_0x30db5b){_0x5ed33c=_0x5ed33c-0x1af;const _0x540cb8=a0_0x540c();let _0x1a4b8b=_0x540cb8[_0x5ed33c];return _0x1a4b8b;}function a0_0x540c(){const _0x3398ef=['token','application/json,\x20text/event-stream','url','3914220FIWCCn','send','/mcp','reply','normal','startsWith','sleep','11863380WleNBg','call','disconnected','task','autoConnect','content','heartbeatTimer','reconnectDelay','1545160KwkaFo','155352EoYLMU','resumeId','registered','slice','6095826qgqwrY','now','completed','toString','getReader','1593675CrzaxG','SSE\x20connected','Authorization','running','sseAbort','log','356WNFEnM','match','name','/events/','heartbeatInterval','processInbox','send_reply','abort','pop','message','offline','connect','AbortError','new_message','4924LlUXBE','text','2.0','emit','report_status','broadcast','sdk','type','status','min','error','get_all_status','Bearer\x20','cwd','sdk-','read','idle','connected','catch','agent','connectSSE','4lWDeOd','alias','parse','new_task','231oYXYZH','application/json','SSE\x20reconnecting\x20in\x20','text/event-stream','result','signal','stringify'];a0_0x540c=function(){return _0x3398ef;};return a0_0x540c();}const a0_0x999825=a0_0x1a4b;(function(_0x4c66d1,_0x418324){const _0x1d1d05=a0_0x1a4b,_0xd91cc0=_0x4c66d1();while(!![]){try{const _0x1f6b79=-parseInt(_0x1d1d05(0x1db))/0x1*(parseInt(_0x1d1d05(0x1cd))/0x2)+parseInt(_0x1d1d05(0x1c7))/0x3+-parseInt(_0x1d1d05(0x1f0))/0x4*(-parseInt(_0x1d1d05(0x1bd))/0x5)+parseInt(_0x1d1d05(0x1fe))/0x6+parseInt(_0x1d1d05(0x1f4))/0x7*(-parseInt(_0x1d1d05(0x1be))/0x8)+-parseInt(_0x1d1d05(0x1c2))/0x9+parseInt(_0x1d1d05(0x1b5))/0xa;if(_0x1f6b79===_0x418324)break;else _0xd91cc0['push'](_0xd91cc0['shift']());}catch(_0x32d0bb){_0xd91cc0['push'](_0xd91cc0['shift']());}}}(a0_0x540c,0x76400));import{EventEmitter as a0_0x5183e4}from'events';import{hostname as a0_0x40be42}from'os';class T extends a0_0x5183e4{[a0_0x999825(0x1fd)];[a0_0x999825(0x1f1)];['token'];[a0_0x999825(0x1ee)];[a0_0x999825(0x1bf)];[a0_0x999825(0x1d1)];[a0_0x999825(0x1bc)];[a0_0x999825(0x1bb)];[a0_0x999825(0x1cb)];[a0_0x999825(0x1ca)]=!0x1;constructor(_0x4f27da){const _0xac7ce3=a0_0x999825;super();if(this[_0xac7ce3(0x1fd)]=_0x4f27da[_0xac7ce3(0x1fd)]['replace'](/\/$/,''),this[_0xac7ce3(0x1f1)]=_0x4f27da['alias'],this['token']=_0x4f27da[_0xac7ce3(0x1fb)],this[_0xac7ce3(0x1ee)]=_0x4f27da[_0xac7ce3(0x1ee)]||_0xac7ce3(0x1e1),this[_0xac7ce3(0x1bf)]=_0xac7ce3(0x1e9)+_0x4f27da['alias']+'-'+Date[_0xac7ce3(0x1c3)]()[_0xac7ce3(0x1c5)](0x24),this[_0xac7ce3(0x1d1)]=_0x4f27da[_0xac7ce3(0x1d1)]??0x2bf20,this[_0xac7ce3(0x1bc)]=_0x4f27da[_0xac7ce3(0x1bc)]??0xbb8,_0x4f27da[_0xac7ce3(0x1b9)]!==!0x1)this[_0xac7ce3(0x1d8)]();}[a0_0x999825(0x1cc)](_0x9d2f4){const _0x25f00c=a0_0x999825;console[_0x25f00c(0x1cc)]('['+new Date()['toTimeString']()[_0x25f00c(0x1c1)](0x0,0x8)+']\x20[commhub:'+this[_0x25f00c(0x1f1)]+']\x20'+_0x9d2f4);}async[a0_0x999825(0x1b6)](_0x2d02b7,_0x5e23f1){const _0x582642=a0_0x999825;let _0x1f8b3d={'Content-Type':_0x582642(0x1f5),'Accept':_0x582642(0x1fc)};if(this[_0x582642(0x1fb)])_0x1f8b3d[_0x582642(0x1c9)]=_0x582642(0x1e7)+this[_0x582642(0x1fb)];let _0x13e135=await(await fetch(this[_0x582642(0x1fd)]+_0x582642(0x1b0),{'method':'POST','headers':_0x1f8b3d,'body':JSON[_0x582642(0x1fa)]({'jsonrpc':_0x582642(0x1dd),'id':Date[_0x582642(0x1c3)](),'method':'tools/call','params':{'name':_0x2d02b7,'arguments':_0x5e23f1}})}))[_0x582642(0x1dc)](),_0x38ecf4=_0x13e135[_0x582642(0x1ce)](/data: (.+)/),_0x23767f=_0x38ecf4?JSON[_0x582642(0x1f2)](_0x38ecf4[0x1]):JSON[_0x582642(0x1f2)](_0x13e135),_0x56f3dc=_0x23767f?.[_0x582642(0x1f8)]?.[_0x582642(0x1ba)]?.[0x0]?.['text'];return _0x56f3dc?JSON[_0x582642(0x1f2)](_0x56f3dc):_0x23767f;}async[a0_0x999825(0x1d8)](){const _0x183084=a0_0x999825;if(this[_0x183084(0x1ca)])return;this[_0x183084(0x1ca)]=!0x0,await this[_0x183084(0x1e3)](_0x183084(0x1eb)),this['log'](_0x183084(0x1c0)),this['heartbeatTimer']=setInterval(()=>{const _0x153a01=_0x183084;this[_0x153a01(0x1e3)](_0x153a01(0x1eb))['catch'](_0xf3a729=>this[_0x153a01(0x1cc)]('heartbeat\x20failed:\x20'+_0xf3a729[_0x153a01(0x1d6)]));},this[_0x183084(0x1d1)]),this[_0x183084(0x1ef)]();}async['disconnect'](){const _0x3cdf0b=a0_0x999825;if(this['running']=!0x1,this[_0x3cdf0b(0x1cb)]?.[_0x3cdf0b(0x1d4)](),this[_0x3cdf0b(0x1bb)])clearInterval(this[_0x3cdf0b(0x1bb)]);await this['status'](_0x3cdf0b(0x1d7))[_0x3cdf0b(0x1ed)](()=>{}),this['log'](_0x3cdf0b(0x1b7));}async[a0_0x999825(0x1af)](_0x3f7ba0,_0x566f3b,_0x3da99c=a0_0x999825(0x1b2)){const _0x241cdf=a0_0x999825;return this[_0x241cdf(0x1b6)]('send_task',{'alias':_0x3f7ba0,'task':_0x566f3b,'priority':_0x3da99c,'from_session':this[_0x241cdf(0x1f1)]});}async['message'](_0x13f328,_0x254f4e){const _0x1d713b=a0_0x999825;return this[_0x1d713b(0x1b6)]('send_message',{'alias':_0x13f328,'message':_0x254f4e,'from_session':this[_0x1d713b(0x1f1)]});}async[a0_0x999825(0x1b1)](_0xe7466a,_0x478922,_0x2f203e=a0_0x999825(0x1c4)){const _0x21f4a5=a0_0x999825;return this[_0x21f4a5(0x1b6)](_0x21f4a5(0x1d3),{'in_reply_to':_0xe7466a,'text':_0x478922,'status':_0x2f203e});}async['status'](_0x5c6444,_0x5095b1){const _0x449cc6=a0_0x999825;return this[_0x449cc6(0x1b6)](_0x449cc6(0x1df),{'resume_id':this[_0x449cc6(0x1bf)],'alias':this[_0x449cc6(0x1f1)],'status':_0x5c6444,'server':a0_0x40be42(),'hostname':a0_0x40be42(),'agent':this[_0x449cc6(0x1ee)],'project_dir':process[_0x449cc6(0x1e8)](),..._0x5095b1});}async['getAllStatus'](){const _0x1f1762=a0_0x999825;return this[_0x1f1762(0x1b6)](_0x1f1762(0x1e6),{});}async[a0_0x999825(0x1e0)](_0x581001,_0x5c146d){const _0x229538=a0_0x999825;return this['call'](_0x229538(0x1e0),{'message':_0x581001,'filter_server':_0x5c146d?.['server'],'filter_status':_0x5c146d?.[_0x229538(0x1e3)]});}async[a0_0x999825(0x1ef)](){const _0x468c96=a0_0x999825;let _0x243ca8=encodeURIComponent(this['alias']),_0x13ba31=this[_0x468c96(0x1fd)]+_0x468c96(0x1d0)+_0x243ca8,_0x320db5=this[_0x468c96(0x1bc)];while(this[_0x468c96(0x1ca)]){try{this['sseAbort']=new AbortController();let _0x5c46cb={'Accept':_0x468c96(0x1f7)};if(this['token'])_0x5c46cb[_0x468c96(0x1c9)]=_0x468c96(0x1e7)+this[_0x468c96(0x1fb)];let _0x2a4087=await fetch(_0x13ba31,{'headers':_0x5c46cb,'signal':this[_0x468c96(0x1cb)][_0x468c96(0x1f9)]});if(!_0x2a4087['ok']||!_0x2a4087['body']){this['log']('SSE\x20failed:\x20'+_0x2a4087[_0x468c96(0x1e3)]),await this[_0x468c96(0x1b4)](_0x320db5),_0x320db5=Math[_0x468c96(0x1e4)](_0x320db5*1.5,0xea60);continue;}_0x320db5=this[_0x468c96(0x1bc)];let _0x24dc80=_0x2a4087['body'][_0x468c96(0x1c6)](),_0x2e483c=new TextDecoder(),_0x1c1ce3='';while(this[_0x468c96(0x1ca)]){let {done:_0x20fc68,value:_0x1314c0}=await _0x24dc80[_0x468c96(0x1ea)]();if(_0x20fc68)break;_0x1c1ce3+=_0x2e483c['decode'](_0x1314c0,{'stream':!0x0});let _0x45d15e=_0x1c1ce3['split']('\x0a');_0x1c1ce3=_0x45d15e[_0x468c96(0x1d5)]()||'';for(let _0x234929 of _0x45d15e){if(!_0x234929[_0x468c96(0x1b3)]('data:\x20'))continue;try{let _0x37ea62=JSON[_0x468c96(0x1f2)](_0x234929[_0x468c96(0x1c1)](0x6));if(_0x37ea62[_0x468c96(0x1e2)]===_0x468c96(0x1ec)){this['log'](_0x468c96(0x1c8)),this[_0x468c96(0x1de)](_0x468c96(0x1ec));continue;}if(_0x37ea62['type']===_0x468c96(0x1f3)||_0x37ea62[_0x468c96(0x1e2)]===_0x468c96(0x1da)||_0x37ea62[_0x468c96(0x1e2)]==='broadcast')await this[_0x468c96(0x1d2)]();}catch{}}}}catch(_0x26936b){if(_0x26936b[_0x468c96(0x1cf)]===_0x468c96(0x1d9))break;this[_0x468c96(0x1de)](_0x468c96(0x1e5),_0x26936b),this[_0x468c96(0x1cc)]('SSE\x20error:\x20'+_0x26936b[_0x468c96(0x1d6)]);}if(this[_0x468c96(0x1ca)])this[_0x468c96(0x1de)](_0x468c96(0x1b7)),this[_0x468c96(0x1cc)](_0x468c96(0x1f6)+_0x320db5/0x3e8+'s...'),await this['sleep'](_0x320db5),_0x320db5=Math[_0x468c96(0x1e4)](_0x320db5*1.5,0xea60);}}async[a0_0x999825(0x1d2)](){const _0x578893=a0_0x999825;try{let _0x2392a8=(await this['call']('get_inbox',{'alias':this['alias'],'limit':0xa}))?.['messages']||[];for(let _0x4474fc of _0x2392a8)await this[_0x578893(0x1b6)]('ack_inbox',{'alias':this[_0x578893(0x1f1)],'message_id':_0x4474fc['id']}),this[_0x578893(0x1cc)]('←\x20'+_0x4474fc['from_session']+':\x20'+_0x4474fc[_0x578893(0x1ba)]['slice'](0x0,0x3c)),this[_0x578893(0x1de)](_0x578893(0x1b8),_0x4474fc),this['emit'](_0x578893(0x1d6),_0x4474fc);}catch(_0x1c4649){this[_0x578893(0x1cc)]('inbox\x20error:\x20'+_0x1c4649['message']);}}['sleep'](_0x5b8686){return new Promise(_0x304075=>setTimeout(_0x304075,_0x5b8686));}}var $=T;export{$ as default,T as CommHub};
|
|
1
|
+
const a0_0x47df6d=a0_0x1c20;(function(_0x4cf617,_0x4a3b26){const _0x3cfabc=a0_0x1c20,_0x515fb2=_0x4cf617();while(!![]){try{const _0x33ca84=-parseInt(_0x3cfabc(0x13a))/0x1*(parseInt(_0x3cfabc(0x148))/0x2)+-parseInt(_0x3cfabc(0x132))/0x3*(-parseInt(_0x3cfabc(0xfc))/0x4)+parseInt(_0x3cfabc(0x13b))/0x5*(parseInt(_0x3cfabc(0x126))/0x6)+-parseInt(_0x3cfabc(0x12a))/0x7*(parseInt(_0x3cfabc(0x124))/0x8)+-parseInt(_0x3cfabc(0xfb))/0x9+-parseInt(_0x3cfabc(0x156))/0xa+parseInt(_0x3cfabc(0x122))/0xb;if(_0x33ca84===_0x4a3b26)break;else _0x515fb2['push'](_0x515fb2['shift']());}catch(_0x212f42){_0x515fb2['push'](_0x515fb2['shift']());}}}(a0_0x49a5,0x18b33));import{EventEmitter as a0_0x3dfc70}from'events';import{hostname as a0_0x55a7b2}from'os';var K=/(?:atok|ntok|utok|ghp|github_pat)_[A-Za-z0-9_-]+|Bearer\s+\S+/gi,L=(_0xd897cd,_0x4d22f6)=>(_0xd897cd??_0x4d22f6)[a0_0x47df6d(0x136)](/[\r\n\x00-\x1f\x7f]/g,'\x20')[a0_0x47df6d(0x137)](0x0,0xf0);function N(_0x3af736){const _0x5b0c3d=a0_0x47df6d;return(_0x3af736 instanceof Error?_0x3af736[_0x5b0c3d(0x111)]:String(_0x3af736??_0x5b0c3d(0xef)))['replace'](K,'[REDACTED]')[_0x5b0c3d(0x136)](/[\r\n\x00-\x1f\x7f]/g,'\x20')[_0x5b0c3d(0x137)](0x0,0xf0);}function a0_0x49a5(){const _0x56bf80=['emit','task.ack','url','get_inbox','duration_ms','autoConnect','task_id','unknown\x20error','disconnected','body','send_task','completed','status','delivered','new_task','registered','catch','signal','sdk','1231047YplFhi','726676hXqbAP','agent','type','log','CommHub\x20rejected\x20task','lifecycle_tracking',']\x20[commhub:','connect','error_message','not_tracked','<pending>','send_message','error_code','Bearer\x20','sseAbort','Authorization','\x20parent_task_id=','match','2.0','connected','sleep','message','transport','decode','sdk-','POST','\x20task_id=','heartbeat\x20failed:\x20','reconnectDelay','AbortError','from_session','from_alias','/events/','SSE\x20reconnecting\x20in\x20','message_id','text','call','connectSSE','2021602vjKgvr','fromAlias','248168SEjRnV','\x20transport=','42scbHHK','send','stringify','[commhub:task]\x20','7VEDoPv','parentTaskId','now','s...','parent_task_id','messages','resumeId','task.send.delivered','3OYhZTp','broadcast','offline','send_rejected','replace','slice','SSE\x20connected','data:\x20','4xYJWcN','119800xKtHxA','result','heartbeatTimer','getReader','content','application/json','abort','running','<unknown>','read','application/json,\x20text/event-stream','inbox\x20error:\x20','\x20lifecycle=','64946pqmDSM','alias','task.replied','idle','error','heartbeatInterval','toString','text/event-stream','to_alias','send_failed','CommHub\x20response\x20omitted\x20task_id','normal','missing_task_id','networkId','1343040kjNNyw','json','failed','token','\x20to=','sending','parse','getAllStatus','\x20from=','processInbox','mcp_http','tools/call','report_status','\x20duration_ms=','\x20network_id=','SSE\x20error:\x20'];a0_0x49a5=function(){return _0x56bf80;};return a0_0x49a5();}function O(_0x3274cd,_0x9301d5=process.env.ANET_TASK_TRACE_FORMAT===a0_0x47df6d(0x157)){const _0xd91b69=a0_0x47df6d;if(_0x9301d5)return JSON['stringify'](_0x3274cd);let _0x3fbf1a=L(_0x3274cd[_0xd91b69(0x12e)],'<missing>'),_0x30ceb9=L(_0x3274cd[_0xd91b69(0xee)],_0xd91b69(0x106)),_0x38ec=_0x3274cd[_0xd91b69(0x108)]?'\x20error='+_0x3274cd[_0xd91b69(0x108)]+':'+(_0x3274cd[_0xd91b69(0x104)]??''):'';return _0xd91b69(0x129)+_0x3274cd[_0xd91b69(0xf4)]+_0xd91b69(0x15e)+L(_0x3274cd[_0xd91b69(0x11b)],_0xd91b69(0x143))+_0xd91b69(0x15a)+L(_0x3274cd[_0xd91b69(0x150)],_0xd91b69(0x143))+_0xd91b69(0x116)+_0x30ceb9+_0xd91b69(0x10c)+_0x3fbf1a+_0xd91b69(0x164)+L(_0x3274cd['network_id'],_0xd91b69(0x143))+_0xd91b69(0x125)+_0x3274cd[_0xd91b69(0x112)]+_0xd91b69(0x163)+_0x3274cd[_0xd91b69(0xec)]+_0xd91b69(0x147)+_0x3274cd[_0xd91b69(0x101)]+_0x38ec;}function U(_0x5d2df5){const _0x30a118=a0_0x47df6d;return{'event':{'sending':'task.send.start','delivered':_0x30a118(0x131),'failed':'task.send.failed','acked':_0x30a118(0x167),'started':'task.started','replied':_0x30a118(0x14a),'expired':'task.expired'}[_0x5d2df5['status']],..._0x5d2df5};}function j(_0x45c46e){const _0x2435b3=a0_0x47df6d;let _0x18cba3=_0x45c46e?.[_0x2435b3(0xee)]||_0x45c46e?.[_0x2435b3(0x11e)]||_0x45c46e?.['id'];if(_0x18cba3)return String(_0x18cba3);let _0x454b52=_0x45c46e?.['content']?.[0x0]?.[_0x2435b3(0x11f)];if(typeof _0x454b52!=='string')return null;try{let _0x3251e5=JSON[_0x2435b3(0x15c)](_0x454b52);return _0x3251e5?.[_0x2435b3(0xee)]||_0x3251e5?.[_0x2435b3(0x11e)]||_0x3251e5?.['id']||null;}catch{return null;}}async function X(_0x520955,_0x526595){const _0xf81a75=a0_0x47df6d;let _0x1541ba=_0x526595[_0xf81a75(0x12c)]??Date['now'],_0x19304b=_0x1541ba(),_0x1ad337=(_0x5e4088,_0x266ee5,_0x3b49fa,_0x155a7a)=>{const _0xe1d848=_0xf81a75;_0x526595['log'](O(U({'from_alias':_0x520955[_0xe1d848(0x123)],'to_alias':_0x520955['toAlias'],'task_id':_0x266ee5,'parent_task_id':_0x520955[_0xe1d848(0x12b)],'network_id':_0x520955[_0xe1d848(0x155)],'transport':_0x520955['transport'],'status':_0x5e4088,'duration_ms':_0x1541ba()-_0x19304b,'lifecycle_tracking':_0x520955['lifecycleTracking'],..._0x3b49fa!==void 0x0?{'error_code':_0x155a7a||_0xe1d848(0x151),'error_message':N(_0x3b49fa)}:{}})));};_0x1ad337(_0xf81a75(0x15b),null);try{let _0x514731=await _0x526595[_0xf81a75(0x127)](),_0x370209=j(_0x514731);if(_0x370209)_0x1ad337(_0xf81a75(0xf5),_0x370209);else{if(_0x514731?.['ok']===!0x1||_0x514731?.[_0xf81a75(0x14c)])_0x1ad337(_0xf81a75(0x158),_0x370209,_0x514731?.['error']||_0xf81a75(0x100),_0xf81a75(0x135));else _0x1ad337(_0xf81a75(0x158),null,_0xf81a75(0x152),_0xf81a75(0x154));}return _0x514731;}catch(_0x1d2cd6){throw _0x1ad337(_0xf81a75(0x158),null,_0x1d2cd6,'send_failed'),_0x1d2cd6;}}async function Y(_0x5209b2,_0x342982){const _0x3f6657=a0_0x47df6d;return X({'fromAlias':_0x5209b2[_0x3f6657(0x123)],'toAlias':_0x5209b2[_0x3f6657(0x149)],'parentTaskId':_0x5209b2[_0x3f6657(0x12b)]||null,'networkId':_0x5209b2[_0x3f6657(0x155)]||null,'transport':_0x3f6657(0x160),'lifecycleTracking':_0x3f6657(0x105)},_0x342982);}class $ extends a0_0x3dfc70{['url'];[a0_0x47df6d(0x149)];[a0_0x47df6d(0x159)];[a0_0x47df6d(0xfd)];[a0_0x47df6d(0x130)];['heartbeatInterval'];[a0_0x47df6d(0x118)];['heartbeatTimer'];[a0_0x47df6d(0x10a)];[a0_0x47df6d(0x142)]=!0x1;constructor(_0xbbdca9){const _0x2ec548=a0_0x47df6d;super();if(this['url']=_0xbbdca9[_0x2ec548(0x168)][_0x2ec548(0x136)](/\/$/,''),this['alias']=_0xbbdca9[_0x2ec548(0x149)],this[_0x2ec548(0x159)]=_0xbbdca9['token'],this['agent']=_0xbbdca9[_0x2ec548(0xfd)]||_0x2ec548(0xfa),this[_0x2ec548(0x130)]=_0x2ec548(0x114)+_0xbbdca9[_0x2ec548(0x149)]+'-'+Date[_0x2ec548(0x12c)]()[_0x2ec548(0x14e)](0x24),this[_0x2ec548(0x14d)]=_0xbbdca9['heartbeatInterval']??0x2bf20,this['reconnectDelay']=_0xbbdca9['reconnectDelay']??0xbb8,_0xbbdca9[_0x2ec548(0xed)]!==!0x1)this[_0x2ec548(0x103)]();}[a0_0x47df6d(0xff)](_0x187535){const _0x446b6d=a0_0x47df6d;console[_0x446b6d(0xff)]('['+new Date()['toTimeString']()['slice'](0x0,0x8)+_0x446b6d(0x102)+this['alias']+']\x20'+_0x187535);}async[a0_0x47df6d(0x120)](_0x42dafb,_0x40b9a4){const _0x259b65=a0_0x47df6d;let _0x56f2e0={'Content-Type':_0x259b65(0x140),'Accept':_0x259b65(0x145)};if(this[_0x259b65(0x159)])_0x56f2e0['Authorization']='Bearer\x20'+this[_0x259b65(0x159)];let _0x48927b=await(await fetch(this[_0x259b65(0x168)]+'/mcp',{'method':_0x259b65(0x115),'headers':_0x56f2e0,'body':JSON[_0x259b65(0x128)]({'jsonrpc':_0x259b65(0x10e),'id':Date[_0x259b65(0x12c)](),'method':_0x259b65(0x161),'params':{'name':_0x42dafb,'arguments':_0x40b9a4}})}))[_0x259b65(0x11f)](),_0x198c6a=_0x48927b[_0x259b65(0x10d)](/data: (.+)/),_0x327ab9=_0x198c6a?JSON['parse'](_0x198c6a[0x1]):JSON['parse'](_0x48927b),_0x58f15e=_0x327ab9?.[_0x259b65(0x13c)]?.[_0x259b65(0x13f)]?.[0x0]?.[_0x259b65(0x11f)];return _0x58f15e?JSON['parse'](_0x58f15e):_0x327ab9;}async[a0_0x47df6d(0x103)](){const _0x438296=a0_0x47df6d;if(this[_0x438296(0x142)])return;this[_0x438296(0x142)]=!0x0,await this[_0x438296(0xf4)](_0x438296(0x14b)),this['log'](_0x438296(0xf7)),this['heartbeatTimer']=setInterval(()=>{const _0x4e0a82=_0x438296;this['status'](_0x4e0a82(0x14b))[_0x4e0a82(0xf8)](_0xb37689=>this['log'](_0x4e0a82(0x117)+_0xb37689[_0x4e0a82(0x111)]));},this['heartbeatInterval']),this[_0x438296(0x121)]();}async['disconnect'](){const _0x124339=a0_0x47df6d;if(this['running']=!0x1,this[_0x124339(0x10a)]?.[_0x124339(0x141)](),this[_0x124339(0x13d)])clearInterval(this[_0x124339(0x13d)]);await this[_0x124339(0xf4)](_0x124339(0x134))['catch'](()=>{}),this[_0x124339(0xff)](_0x124339(0xf0));}async['send'](_0x3dcf1b,_0x557430,_0x3707a9=a0_0x47df6d(0x153)){const _0x3f9a20=a0_0x47df6d;return Y({'alias':_0x3dcf1b,'fromAlias':this['alias']},{'log':_0x5d9839=>console['log'](_0x5d9839),'send':()=>this[_0x3f9a20(0x120)](_0x3f9a20(0xf2),{'alias':_0x3dcf1b,'task':_0x557430,'priority':_0x3707a9,'from_session':this[_0x3f9a20(0x149)]})});}async['message'](_0x22f3a6,_0x3f73cf){const _0x53378e=a0_0x47df6d;return this[_0x53378e(0x120)](_0x53378e(0x107),{'alias':_0x22f3a6,'message':_0x3f73cf,'from_session':this[_0x53378e(0x149)]});}async['reply'](_0xb3427d,_0x5b7405,_0xef62d1=a0_0x47df6d(0xf3)){const _0x2da225=a0_0x47df6d;return this[_0x2da225(0x120)]('send_reply',{'in_reply_to':_0xb3427d,'text':_0x5b7405,'status':_0xef62d1});}async[a0_0x47df6d(0xf4)](_0x4a57c9,_0x21acb7){const _0x499b6b=a0_0x47df6d;return this[_0x499b6b(0x120)](_0x499b6b(0x162),{'resume_id':this[_0x499b6b(0x130)],'alias':this[_0x499b6b(0x149)],'status':_0x4a57c9,'server':a0_0x55a7b2(),'hostname':a0_0x55a7b2(),'agent':this['agent'],'project_dir':process['cwd'](),..._0x21acb7});}async[a0_0x47df6d(0x15d)](){return this['call']('get_all_status',{});}async[a0_0x47df6d(0x133)](_0x40ca3a,_0x39fa68){const _0x502df1=a0_0x47df6d;return this[_0x502df1(0x120)](_0x502df1(0x133),{'message':_0x40ca3a,'filter_server':_0x39fa68?.['server'],'filter_status':_0x39fa68?.['status']});}async[a0_0x47df6d(0x121)](){const _0x53f7f8=a0_0x47df6d;let _0x3ea421=encodeURIComponent(this[_0x53f7f8(0x149)]),_0x10c1f6=this['url']+_0x53f7f8(0x11c)+_0x3ea421,_0x437e8d=this[_0x53f7f8(0x118)];while(this[_0x53f7f8(0x142)]){try{this[_0x53f7f8(0x10a)]=new AbortController();let _0x47138b={'Accept':_0x53f7f8(0x14f)};if(this['token'])_0x47138b[_0x53f7f8(0x10b)]=_0x53f7f8(0x109)+this[_0x53f7f8(0x159)];let _0x424bd6=await fetch(_0x10c1f6,{'headers':_0x47138b,'signal':this[_0x53f7f8(0x10a)][_0x53f7f8(0xf9)]});if(!_0x424bd6['ok']||!_0x424bd6[_0x53f7f8(0xf1)]){this['log']('SSE\x20failed:\x20'+_0x424bd6[_0x53f7f8(0xf4)]),await this['sleep'](_0x437e8d),_0x437e8d=Math['min'](_0x437e8d*1.5,0xea60);continue;}_0x437e8d=this[_0x53f7f8(0x118)];let _0x2659c9=_0x424bd6[_0x53f7f8(0xf1)][_0x53f7f8(0x13e)](),_0x57f05b=new TextDecoder(),_0x39184d='';while(this['running']){let {done:_0x3ace8c,value:_0xc52fa1}=await _0x2659c9[_0x53f7f8(0x144)]();if(_0x3ace8c)break;_0x39184d+=_0x57f05b[_0x53f7f8(0x113)](_0xc52fa1,{'stream':!0x0});let _0x39a4fd=_0x39184d['split']('\x0a');_0x39184d=_0x39a4fd['pop']()||'';for(let _0x2b58ce of _0x39a4fd){if(!_0x2b58ce['startsWith'](_0x53f7f8(0x139)))continue;try{let _0xb98433=JSON[_0x53f7f8(0x15c)](_0x2b58ce[_0x53f7f8(0x137)](0x6));if(_0xb98433['type']===_0x53f7f8(0x10f)){this[_0x53f7f8(0xff)](_0x53f7f8(0x138)),this[_0x53f7f8(0x166)](_0x53f7f8(0x10f));continue;}if(_0xb98433[_0x53f7f8(0xfe)]===_0x53f7f8(0xf6)||_0xb98433['type']==='new_message'||_0xb98433[_0x53f7f8(0xfe)]===_0x53f7f8(0x133))await this[_0x53f7f8(0x15f)]();}catch{}}}}catch(_0x29b76b){if(_0x29b76b['name']===_0x53f7f8(0x119))break;this['emit'](_0x53f7f8(0x14c),_0x29b76b),this[_0x53f7f8(0xff)](_0x53f7f8(0x165)+_0x29b76b[_0x53f7f8(0x111)]);}if(this[_0x53f7f8(0x142)])this[_0x53f7f8(0x166)](_0x53f7f8(0xf0)),this['log'](_0x53f7f8(0x11d)+_0x437e8d/0x3e8+_0x53f7f8(0x12d)),await this['sleep'](_0x437e8d),_0x437e8d=Math['min'](_0x437e8d*1.5,0xea60);}}async['processInbox'](){const _0x1f2d0d=a0_0x47df6d;try{let _0xd6a027=(await this[_0x1f2d0d(0x120)](_0x1f2d0d(0xeb),{'alias':this[_0x1f2d0d(0x149)],'limit':0xa}))?.[_0x1f2d0d(0x12f)]||[];for(let _0x1eb078 of _0xd6a027)await this[_0x1f2d0d(0x120)]('ack_inbox',{'alias':this[_0x1f2d0d(0x149)],'message_id':_0x1eb078['id']}),this[_0x1f2d0d(0xff)]('←\x20'+_0x1eb078[_0x1f2d0d(0x11a)]+':\x20'+_0x1eb078[_0x1f2d0d(0x13f)][_0x1f2d0d(0x137)](0x0,0x3c)),this[_0x1f2d0d(0x166)]('task',_0x1eb078),this[_0x1f2d0d(0x166)](_0x1f2d0d(0x111),_0x1eb078);}catch(_0x4f9b3c){this[_0x1f2d0d(0xff)](_0x1f2d0d(0x146)+_0x4f9b3c[_0x1f2d0d(0x111)]);}}[a0_0x47df6d(0x110)](_0xc3e9e7){return new Promise(_0x4e9c16=>setTimeout(_0x4e9c16,_0xc3e9e7));}}var F=$;function a0_0x1c20(_0x2a794c,_0x1d483e){_0x2a794c=_0x2a794c-0xeb;const _0x49a5ce=a0_0x49a5();let _0x1c20eb=_0x49a5ce[_0x2a794c];return _0x1c20eb;}export{F as default,$ as CommHub};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface CodexHomeInheritedFile {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
readonly mode: number;
|
|
4
|
+
/** Why a node needs it — printed when staged, so the action is legible. */
|
|
5
|
+
readonly because: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const CODEX_HOME_INHERITED_FILES: readonly CodexHomeInheritedFile[];
|
|
8
|
+
export interface StatLike {
|
|
9
|
+
readonly mtimeMs: number;
|
|
10
|
+
}
|
|
11
|
+
export type StatFn = (path: string) => StatLike | null;
|
|
12
|
+
export interface CodexHomeStageStep {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly src: string;
|
|
15
|
+
readonly dst: string;
|
|
16
|
+
readonly mode: number;
|
|
17
|
+
readonly reason: "missing" | "host-newer";
|
|
18
|
+
readonly because: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What to copy from the host CODEX_HOME into the node's, right now.
|
|
22
|
+
*
|
|
23
|
+
* Pure: takes the stat function so the ordering rules are testable without a
|
|
24
|
+
* filesystem. A node file that is NEWER than the host's is left alone — codex
|
|
25
|
+
* refreshes its own copy in place, and clobbering it would throw away a fresher
|
|
26
|
+
* token than the one we hold.
|
|
27
|
+
*/
|
|
28
|
+
export declare function codexHomeStagePlan(hostHome: string, nodeHome: string, stat: StatFn, join: (a: string, b: string) => string, files?: readonly CodexHomeInheritedFile[]): CodexHomeStageStep[];
|
|
29
|
+
export type CodexTuiBlocker = "sign-in" | "update-prompt" | "trust-prompt";
|
|
30
|
+
export type CodexTuiPaneState = CodexTuiBlocker | "not-painted" | "usable";
|
|
31
|
+
/**
|
|
32
|
+
* Classify the TUI, from two different reads of the same pane.
|
|
33
|
+
*
|
|
34
|
+
* They answer different questions and need different tmux flags — the same
|
|
35
|
+
* distinction `waitForTmuxPaneText` documents one screenful up:
|
|
36
|
+
*
|
|
37
|
+
* onScreen (capture-pane, no -S) → "is it sitting on a prompt right now"
|
|
38
|
+
* A prompt that was already answered stays in scrollback forever,
|
|
39
|
+
* so reading history here would report a fixed node as blocked.
|
|
40
|
+
*
|
|
41
|
+
* everSeen (capture-pane -S -N) → "did the TUI ever paint"
|
|
42
|
+
* The banner is printed once and scrolls away — an MCP error burst
|
|
43
|
+
* was enough to push it off screen in under a minute — so reading
|
|
44
|
+
* only the current screen here would report a working node as dead.
|
|
45
|
+
*
|
|
46
|
+
* 🔴 And "the pane has text" answers neither. An earlier version of this used
|
|
47
|
+
* "≥3 non-empty lines ⇒ ready" and passed a node parked on the sign-in page.
|
|
48
|
+
* Measured timeline for that start:
|
|
49
|
+
*
|
|
50
|
+
* t≈3s 22 non-empty lines ← launcher output, TUI not up yet
|
|
51
|
+
* t≈6s 0 non-empty lines ← codex clears the screen and takes over
|
|
52
|
+
* t≈9s 10 non-empty lines ← the sign-in page finally paints
|
|
53
|
+
*
|
|
54
|
+
* The rule fired at t≈3s, six seconds before the thing it was meant to catch
|
|
55
|
+
* existed.
|
|
56
|
+
*/
|
|
57
|
+
export declare function codexTuiPaneState(onScreen: string, everSeen?: string): CodexTuiPaneState;
|
|
58
|
+
/**
|
|
59
|
+
* What the operator should read when a node came up but cannot work. Names the
|
|
60
|
+
* blocker AND where to look — a launcher that only says "not ready" sends
|
|
61
|
+
* someone hunting through three tmux sessions.
|
|
62
|
+
*/
|
|
63
|
+
export declare function describeCodexTuiBlocker(blocker: CodexTuiBlocker, displayName: string, tuiSession: string): string;
|
|
64
|
+
/** The TUI never painted at all — a distinct failure from being blocked. */
|
|
65
|
+
export declare function describeCodexTuiNotPainted(displayName: string, tuiSession: string, waitedMs: number): string;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export interface CodexCopresenceProfileFields {
|
|
2
|
+
readonly runtime?: string;
|
|
3
|
+
readonly codexCopresence?: boolean;
|
|
4
|
+
readonly codexCopresenceFullAccess?: boolean;
|
|
5
|
+
readonly flags?: Record<string, unknown>;
|
|
6
|
+
/** Absolute project directory to launch the codex TUI in. Passed as
|
|
7
|
+
* `-C <dir>` to `codex resume`. Also cross-checked against hub-reported
|
|
8
|
+
* `session.project_dir` in Phase 4.3. */
|
|
9
|
+
readonly codexProjectDir?: string;
|
|
10
|
+
/** Which launch adapter to use in Phase 3.2. `"codex-standard"` = stock
|
|
11
|
+
* `codex app-server` + `codex resume`; `"codex-custom-wrapper"` = TMHR狗-class
|
|
12
|
+
* custom LLM API wrapper (hosted tool / compaction / image-output compat).
|
|
13
|
+
* Unknown / absent → Phase 0 fail-closed (unknown adapters must not fall
|
|
14
|
+
* through to a default that might not fit the node's real topology). */
|
|
15
|
+
readonly codexLaunchAdapter?: "codex-standard" | "codex-custom-wrapper";
|
|
16
|
+
/** Alias of the peer that will verify this node's identity in Phase 6.
|
|
17
|
+
* Peer MUST be (a) registered in the same network, (b) online at resume
|
|
18
|
+
* time, (c) not this node itself. Identity attestation uses a fresh
|
|
19
|
+
* Hub outbound `send_task` from THIS node's Bridge to <codexProbePeer>
|
|
20
|
+
* carrying a nonce; peer echoes the nonce back; CLI cross-checks Hub's
|
|
21
|
+
* recorded `from_name / from_node_id / to_name / to_node_id`.
|
|
22
|
+
*
|
|
23
|
+
* 🔴 self-loop is deliberately not accepted here — it cannot prove
|
|
24
|
+
* cross-node routing closure (design v7 Phase 6 = D, per TMHR
|
|
25
|
+
* 06cfb29a). Single-node users cannot run `anet node resume` in
|
|
26
|
+
* this initial cut; tracked at #1527. */
|
|
27
|
+
readonly codexProbePeer?: string;
|
|
28
|
+
}
|
|
29
|
+
/** #1521 — the three fields `anet node resume <alias>` will require on a
|
|
30
|
+
* codex-copresence node once the resume dispatcher lands (#1528, Draft).
|
|
31
|
+
* In this PR, only shape-level enumeration exists (see
|
|
32
|
+
* `missingCodexResumeFields` below); no CLI code reads that enumeration
|
|
33
|
+
* and the operator-facing remediation flow is deliberately deferred
|
|
34
|
+
* along with its real caller. */
|
|
35
|
+
export type CodexResumeRequiredField = "codexProjectDir" | "codexLaunchAdapter" | "codexProbePeer";
|
|
36
|
+
export declare const CODEX_RESUME_REQUIRED_FIELDS: readonly CodexResumeRequiredField[];
|
|
37
|
+
/** Enumerate which of the three resume-required fields are absent /
|
|
38
|
+
* wrong-shaped on this profile. Returns [] when all three are present
|
|
39
|
+
* and well-shaped.
|
|
40
|
+
*
|
|
41
|
+
* 🔴 SHAPE-ONLY validation:
|
|
42
|
+
* - codexProjectDir : string starting with `/`. This is a CHEAP typo
|
|
43
|
+
* filter, NOT the security gate. Phase 0 (in #1528) MUST additionally
|
|
44
|
+
* `realpath`/canonicalize + reject NUL, `/`, and any untrusted target
|
|
45
|
+
* before passing this value to `codex resume -C <dir>` (per TMHR
|
|
46
|
+
* 4bab8196: "startsWith('/') 不是最终安全门").
|
|
47
|
+
* - codexLaunchAdapter : enum membership.
|
|
48
|
+
* - codexProbePeer : Hub exact-alias lookup, so value MUST equal its own
|
|
49
|
+
* trim. Values with leading/trailing whitespace would fail Hub lookup
|
|
50
|
+
* 100% of the time; rejecting them at the shape layer is a witnessed-red
|
|
51
|
+
* canary rather than a permissive pass-through (per TMHR 048d0061 R2:
|
|
52
|
+
* "不要 pin 一个必然在 Hub exact lookup 阶段失败的 well-shaped 值").
|
|
53
|
+
* Liveness (registered + online + non-self) is a separate hub-side
|
|
54
|
+
* check in Phase 0.
|
|
55
|
+
*
|
|
56
|
+
* Deliberately NOT included: an operator-facing diagnostic helper. An
|
|
57
|
+
* earlier draft included `codexResumeMissingConfigHint` that referenced
|
|
58
|
+
* a `anet node config apply` verb which does not exist in the tree —
|
|
59
|
+
* TMHR 4bab8196 blocked it as the exact "存在 ≠ 会执行" antipattern.
|
|
60
|
+
* The diagnostic lands with its real caller in a later PR that either
|
|
61
|
+
* (a) implements `anet node config apply` first, or (b) reworks the
|
|
62
|
+
* diagnostic to reference existing commands only. */
|
|
63
|
+
export declare function missingCodexResumeFields(profile: CodexCopresenceProfileFields): CodexResumeRequiredField[];
|
|
64
|
+
export declare const CODEX_COPRESENCE_RUNTIME = "codex-app-server";
|
|
65
|
+
/**
|
|
66
|
+
* Should this `anet node start` bring up the co-presence stack?
|
|
67
|
+
*
|
|
68
|
+
* The flag still works for a one-off; the profile field is what makes the
|
|
69
|
+
* second start a single command.
|
|
70
|
+
*/
|
|
71
|
+
export declare function codexCopresenceRequested(flagPassed: boolean, profile: CodexCopresenceProfileFields): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* True when a plain `anet node start` just became enough for this node — i.e.
|
|
74
|
+
* the flag was passed and the profile had not recorded it yet. Callers persist
|
|
75
|
+
* `codexCopresence: true` and tell the user they can drop the flag.
|
|
76
|
+
*/
|
|
77
|
+
export declare function shouldPersistCodexCopresence(flagPassed: boolean, profile: CodexCopresenceProfileFields): boolean;
|
|
78
|
+
export interface CodexCopresencePosture {
|
|
79
|
+
readonly sandboxMode: "danger-full-access" | "read-only";
|
|
80
|
+
readonly approvalPolicy: "never" | "on-request";
|
|
81
|
+
/** Set when the node asked for full access and is NOT getting it. */
|
|
82
|
+
readonly downgradeNotice?: string;
|
|
83
|
+
/** Set when a remembered grant (not this invocation's flag) opened it up. */
|
|
84
|
+
readonly grantedFromProfile: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve the sandbox the co-presence app-server runs under.
|
|
88
|
+
*
|
|
89
|
+
* Precedence: this invocation's flag → a grant already recorded on the profile
|
|
90
|
+
* → read-only. `flags.sandboxMode` alone never opens the sandbox; it only
|
|
91
|
+
* decides whether staying closed is worth telling the user about.
|
|
92
|
+
*/
|
|
93
|
+
export declare function codexCopresencePosture(dangerFlagPassed: boolean, profile: CodexCopresenceProfileFields, displayName?: string): CodexCopresencePosture;
|
|
94
|
+
/** True when an explicit grant should be written to the profile for next time. */
|
|
95
|
+
export declare function shouldPersistCodexFullAccess(dangerFlagPassed: boolean, profile: CodexCopresenceProfileFields): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* What `anet node create --runtime codex-app-server` should record, so that a
|
|
98
|
+
* later `anet node start <name>` is a single command with no flag.
|
|
99
|
+
*
|
|
100
|
+
* Shape copied from opencode, which already does exactly this at create time:
|
|
101
|
+
*
|
|
102
|
+
* ...(runtime === "opencode-cli"
|
|
103
|
+
* ? { opencodeMode: opts.mode === "copresence" || opts.copresence === "true"
|
|
104
|
+
* ? "copresence" : "headless" }
|
|
105
|
+
* : {}),
|
|
106
|
+
*
|
|
107
|
+
* Deliberately NOT keyed on which alias the operator typed. `codex-tui`,
|
|
108
|
+
* `codex-appserver` and `codex-app-server` are documented as aliases of one
|
|
109
|
+
* runtime (agent-node/src/cli.ts); giving one of them a different default would
|
|
110
|
+
* quietly break that contract, and "I typed the short name" is not a reliable
|
|
111
|
+
* statement of intent. Opting in stays explicit, exactly as it is for opencode.
|
|
112
|
+
*/
|
|
113
|
+
export declare function codexCopresenceCreateFields(normalizedRuntime: string | undefined, copresenceOpt: string | boolean | undefined): {
|
|
114
|
+
codexCopresence?: true;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* One line at create time for a node that will start headless.
|
|
118
|
+
*
|
|
119
|
+
* NOT keyed on which alias the operator typed: by the time create builds the
|
|
120
|
+
* profile, `opts.runtime` has already been normalized to `codex-app-server`, so
|
|
121
|
+
* `codex-tui` is simply not recoverable here — a hint that tried to read it
|
|
122
|
+
* would never fire. (It didn't; that is how this was found.) Keying on the
|
|
123
|
+
* runtime instead also keeps the aliases true synonyms.
|
|
124
|
+
*/
|
|
125
|
+
export declare function codexCopresenceCreateHint(normalizedRuntime: string | undefined, copresenceOpt: string | boolean | undefined, displayName: string): string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface CodexRecoveryVerification {
|
|
2
|
+
method: "thread/start" | "thread/resume";
|
|
3
|
+
threadId: string;
|
|
4
|
+
verifiedAt: string;
|
|
5
|
+
historyTurnCount: number;
|
|
6
|
+
historyFingerprint: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CodexRecoveryBackup {
|
|
9
|
+
backupDir: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
stateFiles: string[];
|
|
12
|
+
}
|
|
13
|
+
/** Transaction boundary shared by Windows and POSIX cutovers: the snapshot
|
|
14
|
+
* cannot begin until every authoritative state writer has quiesced. */
|
|
15
|
+
export declare function quiesceThenSnapshot<T>(quiesce: () => Promise<void>, snapshot: () => T): Promise<T>;
|
|
16
|
+
/** A stored thread is never considered resumed until app-server reads the
|
|
17
|
+
* exact thread back with persisted history. This is deliberately stricter
|
|
18
|
+
* than accepting a successful thread/resume RPC response. */
|
|
19
|
+
export declare function verifyCodexThreadHistory(method: "thread/start" | "thread/resume", expectedThreadId: string, readResult: unknown, now?: Date): CodexRecoveryVerification;
|
|
20
|
+
export declare function resumeAndVerifyCodexThread(threadId: string, request: (method: string, params: Record<string, unknown>) => Promise<unknown>): Promise<CodexRecoveryVerification>;
|
|
21
|
+
/** Private, node-local recovery point. config-recovery.json is deliberately
|
|
22
|
+
* redacted non-credential audit/recovery metadata, not an identity backup.
|
|
23
|
+
* Identity recovery relies on leaving the original node config and CODEX_HOME
|
|
24
|
+
* in place. Credentials are never copied into this snapshot. */
|
|
25
|
+
export declare function backupCodexRecoveryState(opts: {
|
|
26
|
+
nodeDir: string;
|
|
27
|
+
codexHome: string;
|
|
28
|
+
now?: Date;
|
|
29
|
+
}): CodexRecoveryBackup;
|
|
30
|
+
export declare function codexTopologyAudit(profile: Record<string, any>, nodeDir: string, cwd: string): {
|
|
31
|
+
launchMode: string;
|
|
32
|
+
cwd: string;
|
|
33
|
+
codexHome: string;
|
|
34
|
+
remote: any;
|
|
35
|
+
threadId: any;
|
|
36
|
+
model: any;
|
|
37
|
+
flags: any;
|
|
38
|
+
lastRecoveryVerification: any;
|
|
39
|
+
lastRecoveryBackup: {
|
|
40
|
+
createdAt: any;
|
|
41
|
+
stateFiles: any;
|
|
42
|
+
} | null;
|
|
43
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type CopresenceThreadPlan = {
|
|
2
|
+
method: "thread/resume";
|
|
3
|
+
params: {
|
|
4
|
+
threadId: string;
|
|
5
|
+
};
|
|
6
|
+
bootstrap: false;
|
|
7
|
+
} | {
|
|
8
|
+
method: "thread/start";
|
|
9
|
+
params: Record<string, never>;
|
|
10
|
+
bootstrap: true;
|
|
11
|
+
};
|
|
12
|
+
/** Restarting a node must preserve its conversation; only a virgin node starts one. */
|
|
13
|
+
export declare function copresenceThreadPlan(storedThreadId?: string): CopresenceThreadPlan;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function bridgeClientHealthReceipt(remote: string, threadId: string): string;
|
|
2
|
+
export interface MigratedCodexPendingThread {
|
|
3
|
+
version: 1;
|
|
4
|
+
threadId: string;
|
|
5
|
+
serverUrl: string;
|
|
6
|
+
marker: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Move a crash-window deferred candidate onto the launcher's new owned
|
|
10
|
+
* app-server generation. The old URL is an authority supplied by the same
|
|
11
|
+
* private node config, not by the pending object itself; every mismatch is a
|
|
12
|
+
* hard stop so restart never guesses or adopts a second thread.
|
|
13
|
+
*/
|
|
14
|
+
export declare function migrateCodexPendingThread(value: unknown, oldServerUrl: unknown, expectedOldMarker: unknown, newServerUrl: string, newMarker: string): MigratedCodexPendingThread;
|
|
15
|
+
export declare function requirePromotedCodexPendingThread(value: unknown, pendingThreadId: string): string;
|
|
16
|
+
export declare function codexTuiLaunchArgs(remote: string, model: string, threadId?: string, dangerFullAccess?: boolean): string[];
|
|
17
|
+
export declare function assertPendingServerQuiesced(serverUrl: unknown, isListening: (port: number) => boolean | Promise<boolean>): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseCommhubToolResult(json: any): any;
|
|
@@ -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>;
|