@sleep2agi/agent-network 2.3.0-preview.14 → 2.3.0-preview.141
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-inbox-wake.d.ts +21 -0
- package/dist/src/channel-meta.d.ts +10 -0
- package/dist/src/channel-peer-reply.d.ts +41 -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-auth-session.d.ts +47 -0
- package/dist/src/cli-errors.d.ts +39 -0
- package/dist/src/cli-exit.d.ts +11 -0
- package/dist/src/cli-help.d.ts +10 -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-adopt.d.ts +46 -0
- package/dist/src/codex-auth-fingerprint.d.ts +211 -0
- package/dist/src/codex-copresence-bridge-log.d.ts +44 -0
- package/dist/src/codex-copresence-login-gate.d.ts +16 -0
- package/dist/src/codex-copresence-model.d.ts +23 -0
- 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 +58 -0
- package/dist/src/codex-copresence-thread.d.ts +13 -0
- package/dist/src/codex-home-enforce.d.ts +71 -0
- package/dist/src/codex-lifecycle-account.d.ts +104 -0
- package/dist/src/codex-lifecycle-canary.d.ts +20 -0
- package/dist/src/codex-lifecycle-facts.d.ts +53 -0
- package/dist/src/codex-lifecycle-fork.d.ts +91 -0
- package/dist/src/codex-lifecycle-preflight.d.ts +77 -0
- package/dist/src/codex-lifecycle-receipt.d.ts +53 -0
- package/dist/src/codex-lifecycle-restart.d.ts +88 -0
- package/dist/src/codex-login-share-guard.d.ts +146 -0
- package/dist/src/codex-menu.d.ts +78 -0
- package/dist/src/codex-model-default.d.ts +6 -0
- package/dist/src/codex-node-login.d.ts +98 -0
- package/dist/src/codex-resume.d.ts +89 -0
- package/dist/src/codex-tui-client-health.d.ts +30 -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 +353 -0
- package/dist/src/copresence-startup-diagnosis.d.ts +17 -0
- package/dist/src/daemon-anet-bin.d.ts +32 -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/grok-create-mode.d.ts +19 -0
- package/dist/src/host-telemetry.d.ts +14 -0
- package/dist/src/hub-health-timeout.d.ts +4 -0
- package/dist/src/hub-server-version.d.ts +64 -0
- package/dist/src/hub-stop.d.ts +74 -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 +80 -0
- package/dist/src/im/feishu/bridge.d.ts +78 -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-route.d.ts +62 -0
- package/dist/src/im/feishu/worker-lifecycle.d.ts +13 -0
- package/dist/src/im/feishu/worker.js +187 -144
- package/dist/src/im/types.d.ts +30 -1
- package/dist/src/inbox-drain.d.ts +20 -0
- package/dist/src/local-process-state.d.ts +12 -0
- package/dist/src/locale-diagnostic.d.ts +12 -0
- package/dist/src/login-client-label.d.ts +6 -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-clone.d.ts +134 -0
- package/dist/src/node-delete-hub.d.ts +42 -0
- package/dist/src/node-files.d.ts +62 -0
- package/dist/src/node-locate.d.ts +60 -0
- package/dist/src/node-not-found.d.ts +1 -0
- package/dist/src/node-running-guard.d.ts +9 -0
- package/dist/src/node-server-identity.d.ts +35 -0
- package/dist/src/node-server-payload.d.ts +66 -0
- package/dist/src/node-server-rules-file.d.ts +14 -0
- package/dist/src/node-server-version.d.ts +39 -0
- package/dist/src/node-server.js +8 -1
- package/dist/src/node-skills.d.ts +32 -0
- 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-free-tier.d.ts +27 -0
- package/dist/src/opencode-generation-create.d.ts +27 -0
- package/dist/src/opencode-generation-record.d.ts +24 -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 +44 -0
- package/dist/src/opencode-pin.d.ts +61 -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 +36 -0
- package/dist/src/opencode-smoke-env.d.ts +1 -0
- package/dist/src/opencode-versions.d.ts +44 -0
- package/dist/src/outbound-attachments.d.ts +60 -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 +35 -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-local-links.d.ts +47 -0
- package/dist/src/reply-originator.d.ts +20 -0
- package/dist/src/resume-runtime-infer.d.ts +18 -0
- package/dist/src/rules-file.d.ts +53 -0
- package/dist/src/secret-shell-guidance.d.ts +4 -0
- package/dist/src/session-status-class.d.ts +48 -0
- package/dist/src/sibling-agent-node.d.ts +23 -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 +11 -0
- package/dist/src/tmux-capability.d.ts +58 -0
- package/dist/src/tmux-exact-target.d.ts +36 -0
- package/dist/src/tmux-format.d.ts +20 -0
- package/dist/src/tmux-pane-prompt.d.ts +22 -0
- package/dist/src/tmux.d.ts +44 -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 +31 -0
- package/dist/src/upgrade-channel.d.ts +31 -0
- package/dist/src/win-launcher.d.ts +20 -0
- package/dist/src/windows-codex-copresence.d.ts +60 -0
- package/package.json +11 -6
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** One attachment as the Hub stores it. `file_id` comes from commhub_upload_file. */
|
|
2
|
+
export interface OutboundAttachment {
|
|
3
|
+
type: "file";
|
|
4
|
+
file_id: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
mime?: string;
|
|
7
|
+
size?: number;
|
|
8
|
+
}
|
|
9
|
+
export type NormalizeResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
attachments: OutboundAttachment[];
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Normalize caller-supplied attachments into what the Hub expects.
|
|
18
|
+
*
|
|
19
|
+
* Absent input is not an error and yields no `attachments` key at all — a call
|
|
20
|
+
* without attachments must produce a byte-identical request to before this
|
|
21
|
+
* existed, or every existing reply changes behaviour the day this ships.
|
|
22
|
+
*/
|
|
23
|
+
/** 2026-09-15 —— LLM 常把数组参数当成 JSON 字符串传(外部团队节点 真机:attachments 是 "[{...}]" 字符串,回复 ok:true 但 hub 里没有附件)。
|
|
24
|
+
* 字符串先 parse;parse 不出来是错误,不是「没有附件」。 */
|
|
25
|
+
export declare function coerceAttachmentsArg(input: unknown): unknown;
|
|
26
|
+
export declare function normalizeOutboundAttachments(input: unknown): NormalizeResult;
|
|
27
|
+
/**
|
|
28
|
+
* Spread into a Hub request body. Empty → `{}`, so a call with no attachments
|
|
29
|
+
* is unchanged from before this module existed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function attachmentsField(attachments: readonly OutboundAttachment[]): Record<string, unknown>;
|
|
32
|
+
/** `send_task` carries them under `meta`, not at the top level. */
|
|
33
|
+
export declare function attachmentsMeta(attachments: readonly OutboundAttachment[], existingMeta?: unknown): Record<string, unknown>;
|
|
34
|
+
/** Shared wording for the two tool schemas, so they cannot drift apart. */
|
|
35
|
+
export declare const ATTACHMENTS_SCHEMA: {
|
|
36
|
+
type: "array";
|
|
37
|
+
description: string;
|
|
38
|
+
items: {
|
|
39
|
+
type: "object";
|
|
40
|
+
properties: {
|
|
41
|
+
file_id: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
};
|
|
45
|
+
name: {
|
|
46
|
+
type: string;
|
|
47
|
+
description: string;
|
|
48
|
+
};
|
|
49
|
+
mime: {
|
|
50
|
+
type: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
53
|
+
size: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
required: string[];
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const OUTBOUND_TOOL_NAMES: Set<string>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type OwnedRootRole = "agent";
|
|
2
|
+
export interface OwnedRoot {
|
|
3
|
+
pid: number;
|
|
4
|
+
birth: string;
|
|
5
|
+
role: OwnedRootRole;
|
|
6
|
+
}
|
|
7
|
+
/** 🔴 #1438 — 发现阶段(`ps`)与后续 `/proc/<pid>/stat` 读之间,pid 可能被回收。
|
|
8
|
+
* 为让 resolveOwnedRoots 能识别「pid 相同、进程不同」,发现阶段必须在
|
|
9
|
+
* **同一次 `ps` 快照**里同时取 pid 与一个稳定的启动时刻签名(lstart),
|
|
10
|
+
* 并把这个签名一路带进来做比对。同一次 ps 内的 pid 与 lstart 无 TOCTOU。 */
|
|
11
|
+
export interface OwnedRootCandidate {
|
|
12
|
+
pid: number;
|
|
13
|
+
/** 发现时刻的 `ps -o lstart=` 字符串(24 字符定宽),用于识别 pid 回收。 */
|
|
14
|
+
discoveredBirth: string;
|
|
15
|
+
}
|
|
16
|
+
export interface OwnedRootProbes {
|
|
17
|
+
/** 进程这一代的出生时刻(以「存储格式」返回,通常是 /proc/stat starttime jiffies);
|
|
18
|
+
* 读不到返回 null(可能已退出,也可能无权限)。用于写进 lifecycle 记录。 */
|
|
19
|
+
birth(pid: number): string | null;
|
|
20
|
+
/** 🔴 只有**确证**进程不存在时返回 true。无权限等一律视为存在。 */
|
|
21
|
+
vanished(pid: number): boolean;
|
|
22
|
+
/** 🔴 #1438 — 返回**当前**pid 的 lstart 签名,和发现阶段同格式。
|
|
23
|
+
* 用来识别 pid 回收:发现时是 A、现在读回来是 B ⇒ pid 已被复用。
|
|
24
|
+
* 读不到返回 null(进程已退出,或读 ps 失败)。 */
|
|
25
|
+
currentBirthSignature(pid: number): string | null;
|
|
26
|
+
}
|
|
27
|
+
/** 把 `ps` 发现的 (pid, birth) 候选冻结成 owned roots。
|
|
28
|
+
*
|
|
29
|
+
* · currentBirthSignature 与 discoveredBirth **相等** ⇒ 同一代,收下
|
|
30
|
+
* · currentBirthSignature 与 discoveredBirth **不等** ⇒ 🔴 #1438 pid 已被复用,
|
|
31
|
+
* 现在这个 pid 属于另一个进程 B。**跳过**(而不是收下 B 当我们的节点):
|
|
32
|
+
* stop 的目的是让 A 没了;B 是无关进程,不能因为它恰好占了 A 的 pid 就被杀。
|
|
33
|
+
* · currentBirthSignature 为 null、**确证已消失** ⇒ 跳过(stop 想要的结果)
|
|
34
|
+
* · currentBirthSignature 为 null、进程仍在 ⇒ throw(权限等真问题)
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveOwnedRoots(candidates: OwnedRootCandidate[], probes: OwnedRootProbes): OwnedRoot[];
|
|
37
|
+
/** 🔴 正向判定「进程确实没了」,而不是「读失败就假设没了」。
|
|
38
|
+
*
|
|
39
|
+
* `kill(pid, 0)` 不送信号,只做存在性与权限探测:
|
|
40
|
+
* · ESRCH ⇒ 确证不存在
|
|
41
|
+
* · EPERM ⇒ 存在,只是不属于本用户
|
|
42
|
+
* · 其他 ⇒ 保守当作存在(判据只在**确证**时放行)
|
|
43
|
+
*/
|
|
44
|
+
export declare function processVanished(pid: number, kill?: (p: number, s: number) => void): boolean;
|
|
45
|
+
/** 🔴 #1458 — kill 前的 pid 回收保护:发现阶段捕获的 discoveredBirth 与
|
|
46
|
+
* **当前**probeCurrentSignature(pid) 相等 ⇒ 同一代,可以信号。
|
|
47
|
+
*
|
|
48
|
+
* · currentSig === discoveredBirth ⇒ true(同代,可 kill)
|
|
49
|
+
* · currentSig !== discoveredBirth ⇒ false(pid 被回收,现在这个 pid 是另一
|
|
50
|
+
* 个进程 B。**不要 kill** —— B 与我们要 stop 的 agent 无关)
|
|
51
|
+
* · currentSig === null ⇒ false(pid 已消失或读不到;caller 判
|
|
52
|
+
* 是消失就该 skip,权限问题应额外处理但不该 kill 未知对象)
|
|
53
|
+
*
|
|
54
|
+
* 与 resolveOwnedRoots 内嵌的同款比较**在语义上一致**,但在**时间点上不同**:
|
|
55
|
+
* resolveOwnedRoots 用于「冻结」时刻(装进 lifecycle owner)
|
|
56
|
+
* isSameIncarnation 用于「发信号」前那一瞬(rename 的 kill 循环)
|
|
57
|
+
* 两个时刻中间可能又有窗口,所以这个检查要**离 process.kill 越近越好**。 */
|
|
58
|
+
export declare function isSameIncarnation(pid: number, discoveredBirth: string, probeCurrentSignature: (pid: number) => string | null): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface UmaskVerdict {
|
|
2
|
+
/** Will a freshly npm-extracted payload fail the (mode & 0o022) === 0 check? */
|
|
3
|
+
willProduceUnsafeModes: boolean;
|
|
4
|
+
/** Octal umask string as an operator would type it. */
|
|
5
|
+
umaskOctal: string;
|
|
6
|
+
/** Which write bits this umask fails to mask off. */
|
|
7
|
+
leaks: Array<"group" | "other">;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read a umask value the way the package check will experience it.
|
|
11
|
+
*
|
|
12
|
+
* A umask bit SET means "withhold this permission". So group-write is withheld
|
|
13
|
+
* only when 0o020 is set in the umask; umask 0002 withholds other-write and
|
|
14
|
+
* nothing else, which is exactly the failing case.
|
|
15
|
+
*/
|
|
16
|
+
export declare function judgeUmask(umask: number): UmaskVerdict;
|
|
17
|
+
/**
|
|
18
|
+
* One line for `anet doctor`, or null when there is nothing to say.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 Windows gets a different sentence, not the same one. There, umask is always
|
|
21
|
+
* 0000 and this fires on every machine, while both remedies it names —
|
|
22
|
+
* `umask 0022` and `chmod -R` — do not exist. The conclusion is still true
|
|
23
|
+
* (opencode-cli will refuse), but for another reason entirely: its check is
|
|
24
|
+
* `if (runtimeUid === undefined) return false`, and Windows has no getuid.
|
|
25
|
+
* An always-on warning whose fix cannot be typed teaches people to ignore
|
|
26
|
+
* the warnings that can be.
|
|
27
|
+
*/
|
|
28
|
+
export declare function describeUmaskRisk(verdict: UmaskVerdict, platform?: NodeJS.Platform): string | null;
|
|
29
|
+
export interface ExtractedPayload {
|
|
30
|
+
path: string;
|
|
31
|
+
uid: number;
|
|
32
|
+
mode: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Which already-extracted payloads would be rejected right now.
|
|
36
|
+
*
|
|
37
|
+
* Reports facts about copies that exist on disk; it never fetches. An empty
|
|
38
|
+
* result means "nothing extracted yet", which is not the same as "safe" — the
|
|
39
|
+
* umask verdict is what speaks to the next fetch.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rejectedPayloads(payloads: ExtractedPayload[], processUid: number): ExtractedPayload[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type ProcRow = {
|
|
2
|
+
pid: number;
|
|
3
|
+
ppid: number;
|
|
4
|
+
start: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function ownedConnectionFromSnapshot(rootPid: number, beforeStart: string | null, afterStart: string | null, rows: ProcRow[], socketOwners: ReadonlyMap<number, ReadonlySet<string>>, establishedInodes: ReadonlySet<string>, unread?: boolean): boolean;
|
|
7
|
+
/** Fail-closed attribution of an exact loopback connection to a managed POSIX TUI tree. */
|
|
8
|
+
export declare function probePosixOwnedLoopbackConnection(rootPid: number, port: number, platform?: NodeJS.Platform): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* #2255 (2026-10-02) —— 一次探不到 ≠ 没连上。`anet node start` 等到 TUI 画出 banner(「OpenAI Codex」)就只探一次:
|
|
11
|
+
* 实测真 codex 0.155.1 先画 banner(~170 ms)、后建 ws(~221 ms),假 TUI 同样先打印后连接。空闲机器上 400 ms 的
|
|
12
|
+
* 「等画出来」恰好把这几十毫秒盖住;CI runner / 忙的机器上盖不住,启动就以「managed TUI tree has no attributable
|
|
13
|
+
* connection」失败 —— 会话其实好好的。Windows 那条路径一直是轮询(TUI_HEALTH_MS 内反复探),这里对齐。
|
|
14
|
+
*
|
|
15
|
+
* 每 intervalMs 探一次,直到:探到(connected)/ TUI 退了(tui-exited,不再白等)/ 到点(deadline)。
|
|
16
|
+
* 判据本身不变:仍是 probePosixOwnedLoopbackConnection 的 fail-closed 归属,只是不再只给一次机会。
|
|
17
|
+
*/
|
|
18
|
+
/** 与 Windows 路径的 TUI_HEALTH_MS 同一个预算(cli.ts):TUI 活着却 25 s 都没连上,才判失败。 */
|
|
19
|
+
export declare const POSIX_TUI_ATTRIBUTION_MS = 25000;
|
|
20
|
+
export type PosixAttributionResult = {
|
|
21
|
+
outcome: "connected" | "tui-exited" | "deadline";
|
|
22
|
+
probes: number;
|
|
23
|
+
waitedMs: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function waitForPosixOwnedLoopbackConnection(opts: {
|
|
26
|
+
rootPid: number;
|
|
27
|
+
port: number;
|
|
28
|
+
deadlineMs: number;
|
|
29
|
+
intervalMs?: number;
|
|
30
|
+
/** TUI 还在不在(pid 还活着、而且还是同一个进程)。不在就别等了。 */
|
|
31
|
+
alive: () => boolean;
|
|
32
|
+
probe?: (rootPid: number, port: number) => boolean;
|
|
33
|
+
sleep?: (ms: number) => Promise<void>;
|
|
34
|
+
now?: () => number;
|
|
35
|
+
}): Promise<PosixAttributionResult>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare function posixFileModes(platform?: NodeJS.Platform): boolean;
|
|
2
|
+
/** chmod where the platform has modes; a no-op where it does not. */
|
|
3
|
+
export declare function chmodIfPosix(path: string, mode: number): void;
|
|
4
|
+
/** fchmod where the platform has modes; a no-op where it does not. */
|
|
5
|
+
export declare function fchmodIfPosix(fd: number, mode: number): void;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a stat's mode may be judged at all.
|
|
8
|
+
*
|
|
9
|
+
* Windows synthesises mode bits, so `(mode & 0o022) !== 0` is true for ordinary
|
|
10
|
+
* files there and every POSIX-shaped safety check reads as "unsafe". A check
|
|
11
|
+
* that can only ever return one answer is not a check.
|
|
12
|
+
*/
|
|
13
|
+
export declare function modeIsJudgeable(platform?: NodeJS.Platform): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* fsync a DIRECTORY handle, where the platform supports it.
|
|
16
|
+
*
|
|
17
|
+
* The durability barrier after an atomic rename: on POSIX, fsyncing the parent
|
|
18
|
+
* makes the rename itself survive a crash, not just the file's bytes. Windows
|
|
19
|
+
* has no equivalent — FlushFileBuffers works on files, and on a directory
|
|
20
|
+
* handle it fails:
|
|
21
|
+
*
|
|
22
|
+
* [anet] FATAL: Error: EPERM: operation not permitted, fsync
|
|
23
|
+
* at fsyncSync (node:fs:1285:11)
|
|
24
|
+
*
|
|
25
|
+
* which is `anet start <node>` dying on its first private-state write. Reported
|
|
26
|
+
* from the Windows box while it was running the previous fix, i.e. this is the
|
|
27
|
+
* next call in the same family, not a regression of that one.
|
|
28
|
+
*
|
|
29
|
+
* 🔴 Note what is NOT skipped: the fsync of the temp FILE before the rename.
|
|
30
|
+
* That one works on Windows and carries the bytes. What is skipped is only
|
|
31
|
+
* the parent-directory barrier, which the platform does not offer at all —
|
|
32
|
+
* NTFS journals the rename's metadata itself. Skipping an operation the OS
|
|
33
|
+
* cannot perform is not a weakened guarantee; crashing instead of writing
|
|
34
|
+
* the file is a much larger one.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fsyncDirectoryIfSupported(fd: number): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface PrimaryNetworkResponse {
|
|
2
|
+
ok?: boolean;
|
|
3
|
+
current_network?: unknown;
|
|
4
|
+
networks?: unknown;
|
|
5
|
+
}
|
|
6
|
+
export type PrimaryNetworkFetch = (input: string, init: {
|
|
7
|
+
headers: Record<string, string>;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
json: () => Promise<PrimaryNetworkResponse>;
|
|
12
|
+
}>;
|
|
13
|
+
export declare class PrimaryNetworkResolutionError extends Error {
|
|
14
|
+
constructor(message: string, options?: ErrorOptions);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the authenticated user's active network from the server's explicit
|
|
18
|
+
* `/api/auth/me.current_network` field.
|
|
19
|
+
*
|
|
20
|
+
* Do not fall back to a network name or list position: both are presentation
|
|
21
|
+
* details, and guessing either can silently run a command in the wrong network.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolvePrimaryNetwork(hub: string, headers: Record<string, string>, fetchImpl?: PrimaryNetworkFetch): Promise<string>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function ensurePrivateDirectory(path: string): void;
|
|
2
|
+
/**
|
|
3
|
+
* Write secret-bearing state without ever publishing an umask-derived file.
|
|
4
|
+
* The 0600 temporary file is complete and fsynced before the atomic rename,
|
|
5
|
+
* so replacing a legacy 0644/0664 target also repairs it with no chmod gap.
|
|
6
|
+
*/
|
|
7
|
+
export declare function atomicWritePrivateFile(path: string, body: string): void;
|
|
8
|
+
export declare function atomicWritePrivateJson(path: string, value: unknown): void;
|
|
9
|
+
/** Repair a legacy umask-derived private file before reading its secret. */
|
|
10
|
+
export declare function repairPrivateFilePermissions(path: string): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ProcessTelemetry {
|
|
2
|
+
rss_bytes: number;
|
|
3
|
+
rss_mb: number;
|
|
4
|
+
cpu_pct: number | null;
|
|
5
|
+
uptime_seconds: number;
|
|
6
|
+
in_flight_count: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function incrementInFlight(): void;
|
|
9
|
+
export declare function decrementInFlight(): void;
|
|
10
|
+
export declare function getInFlightCount(): number;
|
|
11
|
+
export declare function getProcessTelemetry(): ProcessTelemetry;
|
|
12
|
+
export declare function _resetProcessTelemetry(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function serializeProfileForConfigJson(normalized: Record<string, any>, profile: Record<string, any>): Record<string, any>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function encodeCwd(cwd: string): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code-cli 节点(node-server 通道)回复里的「本机文件链接」→ 自动上传成附件。
|
|
3
|
+
* 与 agent-node 的 runtime/reply-file-links.ts 同一形状(两个包不能互相 import,各放一份纯逻辑)。
|
|
4
|
+
* 2026-09-15 真机:外部团队节点 两条带 pptx 的回复到 hub 时附件为空,人只看到「点卡片下载」四个字。
|
|
5
|
+
*/
|
|
6
|
+
export declare const REPLY_LOCAL_LINK_MAX = 6;
|
|
7
|
+
export interface LocalLink {
|
|
8
|
+
readonly raw: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly path: string;
|
|
11
|
+
}
|
|
12
|
+
/** 只认 markdown 链接且目标像本机绝对路径(`/…` 或 `file:///…`);http(s)、/api/files/… 不算。 */
|
|
13
|
+
export declare function findLocalLinks(text: string): LocalLink[];
|
|
14
|
+
export interface UploadedRef {
|
|
15
|
+
file_id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
mime?: string;
|
|
18
|
+
size?: number;
|
|
19
|
+
}
|
|
20
|
+
/** 上传成功的链接目标改成 /api/files/<id>(客户端按附件卡片渲染),失败的在标签后注明原因;返回 attachments 数组。 */
|
|
21
|
+
export declare function rewriteLocalLinks(text: string, results: ReadonlyArray<{
|
|
22
|
+
link: LocalLink;
|
|
23
|
+
uploaded?: UploadedRef;
|
|
24
|
+
reason?: string;
|
|
25
|
+
}>): {
|
|
26
|
+
text: string;
|
|
27
|
+
attachments: Array<{
|
|
28
|
+
type: "file";
|
|
29
|
+
file_id: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
mime?: string;
|
|
32
|
+
size?: number;
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
/** 端到端(上传函数注入,失败抛错即可):没有本机链接时原样返回、零副作用。 */
|
|
36
|
+
export declare function attachLocalLinks(text: string, upload: (path: string) => Promise<UploadedRef>): Promise<{
|
|
37
|
+
text: string;
|
|
38
|
+
attachments: Array<{
|
|
39
|
+
type: "file";
|
|
40
|
+
file_id: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
mime?: string;
|
|
43
|
+
size?: number;
|
|
44
|
+
}>;
|
|
45
|
+
uploaded: number;
|
|
46
|
+
failed: number;
|
|
47
|
+
}>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ReplyAliasDecision =
|
|
2
|
+
/** 把 alias 显式发出去 */
|
|
3
|
+
{
|
|
4
|
+
readonly kind: "known";
|
|
5
|
+
readonly alias: string;
|
|
6
|
+
}
|
|
7
|
+
/** 省略 alias,交给 hub 从 in_reply_to 推导 */
|
|
8
|
+
| {
|
|
9
|
+
readonly kind: "derive";
|
|
10
|
+
}
|
|
11
|
+
/** 没有 task_id 可推导:保持历史行为,发给 hub */
|
|
12
|
+
| {
|
|
13
|
+
readonly kind: "hub";
|
|
14
|
+
readonly alias: "hub";
|
|
15
|
+
};
|
|
16
|
+
export declare function decideReplyAlias(taskId: string | null | undefined, lookup: (taskId: string) => string | undefined): ReplyAliasDecision;
|
|
17
|
+
/** 把决策摊成 send_reply 的参数片段。`derive` 时**不含** alias 键。 */
|
|
18
|
+
export declare function replyAliasArgs(d: ReplyAliasDecision): {
|
|
19
|
+
alias?: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ResumeRuntimeInput {
|
|
2
|
+
/** raw --resume value; "true" means the flag was present with no value */
|
|
3
|
+
resume?: string;
|
|
4
|
+
/** whether --resume-latest was passed */
|
|
5
|
+
resumeLatest?: boolean;
|
|
6
|
+
/** an already-resolved session id, if any (skips inference) */
|
|
7
|
+
session?: string;
|
|
8
|
+
/** normalized --runtime, or empty/undefined when the user passed none */
|
|
9
|
+
explicitRuntime?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ResumeRuntimeResult {
|
|
12
|
+
/** runtime to force onto opts when it was left to default */
|
|
13
|
+
inferredRuntime?: string;
|
|
14
|
+
/** set when an explicit non-cli runtime conflicts with --resume */
|
|
15
|
+
conflictError?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const CLAUDE_CODE_CLI = "claude-code-cli";
|
|
18
|
+
export declare function resolveRuntimeForResume(input: ResumeRuntimeInput): ResumeRuntimeResult;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export declare const RULES_FILE_MAX_BYTES: number;
|
|
2
|
+
export type RulesFileName = "CLAUDE.md" | "AGENTS.md";
|
|
3
|
+
/** claude 节点读 CLAUDE.md,其余运行时(codex / grok / opencode / codex-app-server)读 AGENTS.md。 */
|
|
4
|
+
export declare function rulesFileNameForRuntime(runtime: string | null | undefined): RulesFileName;
|
|
5
|
+
/**
|
|
6
|
+
* 唯一的路径来源。输入里没有任何调用方可控的路径成分;结果再用 dirname
|
|
7
|
+
* 反查一遍,防止将来有人给 workDir 传进带 `..` 的东西。
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveRulesFilePath(workDir: string, runtime: string | null | undefined): string;
|
|
10
|
+
export interface RulesFileReadResult {
|
|
11
|
+
file_name: RulesFileName;
|
|
12
|
+
exists: boolean;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function readRulesFile(workDir: string, runtime: string | null | undefined): Promise<RulesFileReadResult>;
|
|
16
|
+
export interface RulesFileWriteResult {
|
|
17
|
+
file_name: RulesFileName;
|
|
18
|
+
bytes: number;
|
|
19
|
+
}
|
|
20
|
+
/** 临时文件 + rename,写一半掉电不会留下半个规则文件。 */
|
|
21
|
+
export declare function writeRulesFile(workDir: string, runtime: string | null | undefined, content: unknown): Promise<RulesFileWriteResult>;
|
|
22
|
+
export interface RulesFileRequest {
|
|
23
|
+
request_id: string;
|
|
24
|
+
/** read/write = 规则文件;skills_list/skill_read = 只读技能(node-skills.ts),content 是技能名;
|
|
25
|
+
* files_list/file_read = 只读项目文件夹(node-files.ts),content 是相对工作目录的路径;
|
|
26
|
+
* logs_tail = 只读本节点运行日志(node-logs.ts),content 是过滤参数 JSON(没有路径)。 */
|
|
27
|
+
op: "read" | "write" | "skills_list" | "skill_read" | "files_list" | "file_read" | "logs_tail";
|
|
28
|
+
content?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ProcessRulesFileDeps {
|
|
31
|
+
callCommHub: (method: string, params: Record<string, unknown>) => Promise<any>;
|
|
32
|
+
runtime: string | null | undefined;
|
|
33
|
+
workDir: string;
|
|
34
|
+
log: (msg: string) => void;
|
|
35
|
+
warn: (msg: string) => void;
|
|
36
|
+
/** 技能根目录用;缺省取 os.homedir() / process.env.CODEX_HOME。 */
|
|
37
|
+
home?: string;
|
|
38
|
+
codexHome?: string;
|
|
39
|
+
/** logs_tail 用:读本进程自己的日志并脱敏(agent-node 的 runtime/node-logs.ts,由 cli.ts 注入)。
|
|
40
|
+
* 参数是 hub 转来的过滤 JSON;没有路径。缺省(例如 claude-code 通道进程)时 logs_tail 回 failed。 */
|
|
41
|
+
logsTail?: (rawParams: string | undefined) => Promise<{
|
|
42
|
+
files: string[];
|
|
43
|
+
lines: unknown[];
|
|
44
|
+
matched: number;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
/** 一次门铃最多处理这么多条,防止 hub 侧异常堆积把节点拖进死循环。 */
|
|
48
|
+
export declare const RULES_FILE_MAX_PER_DOORBELL = 8;
|
|
49
|
+
/**
|
|
50
|
+
* 门铃处理器:拉 pending 请求直到空,每条 ack done / failed。
|
|
51
|
+
* 返回处理条数(测试用)。
|
|
52
|
+
*/
|
|
53
|
+
export declare function processRulesFileRequests(deps: ProcessRulesFileDeps): Promise<number>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function formatSecretAssignment(platform: string, name: string, value: string): string;
|
|
2
|
+
export declare function secretPersistenceHeading(platform: string): string;
|
|
3
|
+
export declare function secretShellAction(platform: string): "set" | "export";
|
|
4
|
+
export declare function formatSecretLoadCommand(platform: string, name: string, dotenvPath: string): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `anet status` 的会话状态分类。
|
|
3
|
+
*
|
|
4
|
+
* 🔴 为什么单独成文件:原先它是 statusCommand 里的一个闭包,没有任何测试碰得到它 ——
|
|
5
|
+
* 而它决定了运维看到的那三个数字。一个把「卡住」算成「在干活」的分类器,
|
|
6
|
+
* 和一个正确的分类器,在输出上长得一模一样(都是一个数)。
|
|
7
|
+
*
|
|
8
|
+
* 🔴 改了什么:`blocked` / `error` 原先被折进 `working`。
|
|
9
|
+
* 它们的含义是**需要人看一眼**,不是**正在推进**。
|
|
10
|
+
* 运维看到「5 working」会认为一切正常,而其中可能有几个卡了很久 ——
|
|
11
|
+
* #1548 已经证明 `blocked` 是一个**没有出口**的状态:
|
|
12
|
+
* 只有 report_completion 能把它拉回 idle,所以一个 agent 可以永远停在那里。
|
|
13
|
+
*
|
|
14
|
+
* `waiting_input` 保留在 working 里:它是一个**进行中的回合**在等人,
|
|
15
|
+
* 与 blocked/error 不同 —— 那是回合本身出了问题。
|
|
16
|
+
*/
|
|
17
|
+
export type SessionClass = "idle" | "working" | "attention" | "offline";
|
|
18
|
+
export declare function classifySessionStatus(raw: unknown): SessionClass;
|
|
19
|
+
/**
|
|
20
|
+
* `anet status` 顶部那三/四个数字。
|
|
21
|
+
*
|
|
22
|
+
* 🔴 为什么它必须和上面的分类器用同一套判据(#1625):
|
|
23
|
+
* 原先 `cli.ts` 是 `statusRes.summary || sessions.reduce(…)`,而
|
|
24
|
+
* `/api/status` **总是**返回 summary ⇒ 右边那支从不执行,屏幕上的数字
|
|
25
|
+
* 来自**服务端**一份还停在 #1548 之前的分类(它把 `blocked`/`error`
|
|
26
|
+
* 折进 `working`)。于是一个 blocked 节点同时出现在 `working` 和
|
|
27
|
+
* `needs attention` 两格里,四个数加起来比总数多一个。
|
|
28
|
+
*
|
|
29
|
+
* 🔴 为什么本地算是安全的:`/api/status` 只加 `addNetworkScope`,没有任何
|
|
30
|
+
* 状态/别名过滤,而它的 summary 就是从**同一个 sessions 数组** reduce 出来的
|
|
31
|
+
* —— 两者范围逐字相同。(这一点对 MCP 的 `get_all_status` **不成立**:
|
|
32
|
+
* 那边 summary 走独立的 `GROUP BY status` 查询、无视 filter,所以别照搬。)
|
|
33
|
+
*
|
|
34
|
+
* 🔴 `attention` 必须显式初始化为 0。原来的累加器只有
|
|
35
|
+
* `{idle, working, offline, total}`,`acc["attention"]++` 会得到 **NaN**,
|
|
36
|
+
* 而 `summary.attention ?? attention.length` 里的 `??` **不接 NaN**,
|
|
37
|
+
* 屏幕会印 `NaN needs attention`。那一支从不执行,所以从没人见过。
|
|
38
|
+
*/
|
|
39
|
+
export type SessionSummary = {
|
|
40
|
+
idle: number;
|
|
41
|
+
working: number;
|
|
42
|
+
attention: number;
|
|
43
|
+
offline: number;
|
|
44
|
+
total: number;
|
|
45
|
+
};
|
|
46
|
+
export declare function summarizeSessions(sessions: Array<{
|
|
47
|
+
status?: unknown;
|
|
48
|
+
}>): SessionSummary;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SiblingAgentNode {
|
|
2
|
+
/** 直接可 `node <entrypoint>` 的文件。 */
|
|
3
|
+
entrypoint: string;
|
|
4
|
+
packageDir: string;
|
|
5
|
+
version: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface SiblingFs {
|
|
8
|
+
exists(path: string): boolean;
|
|
9
|
+
readJson(path: string): unknown;
|
|
10
|
+
/**
|
|
11
|
+
* #1832 —— npm -g / --prefix 布局下 process.argv[1] 是 `<prefix>/bin/anet` 符号链接,真实文件在
|
|
12
|
+
* `<prefix>/lib/node_modules/@sleep2agi/agent-network/…`;不解析就从 `<prefix>/bin` 往上找不到
|
|
13
|
+
* node_modules,旁边的 agent-node 永远命不中。可选:不提供或抛错时按传入路径原样找。
|
|
14
|
+
*/
|
|
15
|
+
realpath?(path: string): string;
|
|
16
|
+
}
|
|
17
|
+
/** 入口路径先走 realpath(符号链接 → 真实文件);失败(不存在 / 无权限)退回原路径。 */
|
|
18
|
+
export declare function resolveCliEntry(cliEntry: string, fs: SiblingFs): string;
|
|
19
|
+
/** 从 anet 入口文件路径往上,返回最近的 `…/node_modules` 目录;找不到返回 null。 */
|
|
20
|
+
export declare function nearestNodeModules(cliEntry: string): string | null;
|
|
21
|
+
/** package.json 的 bin 可以是字符串或 { name: path };只认名为 agent-node 的那条。 */
|
|
22
|
+
export declare function binEntryFromPackageJson(pkg: unknown): string | null;
|
|
23
|
+
export declare function siblingAgentNodeEntrypoint(cliEntry: string, fs: SiblingFs): SiblingAgentNode | null;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export interface StaleSocketProbes {
|
|
2
|
+
/** 读 /proc/net/unix 全文;读不到就 throw(调用方按 fail-closed 处理)。 */
|
|
3
|
+
procNetUnix(): string;
|
|
4
|
+
/** 路径不存在返回 null。 */
|
|
5
|
+
lstat(path: string): {
|
|
6
|
+
dev: number;
|
|
7
|
+
ino: number;
|
|
8
|
+
uid: number;
|
|
9
|
+
isSocket: boolean;
|
|
10
|
+
} | null;
|
|
11
|
+
unlink(path: string): void;
|
|
12
|
+
/** 当前进程 uid —— 只回收自己拥有的 socket。 */
|
|
13
|
+
currentUid(): number;
|
|
14
|
+
}
|
|
15
|
+
export interface ReapScope {
|
|
16
|
+
/**
|
|
17
|
+
* 只允许回收这个目录**之下**的路径。
|
|
18
|
+
* 🔴 这一格防的不是文件系统竞态,是**被污染的 identity**:socket 路径来自
|
|
19
|
+
* profile,如果 profile 被写坏成 `/run/systemd/private` 之类,前面所有
|
|
20
|
+
* "无监听者"检查都会照常通过,然后 unlink 掉一个不属于这个节点的东西。
|
|
21
|
+
* 删除不可逆,所以范围校验必须在**所有**其它检查之前。
|
|
22
|
+
*/
|
|
23
|
+
allowedRoot: string;
|
|
24
|
+
}
|
|
25
|
+
export type ReapOutcome =
|
|
26
|
+
/** 路径已经不在了 —— 正常的成功拆卸,无事可做。 */
|
|
27
|
+
{
|
|
28
|
+
kind: "absent";
|
|
29
|
+
}
|
|
30
|
+
/** 确认无人引用,已删除。 */
|
|
31
|
+
| {
|
|
32
|
+
kind: "removed";
|
|
33
|
+
}
|
|
34
|
+
/** /proc/net/unix 里仍能找到这个路径 ⇒ 有人在用,**不删**,调用方应判红。 */
|
|
35
|
+
| {
|
|
36
|
+
kind: "in-use";
|
|
37
|
+
}
|
|
38
|
+
/** /proc/net/unix 读不到 ⇒ fail-closed,不删。 */
|
|
39
|
+
| {
|
|
40
|
+
kind: "unreadable";
|
|
41
|
+
detail: string;
|
|
42
|
+
}
|
|
43
|
+
/** 检查与删除之间文件身份变了(dev/ino/uid),或已不是 socket ⇒ 不删。 */
|
|
44
|
+
| {
|
|
45
|
+
kind: "changed";
|
|
46
|
+
}
|
|
47
|
+
/** 路径不在该节点自己的 runtime 目录之下 ⇒ 一律不删。 */
|
|
48
|
+
| {
|
|
49
|
+
kind: "out-of-scope";
|
|
50
|
+
detail: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* `/proc/net/unix` 的每一行以路径结尾(没有绑定路径的行就没有这一列)。
|
|
54
|
+
* 这里只问「这个路径出现过没有」,不解释 flags/type/state。
|
|
55
|
+
*/
|
|
56
|
+
export declare function unixSocketPathInUse(procNetUnix: string, path: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* 属主进程**已被证明消失之后**,回收一个陈旧的 socket 路径名。
|
|
59
|
+
* 任何一种不确定都不删 —— 删文件不可逆,而留下一个 socket 只是让 stop 判红,
|
|
60
|
+
* 那正是本函数之前的行为。
|
|
61
|
+
*/
|
|
62
|
+
export declare function reapStaleSocket(path: string, probes: StaleSocketProbes, scope: ReapScope): ReapOutcome;
|
|
63
|
+
/**
|
|
64
|
+
* 从「本次 stop 看到的残留路径」里挑出**允许回收**的那些。
|
|
65
|
+
*
|
|
66
|
+
* 🔴 判据是「与重新算出来的规范路径**完全相等**」,不是前缀、不是包含。
|
|
67
|
+
* profile 里存的 socket 路径是**被写坏就会跟着坏**的数据;
|
|
68
|
+
* grokCopresenceSocketPaths 是纯函数,同样的 nodeId + home 永远算出同一个值。
|
|
69
|
+
* 所以这里不问「这条路径看起来对不对」,只问「它是不是我自己算出来的那两个之一」。
|
|
70
|
+
* ——一个被污染的 profile 因此一条都带不进来。
|
|
71
|
+
*/
|
|
72
|
+
export declare function planReapableSockets(canonical: {
|
|
73
|
+
leaderSocket: string;
|
|
74
|
+
attachSocket: string;
|
|
75
|
+
}, residualPaths: readonly (string | undefined)[]): string[];
|
|
76
|
+
export interface CanonicalSocketProfile {
|
|
77
|
+
node_id?: string;
|
|
78
|
+
grokLeaderSocket?: string;
|
|
79
|
+
grokAttachSocket?: string;
|
|
80
|
+
}
|
|
81
|
+
export type CanonicalSocketsOutcome = {
|
|
82
|
+
kind: "ok";
|
|
83
|
+
leaderSocket: string;
|
|
84
|
+
attachSocket: string;
|
|
85
|
+
}
|
|
86
|
+
/** profile 里没有 node_id —— 算不出来,不动手。 */
|
|
87
|
+
| {
|
|
88
|
+
kind: "no-node-id";
|
|
89
|
+
}
|
|
90
|
+
/** 重新算出来的与 profile 存的不一致 —— profile 被写坏、HOME 变了、或**调用方喂错了 id**。 */
|
|
91
|
+
| {
|
|
92
|
+
kind: "mismatch";
|
|
93
|
+
recomputedLeader: string;
|
|
94
|
+
storedLeader: string;
|
|
95
|
+
}
|
|
96
|
+
/** 路径算不出来(grokCopresenceSocketPaths 抛错)—— 不动手,但也不该把 stop 整个弄失败。 */
|
|
97
|
+
| {
|
|
98
|
+
kind: "uncomputable";
|
|
99
|
+
detail: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* 从 profile 推出规范 socket 路径,并**与 profile 里存的那份交叉校验**。
|
|
103
|
+
*
|
|
104
|
+
* 🔴 这个交叉校验是一次真实缺陷催生的,不是补全性写的:
|
|
105
|
+
* 我第一版在 cli.ts 里写的是 `grokCopresenceSocketPaths(resolved.id)`,而
|
|
106
|
+
* `resolveNodeRef` 返回的 `id` 是**目录名(别名)**,socket 路径却是
|
|
107
|
+
* `anet node create` 用 **node_id** 算的。两者哈希不同 ⇒ 算出的路径永远对不上 ⇒
|
|
108
|
+
* 可回收集合恒为空 ⇒ **一条都回收不了,而且完全静默**(循环体一次都不进)。
|
|
109
|
+
* 12 轮验收里 4 次红、0 次回收,红话与修复前逐字相同 —— 从外面看,
|
|
110
|
+
* 「守卫拒绝了一切」和「这个修复不存在」是同一个样子。
|
|
111
|
+
*
|
|
112
|
+
* 所以判据不能只是「算一个路径出来」,必须是「算出来的和存着的对得上」。
|
|
113
|
+
* 对不上就说出来,别静默跳过。
|
|
114
|
+
*/
|
|
115
|
+
export declare function canonicalSocketsForProfile(profile: CanonicalSocketProfile, compute: (nodeId: string) => {
|
|
116
|
+
leaderSocket: string;
|
|
117
|
+
attachSocket: string;
|
|
118
|
+
}): CanonicalSocketsOutcome;
|