shennian 0.3.1 → 0.3.3
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 +3 -1
- package/dist/publish-build-manifest.json +291 -341
- package/dist/src/agents/codex.js +1 -1
- package/dist/src/agents/command-spec.js +2 -2
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/model-registry/discovery.js +2 -2
- package/dist/src/agents/model-registry/parsers.d.ts +1 -0
- package/dist/src/agents/model-registry/parsers.js +4 -4
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/native-capabilities.d.ts +10 -0
- package/dist/src/agents/native-capabilities.js +1 -0
- package/dist/src/agents/platform-instructions.js +5 -5
- package/dist/src/agents/runtime-compatibility.d.ts +11 -0
- package/dist/src/agents/runtime-compatibility.js +1 -0
- package/dist/src/agents/workbuddy.d.ts +44 -0
- package/dist/src/agents/workbuddy.js +3 -0
- package/dist/src/commands/daemon.d.ts +1 -0
- package/dist/src/commands/daemon.js +9 -9
- package/dist/src/commands/integration.d.ts +2 -0
- package/dist/src/commands/integration.js +1 -0
- package/dist/src/commands/manager.js +1 -2
- package/dist/src/commands/pair-browser.d.ts +43 -0
- package/dist/src/commands/pair-browser.js +1 -0
- package/dist/src/commands/pair.d.ts +2 -0
- package/dist/src/commands/pair.js +11 -8
- package/dist/src/commands/room.d.ts +13 -0
- package/dist/src/commands/room.js +4 -0
- package/dist/src/commands/upgrade.d.ts +3 -0
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/config/index.d.ts +3 -1
- package/dist/src/config/index.js +2 -1
- package/dist/src/daemon-ipc/client.d.ts +8 -0
- package/dist/src/daemon-ipc/client.js +3 -0
- package/dist/src/daemon-ipc/paths.d.ts +7 -0
- package/dist/src/daemon-ipc/paths.js +1 -0
- package/dist/src/daemon-ipc/protocol.d.ts +30 -0
- package/dist/src/daemon-ipc/protocol.js +1 -0
- package/dist/src/daemon-ipc/secret.d.ts +3 -0
- package/dist/src/daemon-ipc/secret.js +3 -0
- package/dist/src/daemon-ipc/server.d.ts +13 -0
- package/dist/src/daemon-ipc/server.js +3 -0
- package/dist/src/index.js +2 -2
- package/dist/src/integrations/claude-room-plugin-lifecycle.d.ts +47 -0
- package/dist/src/integrations/claude-room-plugin-lifecycle.js +1 -0
- package/dist/src/integrations/codex-room-plugin-lifecycle.d.ts +49 -0
- package/dist/src/integrations/codex-room-plugin-lifecycle.js +1 -0
- package/dist/src/integrations/room-plugin-bundles.d.ts +23 -0
- package/dist/src/integrations/room-plugin-bundles.js +1 -0
- package/dist/src/integrations/room-plugin-lifecycle-common.d.ts +17 -0
- package/dist/src/integrations/room-plugin-lifecycle-common.js +1 -0
- package/dist/src/integrations/room-plugin-shared.d.ts +24 -0
- package/dist/src/integrations/room-plugin-shared.js +2 -0
- package/dist/src/integrations/room-session-hook.d.ts +30 -0
- package/dist/src/integrations/room-session-hook.js +2 -0
- package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +15 -0
- package/dist/src/integrations/workbuddy-plugin-bundle.js +1 -0
- package/dist/src/integrations/workbuddy-plugin-lifecycle.d.ts +52 -0
- package/dist/src/integrations/workbuddy-plugin-lifecycle.js +2 -0
- package/dist/src/integrations/workbuddy-session-hook.d.ts +22 -0
- package/dist/src/integrations/workbuddy-session-hook.js +1 -0
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +1 -1
- package/dist/src/manager/retired-external-channel-compat.d.ts +78 -0
- package/dist/src/manager/retired-external-channel-compat.js +1 -0
- package/dist/src/manager/runtime.d.ts +1 -3
- package/dist/src/manager/runtime.js +7 -7
- package/dist/src/relay/client.d.ts +2 -0
- package/dist/src/relay/client.js +1 -1
- package/dist/src/room/activation-audit.d.ts +15 -0
- package/dist/src/room/activation-audit.js +2 -0
- package/dist/src/room/activation-policy.d.ts +8 -0
- package/dist/src/room/activation-policy.js +1 -0
- package/dist/src/room/activation-service.d.ts +25 -0
- package/dist/src/room/activation-service.js +2 -0
- package/dist/src/room/agent-tool.d.ts +62 -0
- package/dist/src/room/agent-tool.js +1 -0
- package/dist/src/room/attachment-cache.d.ts +40 -0
- package/dist/src/room/attachment-cache.js +2 -0
- package/dist/src/room/attachment-upload.d.ts +20 -0
- package/dist/src/room/attachment-upload.js +1 -0
- package/dist/src/room/authorized-action-coordinator.d.ts +40 -0
- package/dist/src/room/authorized-action-coordinator.js +1 -0
- package/dist/src/room/authorized-action-ipc.d.ts +11 -0
- package/dist/src/room/authorized-action-ipc.js +1 -0
- package/dist/src/room/device-authorization-client.d.ts +49 -0
- package/dist/src/room/device-authorization-client.js +1 -0
- package/dist/src/room/host-session-observation-ipc.d.ts +9 -0
- package/dist/src/room/host-session-observation-ipc.js +1 -0
- package/dist/src/room/host-session-observations.d.ts +54 -0
- package/dist/src/room/host-session-observations.js +2 -0
- package/dist/src/room/invite-bootstrap.d.ts +35 -0
- package/dist/src/room/invite-bootstrap.js +1 -0
- package/dist/src/room/invocation-token-ipc.d.ts +9 -0
- package/dist/src/room/invocation-token-ipc.js +1 -0
- package/dist/src/room/invocation-token.d.ts +53 -0
- package/dist/src/room/invocation-token.js +1 -0
- package/dist/src/room/jsonl-mirror.d.ts +73 -0
- package/dist/src/room/jsonl-mirror.js +6 -0
- package/dist/src/room/managed-room-binding.d.ts +42 -0
- package/dist/src/room/managed-room-binding.js +1 -0
- package/dist/src/room/managed-room-session.d.ts +143 -0
- package/dist/src/room/managed-room-session.js +3 -0
- package/dist/src/room/mirror-sync-service.d.ts +33 -0
- package/dist/src/room/mirror-sync-service.js +1 -0
- package/dist/src/room/operator-ipc.d.ts +11 -0
- package/dist/src/room/operator-ipc.js +1 -0
- package/dist/src/room/pending-authorized-actions.d.ts +69 -0
- package/dist/src/room/pending-authorized-actions.js +3 -0
- package/dist/src/room/room-action-client.d.ts +210 -0
- package/dist/src/room/room-action-client.js +1 -0
- package/dist/src/room/room-action-ipc.d.ts +33 -0
- package/dist/src/room/room-action-ipc.js +1 -0
- package/dist/src/room/room-action-schema.d.ts +3 -0
- package/dist/src/room/room-action-schema.js +1 -0
- package/dist/src/room/room-authorized-action-executor.d.ts +23 -0
- package/dist/src/room/room-authorized-action-executor.js +1 -0
- package/dist/src/room/room-binding-client.d.ts +51 -0
- package/dist/src/room/room-binding-client.js +1 -0
- package/dist/src/room/room-binding-ipc.d.ts +29 -0
- package/dist/src/room/room-binding-ipc.js +1 -0
- package/dist/src/room/session-registry-ipc.d.ts +14 -0
- package/dist/src/room/session-registry-ipc.js +1 -0
- package/dist/src/room/session-registry.d.ts +128 -0
- package/dist/src/room/session-registry.js +2 -0
- package/dist/src/room/tool-schema.d.ts +118 -0
- package/dist/src/room/tool-schema.js +1 -0
- package/dist/src/room/tool-server.d.ts +5 -0
- package/dist/src/room/tool-server.js +3 -0
- package/dist/src/session/handlers/agent-capabilities.d.ts +3 -0
- package/dist/src/session/handlers/agent-capabilities.js +1 -0
- package/dist/src/session/handlers/agent-config.js +1 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/handlers/control.js +1 -1
- package/dist/src/session/handlers/fs.d.ts +1 -0
- package/dist/src/session/handlers/fs.js +1 -1
- package/dist/src/session/handlers/room-managed.d.ts +4 -0
- package/dist/src/session/handlers/room-managed.js +1 -0
- package/dist/src/session/manager.d.ts +18 -6
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/types.d.ts +5 -0
- package/dist/src/upgrade/engine.d.ts +14 -1
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/scheduler.d.ts +45 -0
- package/dist/src/upgrade/scheduler.js +1 -0
- package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +17 -0
- package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts.map +1 -0
- package/node_modules/@shennian/wire/dist/agent-capabilities.js +2 -0
- package/node_modules/@shennian/wire/dist/agent-capabilities.js.map +1 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +41 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.d.ts.map +1 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.js +3 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.js.map +1 -0
- package/node_modules/@shennian/wire/dist/index.d.ts +3 -1
- package/node_modules/@shennian/wire/dist/index.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/index.js +3 -1
- package/node_modules/@shennian/wire/dist/index.js.map +1 -1
- package/node_modules/@shennian/wire/dist/machine.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/room.d.ts +112 -0
- package/node_modules/@shennian/wire/dist/room.d.ts.map +1 -0
- package/node_modules/@shennian/wire/dist/room.js +4 -0
- package/node_modules/@shennian/wire/dist/room.js.map +1 -0
- package/node_modules/@shennian/wire/dist/session.d.ts +15 -2
- package/node_modules/@shennian/wire/dist/session.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/session.js +1 -1
- package/node_modules/@shennian/wire/dist/session.js.map +1 -1
- package/package.json +3 -5
- package/dist/scripts/wechat-rpa-confirmation.mjs +0 -5
- package/dist/src/agents/pi-context.d.ts +0 -42
- package/dist/src/agents/pi-context.js +0 -12
- package/dist/src/agents/pi.d.ts +0 -70
- package/dist/src/agents/pi.js +0 -17
- package/dist/src/channels/base.d.ts +0 -144
- package/dist/src/channels/base.js +0 -1
- package/dist/src/channels/registry.d.ts +0 -8
- package/dist/src/channels/registry.js +0 -1
- package/dist/src/channels/reply-split.d.ts +0 -1
- package/dist/src/channels/reply-split.js +0 -10
- package/dist/src/channels/runtime.d.ts +0 -138
- package/dist/src/channels/runtime.js +0 -5
- package/dist/src/channels/secret-registry.d.ts +0 -35
- package/dist/src/channels/secret-registry.js +0 -1
- package/dist/src/channels/websocket.d.ts +0 -55
- package/dist/src/channels/websocket.js +0 -8
- package/dist/src/channels/wechat-channel/anchor.d.ts +0 -10
- package/dist/src/channels/wechat-channel/anchor.js +0 -1
- package/dist/src/channels/wechat-channel/automation-lane.d.ts +0 -35
- package/dist/src/channels/wechat-channel/automation-lane.js +0 -2
- package/dist/src/channels/wechat-channel/client.d.ts +0 -283
- package/dist/src/channels/wechat-channel/client.js +0 -1
- package/dist/src/channels/wechat-channel/cooldown.d.ts +0 -21
- package/dist/src/channels/wechat-channel/cooldown.js +0 -1
- package/dist/src/channels/wechat-channel/core/fixture-compare.d.ts +0 -52
- package/dist/src/channels/wechat-channel/core/fixture-compare.js +0 -1
- package/dist/src/channels/wechat-channel/core/media-action-plan.d.ts +0 -3
- package/dist/src/channels/wechat-channel/core/media-action-plan.js +0 -1
- package/dist/src/channels/wechat-channel/core/schema.d.ts +0 -67
- package/dist/src/channels/wechat-channel/core/schema.js +0 -1
- package/dist/src/channels/wechat-channel/core/trace-comparator.d.ts +0 -28
- package/dist/src/channels/wechat-channel/core/trace-comparator.js +0 -1
- package/dist/src/channels/wechat-channel/fingerprint.d.ts +0 -31
- package/dist/src/channels/wechat-channel/fingerprint.js +0 -1
- package/dist/src/channels/wechat-channel/helper-assets.d.ts +0 -103
- package/dist/src/channels/wechat-channel/helper-assets.js +0 -1
- package/dist/src/channels/wechat-channel/helper-client.d.ts +0 -87
- package/dist/src/channels/wechat-channel/helper-client.js +0 -3
- package/dist/src/channels/wechat-channel/helper-protocol.d.ts +0 -122
- package/dist/src/channels/wechat-channel/helper-protocol.js +0 -1
- package/dist/src/channels/wechat-channel/human-coordination.d.ts +0 -25
- package/dist/src/channels/wechat-channel/human-coordination.js +0 -1
- package/dist/src/channels/wechat-channel/index.d.ts +0 -21
- package/dist/src/channels/wechat-channel/index.js +0 -1
- package/dist/src/channels/wechat-channel/ledger.d.ts +0 -48
- package/dist/src/channels/wechat-channel/ledger.js +0 -2
- package/dist/src/channels/wechat-channel/media-cache-resolver.d.ts +0 -42
- package/dist/src/channels/wechat-channel/media-cache-resolver.js +0 -1
- package/dist/src/channels/wechat-channel/media-resolver.d.ts +0 -186
- package/dist/src/channels/wechat-channel/media-resolver.js +0 -1
- package/dist/src/channels/wechat-channel/message-key.d.ts +0 -19
- package/dist/src/channels/wechat-channel/message-key.js +0 -1
- package/dist/src/channels/wechat-channel/observer.d.ts +0 -171
- package/dist/src/channels/wechat-channel/observer.js +0 -7
- package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +0 -96
- package/dist/src/channels/wechat-channel/outbound-ledger.js +0 -2
- package/dist/src/channels/wechat-channel/outbound-sender.d.ts +0 -74
- package/dist/src/channels/wechat-channel/outbound-sender.js +0 -1
- package/dist/src/channels/wechat-channel/pacing.d.ts +0 -3
- package/dist/src/channels/wechat-channel/pacing.js +0 -1
- package/dist/src/channels/wechat-channel/preflight.d.ts +0 -46
- package/dist/src/channels/wechat-channel/preflight.js +0 -1
- package/dist/src/channels/wechat-channel/runner.d.ts +0 -43
- package/dist/src/channels/wechat-channel/runner.js +0 -1
- package/dist/src/channels/wechat-channel/runtime.d.ts +0 -50
- package/dist/src/channels/wechat-channel/runtime.js +0 -1
- package/dist/src/channels/wechat-channel/scheduler.d.ts +0 -74
- package/dist/src/channels/wechat-channel/scheduler.js +0 -1
- package/dist/src/channels/wechat-channel/vector-store.d.ts +0 -44
- package/dist/src/channels/wechat-channel/vector-store.js +0 -1
- package/dist/src/channels/wechat-rpa/macos-flow.d.ts +0 -55
- package/dist/src/channels/wechat-rpa/macos-flow.js +0 -1
- package/dist/src/channels/wechat-rpa/macos.d.ts +0 -11
- package/dist/src/channels/wechat-rpa/macos.js +0 -21
- package/dist/src/channels/wechat-rpa/normalizer.d.ts +0 -49
- package/dist/src/channels/wechat-rpa/normalizer.js +0 -7
- package/dist/src/channels/wechat-rpa/product-channel.d.ts +0 -76
- package/dist/src/channels/wechat-rpa/product-channel.js +0 -16
- package/dist/src/channels/wechat-rpa-session-sync.d.ts +0 -26
- package/dist/src/channels/wechat-rpa-session-sync.js +0 -1
- package/dist/src/channels/wechat-rpa.d.ts +0 -104
- package/dist/src/channels/wechat-rpa.js +0 -6
- package/dist/src/channels/wecom.d.ts +0 -51
- package/dist/src/channels/wecom.js +0 -4
- package/dist/src/commands/external-attachments.d.ts +0 -9
- package/dist/src/commands/external-attachments.js +0 -1
- package/dist/src/commands/external.d.ts +0 -2
- package/dist/src/commands/external.js +0 -1
- package/dist/src/commands/helper-runtime-official-download.d.ts +0 -80
- package/dist/src/commands/helper-runtime-official-download.js +0 -1
- package/dist/src/commands/runtime.d.ts +0 -120
- package/dist/src/commands/runtime.js +0 -1
- package/dist/src/commands/wechat/command.d.ts +0 -3
- package/dist/src/commands/wechat/command.js +0 -1
- package/dist/src/commands/wechat/direct.d.ts +0 -14
- package/dist/src/commands/wechat/direct.js +0 -2
- package/dist/src/commands/wechat/doctor.d.ts +0 -2
- package/dist/src/commands/wechat/doctor.js +0 -1
- package/dist/src/commands/wechat/ipc.d.ts +0 -9
- package/dist/src/commands/wechat/ipc.js +0 -1
- package/dist/src/commands/wechat/operations.d.ts +0 -3
- package/dist/src/commands/wechat/operations.js +0 -4
- package/dist/src/commands/wechat/output.d.ts +0 -7
- package/dist/src/commands/wechat/output.js +0 -4
- package/dist/src/commands/wechat/types.d.ts +0 -142
- package/dist/src/commands/wechat/types.js +0 -1
- package/dist/src/commands/wechat/utils.d.ts +0 -16
- package/dist/src/commands/wechat/utils.js +0 -1
- package/dist/src/commands/wechat.d.ts +0 -6
- package/dist/src/commands/wechat.js +0 -1
- package/dist/src/devtools/wechat-channel-action-smoke.d.ts +0 -105
- package/dist/src/devtools/wechat-channel-action-smoke.js +0 -10
- package/dist/src/devtools/wechat-channel-download-report.d.ts +0 -111
- package/dist/src/devtools/wechat-channel-download-report.js +0 -2
- package/dist/src/devtools/wechat-channel-markdown-report.d.ts +0 -6
- package/dist/src/devtools/wechat-channel-markdown-report.js +0 -5
- package/dist/src/devtools/wechat-channel-product-steps.d.ts +0 -9
- package/dist/src/devtools/wechat-channel-product-steps.js +0 -40
- package/dist/src/session/handlers/skills.d.ts +0 -7
- package/dist/src/session/handlers/skills.js +0 -1
- package/dist/src/session/handlers/wechat-helper-runtime.d.ts +0 -33
- package/dist/src/session/handlers/wechat-helper-runtime.js +0 -1
- package/dist/src/skills/registry.d.ts +0 -16
- package/dist/src/skills/registry.js +0 -15
- package/dist/src/skills/setup.d.ts +0 -11
- package/dist/src/skills/setup.js +0 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { JsonValue } from './pending-authorized-actions.js';
|
|
2
|
+
import type { RoomInvocationAction } from './invocation-token.js';
|
|
3
|
+
export declare const SHENNIAN_ROOM_TOOL_NAME = "shennian_room";
|
|
4
|
+
export declare const SHENNIAN_ROOM_TOOL: {
|
|
5
|
+
readonly name: "shennian_room";
|
|
6
|
+
readonly title: "Shennian Room";
|
|
7
|
+
readonly description: "Create, join, read, search, send to, configure, or inspect the single Shennian Room bound to this Agent session.";
|
|
8
|
+
readonly inputSchema: {
|
|
9
|
+
readonly type: "object";
|
|
10
|
+
readonly additionalProperties: false;
|
|
11
|
+
readonly required: readonly ["action"];
|
|
12
|
+
readonly properties: {
|
|
13
|
+
readonly action: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly enum: readonly ["create", "join", "read", "send", "mode", "status"];
|
|
16
|
+
readonly description: "The Room action to perform.";
|
|
17
|
+
};
|
|
18
|
+
readonly name: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly minLength: 1;
|
|
21
|
+
readonly maxLength: 80;
|
|
22
|
+
};
|
|
23
|
+
readonly agentAlias: {
|
|
24
|
+
readonly type: "string";
|
|
25
|
+
readonly minLength: 1;
|
|
26
|
+
readonly maxLength: 80;
|
|
27
|
+
};
|
|
28
|
+
readonly agentDescription: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly maxLength: 1000;
|
|
31
|
+
};
|
|
32
|
+
readonly inviteUrl: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly format: "uri";
|
|
35
|
+
readonly maxLength: 2048;
|
|
36
|
+
};
|
|
37
|
+
readonly message: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
readonly maxLength: 500;
|
|
40
|
+
};
|
|
41
|
+
readonly activationMode: {
|
|
42
|
+
readonly type: "string";
|
|
43
|
+
readonly enum: readonly ["auto", "auto_follow", "mention", "mention_only", "paused"];
|
|
44
|
+
};
|
|
45
|
+
readonly afterSequence: {
|
|
46
|
+
readonly type: "string";
|
|
47
|
+
readonly pattern: "^(0|[1-9]\\d{0,19})$";
|
|
48
|
+
};
|
|
49
|
+
readonly limit: {
|
|
50
|
+
readonly type: "integer";
|
|
51
|
+
readonly minimum: 1;
|
|
52
|
+
readonly maximum: 100;
|
|
53
|
+
};
|
|
54
|
+
readonly query: {
|
|
55
|
+
readonly type: "string";
|
|
56
|
+
readonly minLength: 1;
|
|
57
|
+
readonly maxLength: 200;
|
|
58
|
+
};
|
|
59
|
+
readonly attachmentIds: {
|
|
60
|
+
readonly type: "array";
|
|
61
|
+
readonly maxItems: 10;
|
|
62
|
+
readonly uniqueItems: true;
|
|
63
|
+
readonly items: {
|
|
64
|
+
readonly type: "string";
|
|
65
|
+
readonly minLength: 1;
|
|
66
|
+
readonly maxLength: 191;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
readonly clientMessageId: {
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
readonly minLength: 1;
|
|
72
|
+
readonly maxLength: 191;
|
|
73
|
+
};
|
|
74
|
+
readonly text: {
|
|
75
|
+
readonly type: "string";
|
|
76
|
+
readonly minLength: 1;
|
|
77
|
+
readonly maxLength: 20000;
|
|
78
|
+
};
|
|
79
|
+
readonly files: {
|
|
80
|
+
readonly type: "array";
|
|
81
|
+
readonly description: "Up to 10 files inside the current Agent workspace to upload privately with this message.";
|
|
82
|
+
readonly minItems: 1;
|
|
83
|
+
readonly maxItems: 10;
|
|
84
|
+
readonly items: {
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
readonly minLength: 1;
|
|
87
|
+
readonly maxLength: 4096;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
readonly mentionMembershipIds: {
|
|
91
|
+
readonly type: "array";
|
|
92
|
+
readonly maxItems: 20;
|
|
93
|
+
readonly uniqueItems: true;
|
|
94
|
+
readonly items: {
|
|
95
|
+
readonly type: "string";
|
|
96
|
+
readonly minLength: 1;
|
|
97
|
+
readonly maxLength: 191;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
readonly replyToMessageId: {
|
|
101
|
+
readonly type: "string";
|
|
102
|
+
readonly minLength: 1;
|
|
103
|
+
readonly maxLength: 191;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export declare function normalizeRoomToolInput(value: unknown): {
|
|
109
|
+
action: Exclude<RoomInvocationAction, 'room.bind'>;
|
|
110
|
+
actionParameters: JsonValue;
|
|
111
|
+
registryKey?: string;
|
|
112
|
+
invocationToken?: string;
|
|
113
|
+
authorizedActionId?: string;
|
|
114
|
+
};
|
|
115
|
+
export declare class RoomToolInputError extends Error {
|
|
116
|
+
readonly code: string;
|
|
117
|
+
constructor(code: string, message: string);
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{normalizeRoomActionParameters as c}from"./room-action-schema.js";const d="shennian_room",l={name:d,title:"Shennian Room",description:"Create, join, read, search, send to, configure, or inspect the single Shennian Room bound to this Agent session.",inputSchema:{type:"object",additionalProperties:!1,required:["action"],properties:{action:{type:"string",enum:["create","join","read","send","mode","status"],description:"The Room action to perform."},name:{type:"string",minLength:1,maxLength:80},agentAlias:{type:"string",minLength:1,maxLength:80},agentDescription:{type:"string",maxLength:1e3},inviteUrl:{type:"string",format:"uri",maxLength:2048},message:{type:"string",maxLength:500},activationMode:{type:"string",enum:["auto","auto_follow","mention","mention_only","paused"]},afterSequence:{type:"string",pattern:"^(0|[1-9]\\d{0,19})$"},limit:{type:"integer",minimum:1,maximum:100},query:{type:"string",minLength:1,maxLength:200},attachmentIds:{type:"array",maxItems:10,uniqueItems:!0,items:{type:"string",minLength:1,maxLength:191}},clientMessageId:{type:"string",minLength:1,maxLength:191},text:{type:"string",minLength:1,maxLength:2e4},files:{type:"array",description:"Up to 10 files inside the current Agent workspace to upload privately with this message.",minItems:1,maxItems:10,items:{type:"string",minLength:1,maxLength:4096}},mentionMembershipIds:{type:"array",maxItems:20,uniqueItems:!0,items:{type:"string",minLength:1,maxLength:191}},replyToMessageId:{type:"string",minLength:1,maxLength:191}}}},u=new Map([["create","room.create"],["join","room.join"],["read","room.read"],["send","room.send"],["mode","room.mode"],["status","room.status"]]);function f(i){(!i||typeof i!="object"||Array.isArray(i))&&g();const t=i,n=typeof t.action=="string"?u.get(t.action):void 0;n||g();const o=t._shennianRegistryKey,r=t._shennianInvocationToken,s=t._shennianAuthorizedActionId,a=Object.keys(t).filter(e=>e.startsWith("_shennian"));if(typeof s=="string"&&/^[A-Za-z0-9_-]{8,128}$/.test(s)&&a.length===1){const e=Object.fromEntries(Object.entries(t).filter(([m])=>m!=="action"&&m!=="_shennianAuthorizedActionId"));return{action:n,actionParameters:c(n,e),authorizedActionId:s}}if(typeof o!="string"||!/^[a-f0-9]{64}$/.test(o)||typeof r!="string"||!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(r)||a.some(e=>e!=="_shennianRegistryKey"&&e!=="_shennianInvocationToken"))throw new p("session_context_required","The current Agent session is not connected to Shennian Room yet.");const h=Object.fromEntries(Object.entries(t).filter(([e])=>e!=="action"&&e!=="_shennianRegistryKey"&&e!=="_shennianInvocationToken"));return{action:n,actionParameters:c(n,h),registryKey:o,invocationToken:r}}class p extends Error{code;constructor(t,n){super(n),this.code=t,this.name="RoomToolInputError"}}function g(){throw new p("invalid_tool_input","Invalid Shennian Room tool input.")}export{p as RoomToolInputError,l as SHENNIAN_ROOM_TOOL,d as SHENNIAN_ROOM_TOOL_NAME,f as normalizeRoomToolInput};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { callDaemonIpc } from '../daemon-ipc/client.js';
|
|
2
|
+
import type { JsonValue } from './pending-authorized-actions.js';
|
|
3
|
+
export declare function executeRoomTool(rawInput: unknown, invoke?: typeof callDaemonIpc): Promise<JsonValue>;
|
|
4
|
+
export declare function handleRoomToolRpc(value: unknown, invoke?: typeof callDaemonIpc): Promise<Record<string, unknown> | null>;
|
|
5
|
+
export declare function runRoomToolServer(input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream): Promise<void>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import l from"node:readline";import{callDaemonIpc as u}from"../daemon-ipc/client.js";import{DaemonIpcError as m}from"../daemon-ipc/protocol.js";import{normalizeRoomToolInput as d,RoomToolInputError as p,SHENNIAN_ROOM_TOOL as y,SHENNIAN_ROOM_TOOL_NAME as g}from"./tool-schema.js";import{AgentToolError as N,executeAgentTool as h,SHENNIAN_AGENT_TOOL as I,SHENNIAN_AGENT_TOOL_NAME as f}from"./agent-tool.js";async function O(t,e=u){const o=d(t);return o.authorizedActionId?e({method:"authorized-action.wait",params:{id:o.authorizedActionId,timeoutMs:12e4},timeoutMs:135e3}):e({method:"room-action.execute",params:{registryKey:o.registryKey,invocationToken:o.invocationToken,action:o.action,actionParameters:o.actionParameters},timeoutMs:o.action==="room.read"?65e3:15e3})}async function T(t,e=u){if(!t||typeof t!="object"||Array.isArray(t))return a(null,-32600,"Invalid Request");const o=t,r=A(o.id);if(o.jsonrpc!=="2.0"||typeof o.method!="string")return a(r,-32600,"Invalid Request");if(o.method.startsWith("notifications/"))return null;if(o.method==="initialize")return s(r,{protocolVersion:_(o.params),capabilities:{tools:{listChanged:!1}},serverInfo:{name:"shennian-room",version:"1.0.0"}});if(o.method==="ping")return s(r,{});if(o.method==="tools/list")return s(r,{tools:[y,I]});if(o.method!=="tools/call")return a(r,-32601,"Method not found");const n=E(o.params);if(!n||n.name!==g&&n.name!==f)return a(r,-32602,"Invalid params");try{const i=n.name===f?await h(n.arguments):await O(n.arguments,e);return s(r,{content:[{type:"text",text:JSON.stringify(i)}],structuredContent:i,isError:!1})}catch(i){const c=R(i);return s(r,{content:[{type:"text",text:JSON.stringify(c)}],structuredContent:c,isError:!0})}}async function b(t=process.stdin,e=process.stdout){const o=l.createInterface({input:t,crlfDelay:1/0});for await(const r of o){if(!r.trim())continue;let n;try{n=JSON.parse(r)}catch{e.write(`${JSON.stringify(a(null,-32700,"Parse error"))}
|
|
2
|
+
`);continue}const i=await T(n);i&&e.write(`${JSON.stringify(i)}
|
|
3
|
+
`)}}function A(t){return typeof t=="string"||typeof t=="number"||t===null?t:null}function _(t){if(t&&typeof t=="object"){const e=t.protocolVersion;if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e))return e}return"2025-06-18"}function E(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const e=t;return typeof e.name=="string"?{name:e.name,arguments:e.arguments??{}}:null}function R(t){return t instanceof p||t instanceof N||t instanceof m?{ok:!1,error:{code:t.code,message:t.message}}:{ok:!1,error:{code:"room_tool_failed",message:"Shennian Room action failed."}}}function s(t,e){return{jsonrpc:"2.0",id:t,result:e}}function a(t,e,o){return{jsonrpc:"2.0",id:t,error:{code:e,message:o}}}export{O as executeRoomTool,T as handleRoomToolRpc,b as runRoomToolServer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isNativeCapabilityProvider as o,listAgentNativeCapabilities as n}from"../../agents/native-capabilities.js";async function c(s,t){const i=typeof t.params.agentType=="string"?t.params.agentType:"",e=typeof t.params.workDir=="string"?t.params.workDir:process.cwd(),a=o(i),p={supported:a,agentType:i,capabilities:a?n({agentType:i,workDir:e}):[]};s.client.sendRes({type:"res",id:t.id,ok:!0,payload:p})}export{c as handleAgentCapabilitiesList};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mkdir as y}from"node:fs/promises";import{createAgent as w}from"../../agents/adapter.js";import{buildManagedAgentEnv as h,deleteManagedAgentProviderConfig as A,getAgentConfigSummary as s,upsertManagedAgentProviderConfig as E}from"../../agents/config-status.js";import{resolveShennianPath as T}from"../../config/index.js";import{handleAgentsRefresh as C}from"./agents.js";const k=3e4,R="\u8FD9\u662F\u4E00\u6B21\u795E\u5FF5\u8FDE\u63A5\u6D4B\u8BD5\u3002\u8BF7\u53EA\u56DE\u590D OK\uFF0C\u4E0D\u8981\u89E3\u91CA\u3002",p=T("tmp","agent-config-test");function c(
|
|
1
|
+
import{mkdir as y}from"node:fs/promises";import{createAgent as w}from"../../agents/adapter.js";import{buildManagedAgentEnv as h,deleteManagedAgentProviderConfig as A,getAgentConfigSummary as s,upsertManagedAgentProviderConfig as E}from"../../agents/config-status.js";import{resolveShennianPath as T}from"../../config/index.js";import{handleAgentsRefresh as C}from"./agents.js";const k=3e4,R="\u8FD9\u662F\u4E00\u6B21\u795E\u5FF5\u8FDE\u63A5\u6D4B\u8BD5\u3002\u8BF7\u53EA\u56DE\u590D OK\uFF0C\u4E0D\u8981\u89E3\u91CA\u3002",p=T("tmp","agent-config-test");function c(t){if(t==="codex"||t==="claude")return t;throw new Error("Only Codex, Claude Code, and Nian provider config are supported")}async function O(t,e){const n=c(e.params.agent);t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{agent:n,config:s(n)}})}async function G(t,e){const n=c(e.params.agent),r=typeof e.params.baseUrl=="string"?e.params.baseUrl.trim():void 0,o=typeof e.params.token=="string"?e.params.token.trim():void 0;if(!r&&!o)throw new Error("Base URL or token is required");E({agent:n,baseUrl:r,token:o}),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{agent:n,config:s(n)}}),await m(t)}async function N(t,e){const n=c(e.params.agent);A(n),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{agent:n,config:s(n)}}),await m(t)}async function $(t,e){const n=c(e.params.agent),r=s(n);try{const o=await x(n);t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{agent:n,config:s(n),test:o}})}catch(o){t.client.sendRes({type:"res",id:e.id,ok:!1,payload:{agent:n,config:r},error:o instanceof Error?o.message:String(o)})}}async function x(t){const e=w(t);if(!e)throw new Error(`Unsupported agent: ${t}`);const n=`agent-test-${t}-${Date.now()}`;await y(p,{recursive:!0});let r=!1,o=()=>{};const g=new Promise((d,i)=>{const l=setTimeout(()=>{r||(r=!0,i(new Error("Agent test timed out after 30 seconds")))},k);function u(a){r||(r=!0,clearTimeout(l),d(a))}function f(a){r||(r=!0,clearTimeout(l),i(a))}o=f,e.on("agentEvent",a=>{if(a.state==="delta"&&a.text&&!a.thinking){u({mode:"agent-run",reply:a.text.slice(0,120)});return}if(a.state==="final"){u({mode:"agent-run"});return}a.state==="error"&&f(new Error(a.message||"Agent test failed"))}),e.on("error",f)});try{e.configure?.({sessionId:n,env:_(t)}),await e.start(n,p,null);const d=e.send(R).catch(i=>{o(i instanceof Error?i:new Error(String(i)))});return await Promise.race([g,d.then(()=>g)])}finally{e.removeAllListeners(),await e.stop().catch(()=>{})}}async function m(t){const e={type:"req",id:`agent-config-refresh-${Date.now()}`,method:"agents.refresh",params:{}};await C(t,e)}function _(t){return t==="codex"||t==="claude"?h(t):{}}export{_ as getManagedEnvForAgent,N as handleAgentConfigClear,O as handleAgentConfigGet,$ as handleAgentConfigTest,G as handleAgentConfigUpsert};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),a=e>0?Math.min(e,80):Math.min(t.length,80);return t.slice(0,a)}function ee(t,e,a={}){if(t.state==="tool-call"||t.state==="tool-result"){const s={runId:t.runId,sourceSeq:t.seq};return{state:t.state,runId:t.runId,seq:t.seq,sessionId:e,detailRef:s,...t.name?{name:t.name}:{},...t.source?{source:t.source}:{},...t.agentSessionId?{agentSessionId:t.agentSessionId}:{},...a}}return{...t,sessionId:e,...a}}const te=3e4;function ne(t){return t.state==="heartbeat"?t.runPhase??null:t.state==="tool-call"||t.state==="tool-result"?"tool_running":t.state==="approval-pending"?"waiting_approval":t.state==="delta"?t.thinking?"thinking":"streaming_text":t.state==="init"||t.state==="start"?"thinking":null}function ae(t,e){const a=e instanceof Error?e.message:String(e);return t==="pi"?X(a):`Agent send failed: ${a}`}function B(t,e){return t!=="manager"?t:e==="claude"?"claude":"codex"}function L(t,e,a){t.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:e.sessionId,message:e,session:{id:e.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:H(t,e.sessionId,a.agentType)}}})}function re(t,e){v({sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e.sessionId,agentType:e.agentType,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,workDir:e.workDir,status:"active",externalChannel:H(t,e.sessionId,e.agentType)}}})}function se(t){if(!t||typeof t!="object")return null;const e=t;return{configured:e.configured===void 0?void 0:!!e.configured,connected:!!e.connected,type:typeof e.type=="string"?e.type:null,channelId:typeof e.channelId=="string"?e.channelId:null,name:typeof e.name=="string"?e.name:null,canReply:e.canReply===void 0||e.canReply===null?null:!!e.canReply,systemPrompt:typeof e.systemPrompt=="string"?e.systemPrompt:null,wechatRpaSource:typeof e.wechatRpaSource=="string"?e.wechatRpaSource:null,wechatRpaGroups:Array.isArray(e.wechatRpaGroups)?e.wechatRpaGroups.map(a=>({name:String(a?.name||"").trim()})).filter(a=>a.name):null,pollIntervalMs:Number.isFinite(e.pollIntervalMs)?Number(e.pollIntervalMs):null,recentLimit:Number.isFinite(e.recentLimit)?Number(e.recentLimit):null,idleSeconds:Number.isFinite(e.idleSeconds)?Number(e.idleSeconds):null,forceForeground:e.forceForeground===void 0||e.forceForeground===null?null:!!e.forceForeground,noRestore:e.noRestore===void 0||e.noRestore===null?null:!!e.noRestore,downloadAttachments:e.downloadAttachments===void 0||e.downloadAttachments===null?null:!!e.downloadAttachments,downloadAttachmentsDir:typeof e.downloadAttachmentsDir=="string"?e.downloadAttachmentsDir:null}}function oe(t){return!!(t?.configured??t?.connected)}function z(t){return K(t)}function W(t,e,a){return oe(e)?{...Q()?.getInjectedEnv(t,null,process.cwd(),"external")??{},SHENNIAN_EXTERNAL_SESSION_ID:t,SHENNIAN_MANAGER_SESSION_ID:t,...a?.trim()?{SHENNIAN_EXTERNAL_REPLY_TARGET:a.trim()}:{}}:{}}function le(t,e,a,s,d){t.configure?.({sessionId:e,externalChannel:s??null,env:{...z(a),...W(e,s,d)}})}function H(t,e,a){const s=t.sessions.get(e);return s?.externalChannel?s.externalChannel:a==="manager"?t.managerRuntime?.getExternalChannelStatus(e)??null:null}function ie(t,e,a){if(t!=="claude"||!a)return e;const s=e.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?U(a)??e:e}function de(t,e,a,s){let d=null;function f(n,r={}){t.client.sendAgentEvent({type:"event",event:"agent",payload:ee(n,e,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function l(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function i(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function S(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{i(n)},te),n.heartbeatTimer.unref?.())}function E(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(f({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=t.sessions.get(e),T=ne(n),R=n.state==="final"||n.state==="error"||n.state==="aborted";r&&(r.nextEventSeq=n.seq+1,R||(r.currentRunId=n.runId),!R&&T&&(r.currentRunPhase=T,S(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),t.managerRuntime?.noteAgentEvent(e,n),n.state!=="delta"&&x({level:"info",sessionId:e,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?A(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?A(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?A(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:O(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&A(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.message});const h=`${e}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&t.runTextAcc.set(h,(t.runTextAcc.get(h)??"")+n.text),n.agentSessionId&&n.agentSessionId!==d&&(d=n.agentSessionId,t.nativeFusion?.noteManagedSourceSession(e,B(a,n.source),n.agentSessionId),r&&v({sessionId:e,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const p=n.text??"";if(!p)return;const b=!!n.thinking;r?.pendingTextEvent&&(r.pendingTextEvent.runId!==n.runId||r.pendingTextEvent.thinking!==b)&&E(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:b}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=p,r.pendingTextEvent.seq=n.seq);return}let m={};if(n.state==="final"){E(r);const p=t.runTextAcc.get(h)??"";p&&(m={messageSummary:Z(p)}),t.runTextAcc.delete(h)}else n.state==="error"||n.state==="aborted"?(E(r),t.runTextAcc.delete(h)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&E(r);R&&r?.currentRunId===n.runId&&(r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,l(r),t.chatQueue?.noteTerminal(e)),f(n,m)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${a}: ${n.message}`),t.sessions.delete(e),t.chatQueue?.noteTerminal(e),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:n.message,runId:"",seq:0}})})}function ce(t,e){if(t.processedReqIds.add(e),t.processedReqIds.size>1e3){const a=t.processedReqIds.values().next().value;t.processedReqIds.delete(a)}}async function q(t){t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}async function ue(t,e,a,s,d,f,l){t.evictIdleSessions();const i=j(a);if(!i)throw new Error(`Unsupported agent: ${a}`);le(i,e,a,f,l),await i.start(e,s,d);const S={adapter:i,workDir:s,agentType:a,agentSessionId:d??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:f??null,externalReplyTarget:l??null,externalChannelEnv:{...z(a),...W(e,f,l)}};return t.sessions.set(e,S),de(t,e,a,i),S}function ge(t,e){const a=t.sessions.get(e),s=a?.currentRunId;if(!a||!s)return;const d=a.nextEventSeq;t.runTextAcc.delete(`${e}:${s}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:e,runId:s,seq:d},seq:d,id:`agent-evt-${s}-${d}`}),t.chatQueue?.noteTerminal(e)}async function Te(t,e){if(t.processedReqIds.has(e.id)){t.client.sendRes({type:"res",id:e.id,ok:!0});return}ce(t,e.id);const{sessionId:a,text:s,agentType:d,workDir:f,agentSessionId:l,modelId:i,managerDefaultWorkerAgentType:S,managerDefaultWorkerModelId:E,reasoningEffort:n,clientMessageId:r,sessionListProjection:T,waitForDispatch:R,responseId:h}=e.params,m=h||e.id;J(T);const p=se(e.params.externalChannel),b=typeof e.params.replyTarget=="string"?e.params.replyTarget.trim():"";if(!a||!s){t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:m,ok:!1,error:"sessionId and text are required"});return}const u=d;u==="manager"&&t.managerRuntime?.setManagerWorkerDefaults(a,S??null,E??null);const y=(u==="claude"||u==="codex")&&typeof n=="string"&&n.trim()?n.trim():void 0,I=t.resolvePath(ie(u,f||F.homedir(),l)||F.homedir()),D=I,$=Y(e.params.attachments),g=$?.length?await V({text:s,attachments:$,workDir:I}):{text:s,attachments:$,localized:!1};let o=t.sessions.get(a);if(o){if(o.lastActiveAt=Date.now(),o.agentType!==u||o.workDir!==I||JSON.stringify(o.externalChannel??null)!==JSON.stringify(p??null)){t.sessions.delete(a);try{await q(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}else if(l&&o.agentSessionId!==l)try{await o.adapter.resume(l),o.agentSessionId=l}catch{t.sessions.delete(a);try{await q(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}}if(!o)try{o=await ue(t,a,u,I,l,p,b)}catch(c){const w=c instanceof Error&&c.message.startsWith("Unsupported agent:")?c.message:`Failed to start ${d}: ${c instanceof Error?c.message:String(c)}`;console.error(`[chat.send] start failed reqId=${e.id} sessionId=${a} agentType=${d} workDir=${I} agentSessionId=${l??""}: ${w}`),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:w,runId:"",seq:0}}),t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:m,ok:!1,error:w});return}t.managerRuntime?.notifyWeChatRpaSessionBinding({sessionId:a,workDir:I,agentType:u,agentSessionId:o.agentSessionId??l??null,modelId:i??null,externalChannel:p});const N={id:r??`user-${e.id}`,sessionId:a,role:"user",ts:Date.now(),payload:G(g.text,g.attachments)};x({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:e.id,agentType:u,modelId:i,reasoningEffort:y}});const P=()=>{re(t,{sessionId:a,agentType:u,workDir:D,agentSessionId:o.agentSessionId??l??null,modelId:i}),o.currentRunId||(o.nextEventSeq=0),t.nativeFusion?.registerManagedSend({sessionId:a,agentType:u,sourceAgentType:B(u,i),canonicalMessageId:r??null,sourceSessionKey:o.agentSessionId??l??null,text:g.text}),A(a,N),L(t,N,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??l??null,modelId:i})},C=async(c,w)=>{const k=ae(u,c);console.error(`[chat.send] send failed reqId=${e.id} sessionId=${a} agentType=${d} workDir=${I} agentSessionId=${o.agentSessionId??l??""}: ${k}`),t.sessions.delete(a);try{await q(o)}catch{}if(t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:k,runId:"",seq:0}}),!w){const _={id:`agent-error-${e.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:k};A(a,_),L(t,_,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??l??null,modelId:i})}w&&(t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:m,ok:!1,error:k}))};if(R){try{const c=g.attachments;c?.length?await o.adapter.send(g.text,i,y,c):y?await o.adapter.send(g.text,i,y):await o.adapter.send(g.text,i),x({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}catch(c){await C(c,!0);return}P(),t.client.sendRes({type:"res",id:m,ok:!0,...g.localized?{payload:{localizedAttachments:!0}}:{}}),x({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});return}P(),t.client.sendRes({type:"res",id:m,ok:!0,...g.localized?{payload:{localizedAttachments:!0}}:{}}),x({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});const M=g.attachments;(M?.length?o.adapter.send(g.text,i,y,M):y?o.adapter.send(g.text,i,y):o.adapter.send(g.text,i)).then(()=>{x({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}).catch(c=>{C(c,!1)})}async function be(t,e){const{sessionId:a}=e.params,s=t.sessions.get(a);if(s){try{await s.adapter.stop()}catch{}ge(t,a),t.activityPublisher?.publish(a,null)}t.client.sendRes({type:"res",id:e.id,ok:!0})}export{be as handleChatAbort,Te as handleChatSend};
|
|
1
|
+
import z from"node:os";import{createAgent as O}from"../../agents/adapter.js";import{buildApprovalPendingPayload as j,buildUserMessagePayload as H}from"@shennian/wire";import{reportLog as E}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as U}from"../../native-fusion/parsers.js";import{appendMessage as A,recordSession as B}from"../store.js";import{mergeProjectedSessions as G}from"../projection.js";import{buildManagedAgentEnv as J}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as Q}from"../remote-attachments.js";function K(t){if(!Array.isArray(t))return;const e=t.map(a=>{if(!a||typeof a!="object")return null;const s=a,i=typeof s.path=="string"?s.path:"",f=typeof s.name=="string"?s.name:"",l=typeof s.mimeType=="string"?s.mimeType:"";if(!i||!f||!l)return null;const d=typeof s.previewData=="string"&&s.previewData.trim()?s.previewData.trim():void 0;return{path:i,name:f,mimeType:l,kind:l.startsWith("image/")?"image":"file",...d?{previewData:d}:{}}}).filter(a=>a!=null);return e.length?e:void 0}function V(t){const e=t.indexOf(`
|
|
2
|
+
`),a=e>0?Math.min(e,80):Math.min(t.length,80);return t.slice(0,a)}function Y(t,e,a={}){if(t.state==="tool-call"||t.state==="tool-result"){const s={runId:t.runId,sourceSeq:t.seq};return{state:t.state,runId:t.runId,seq:t.seq,sessionId:e,detailRef:s,...t.name?{name:t.name}:{},...t.source?{source:t.source}:{},...t.agentSessionId?{agentSessionId:t.agentSessionId}:{},...a}}return{...t,sessionId:e,...a}}const X=3e4;function Z(t){return t.state==="heartbeat"?t.runPhase??null:t.state==="tool-call"||t.state==="tool-result"?"tool_running":t.state==="approval-pending"?"waiting_approval":t.state==="delta"?t.thinking?"thinking":"streaming_text":t.state==="init"||t.state==="start"?"thinking":null}function ee(t,e){return`Agent send failed: ${e instanceof Error?e.message:String(e)}`}function _(t,e){return t!=="manager"?t:e==="claude"?"claude":"codex"}function L(t,e,a){t.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:e.sessionId,message:e,session:{id:e.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function te(t,e){B({sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e.sessionId,agentType:e.agentType,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,workDir:e.workDir,status:"active",externalChannel:null}}})}function Ie(t){if(!t||typeof t!="object")return null;const e=t;return{configured:e.configured===void 0?void 0:!!e.configured,connected:!!e.connected,type:typeof e.type=="string"?e.type:null,channelId:typeof e.channelId=="string"?e.channelId:null,name:typeof e.name=="string"?e.name:null,canReply:e.canReply===void 0||e.canReply===null?null:!!e.canReply,systemPrompt:typeof e.systemPrompt=="string"?e.systemPrompt:null,wechatRpaSource:typeof e.wechatRpaSource=="string"?e.wechatRpaSource:null,wechatRpaGroups:Array.isArray(e.wechatRpaGroups)?e.wechatRpaGroups.map(a=>({name:String(a?.name||"").trim()})).filter(a=>a.name):null,pollIntervalMs:Number.isFinite(e.pollIntervalMs)?Number(e.pollIntervalMs):null,recentLimit:Number.isFinite(e.recentLimit)?Number(e.recentLimit):null,idleSeconds:Number.isFinite(e.idleSeconds)?Number(e.idleSeconds):null,forceForeground:e.forceForeground===void 0||e.forceForeground===null?null:!!e.forceForeground,noRestore:e.noRestore===void 0||e.noRestore===null?null:!!e.noRestore,downloadAttachments:e.downloadAttachments===void 0||e.downloadAttachments===null?null:!!e.downloadAttachments,downloadAttachmentsDir:typeof e.downloadAttachmentsDir=="string"?e.downloadAttachmentsDir:null}}function we(t){return!!(t?.configured??t?.connected)}function W(t){return J(t)}function v(t,e,a){return{}}function ne(t,e,a,s,i){t.configure?.({sessionId:e,externalChannel:s??null,env:{...W(a),...v(e,s,i)}})}function Se(t,e,a){return null}function ae(t,e,a){if(t!=="claude"||!a)return e;const s=e.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?U(a)??e:e}function re(t,e,a,s){let i=null;function f(n,r={}){t.client.sendAgentEvent({type:"event",event:"agent",payload:Y(n,e,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function l(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function d(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function I(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{d(n)},X),n.heartbeatTimer.unref?.())}function w(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(f({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=t.sessions.get(e),R=Z(n),T=n.state==="final"||n.state==="error"||n.state==="aborted";r&&(r.nextEventSeq=n.seq+1,T||(r.currentRunId=n.runId),!T&&R&&(r.currentRunPhase=R,I(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),t.managerRuntime?.noteAgentEvent(e,n),n.state!=="delta"&&E({level:"info",sessionId:e,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?A(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?A(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?A(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:j(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&A(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.message});const m=`${e}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&t.runTextAcc.set(m,(t.runTextAcc.get(m)??"")+n.text),n.agentSessionId&&n.agentSessionId!==i&&(i=n.agentSessionId,t.nativeFusion?.noteManagedSourceSession(e,_(a,n.source),n.agentSessionId),r&&B({sessionId:e,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const h=n.text??"";if(!h)return;const b=!!n.thinking;r?.pendingTextEvent&&(r.pendingTextEvent.runId!==n.runId||r.pendingTextEvent.thinking!==b)&&w(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:b}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=h,r.pendingTextEvent.seq=n.seq);return}let p={};if(n.state==="final"){w(r);const h=t.runTextAcc.get(m)??"";h&&(p={messageSummary:V(h)}),t.runTextAcc.delete(m)}else n.state==="error"||n.state==="aborted"?(w(r),t.runTextAcc.delete(m)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&w(r);T&&r?.currentRunId===n.runId&&(r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,l(r),t.chatQueue?.noteTerminal(e)),f(n,p)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${a}: ${n.message}`),t.sessions.delete(e),t.chatQueue?.noteTerminal(e),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:n.message,runId:"",seq:0}})})}function se(t,e){if(t.processedReqIds.add(e),t.processedReqIds.size>1e3){const a=t.processedReqIds.values().next().value;t.processedReqIds.delete(a)}}async function q(t){t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}async function oe(t,e,a,s,i,f,l){t.evictIdleSessions();const d=O(a);if(!d)throw new Error(`Unsupported agent: ${a}`);ne(d,e,a,f,l),await d.start(e,s,i);const I={adapter:d,workDir:s,agentType:a,agentSessionId:i??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:f??null,externalReplyTarget:l??null,externalChannelEnv:{...W(a),...v(e,f,l)}};return t.sessions.set(e,I),re(t,e,a,d),I}function le(t,e){const a=t.sessions.get(e),s=a?.currentRunId;if(!a||!s)return;const i=a.nextEventSeq;t.runTextAcc.delete(`${e}:${s}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:e,runId:s,seq:i},seq:i,id:`agent-evt-${s}-${i}`}),t.chatQueue?.noteTerminal(e)}async function xe(t,e){if(t.processedReqIds.has(e.id)){t.client.sendRes({type:"res",id:e.id,ok:!0});return}se(t,e.id);const{sessionId:a,text:s,agentType:i,workDir:f,agentSessionId:l,modelId:d,managerDefaultWorkerAgentType:I,managerDefaultWorkerModelId:w,reasoningEffort:n,clientMessageId:r,sessionListProjection:R,waitForDispatch:T,responseId:m}=e.params,p=m||e.id;G(R);const h=null,b="";if(!a||!s){t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:p,ok:!1,error:"sessionId and text are required"});return}const u=i;u==="manager"&&t.managerRuntime?.setManagerWorkerDefaults(a,I??null,w??null);const y=(u==="claude"||u==="codex"||u==="workbuddy")&&typeof n=="string"&&n.trim()?n.trim():void 0,S=t.resolvePath(ae(u,f||z.homedir(),l)||z.homedir()),$=S,D=K(e.params.attachments),g=D?.length?await Q({text:s,attachments:D,workDir:S}):{text:s,attachments:D,localized:!1};let o=t.sessions.get(a);if(o){if(o.lastActiveAt=Date.now(),o.agentType!==u||o.workDir!==S||JSON.stringify(o.externalChannel??null)!==JSON.stringify(h??null)){t.sessions.delete(a);try{await q(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}else if(l&&o.agentSessionId!==l)try{await o.adapter.resume(l),o.agentSessionId=l}catch{t.sessions.delete(a);try{await q(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}}if(!o)try{o=await oe(t,a,u,S,l,h,b)}catch(c){const x=c instanceof Error&&c.message.startsWith("Unsupported agent:")?c.message:`Failed to start ${i}: ${c instanceof Error?c.message:String(c)}`;console.error(`[chat.send] start failed reqId=${e.id} sessionId=${a} agentType=${i} workDir=${S} agentSessionId=${l??""}: ${x}`),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:x,runId:"",seq:0}}),t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:p,ok:!1,error:x});return}const P={id:r??`user-${e.id}`,sessionId:a,role:"user",ts:Date.now(),payload:H(g.text,g.attachments)};E({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:e.id,agentType:u,modelId:d,reasoningEffort:y}});const C=()=>{te(t,{sessionId:a,agentType:u,workDir:$,agentSessionId:o.agentSessionId??l??null,modelId:d}),o.currentRunId||(o.nextEventSeq=0),t.nativeFusion?.registerManagedSend({sessionId:a,agentType:u,sourceAgentType:_(u,d),canonicalMessageId:r??null,sourceSessionKey:o.agentSessionId??l??null,text:g.text}),A(a,P),L(t,P,{agentType:u,workDir:$,agentSessionId:o.agentSessionId??l??null,modelId:d})},M=async(c,x)=>{const k=ee(u,c);console.error(`[chat.send] send failed reqId=${e.id} sessionId=${a} agentType=${i} workDir=${S} agentSessionId=${o.agentSessionId??l??""}: ${k}`),t.sessions.delete(a);try{await q(o)}catch{}if(t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:k,runId:"",seq:0}}),!x){const N={id:`agent-error-${e.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:k};A(a,N),L(t,N,{agentType:u,workDir:$,agentSessionId:o.agentSessionId??l??null,modelId:d})}x&&(t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:p,ok:!1,error:k}))};if(T){try{const c=g.attachments;c?.length?await o.adapter.send(g.text,d,y,c):y?await o.adapter.send(g.text,d,y):await o.adapter.send(g.text,d),E({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}catch(c){await M(c,!0);return}C(),t.client.sendRes({type:"res",id:p,ok:!0,...g.localized?{payload:{localizedAttachments:!0}}:{}}),E({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});return}C(),t.client.sendRes({type:"res",id:p,ok:!0,...g.localized?{payload:{localizedAttachments:!0}}:{}}),E({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});const F=g.attachments;(F?.length?o.adapter.send(g.text,d,y,F):y?o.adapter.send(g.text,d,y):o.adapter.send(g.text,d)).then(()=>{E({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}).catch(c=>{M(c,!1)})}async function Ee(t,e){const{sessionId:a}=e.params,s=t.sessions.get(a);if(s){try{await s.adapter.stop()}catch{}le(t,a),t.activityPublisher?.publish(a,null)}t.client.sendRes({type:"res",id:e.id,ok:!0})}export{Ee as handleChatAbort,xe as handleChatSend};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SERVERS as r}from"../../region.js";import{loadConfig as
|
|
1
|
+
import{SERVERS as r}from"../../region.js";import{loadConfig as i,saveConfig as p}from"../../config/index.js";async function f(o,t){const n=await Promise.all(Object.keys(r).map(async a=>{const{url:s,label:c}=r[a],d=performance.now();try{const l=new AbortController,u=setTimeout(()=>l.abort(),5e3);return await fetch(`${s}/health`,{signal:l.signal}),clearTimeout(u),{region:a,url:s,label:c,latencyMs:Math.round(performance.now()-d)}}catch{return{region:a,url:s,label:c,latencyMs:null}}}));o.client.sendRes({type:"res",id:t.id,ok:!0,payload:{current:"cn",servers:n}})}async function m(o,t){const e=t.params.region;if(e!=="cn"&&e!=="global"){o.client.sendRes({type:"res",id:t.id,ok:!1,error:'region must be "cn" or "global"'});return}const n=i();n.serverUrl=r[e].url,p(n),o.client.sendRes({type:"res",id:t.id,ok:!0,payload:{region:e,url:r[e].url}}),setTimeout(()=>process.exit(0),500)}async function h(o,t){const e=t.params.policy;if(e!=="none"&&e!=="patch"){o.client.sendRes({type:"res",id:t.id,ok:!1,error:'policy must be "none" or "patch"'});return}const n=i();n.autoUpgrade=e,p(n),o.upgradePolicyController?.setPolicy(e),o.client.sendEvent({type:"event",event:"machine.upgrade-policy",payload:{policy:e}}),o.client.sendRes({type:"res",id:t.id,ok:!0,payload:{autoUpgrade:e}})}export{f as handleRegionProbe,m as handleRegionSwitch,h as handleUpgradeSetPolicy};
|
|
@@ -5,6 +5,7 @@ export declare function handleFsRead(runtime: SessionManagerRuntime, req: ReqFra
|
|
|
5
5
|
export declare function handleFsWrite(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
6
6
|
export declare function handleFsRename(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
7
7
|
export declare function handleFsExportMarkdownPdf(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
8
|
+
export declare function handleFsExportMarkdownPdfSetup(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
8
9
|
export declare function handleFsArchiveZip(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
9
10
|
export declare function handleFsTransfer(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
10
11
|
export declare function handleFsTransferStart(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import d from"node:fs";import g from"node:os";import p from"node:path";import{convertMarkdownToPdf as C,defaultPdfOutputPath as j,MarkdownPdfBrowserMissingError as O}from"../../tools/markdown-to-pdf.js";import{createZipArchive as L}from"../archive-zip.js";import{BinaryTextPreviewError as B,decodeTextBufferAuto as U,isAutoTextEncoding as X}from"../../fs/text-decoder.js";const D="__roots__",Z=2e3,H=1024*1024*1024,W=5e3,V=1024*1024*1024;function E(e){return/^[A-Za-z]:([\\/]|$)/.test(e)||/^\\\\[^\\]+\\[^\\]+/.test(e)}function v(e){return E(e)?p.win32:p.posix}function T(e){const t=d.statSync(e);return{name:v(e).basename(e),path:e,isDir:t.isDirectory(),size:t.isFile()?t.size:void 0,modifiedAt:t.mtimeMs}}function Y(){if(g.platform()==="win32"){const e=[];for(let t=65;t<=90;t+=1){const r=`${String.fromCharCode(t)}:\\`;d.existsSync(r)&&e.push({name:r,path:r,isDir:!0})}return e}return[{name:"/",path:"/",isDir:!0}]}function G(e,t){const r=typeof e=="object"&&e&&"code"in e?String(e.code):"";return r==="ENOENT"?`Directory not found: ${t}`:r==="EACCES"||r==="EPERM"?`Permission denied: ${t}`:e instanceof Error?e.message:String(e)}function J(e){if(!e||e==="."||e.trim()!==e||p.posix.isAbsolute(e)||p.win32.isAbsolute(e))return!1;const t=e.replace(/\\/g,"/");return t!==e?!1:t.split("/").every(r=>r&&r!=="."&&r!==".."&&!M(r))}function $(e){const t=e.trim();if(!t||t!==e||e==="."||e===".."||M(e)||/[\\/:]/.test(e)||/[<>|"?*]/.test(e)||/[. ]$/.test(e))return!1;const r=e.split(".")[0]?.toUpperCase();return!(r&&/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/.test(r))}function M(e){return Array.from(e).some(t=>{const r=t.charCodeAt(0);return r>=0&&r<=31})}function K(e){return Array.isArray(e)?e.map(t=>({relativePath:String(t.relativePath||""),size:Number(t.size||0),mimeType:t.mimeType,modifiedAt:t.modifiedAt})):[]}async function ne(e,t){const r=t.params.path||g.homedir(),a=t.params.rootPath||r;if(r===D){e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:D,entries:Y()}});return}const n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}const s=n.path;try{const o=d.readdirSync(s,{withFileTypes:!0}),c=E(s)?p.win32.join:p.join,i=o.map(l=>({name:l.name,path:c(s,l.name),isDir:l.isDirectory()})).sort((l,f)=>l.isDir!==f.isDir?l.isDir?-1:1:l.name.localeCompare(f.name));e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:s,entries:i}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:G(o,s)})}}async function oe(e,t){const r=t.params.path,a=t.params.rootPath||r,n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}const s=n.path,o=t.params.encoding,c=typeof o=="string"?o:"utf8",i=t.params.offset,l=t.params.length;try{const f=d.statSync(s);if(!f.isFile()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a file"});return}if(i!=null&&l!=null){const u=Math.min(l,Math.max(0,f.size-i));if(u<=0){e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:"",offset:i,length:0,totalSize:f.size,path:s}});return}const P=d.openSync(s,"r");try{const k=Buffer.alloc(u),S=d.readSync(P,k,0,u,i);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:k.subarray(0,S).toString("base64"),offset:i,length:S,totalSize:f.size,path:s}})}finally{d.closeSync(P)}return}const y=t.params.maxSize||(c==="base64"?5*1024*1024:512*1024);if(f.size>y){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`File too large: ${f.size} bytes (max ${y})`,payload:{size:f.size,path:s}});return}if(c==="base64"){const u=d.readFileSync(s);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:u.toString("base64"),path:s,size:f.size}});return}if(X(c)){const u=d.readFileSync(s),P=U(u);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{content:P.content,path:s,size:f.size,encoding:P.encoding,encodingFallback:P.fallback}});return}const m=d.readFileSync(s,"utf-8");e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{content:m,path:s,size:f.size}})}catch(f){e.client.sendRes({type:"res",id:t.id,ok:!1,error:f instanceof B?f.message:String(f)})}}async function ie(e,t){const r=t.params.path,a=t.params.content,n=t.params.rootPath||r;if(!r||typeof a!="string"){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path and content are required"});return}const s=e.resolveAuthorizedPath(r,n);if(!s.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s.error});return}try{const o=d.existsSync(s.path)?d.statSync(s.path):null;if(o&&!o.isFile()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a file"});return}d.writeFileSync(s.path,a,"utf-8");const c=d.statSync(s.path);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:s.path,size:c.size,modifiedAt:c.mtimeMs}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(o)})}}async function de(e,t){const r=t.params.path,a=t.params.newName;if(!r||!a){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path and newName are required"});return}if(!$(a)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid newName"});return}const n=v(r),s=t.params.rootPath||n.dirname(r),o=e.resolveAuthorizedPath(r,s);if(!o.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:o.error});return}try{const c=v(o.path),i=c.join(c.dirname(o.path),a),l=e.resolveAuthorizedPath(i,s);if(!l.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:l.error});return}if(d.existsSync(l.path)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Target already exists"});return}d.renameSync(o.path,l.path),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{oldPath:o.path,newPath:l.path,entry:T(l.path)}})}catch(c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(c)})}}async function ce(e,t){const r=t.params.path,a=t.params.rootPath||p.dirname(r||"."),n=typeof t.params.title=="string"?t.params.title:void 0;if(!r){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path is required"});return}const s=e.resolveAuthorizedPath(r,a);if(!s.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s.error});return}if(!/\.mdx?$/i.test(s.path)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Only Markdown files can be exported to PDF"});return}const o=j(s.path),c=e.resolveAuthorizedPath(o,a);if(!c.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:c.error});return}try{const i=await C(s.path,{outputPath:c.path,title:n});e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{sourcePath:s.path,outputPath:i.outputPath,entry:T(i.outputPath)}})}catch(i){if(i instanceof O){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"This machine needs the PDF export component before Markdown files can be exported to PDF.",payload:i.setup});return}e.client.sendRes({type:"res",id:t.id,ok:!1,error:i instanceof Error?i.message:String(i)})}}function Q(e){let t="";for(const a of e.trim())a.charCodeAt(0)<=31||'<>:"/\\|?*'.includes(a)?t.endsWith("-")||(t+="-"):t+=a;return t.replace(/[. ]+$/g,"")||"folder"}async function le(e,t){const r=t.params.path,a=t.params.rootPath||r;if(!r){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path is required"});return}const n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}try{if(!d.statSync(n.path).isDirectory()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a directory"});return}const o=v(n.path),c=Q(o.basename(n.path)),i=p.join(g.tmpdir(),"shennian-archives"),l=p.join(i,`${c}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),f=L(n.path,l,{maxFiles:W,maxTotalSize:V});e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{sourcePath:n.path,outputPath:f.outputPath,entry:T(f.outputPath),fileCount:f.fileCount,totalSize:f.totalSize}})}catch(s){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s instanceof Error?s.message:String(s)})}}async function fe(e,t){const{name:r,targetPath:a,data:n,direct:s}=t.params;if(!r||!n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"name and data are required"});return}try{const o=t.params.rootPath||a||g.homedir(),c=e.resolveAuthorizedPath(a||o,o);if(!c.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:c.error});return}const i=c.path,l=s?i:p.join(i,".uploads");s||d.mkdirSync(l,{recursive:!0});const f=p.join(l,p.basename(r)),y=Buffer.from(n,"base64");d.writeFileSync(f,y),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:f}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(o)})}}async function pe(e,t){const{name:r,targetPath:a,totalSize:n,direct:s,kind:o,baseName:c}=t.params,i=K(t.params.manifest),l=o==="folder",f=l&&c||r;if(!f||!l&&!n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"name and totalSize are required"});return}try{const y=t.params.rootPath||a||g.homedir(),m=e.resolveAuthorizedPath(a||y,y);if(!m.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:m.error});return}const u=m.path,P=s?u:p.join(u,".uploads");s||d.mkdirSync(P,{recursive:!0});const k=`tf-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;if(l){if(!i.length){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"manifest is required for folder uploads"});return}if(i.length>Z){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Too many files: ${i.length}`});return}const x=p.basename(f);if(!$(x)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid folder name"});return}const I=p.join(P,x),R=e.resolveAuthorizedPath(I,y);if(!R.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:R.error});return}const b=new Set;let z=0;const A=new Map;for(const h of i){if(!J(h.relativePath)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Invalid relativePath: ${h.relativePath}`});return}if(!Number.isFinite(h.size)||h.size<0){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Invalid size: ${h.relativePath}`});return}if(b.has(h.relativePath)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Duplicate relativePath: ${h.relativePath}`});return}if(b.add(h.relativePath),z+=h.size,z>H){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Folder too large: ${z} bytes`});return}const _=p.join(R.path,...h.relativePath.split("/")),F=e.resolveAuthorizedPath(_,y);if(!F.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:F.error});return}const w=p.join(g.tmpdir(),`.shennian-upload-${k}-${A.size}`);d.writeFileSync(w,Buffer.alloc(0)),A.set(h.relativePath,{relativePath:h.relativePath,tempPath:w,targetPath:F.path,size:h.size})}e.pendingTransfers.set(k,{tempPath:"",targetPath:R.path,totalSize:z,kind:"folder",rootPath:y,targetDir:R.path,files:A}),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{transferId:k,path:R.path}});return}const S=p.join(g.tmpdir(),`.shennian-upload-${k}`),N=p.join(P,p.basename(f));d.writeFileSync(S,Buffer.alloc(0)),e.pendingTransfers.set(k,{tempPath:S,targetPath:N,totalSize:n,kind:"file"}),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{transferId:k}})}catch(y){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(y)})}}async function he(e,t){const{transferId:r,offset:a,data:n,relativePath:s}=t.params,o=e.pendingTransfers.get(r);if(!o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid transferId"});return}try{const c=o.kind==="folder"?o.files?.get(String(s||"")):{tempPath:o.tempPath,size:o.totalSize};if(!c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid relativePath"});return}const i=Buffer.from(n,"base64");if(a<0||a+i.length>c.size){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Chunk exceeds declared size"});return}const l=d.openSync(c.tempPath,"r+");try{d.writeSync(l,i,0,i.length,a)}finally{d.closeSync(l)}e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{written:i.length}})}catch(c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(c)})}}async function ye(e,t){const{transferId:r}=t.params,a=e.pendingTransfers.get(r);if(!a){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid transferId"});return}try{if(a.kind==="folder"){let n=0;for(const s of a.files?.values()??[])d.mkdirSync(p.dirname(s.targetPath),{recursive:!0}),d.renameSync(s.tempPath,s.targetPath),n+=1;e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetDir??a.targetPath,count:n}});return}d.renameSync(a.tempPath,a.targetPath),e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetPath}})}catch{try{d.copyFileSync(a.tempPath,a.targetPath),d.unlinkSync(a.tempPath),e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetPath}})}catch(n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(n)})}}}async function ue(e,t){const{transferId:r}=t.params,a=e.pendingTransfers.get(r);if(a){const n=a.kind==="folder"?Array.from(a.files?.values()??[]).map(s=>s.tempPath):[a.tempPath];for(const s of n)try{s&&d.unlinkSync(s)}catch{}e.pendingTransfers.delete(r)}e.client.sendRes({type:"res",id:t.id,ok:!0})}function Pe(e){for(const[,t]of e.pendingTransfers){const r=t.kind==="folder"?Array.from(t.files?.values()??[]).map(a=>a.tempPath):[t.tempPath];for(const a of r)try{a&&d.unlinkSync(a)}catch{}}e.pendingTransfers.clear()}export{Pe as cleanupPendingTransfers,le as handleFsArchiveZip,ce as handleFsExportMarkdownPdf,ne as handleFsLs,oe as handleFsRead,de as handleFsRename,fe as handleFsTransfer,ue as handleFsTransferAbort,he as handleFsTransferChunk,ye as handleFsTransferFinish,pe as handleFsTransferStart,ie as handleFsWrite};
|
|
1
|
+
import d from"node:fs";import g from"node:os";import p from"node:path";import{convertMarkdownToPdf as _,defaultPdfOutputPath as j,findChromiumExecutable as O,installManagedChromium as L,managedChromiumBrowsersPath as B,MarkdownPdfBrowserMissingError as U}from"../../tools/markdown-to-pdf.js";import{createZipArchive as X}from"../archive-zip.js";import{BinaryTextPreviewError as Z,decodeTextBufferAuto as H,isAutoTextEncoding as W}from"../../fs/text-decoder.js";const D="__roots__",V=2e3,Y=1024*1024*1024,G=5e3,J=1024*1024*1024;function E(e){return/^[A-Za-z]:([\\/]|$)/.test(e)||/^\\\\[^\\]+\\[^\\]+/.test(e)}function v(e){return E(e)?p.win32:p.posix}function x(e){const t=d.statSync(e);return{name:v(e).basename(e),path:e,isDir:t.isDirectory(),size:t.isFile()?t.size:void 0,modifiedAt:t.mtimeMs}}function K(){if(g.platform()==="win32"){const e=[];for(let t=65;t<=90;t+=1){const r=`${String.fromCharCode(t)}:\\`;d.existsSync(r)&&e.push({name:r,path:r,isDir:!0})}return e}return[{name:"/",path:"/",isDir:!0}]}function Q(e,t){const r=typeof e=="object"&&e&&"code"in e?String(e.code):"";return r==="ENOENT"?`Directory not found: ${t}`:r==="EACCES"||r==="EPERM"?`Permission denied: ${t}`:e instanceof Error?e.message:String(e)}function q(e){if(!e||e==="."||e.trim()!==e||p.posix.isAbsolute(e)||p.win32.isAbsolute(e))return!1;const t=e.replace(/\\/g,"/");return t!==e?!1:t.split("/").every(r=>r&&r!=="."&&r!==".."&&!$(r))}function M(e){const t=e.trim();if(!t||t!==e||e==="."||e===".."||$(e)||/[\\/:]/.test(e)||/[<>|"?*]/.test(e)||/[. ]$/.test(e))return!1;const r=e.split(".")[0]?.toUpperCase();return!(r&&/^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/.test(r))}function $(e){return Array.from(e).some(t=>{const r=t.charCodeAt(0);return r>=0&&r<=31})}function ee(e){return Array.isArray(e)?e.map(t=>({relativePath:String(t.relativePath||""),size:Number(t.size||0),mimeType:t.mimeType,modifiedAt:t.modifiedAt})):[]}async function de(e,t){const r=t.params.path||g.homedir(),a=t.params.rootPath||r;if(r===D){e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:D,entries:K()}});return}const n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}const s=n.path;try{const o=d.readdirSync(s,{withFileTypes:!0}),c=E(s)?p.win32.join:p.join,i=o.map(l=>({name:l.name,path:c(s,l.name),isDir:l.isDirectory()})).sort((l,f)=>l.isDir!==f.isDir?l.isDir?-1:1:l.name.localeCompare(f.name));e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:s,entries:i}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:Q(o,s)})}}async function ce(e,t){const r=t.params.path,a=t.params.rootPath||r,n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}const s=n.path,o=t.params.encoding,c=typeof o=="string"?o:"utf8",i=t.params.offset,l=t.params.length;try{const f=d.statSync(s);if(!f.isFile()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a file"});return}if(i!=null&&l!=null){const u=Math.min(l,Math.max(0,f.size-i));if(u<=0){e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:"",offset:i,length:0,totalSize:f.size,path:s}});return}const P=d.openSync(s,"r");try{const k=Buffer.alloc(u),S=d.readSync(P,k,0,u,i);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:k.subarray(0,S).toString("base64"),offset:i,length:S,totalSize:f.size,path:s}})}finally{d.closeSync(P)}return}const y=t.params.maxSize||(c==="base64"?5*1024*1024:512*1024);if(f.size>y){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`File too large: ${f.size} bytes (max ${y})`,payload:{size:f.size,path:s}});return}if(c==="base64"){const u=d.readFileSync(s);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{data:u.toString("base64"),path:s,size:f.size}});return}if(W(c)){const u=d.readFileSync(s),P=H(u);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{content:P.content,path:s,size:f.size,encoding:P.encoding,encodingFallback:P.fallback}});return}const R=d.readFileSync(s,"utf-8");e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{content:R,path:s,size:f.size}})}catch(f){e.client.sendRes({type:"res",id:t.id,ok:!1,error:f instanceof Z?f.message:String(f)})}}async function le(e,t){const r=t.params.path,a=t.params.content,n=t.params.rootPath||r;if(!r||typeof a!="string"){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path and content are required"});return}const s=e.resolveAuthorizedPath(r,n);if(!s.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s.error});return}try{const o=d.existsSync(s.path)?d.statSync(s.path):null;if(o&&!o.isFile()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a file"});return}d.writeFileSync(s.path,a,"utf-8");const c=d.statSync(s.path);e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:s.path,size:c.size,modifiedAt:c.mtimeMs}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(o)})}}async function fe(e,t){const r=t.params.path,a=t.params.newName;if(!r||!a){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path and newName are required"});return}if(!M(a)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid newName"});return}const n=v(r),s=t.params.rootPath||n.dirname(r),o=e.resolveAuthorizedPath(r,s);if(!o.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:o.error});return}try{const c=v(o.path),i=c.join(c.dirname(o.path),a),l=e.resolveAuthorizedPath(i,s);if(!l.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:l.error});return}if(d.existsSync(l.path)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Target already exists"});return}d.renameSync(o.path,l.path),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{oldPath:o.path,newPath:l.path,entry:x(l.path)}})}catch(c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(c)})}}async function pe(e,t){const r=t.params.path,a=t.params.rootPath||p.dirname(r||"."),n=typeof t.params.title=="string"?t.params.title:void 0;if(!r){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path is required"});return}const s=e.resolveAuthorizedPath(r,a);if(!s.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s.error});return}if(!/\.mdx?$/i.test(s.path)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Only Markdown files can be exported to PDF"});return}const o=j(s.path),c=e.resolveAuthorizedPath(o,a);if(!c.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:c.error});return}try{const i=await _(s.path,{outputPath:c.path,title:n});e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{sourcePath:s.path,outputPath:i.outputPath,entry:x(i.outputPath)}})}catch(i){if(i instanceof U){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"This machine needs the PDF export component before Markdown files can be exported to PDF.",payload:i.setup});return}e.client.sendRes({type:"res",id:t.id,ok:!1,error:i instanceof Error?i.message:String(i)})}}async function he(e,t){try{await L();const r=await O();e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{ready:!0,browserPath:r,managedPath:B()}})}catch(r){e.client.sendRes({type:"res",id:t.id,ok:!1,error:r instanceof Error?r.message:String(r)})}}function te(e){let t="";for(const a of e.trim())a.charCodeAt(0)<=31||'<>:"/\\|?*'.includes(a)?t.endsWith("-")||(t+="-"):t+=a;return t.replace(/[. ]+$/g,"")||"folder"}async function ye(e,t){const r=t.params.path,a=t.params.rootPath||r;if(!r){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"path is required"});return}const n=e.resolveAuthorizedPath(r,a);if(!n.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:n.error});return}try{if(!d.statSync(n.path).isDirectory()){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Not a directory"});return}const o=v(n.path),c=te(o.basename(n.path)),i=p.join(g.tmpdir(),"shennian-archives"),l=p.join(i,`${c}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),f=X(n.path,l,{maxFiles:G,maxTotalSize:J});e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{sourcePath:n.path,outputPath:f.outputPath,entry:x(f.outputPath),fileCount:f.fileCount,totalSize:f.totalSize}})}catch(s){e.client.sendRes({type:"res",id:t.id,ok:!1,error:s instanceof Error?s.message:String(s)})}}async function ue(e,t){const{name:r,targetPath:a,data:n,direct:s}=t.params;if(!r||!n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"name and data are required"});return}try{const o=t.params.rootPath||a||g.homedir(),c=e.resolveAuthorizedPath(a||o,o);if(!c.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:c.error});return}const i=c.path,l=s?i:p.join(i,".uploads");s||d.mkdirSync(l,{recursive:!0});const f=p.join(l,p.basename(r)),y=Buffer.from(n,"base64");d.writeFileSync(f,y),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:f}})}catch(o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(o)})}}async function Pe(e,t){const{name:r,targetPath:a,totalSize:n,direct:s,kind:o,baseName:c}=t.params,i=ee(t.params.manifest),l=o==="folder",f=l&&c||r;if(!f||!l&&!n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"name and totalSize are required"});return}try{const y=t.params.rootPath||a||g.homedir(),R=e.resolveAuthorizedPath(a||y,y);if(!R.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:R.error});return}const u=R.path,P=s?u:p.join(u,".uploads");s||d.mkdirSync(P,{recursive:!0});const k=`tf-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;if(l){if(!i.length){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"manifest is required for folder uploads"});return}if(i.length>V){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Too many files: ${i.length}`});return}const T=p.basename(f);if(!M(T)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid folder name"});return}const C=p.join(P,T),m=e.resolveAuthorizedPath(C,y);if(!m.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:m.error});return}const b=new Set;let z=0;const A=new Map;for(const h of i){if(!q(h.relativePath)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Invalid relativePath: ${h.relativePath}`});return}if(!Number.isFinite(h.size)||h.size<0){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Invalid size: ${h.relativePath}`});return}if(b.has(h.relativePath)){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Duplicate relativePath: ${h.relativePath}`});return}if(b.add(h.relativePath),z+=h.size,z>Y){e.client.sendRes({type:"res",id:t.id,ok:!1,error:`Folder too large: ${z} bytes`});return}const I=p.join(m.path,...h.relativePath.split("/")),F=e.resolveAuthorizedPath(I,y);if(!F.ok){e.client.sendRes({type:"res",id:t.id,ok:!1,error:F.error});return}const w=p.join(g.tmpdir(),`.shennian-upload-${k}-${A.size}`);d.writeFileSync(w,Buffer.alloc(0)),A.set(h.relativePath,{relativePath:h.relativePath,tempPath:w,targetPath:F.path,size:h.size})}e.pendingTransfers.set(k,{tempPath:"",targetPath:m.path,totalSize:z,kind:"folder",rootPath:y,targetDir:m.path,files:A}),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{transferId:k,path:m.path}});return}const S=p.join(g.tmpdir(),`.shennian-upload-${k}`),N=p.join(P,p.basename(f));d.writeFileSync(S,Buffer.alloc(0)),e.pendingTransfers.set(k,{tempPath:S,targetPath:N,totalSize:n,kind:"file"}),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{transferId:k}})}catch(y){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(y)})}}async function ke(e,t){const{transferId:r,offset:a,data:n,relativePath:s}=t.params,o=e.pendingTransfers.get(r);if(!o){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid transferId"});return}try{const c=o.kind==="folder"?o.files?.get(String(s||"")):{tempPath:o.tempPath,size:o.totalSize};if(!c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid relativePath"});return}const i=Buffer.from(n,"base64");if(a<0||a+i.length>c.size){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Chunk exceeds declared size"});return}const l=d.openSync(c.tempPath,"r+");try{d.writeSync(l,i,0,i.length,a)}finally{d.closeSync(l)}e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{written:i.length}})}catch(c){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(c)})}}async function ge(e,t){const{transferId:r}=t.params,a=e.pendingTransfers.get(r);if(!a){e.client.sendRes({type:"res",id:t.id,ok:!1,error:"Invalid transferId"});return}try{if(a.kind==="folder"){let n=0;for(const s of a.files?.values()??[])d.mkdirSync(p.dirname(s.targetPath),{recursive:!0}),d.renameSync(s.tempPath,s.targetPath),n+=1;e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetDir??a.targetPath,count:n}});return}d.renameSync(a.tempPath,a.targetPath),e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetPath}})}catch{try{d.copyFileSync(a.tempPath,a.targetPath),d.unlinkSync(a.tempPath),e.pendingTransfers.delete(r),e.client.sendRes({type:"res",id:t.id,ok:!0,payload:{path:a.targetPath}})}catch(n){e.client.sendRes({type:"res",id:t.id,ok:!1,error:String(n)})}}}async function me(e,t){const{transferId:r}=t.params,a=e.pendingTransfers.get(r);if(a){const n=a.kind==="folder"?Array.from(a.files?.values()??[]).map(s=>s.tempPath):[a.tempPath];for(const s of n)try{s&&d.unlinkSync(s)}catch{}e.pendingTransfers.delete(r)}e.client.sendRes({type:"res",id:t.id,ok:!0})}function Re(e){for(const[,t]of e.pendingTransfers){const r=t.kind==="folder"?Array.from(t.files?.values()??[]).map(a=>a.tempPath):[t.tempPath];for(const a of r)try{a&&d.unlinkSync(a)}catch{}}e.pendingTransfers.clear()}export{Re as cleanupPendingTransfers,ye as handleFsArchiveZip,pe as handleFsExportMarkdownPdf,he as handleFsExportMarkdownPdfSetup,de as handleFsLs,ce as handleFsRead,fe as handleFsRename,ue as handleFsTransfer,me as handleFsTransferAbort,ke as handleFsTransferChunk,ge as handleFsTransferFinish,Pe as handleFsTransferStart,le as handleFsWrite};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReqFrame } from '@shennian/wire';
|
|
2
|
+
import type { ManagedRoomBinder } from '../../room/managed-room-binding.js';
|
|
3
|
+
import type { SessionManagerRuntime } from '../types.js';
|
|
4
|
+
export declare function handleManagedRoomBind(runtime: SessionManagerRuntime, binder: ManagedRoomBinder | null, req: ReqFrame): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const m=/^[A-Za-z0-9_.:@/-]{1,191}$/,c=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId"]);async function p(n,e,t){if(!e)throw new Error("Managed Room binding is unavailable.");if(Object.keys(t.params).some(g=>!c.has(g)))throw new Error("Managed Room binding contains unsupported parameters.");const i=o(t.params.roomId,"roomId"),d=o(t.params.agentIdentityId,"agentIdentityId"),a=o(t.params.nianSessionId,"nianSessionId"),r=o(t.params.assignmentIntentId,"assignmentIntentId"),s=n.sessions.get(a);if(!s?.agentSessionId)throw new Error("The exact native Agent session is not ready for Room binding.");const I=await e.bind({roomId:i,agentIdentityId:d,nianSessionId:a,assignmentIntentId:r,agentSessionId:s.agentSessionId,workDir:s.workDir,agentType:s.agentType});n.client.sendRes({type:"res",id:t.id,ok:!0,payload:I})}function o(n,e){if(typeof n!="string"||!m.test(n))throw new Error(`Invalid ${e}.`);return n}export{p as handleManagedRoomBind};
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import type { ReqFrame } from '@shennian/wire';
|
|
1
|
+
import type { AgentType, ReqFrame } from '@shennian/wire';
|
|
2
2
|
import type { CliRelayClient } from '../relay/client.js';
|
|
3
|
+
import type { UpgradePolicyController } from './types.js';
|
|
4
|
+
import type { UpgradeIdleState } from '../upgrade/scheduler.js';
|
|
3
5
|
import type { NativeSessionFusionService } from '../native-fusion/service.js';
|
|
4
6
|
import '../agents/claude.js';
|
|
5
7
|
import '../agents/codex.js';
|
|
8
|
+
import '../agents/workbuddy.js';
|
|
6
9
|
import '../agents/gemini.js';
|
|
7
10
|
import '../agents/cursor.js';
|
|
8
11
|
import '../agents/opencode.js';
|
|
9
|
-
import '../agents/pi.js';
|
|
10
12
|
import '../agents/manager.js';
|
|
13
|
+
import type { ManagedRoomBinder } from '../room/managed-room-binding.js';
|
|
11
14
|
export { resolveSessionWorkDir } from '../fs/boundary.js';
|
|
12
15
|
export declare class SessionManager {
|
|
13
16
|
private client;
|
|
14
17
|
private nativeFusion;
|
|
15
18
|
private cliVersion?;
|
|
19
|
+
private upgradePolicyController;
|
|
20
|
+
private managedRoomBinder;
|
|
16
21
|
private sessions;
|
|
17
22
|
/** Track processed request IDs to deduplicate replayed offline messages */
|
|
18
23
|
private processedReqIds;
|
|
@@ -23,13 +28,20 @@ export declare class SessionManager {
|
|
|
23
28
|
private managerRuntime;
|
|
24
29
|
private chatQueue;
|
|
25
30
|
private activityProbeTimer;
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined);
|
|
31
|
+
private activeRequests;
|
|
32
|
+
constructor(client: CliRelayClient, nativeFusion?: NativeSessionFusionService | null, cliVersion?: string | undefined, upgradePolicyController?: UpgradePolicyController | null, managedRoomBinder?: ManagedRoomBinder | null);
|
|
29
33
|
private getRuntime;
|
|
34
|
+
getUpgradeIdleState(): UpgradeIdleState;
|
|
35
|
+
activateRoomSession(input: {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
agentType: AgentType;
|
|
38
|
+
agentSessionId: string;
|
|
39
|
+
workDir: string;
|
|
40
|
+
text: string;
|
|
41
|
+
clientMessageId: string;
|
|
42
|
+
}): Promise<void>;
|
|
30
43
|
private publishSessionActivity;
|
|
31
44
|
private publishManagedActivitySnapshots;
|
|
32
|
-
private publishWeChatRuntimeStatus;
|
|
33
45
|
private reloadCustomAgents;
|
|
34
46
|
handleReq(req: ReqFrame): Promise<void>;
|
|
35
47
|
private evictIdleSessions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getRegisteredAgents as
|
|
1
|
+
import{getRegisteredAgents as c,unregisterAgent as h}from"../agents/adapter.js";import{loadConfig as l}from"../config/index.js";import{handleUpgradeStart as d,handleUpgradeStatus as u}from"../commands/upgrade.js";import{handleAgentsRefresh as m,handleModelsRefresh as p}from"./handlers/agents.js";import{handleAgentCapabilitiesList as f}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as g,handleAgentConfigGet as b,handleAgentConfigTest as w,handleAgentConfigUpsert as v}from"./handlers/agent-config.js";import{handleChatAbort as k,handleChatSend as r}from"./handlers/chat.js";import{handleSessionRefresh as R}from"./handlers/session-refresh.js";import{handleSessionToolDetail as S}from"./handlers/tool-detail.js";import{handleSessionTitleSet as y}from"./handlers/title.js";import{cleanupPendingTransfers as A,handleFsLs as T,handleFsRead as P,handleFsRename as I,handleFsWrite as C,handleFsTransfer as F,handleFsTransferAbort as M,handleFsTransferChunk as x,handleFsTransferFinish as Q,handleFsTransferStart as z,handleFsExportMarkdownPdf as D,handleFsExportMarkdownPdfSetup as E,handleFsArchiveZip as U}from"./handlers/fs.js";import{handleRegionProbe as V,handleRegionSwitch as B,handleUpgradeSetPolicy as O}from"./handlers/control.js";import{ManagerRuntimeService as W,setManagerRuntimeService as o}from"../manager/runtime.js";import{ChatQueueManager as _}from"./queue.js";import{createAuthorizedFsRoot as j,resolveAuthorizedPath as L,resolveSessionWorkDir as $}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/manager.js";import{registerCustomAgent as G}from"../agents/custom.js";import{handleManagedRoomBind as N}from"./handlers/room-managed.js";const X=50;import{resolveSessionWorkDir as Se}from"../fs/boundary.js";class ve{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;activityProbeTimer=null;activeRequests=0;constructor(e,t=null,s,i=null,a=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=i,this.managedRoomBinder=a,this.managerRuntime=new W({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),this.chatQueue=new _({getRuntime:()=>this.getRuntime(),dispatchReq:n=>this.handleReq(n)}),o(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(n=>{console.error("[session.activity] managed probe failed",n)})},15e3),this.activityProbeTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}async activateRoomSession(e){const t=new Proxy(this.client,{get:(s,i)=>{if(i==="sendRes")return()=>{};const a=Reflect.get(s,i,s);return typeof a=="function"?a.bind(s):a}});await r({...this.getRuntime(),client:t},{type:"req",id:e.clientMessageId,method:"chat.send",params:{...e,waitForDispatch:!0}})}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const i=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:i,canStop:s.canStop??!0});const a=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:a,runPhase:s.runPhase,canStop:s.canStop??!0},seq:a,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of c())t.startsWith("custom:")&&h(t);const e=l();for(const[t,s]of Object.entries(e.customAgents??{}))G(t,s)}async handleReq(e){const t=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":await r(t,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await k(t,e);break;case"session.refresh":await R(t,e);break;case"session.tool.detail":await S(t,e);break;case"session.title.set":await y(t,e);break;case"room.agent.bind-managed":await N(t,this.managedRoomBinder,e);break;case"fs.ls":await T(t,e);break;case"fs.read":await P(t,e);break;case"fs.write":await C(t,e);break;case"fs.export.markdown-pdf":await D(t,e);break;case"fs.export.markdown-pdf.setup":await E(t,e);break;case"fs.archive.zip":await U(t,e);break;case"fs.rename":await I(t,e);break;case"fs.transfer":await F(t,e);break;case"fs.transfer.start":await z(t,e);break;case"fs.transfer.chunk":await x(t,e);break;case"fs.transfer.finish":await Q(t,e);break;case"fs.transfer.abort":await M(t,e);break;case"region.probe":await V(t,e);break;case"region.switch":await B(t,e);break;case"upgrade.start":await d(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await u(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await O(t,e);break;case"agents.refresh":await m(t,e);break;case"models.refresh":await p(t,e);break;case"agent.config.get":await b(t,e);break;case"agent.config.upsert":await v(t,e);break;case"agent.config.clear":await g(t,e);break;case"agent.config.test":await w(t,e);break;case"agent.capabilities.list":await f(t,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(s){this.client.sendRes({type:"res",id:e.id,ok:!1,error:s instanceof Error?s.message:String(s)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<X)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return $(e)}resolveAuthorizedPath(e,t){return L(e,j(t))}async cleanup(){this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null);for(const[,e]of this.sessions)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),A(this.getRuntime()),await this.managerRuntime.stop(),o(null)}}export{ve as SessionManager,Se as resolveSessionWorkDir};
|
|
@@ -51,6 +51,10 @@ export type ChatQueueService = {
|
|
|
51
51
|
export type SessionActivityPublisher = {
|
|
52
52
|
publish(sessionId: string, activity: import('@shennian/wire').SessionActivitySnapshot | null): void;
|
|
53
53
|
};
|
|
54
|
+
export type UpgradePolicyController = {
|
|
55
|
+
setPolicy(policy: 'none' | 'patch'): void;
|
|
56
|
+
getPolicy(): 'none' | 'patch';
|
|
57
|
+
};
|
|
54
58
|
export type SessionManagerRuntime = {
|
|
55
59
|
client: CliRelayClient;
|
|
56
60
|
pendingTransfers: Map<string, PendingTransfer>;
|
|
@@ -65,4 +69,5 @@ export type SessionManagerRuntime = {
|
|
|
65
69
|
managerRuntime: ManagerRuntimeService | null;
|
|
66
70
|
chatQueue: ChatQueueService | null;
|
|
67
71
|
activityPublisher?: SessionActivityPublisher | null;
|
|
72
|
+
upgradePolicyController?: UpgradePolicyController | null;
|
|
68
73
|
};
|
|
@@ -54,10 +54,17 @@ export declare function fetchLatestVersion(): Promise<string>;
|
|
|
54
54
|
* 'major' — major differs
|
|
55
55
|
*/
|
|
56
56
|
export declare function compareVersions(current: string, latest: string): 'none' | 'patch' | 'minor' | 'major';
|
|
57
|
-
export declare function copyPackageRuntimeFiles(src: string, dest: string): void;
|
|
58
57
|
export declare function readUpgradeAttempt(): UpgradeAttempt | null;
|
|
59
58
|
export declare function writeUpgradeAttempt(attempt: UpgradeAttempt): void;
|
|
60
59
|
export declare function clearUpgradeAttempt(): void;
|
|
60
|
+
export type UpgradeLock = {
|
|
61
|
+
release(): void;
|
|
62
|
+
};
|
|
63
|
+
export declare function acquireUpgradeLock(filePath?: string, opts?: {
|
|
64
|
+
now?: number;
|
|
65
|
+
pid?: number;
|
|
66
|
+
isProcessAlive?: (pid: number) => boolean;
|
|
67
|
+
}): UpgradeLock;
|
|
61
68
|
export declare function readUpgradeFailures(filePath?: string): UpgradeFailures;
|
|
62
69
|
export declare function getUpgradeRetryDelayMs(failureCount: number): number;
|
|
63
70
|
export declare function isUpgradeVersionInCooldown(version: string, now?: number, failures?: UpgradeFailures): boolean;
|
|
@@ -72,6 +79,12 @@ export declare function handleStartupCrashCheck(): Promise<boolean>;
|
|
|
72
79
|
export declare function performUpgrade(targetVersion: string, onProgress: (p: UpgradeProgress) => void, opts?: {
|
|
73
80
|
currentVersion?: string;
|
|
74
81
|
}): Promise<UpgradeResult>;
|
|
82
|
+
export declare function installShennianVersion(targetVersion: string): Promise<void>;
|
|
83
|
+
export declare function verifyInstalledVersion(targetVersion: string): Promise<void>;
|
|
84
|
+
export declare function rollbackToExactVersion(version: string, deps?: {
|
|
85
|
+
install?: (version: string) => Promise<void>;
|
|
86
|
+
verify?: (version: string) => Promise<void>;
|
|
87
|
+
}): Promise<void>;
|
|
75
88
|
export type VersionCheckResult = {
|
|
76
89
|
hasUpdate: false;
|
|
77
90
|
} | {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import i from"node:fs";import
|
|
1
|
+
import i from"node:fs";import p from"node:path";import{execSync as b,execFile as T}from"node:child_process";import{promisify as D}from"node:util";import{fileURLToPath as L}from"node:url";import{getShennianDir as F,resolveShennianPath as d}from"../config/index.js";const f=D(T),m=d("upgrade-attempt.json"),O=3,J="https://registry.npmjs.org",H=10*6e4,$=[5*6e4,30*6e4,120*6e4,360*6e4];function y(){try{const r=I();if(r)return JSON.parse(i.readFileSync(r,"utf-8")).version}catch{}return"0.0.0"}function I(r=L(import.meta.url)){let e=p.dirname(r);for(let n=0;n<6;n++){const t=p.join(e,"package.json");if(i.existsSync(t)&&JSON.parse(i.readFileSync(t,"utf-8")).name==="shennian")return t;e=p.dirname(e)}return null}async function B(){const r=await fetch("https://registry.npmjs.org/shennian/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(1e4)});if(!r.ok)throw new Error(`npm registry returned ${r.status}`);return(await r.json()).version}function G(r,e){const n=u=>u.replace(/^v/,"").split(".").map(Number),[t,s,o]=n(r),[a,l,c]=n(e);return a>t?"major":a===t&&l>s?"minor":a===t&&l===s&&c>o?"patch":"none"}function P(){return b("npm root -g",{encoding:"utf-8",stdio:"pipe",windowsHide:!0}).trim()}function Y(){const r=P();return p.join(r,"shennian","dist","bin","shennian.js")}function z(){try{return JSON.parse(i.readFileSync(m,"utf-8"))}catch{return null}}function g(r){i.mkdirSync(F(),{recursive:!0}),i.writeFileSync(m,JSON.stringify(r,null,2))}function h(){try{i.unlinkSync(m)}catch{}}function U(){return d("upgrade-failures.json")}function K(){return d("upgrade.lock")}function _(r=K(),e={}){const n=e.now??Date.now(),t=e.pid??process.pid,s=e.isProcessAlive??(c=>{try{return process.kill(c,0),!0}catch{return!1}});i.mkdirSync(p.dirname(r),{recursive:!0});const o=()=>i.openSync(r,"wx",384);let a;try{a=o()}catch(c){if(c.code!=="EEXIST")throw c;let u=!1;try{const A=JSON.parse(i.readFileSync(r,"utf-8")),v=Number(A.pid),E=Number(A.createdAt);u=!Number.isInteger(v)||!Number.isFinite(E)||n-E>H||!s(v)}catch{u=!0}if(!u)throw new Error("Another Shennian upgrade is already running");i.rmSync(r,{force:!0}),a=o()}i.writeFileSync(a,JSON.stringify({pid:t,createdAt:n})),i.closeSync(a);let l=!1;return{release(){if(!l){l=!0;try{i.unlinkSync(r)}catch{}}}}}function w(r=U()){try{const e=JSON.parse(i.readFileSync(r,"utf-8"));return!e||typeof e!="object"||!e.versions?{versions:{}}:{versions:e.versions}}catch{return{versions:{}}}}function N(r){i.mkdirSync(F(),{recursive:!0}),i.writeFileSync(U(),JSON.stringify(r,null,2))}function X(r){return $[Math.min(Math.max(r-1,0),$.length-1)]}function tr(r,e=Date.now(),n=w()){const t=n.versions[r];return!!(t&&t.nextRetryAt>e)}function nr(r,e,n=Date.now()){const t=w(),s=t.versions[r],o=(s?.failureCount??0)+1,a={version:r,failureCount:o,firstFailedAt:s?.firstFailedAt??n,lastFailedAt:n,nextRetryAt:n+X(o),error:e};return t.versions[r]=a,N(t),a}function k(r){const e=w();e.versions[r]&&(delete e.versions[r],N(e))}async function or(){const r=z();if(!r)return!1;if(Date.now()-r.attemptAt>600*1e3)return h(),!1;if(r.attemptCount++,r.attemptCount>=O){console.error(`[upgrade] New version crashed ${r.attemptCount} times, rolling back to ${r.from}...`);let n;try{n=_()}catch(t){return console.error(`[upgrade] Rollback deferred: ${t instanceof Error?t.message:String(t)}`),!1}try{return await j(r.from),h(),console.error(`[upgrade] Rolled back to ${r.from}. Restarting...`),!0}catch(t){return console.error(`[upgrade] Rollback failed: ${t instanceof Error?t.message:String(t)}`),h(),!1}finally{n.release()}}return g(r),!1}async function ar(r,e,n={}){let t;try{t=_()}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}try{return await Z(r,e,n)}finally{t.release()}}async function Z(r,e,n={}){const t=n.currentVersion??y(),s=y();if(t===r)return k(r),{ok:!1,error:`Already on version ${r}`};e({step:"checking"});try{b("npm --version",{stdio:"pipe",windowsHide:!0})}catch{return{ok:!1,error:"npm is not available in PATH"}}if(s===r){e({step:"verifying",version:r});try{await x(r)}catch(o){return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}`}}return g({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}e({step:"backing-up"}),e({step:"installing",version:r});try{await C(r)}catch(o){const a=await M(t);return{ok:!1,error:`npm install failed: ${o instanceof Error?o.message:String(o)}${a.ok?"":`; rollback failed: ${a.error}`}`,rolledBack:a.ok}}e({step:"verifying",version:r});try{await x(r)}catch(o){const a=await M(t);return{ok:!1,error:`Smoke test failed: ${o instanceof Error?o.message:String(o)}${a.ok?"":`; rollback failed: ${a.error}`}`,rolledBack:a.ok}}return g({from:t,to:r,attemptCount:0,attemptAt:Date.now()}),k(r),e({step:"restarting",from:t,to:r}),{ok:!0,from:t,to:r}}function R(r){if(!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(r))throw new Error(`Invalid npm version: ${r}`)}function S(){return process.platform==="win32"?"npm.cmd":"npm"}async function C(r){R(r);try{await f(S(),["install","-g",`shennian@${r}`],{timeout:12e4,windowsHide:!0})}catch(e){const n=e instanceof Error?e.message:String(e);try{await f(S(),["install","-g",`shennian@${r}`,"--registry",J],{timeout:12e4,windowsHide:!0})}catch(t){const s=t instanceof Error?t.message:String(t);throw new Error(`${n}
|
|
2
2
|
--- npmjs fallback ---
|
|
3
|
-
${s}`)}}}async function
|
|
3
|
+
${s}`)}}}async function x(r){R(r);const e=Y(),{stdout:n}=await f(process.execPath,[e,"--version"],{timeout:1e4,windowsHide:!0});if(n.trim()!==r)throw new Error(`Version verification failed: expected ${r}, got ${n.trim()||"empty output"}`);await f(S(),["ls","-g",`shennian@${r}`,"--omit=dev","--depth=0"],{timeout:3e4,windowsHide:!0})}async function j(r,e={}){await(e.install??C)(r),await(e.verify??x)(r)}async function M(r){try{return await j(r),{ok:!0}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async function ir(r){const e=r??y(),n=await B(),t=G(e,n);return t==="none"?{hasUpdate:!1}:{hasUpdate:!0,current:e,latest:n,changeType:t}}export{_ as acquireUpgradeLock,ir as checkForUpdate,h as clearUpgradeAttempt,k as clearUpgradeFailure,G as compareVersions,B as fetchLatestVersion,I as findPackageJson,y as getCurrentVersion,X as getUpgradeRetryDelayMs,or as handleStartupCrashCheck,C as installShennianVersion,tr as isUpgradeVersionInCooldown,ar as performUpgrade,z as readUpgradeAttempt,w as readUpgradeFailures,nr as recordUpgradeFailure,j as rollbackToExactVersion,x as verifyInstalledVersion,g as writeUpgradeAttempt};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { RuntimeAutoUpgradePolicy } from '../config/index.js';
|
|
2
|
+
import { type VersionCheckResult } from './engine.js';
|
|
3
|
+
export type UpgradeIdleState = {
|
|
4
|
+
idle: boolean;
|
|
5
|
+
reasons: string[];
|
|
6
|
+
};
|
|
7
|
+
export type CliReleasePolicy = {
|
|
8
|
+
automaticPatchEnabled: boolean;
|
|
9
|
+
rolloutPercentage: number;
|
|
10
|
+
pausedVersions: string[];
|
|
11
|
+
blockedVersions: string[];
|
|
12
|
+
killSwitch: boolean;
|
|
13
|
+
};
|
|
14
|
+
type AutoUpgradeSchedulerDeps = {
|
|
15
|
+
currentVersion: string;
|
|
16
|
+
machineId: string;
|
|
17
|
+
serverUrl: string;
|
|
18
|
+
getIdleState: () => UpgradeIdleState;
|
|
19
|
+
startUpgrade: (targetVersion: string) => Promise<void>;
|
|
20
|
+
checkForUpdate?: (currentVersion?: string) => Promise<VersionCheckResult>;
|
|
21
|
+
fetchReleasePolicy?: () => Promise<CliReleasePolicy>;
|
|
22
|
+
inCooldown?: (version: string) => boolean;
|
|
23
|
+
initialDelayMs?: number;
|
|
24
|
+
intervalMs?: number;
|
|
25
|
+
setTimeout?: (handler: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
|
|
26
|
+
clearTimeout?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
27
|
+
};
|
|
28
|
+
export declare function deterministicRolloutBucket(machineId: string): number;
|
|
29
|
+
export declare function isAutomaticPatchAllowed(policy: CliReleasePolicy, machineId: string, currentVersion: string, targetVersion: string): boolean;
|
|
30
|
+
export declare function fetchCliReleasePolicy(serverUrl: string): Promise<CliReleasePolicy>;
|
|
31
|
+
export declare class AutoUpgradeScheduler {
|
|
32
|
+
private deps;
|
|
33
|
+
private policy;
|
|
34
|
+
private timer;
|
|
35
|
+
private started;
|
|
36
|
+
private upgradeInFlight;
|
|
37
|
+
constructor(deps: AutoUpgradeSchedulerDeps);
|
|
38
|
+
start(): void;
|
|
39
|
+
stop(): void;
|
|
40
|
+
setPolicy(policy: RuntimeAutoUpgradePolicy): void;
|
|
41
|
+
getPolicy(): RuntimeAutoUpgradePolicy;
|
|
42
|
+
runOnce(): Promise<void>;
|
|
43
|
+
private arm;
|
|
44
|
+
}
|
|
45
|
+
export {};
|