shennian 0.3.1 → 0.3.2
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 +273 -343
- 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/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/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 +23 -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/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-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 +208 -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 +114 -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/manager.d.ts +16 -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/index.d.ts +2 -1
- package/node_modules/@shennian/wire/dist/index.d.ts.map +1 -1
- package/node_modules/@shennian/wire/dist/index.js +2 -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 +2 -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,143 @@
|
|
|
1
|
+
import type { RoomActivationMode, RoomBindingStatus, RoomRuntimeAdapter } from '@shennian/wire';
|
|
2
|
+
export type ManagedRoomSessionAgent = 'codex' | 'claude' | 'workbuddy';
|
|
3
|
+
export type ManagedRoomSessionInput = {
|
|
4
|
+
inviteUrl: string;
|
|
5
|
+
agent: ManagedRoomSessionAgent;
|
|
6
|
+
workDir: string;
|
|
7
|
+
alias: string;
|
|
8
|
+
mode?: RoomActivationMode;
|
|
9
|
+
modelId?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ManagedRoomSessionPhase = 'initialized' | 'session_created' | 'native_session_ready' | 'session_registered' | 'joined';
|
|
12
|
+
export type ManagedRoomSessionFailureStage = 'validate_input' | 'create_session' | 'wait_for_native_session' | 'register_session' | 'join_room';
|
|
13
|
+
type NormalizedInput = Required<Pick<ManagedRoomSessionInput, 'inviteUrl' | 'agent' | 'workDir' | 'alias' | 'mode'>> & {
|
|
14
|
+
modelId: string | null;
|
|
15
|
+
};
|
|
16
|
+
export type ManagedRoomSessionRegistration = {
|
|
17
|
+
registryKey: string;
|
|
18
|
+
};
|
|
19
|
+
export type ManagedRoomJoinResult = {
|
|
20
|
+
outcome: 'joined' | 'pending_approval';
|
|
21
|
+
room: {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
webUrl: string;
|
|
25
|
+
};
|
|
26
|
+
binding: {
|
|
27
|
+
id: string;
|
|
28
|
+
agentIdentityId: string;
|
|
29
|
+
agentMembershipId: string | null;
|
|
30
|
+
status: RoomBindingStatus;
|
|
31
|
+
activationMode: RoomActivationMode;
|
|
32
|
+
};
|
|
33
|
+
idempotent: boolean;
|
|
34
|
+
};
|
|
35
|
+
export type ManagedRoomSessionOperation = {
|
|
36
|
+
schemaVersion: 1;
|
|
37
|
+
operationId: string;
|
|
38
|
+
inputFingerprint: string;
|
|
39
|
+
input: NormalizedInput;
|
|
40
|
+
runtimeAdapter: RoomRuntimeAdapter;
|
|
41
|
+
phase: ManagedRoomSessionPhase;
|
|
42
|
+
nianSessionId: string | null;
|
|
43
|
+
sourceSessionKey: string | null;
|
|
44
|
+
hostProcessIdentity: string | null;
|
|
45
|
+
registryKey: string | null;
|
|
46
|
+
result: ManagedRoomJoinResult | null;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
};
|
|
50
|
+
export interface ManagedRoomSessionOperationStore {
|
|
51
|
+
load(operationId: string): ManagedRoomSessionOperation | null;
|
|
52
|
+
save(operation: ManagedRoomSessionOperation): void;
|
|
53
|
+
}
|
|
54
|
+
export type ManagedRoomSessionDependencies = {
|
|
55
|
+
operations: ManagedRoomSessionOperationStore;
|
|
56
|
+
/**
|
|
57
|
+
* Must be idempotent for creationKey. A successful call followed by a daemon
|
|
58
|
+
* crash may be repeated before the operation record reaches disk.
|
|
59
|
+
*/
|
|
60
|
+
createManagedSession(input: {
|
|
61
|
+
creationKey: string;
|
|
62
|
+
agent: ManagedRoomSessionAgent;
|
|
63
|
+
workDir: string;
|
|
64
|
+
modelId: string | null;
|
|
65
|
+
initialMessage: string;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
nianSessionId: string;
|
|
68
|
+
}>;
|
|
69
|
+
/** Resolves only the native Session created for nianSessionId; never a recent Session. */
|
|
70
|
+
waitForNativeSession(input: {
|
|
71
|
+
nianSessionId: string;
|
|
72
|
+
runtimeAdapter: RoomRuntimeAdapter;
|
|
73
|
+
timeoutMs: number;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
sourceSessionKey: string;
|
|
76
|
+
hostProcessIdentity: string;
|
|
77
|
+
}>;
|
|
78
|
+
/** Persists the trusted observation and exact managed Session Registry record. */
|
|
79
|
+
registerManagedSession(input: {
|
|
80
|
+
nianSessionId: string;
|
|
81
|
+
runtimeAdapter: RoomRuntimeAdapter;
|
|
82
|
+
sourceSessionKey: string;
|
|
83
|
+
hostProcessIdentity: string;
|
|
84
|
+
workDir: string;
|
|
85
|
+
capabilities: {
|
|
86
|
+
canIdentifySession: true;
|
|
87
|
+
canBindAutomatically: true;
|
|
88
|
+
canOpenEmbeddedRoomPage: false;
|
|
89
|
+
canResumeIdleSession: true;
|
|
90
|
+
canActivateWhileHostOpen: true;
|
|
91
|
+
};
|
|
92
|
+
}): Promise<ManagedRoomSessionRegistration>;
|
|
93
|
+
/**
|
|
94
|
+
* Uses daemon-owned machine credentials internally. Public callers never
|
|
95
|
+
* supply Room, Binding, Identity, native Session, or machine credential IDs.
|
|
96
|
+
*/
|
|
97
|
+
joinRegisteredSession(input: {
|
|
98
|
+
bindingIntentId: string;
|
|
99
|
+
registryKey: string;
|
|
100
|
+
inviteUrl: string;
|
|
101
|
+
agentAlias: string;
|
|
102
|
+
activationMode: RoomActivationMode;
|
|
103
|
+
activationCapability: 'managed';
|
|
104
|
+
}): Promise<ManagedRoomJoinResult>;
|
|
105
|
+
allowedInviteOrigins?: string[];
|
|
106
|
+
nativeSessionTimeoutMs?: number;
|
|
107
|
+
now?: () => Date;
|
|
108
|
+
};
|
|
109
|
+
export declare class ManagedRoomSessionError extends Error {
|
|
110
|
+
readonly code: string;
|
|
111
|
+
readonly stage: ManagedRoomSessionFailureStage;
|
|
112
|
+
readonly operationId: string | null;
|
|
113
|
+
readonly phase: ManagedRoomSessionPhase;
|
|
114
|
+
readonly nianSessionId: string | null;
|
|
115
|
+
constructor(code: string, message: string, stage: ManagedRoomSessionFailureStage, operationId: string | null, phase: ManagedRoomSessionPhase, nianSessionId: string | null);
|
|
116
|
+
}
|
|
117
|
+
export declare class ManagedRoomSessionOrchestrator {
|
|
118
|
+
private readonly deps;
|
|
119
|
+
private readonly mutexTails;
|
|
120
|
+
constructor(deps: ManagedRoomSessionDependencies);
|
|
121
|
+
execute(operationId: string, rawInput: ManagedRoomSessionInput): Promise<{
|
|
122
|
+
operationId: string;
|
|
123
|
+
phase: 'joined';
|
|
124
|
+
nianSessionId: string;
|
|
125
|
+
result: ManagedRoomJoinResult;
|
|
126
|
+
}>;
|
|
127
|
+
private executeExclusive;
|
|
128
|
+
private save;
|
|
129
|
+
}
|
|
130
|
+
export declare class InMemoryManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
|
|
131
|
+
private readonly records;
|
|
132
|
+
load(operationId: string): ManagedRoomSessionOperation | null;
|
|
133
|
+
save(operation: ManagedRoomSessionOperation): void;
|
|
134
|
+
}
|
|
135
|
+
/** Private per-operation files allow daemon restart recovery without one operation overwriting another. */
|
|
136
|
+
export declare class FileManagedRoomSessionOperationStore implements ManagedRoomSessionOperationStore {
|
|
137
|
+
readonly directory: string;
|
|
138
|
+
constructor(directory: string);
|
|
139
|
+
load(operationId: string): ManagedRoomSessionOperation | null;
|
|
140
|
+
save(operation: ManagedRoomSessionOperation): void;
|
|
141
|
+
private filePath;
|
|
142
|
+
}
|
|
143
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import S from"node:crypto";import a from"node:fs";import y from"node:path";class h extends Error{code;stage;operationId;phase;nianSessionId;constructor(i,o,t,r,n,s){super(o),this.code=i,this.stage=t,this.operationId=r,this.phase=n,this.nianSessionId=s,this.name="ManagedRoomSessionError"}}const j=/^[A-Za-z0-9_-]{8,96}$/,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,w=/^[a-f0-9]{64}$/,k=/^[A-Za-z0-9_-]{43}$/,D=new Set(["inviteUrl","agent","workDir","alias","mode","modelId"]),K=new Set(["codex","claude","workbuddy"]),N=new Set(["auto_follow","mention_only","paused"]),P=["https://shennian.net"],$=3e4;class B{deps;mutexTails=new Map;constructor(i){this.deps=i}async execute(i,o){_(i);let t;const r=this.mutexTails.get(i)??Promise.resolve(),n=new Promise(l=>{t=l}),s=r.then(()=>n);this.mutexTails.set(i,s),await r;try{return await this.executeExclusive(i,o)}finally{t(),this.mutexTails.get(i)===s&&this.mutexTails.delete(i)}}async executeExclusive(i,o){let t;try{t=I(o,this.deps.allowedInviteOrigins??P)}catch(s){throw m(s,"validate_input",i,null)}const r=b(t);let n=this.deps.operations.load(i);if(n&&n.inputFingerprint!==r)throw new h("managed_room_operation_conflict","This operation ID was already used with different inputs.","validate_input",i,n.phase,n.nianSessionId);if(!n){const s=(this.deps.now?.()??new Date).toISOString();n={schemaVersion:1,operationId:i,inputFingerprint:r,input:t,runtimeAdapter:v(t.agent),phase:"initialized",nianSessionId:null,sourceSessionKey:null,hostProcessIdentity:null,registryKey:null,result:null,createdAt:s,updatedAt:s},this.save(n)}if(n.phase==="joined")return E(n);if(n.phase==="initialized")try{const s=await this.deps.createManagedSession({creationKey:`room-managed-session:${i}`,agent:t.agent,workDir:t.workDir,modelId:t.modelId,initialMessage:U()});c(s.nianSessionId,"nian_session_id"),n.nianSessionId=s.nianSessionId,n.phase="session_created",this.save(n)}catch(s){throw m(s,"create_session",i,n)}if(n.phase==="session_created")try{const s=await this.deps.waitForNativeSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,timeoutMs:this.deps.nativeSessionTimeoutMs??$});A(s.sourceSessionKey),c(s.hostProcessIdentity,"host_process_identity"),n.sourceSessionKey=s.sourceSessionKey,n.hostProcessIdentity=s.hostProcessIdentity,n.phase="native_session_ready",this.save(n)}catch(s){throw m(s,"wait_for_native_session",i,n)}if(n.phase==="native_session_ready")try{const s=await this.deps.registerManagedSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostProcessIdentity:n.hostProcessIdentity,workDir:t.workDir,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});if(!w.test(s.registryKey))throw u("registry_key");n.registryKey=s.registryKey,n.phase="session_registered",this.save(n)}catch(s){throw m(s,"register_session",i,n)}if(n.phase==="session_registered")try{n.result=await this.deps.joinRegisteredSession({bindingIntentId:`room-managed-session-join:${i}`,registryKey:n.registryKey,inviteUrl:t.inviteUrl,agentAlias:t.alias,activationMode:t.mode,activationCapability:"managed"}),O(n.result),n.phase="joined",this.save(n)}catch(s){throw m(s,"join_room",i,n)}return E(n)}save(i){i.updatedAt=(this.deps.now?.()??new Date).toISOString(),this.deps.operations.save(g(i))}}class G{records=new Map;load(i){const o=this.records.get(i);return o?g(o):null}save(i){this.records.set(i.operationId,g(i))}}class Z{directory;constructor(i){this.directory=i}load(i){_(i);const o=this.filePath(i);try{const t=a.lstatSync(o);if(!t.isFile()||t.isSymbolicLink())throw p();process.platform!=="win32"&&(t.mode&63)!==0&&a.chmodSync(o,384);const r=JSON.parse(a.readFileSync(o,"utf8"));return M(r,i),g(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}save(i){_(i.operationId),M(i,i.operationId),a.mkdirSync(this.directory,{recursive:!0,mode:448});const o=a.lstatSync(this.directory);if(!o.isDirectory()||o.isSymbolicLink())throw p();process.platform!=="win32"&&a.chmodSync(this.directory,448);const t=this.filePath(i.operationId),r=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;try{if(a.writeFileSync(r,`${JSON.stringify(i,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&a.chmodSync(r,384),process.platform==="win32"&&a.existsSync(t)){const n=a.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw p();a.unlinkSync(t)}a.renameSync(r,t)}finally{try{a.unlinkSync(r)}catch{}}}filePath(i){return y.join(this.directory,`${i}.json`)}}function I(e,i){(!e||typeof e!="object"||Array.isArray(e))&&d("input");const o=e;Object.keys(o).find(T=>!D.has(T))&&d("unsupported_field");const r=o.agent;(typeof r!="string"||!K.has(r))&&d("agent");const n=f(o.workDir,"work_dir",4096);(!y.isAbsolute(n)||y.normalize(n)!==n)&&d("work_dir");const s=f(o.alias,"alias",80),l=o.mode??"auto_follow";(typeof l!="string"||!N.has(l))&&d("mode");const x=o.modelId==null?null:f(o.modelId,"model_id",191);return{inviteUrl:F(o.inviteUrl,i),agent:r,workDir:n,alias:s,mode:l,modelId:x}}function F(e,i){const o=f(e,"invite_url",2048);let t;try{t=new URL(o)}catch{d("invite_url")}(!new Set(i.map(l=>new URL(l).origin)).has(t.origin)||t.username||t.password||t.search||t.hash)&&d("invite_url");const n=t.pathname.split("/").filter(Boolean),s=n.at(-1);return(!s||!k.test(s)||n.at(-2)!=="join")&&d("invite_url"),t.toString()}function f(e,i,o){typeof e!="string"&&d(i);const t=e.trim();return(!t||t.length>o||/[\0\r\n]/.test(t))&&d(i),t}function _(e){if(!j.test(e))throw new h("invalid_managed_room_operation_id","Managed Room operation ID is invalid.","validate_input",null,"initialized",null)}function v(e){return e==="claude"?"claude_code":e}function b(e){return S.createHash("sha256").update(JSON.stringify(e),"utf8").digest("hex")}function c(e,i){if(!R.test(e))throw u(i)}function A(e){if(!e||e.length>1024||/[\0\r\n]/.test(e))throw u("source_session_key")}function O(e){if(c(e.room?.id,"room_id"),c(e.binding?.id,"binding_id"),c(e.binding?.agentIdentityId,"agent_identity_id"),e.binding.agentMembershipId!==null&&c(e.binding.agentMembershipId,"agent_membership_id"),e.outcome!=="joined"&&e.outcome!=="pending_approval")throw u("join_outcome");if(e.outcome==="joined"&&e.binding.status!=="active"&&e.binding.status!=="paused"||e.outcome==="pending_approval"&&e.binding.status!=="pending_approval"||e.binding.status==="pending_approval"&&e.binding.agentMembershipId!==null||(e.binding.status==="active"||e.binding.status==="paused")&&e.binding.agentMembershipId===null)throw u("join_binding_state")}function U(){return["\u8FD9\u662F\u4E00\u4E2A\u7531\u795E\u5FF5\u521B\u5EFA\u7684\u53D7\u7BA1 Agent \u5BF9\u8BDD\u3002","\u8BF7\u5148\u4FDD\u6301\u7A7A\u95F2\uFF1B\u795E\u5FF5\u6B63\u5728\u4E3A\u8BE5\u7CBE\u786E\u5BF9\u8BDD\u5EFA\u7ACB\u7FA4\u804A\u8FDE\u63A5\uFF0C\u5B8C\u6210\u540E\u4F1A\u53D1\u9001\u540E\u7EED\u6D88\u606F\u3002"].join(`
|
|
3
|
+
`)}function E(e){if(!e.nianSessionId||!e.result||e.phase!=="joined")throw u("operation_state");return{operationId:e.operationId,phase:"joined",nianSessionId:e.nianSessionId,result:g(e.result)}}function m(e,i,o,t){if(e instanceof h)return e;const r=e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:`managed_room_${i}_failed`;return new h(r,`Managed Room Session failed during ${i}. The created conversation was preserved for retry.`,i,o,t?.phase??"initialized",t?.nianSessionId??null)}function d(e){throw new h(`invalid_managed_room_${e}`,`Managed Room Session ${e} is invalid.`,"validate_input",null,"initialized",null)}function u(e){return Object.assign(new Error(`Managed Room dependency returned an invalid ${e}.`),{code:`invalid_managed_room_${e}`})}function M(e,i){if(!e||e.schemaVersion!==1||e.operationId!==i||!/^[a-f0-9]{64}$/.test(e.inputFingerprint)||!["initialized","session_created","native_session_ready","session_registered","joined"].includes(e.phase)||!e.input||v(e.input.agent)!==e.runtimeAdapter)throw p();const o=I(e.input,[new URL(e.input.inviteUrl).origin]);if(b(o)!==e.inputFingerprint||(e.nianSessionId!==null&&c(e.nianSessionId,"nian_session_id"),e.sourceSessionKey!==null&&A(e.sourceSessionKey),e.hostProcessIdentity!==null&&c(e.hostProcessIdentity,"host_process_identity"),e.registryKey!==null&&!w.test(e.registryKey)))throw p();e.result!==null&&O(e.result)}function p(){return Object.assign(new Error("Managed Room Session operation store is unsafe or corrupt."),{code:"unsafe_managed_room_operation_store"})}function g(e){return JSON.parse(JSON.stringify(e))}export{Z as FileManagedRoomSessionOperationStore,G as InMemoryManagedRoomSessionOperationStore,h as ManagedRoomSessionError,B as ManagedRoomSessionOrchestrator};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HttpRoomActionClient } from './room-action-client.js';
|
|
2
|
+
import { RoomJsonlMirror } from './jsonl-mirror.js';
|
|
3
|
+
import { RoomSessionRegistry, type RoomSessionRegistration } from './session-registry.js';
|
|
4
|
+
import type { RoomMessageView } from '@shennian/wire';
|
|
5
|
+
type Mirror = Pick<RoomJsonlMirror, 'append' | 'markAccessRevoked' | 'readState' | 'recordSyncError'>;
|
|
6
|
+
type Timer = ReturnType<typeof setTimeout>;
|
|
7
|
+
export declare class RoomMirrorSyncService {
|
|
8
|
+
private readonly deps;
|
|
9
|
+
private timer;
|
|
10
|
+
private running;
|
|
11
|
+
private started;
|
|
12
|
+
constructor(deps: {
|
|
13
|
+
registry: RoomSessionRegistry;
|
|
14
|
+
actionClient: Pick<HttpRoomActionClient, 'status' | 'read'>;
|
|
15
|
+
getMachineCredentials: () => {
|
|
16
|
+
machineToken?: string;
|
|
17
|
+
};
|
|
18
|
+
mirrorFactory?: (roomId: string) => Mirror;
|
|
19
|
+
initialDelayMs?: number;
|
|
20
|
+
intervalMs?: number;
|
|
21
|
+
maxPagesPerRun?: number;
|
|
22
|
+
setTimeout?: (handler: () => void, delayMs: number) => Timer;
|
|
23
|
+
clearTimeout?: (timer: Timer) => void;
|
|
24
|
+
onMessages?: (roomId: string, messages: RoomMessageView[], registrations: RoomSessionRegistration[]) => Promise<void>;
|
|
25
|
+
});
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
runOnce(): Promise<void>;
|
|
29
|
+
private syncRoom;
|
|
30
|
+
private pullPages;
|
|
31
|
+
private arm;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{RoomActionHttpError as m}from"./room-action-client.js";import{RoomJsonlMirror as h}from"./jsonl-mirror.js";class S{deps;timer=null;running=!1;started=!1;constructor(e){this.deps=e}start(){this.started||(this.started=!0,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async runOnce(){if(this.running)return;const e=this.deps.getMachineCredentials().machineToken?.trim();if(e){this.running=!0;try{const i=l(this.deps.registry.list());for(const[r,o]of i)await this.syncRoom(r,o,e)}finally{this.running=!1}}}async syncRoom(e,i,r){const o=(this.deps.mirrorFactory??(t=>new h(t)))(e),u=i.filter(t=>t.binding&&(t.binding.status==="active"||t.binding.status==="paused"));if(u.length===0){i.some(t=>t.binding?.status==="detached")&&!i.some(t=>t.binding?.status==="pending_approval")&&o.markAccessRevoked();return}let a=0,c=null;for(const t of u){const d=t.binding,g={machineToken:r,roomId:e,bindingId:d.bindingId};try{const s=await this.deps.actionClient.status(g);if(this.deps.registry.setBinding({registryKey:t.registryKey,bindingId:s.bindingId,roomId:s.roomId,agentIdentityId:s.agentIdentityId,agentMembershipId:s.agentMembershipId,status:s.status,activationMode:s.activationMode}),s.status==="detached"){a+=1;continue}if(s.status==="pending_approval")continue;c??=g}catch(s){if(p(s)){this.deps.registry.detachBinding(t.registryKey),a+=1;continue}o.recordSyncError(f(s));return}}if(c){const t=i.map(d=>this.deps.registry.getByRegistryKey(d.registryKey)??d);await this.pullPages(o,c,e,t);return}a===u.length&&o.markAccessRevoked()}async pullPages(e,i,r,o){const u=this.deps.maxPagesPerRun??100;let a=e.readState().lastSyncedSequence;for(let c=0;c<u;c+=1){const t=await this.deps.actionClient.read({...i,afterSequence:a,limit:100}),d=e.append({messages:t.messages,historyFromSequence:t.historyFromSequence,roomName:t.roomName});if(this.deps.onMessages&&await this.deps.onMessages(r,t.messages,o),!t.hasMore)return;if(d.lastSyncedSequence===a)throw new m("room_sync_no_progress","Room mirror synchronization made no progress.");a=d.lastSyncedSequence}}arm(e){if(!this.started||this.timer)return;const i=this.deps.setTimeout??setTimeout;this.timer=i(()=>{this.timer=null,this.runOnce().catch(()=>{}).finally(()=>{this.started&&this.arm(this.deps.intervalMs??5e3)})},e),this.timer.unref?.()}}function l(n){const e=new Map;for(const i of n){if(!i.binding||i.lifecycleState==="closed")continue;const r=e.get(i.binding.roomId)??[];r.push(i),e.set(i.binding.roomId,r)}return e}function p(n){return n instanceof m&&(n.code==="room_binding_unavailable"||n.code==="room_action_http_403")}function f(n){return n instanceof m?n.code:"room_sync_failed"}export{S as RoomMirrorSyncService};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
|
|
2
|
+
import type { RoomSessionRegistry } from './session-registry.js';
|
|
3
|
+
import type { HttpRoomActionClient } from './room-action-client.js';
|
|
4
|
+
export declare function createRoomOperatorIpcHandlers(input: {
|
|
5
|
+
registry: Pick<RoomSessionRegistry, 'list'>;
|
|
6
|
+
actionClient: Pick<HttpRoomActionClient, 'createOperatorRoom'>;
|
|
7
|
+
getMachineCredentials: () => {
|
|
8
|
+
machineId?: string;
|
|
9
|
+
machineToken?: string;
|
|
10
|
+
};
|
|
11
|
+
}): Record<string, DaemonIpcHandler>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DaemonIpcError as r}from"../daemon-ipc/protocol.js";function w(e){return{"room-operator.inspect":n=>{const o=u(n),t=e.getMachineCredentials(),i=t.machineId?.trim(),l=!!(i&&t.machineToken?.trim()),c=i?e.registry.list().filter(s=>s.machineId===i):[],m=c.filter(s=>s.binding!==null),d=h(m);return a(o==="status"?{source:"local_registry",signedIn:l,registeredSessions:c.length,boundSessions:m.length,rooms:d.length}:{source:"local_registry",complete:!1,rooms:d})},"room-operator.create":async n=>{const o=f(n),t=e.getMachineCredentials();if(!t.machineId?.trim()||!t.machineToken?.trim())throw new r("cli_not_signed_in","Sign in to the Shennian CLI first.");try{return a(await e.actionClient.createOperatorRoom({machineToken:t.machineToken,name:o}))}catch(i){throw g(i)}}}}function a(e){return JSON.parse(JSON.stringify(e))}function u(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const n=e;if(Object.keys(n).length!==1||n.action!=="status"&&n.action!=="list")throw new r("invalid_params","Room operator action is invalid.");return n.action}function f(e){if(!e||Array.isArray(e)||typeof e!="object")throw new r("invalid_params","Room operator parameters are required.");const n=e;if(Object.keys(n).length!==1||typeof n.name!="string")throw new r("invalid_params","Room operator create parameters are invalid.");const o=n.name.trim();if(!o||o.length>80)throw new r("invalid_params","Room name must contain 1 to 80 characters.");return o}function g(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"&&"message"in e&&typeof e.message=="string"?new r(e.code,e.message):new r("room_operator_failed","Shennian Room operator action failed.")}function h(e){const n=new Map;for(const o of e){const t=o.binding;if(!t)continue;const i=n.get(t.roomId)??{sessions:0,runtimes:new Set,statuses:new Set,modes:new Set};i.sessions+=1,i.runtimes.add(o.runtimeAdapter),i.statuses.add(t.status),i.modes.add(t.activationMode),n.set(t.roomId,i)}return[...n.entries()].sort(([o],[t])=>o.localeCompare(t)).map(([o,t])=>({roomId:o,sessions:t.sessions,runtimes:[...t.runtimes].sort(),bindingStatuses:[...t.statuses].sort(),activationModes:[...t.modes].sort()}))}export{w as createRoomOperatorIpcHandlers};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type AuthorizedActionStatus = 'waiting_user' | 'approved' | 'executing' | 'completed' | 'failed' | 'expired';
|
|
2
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export type PendingAuthorizedAction = {
|
|
6
|
+
schemaVersion: 1;
|
|
7
|
+
id: string;
|
|
8
|
+
loginIntentId: string;
|
|
9
|
+
publicLoginUrl: string;
|
|
10
|
+
actionType: string;
|
|
11
|
+
canonicalActionPayload: JsonValue;
|
|
12
|
+
actionPayloadHash: string;
|
|
13
|
+
idempotencyKey: string;
|
|
14
|
+
deviceSecretHash: string;
|
|
15
|
+
status: AuthorizedActionStatus;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
browserOpenedAt?: string;
|
|
20
|
+
approvedAt?: string;
|
|
21
|
+
executingAt?: string;
|
|
22
|
+
completedAt?: string;
|
|
23
|
+
failedAt?: string;
|
|
24
|
+
errorCode?: string;
|
|
25
|
+
result?: JsonValue;
|
|
26
|
+
};
|
|
27
|
+
type CreatePendingActionInput = {
|
|
28
|
+
id?: string;
|
|
29
|
+
loginIntentId: string;
|
|
30
|
+
publicLoginUrl: string;
|
|
31
|
+
actionType: string;
|
|
32
|
+
actionPayload: JsonValue;
|
|
33
|
+
idempotencyKey: string;
|
|
34
|
+
deviceSecret: string;
|
|
35
|
+
expiresAt: string;
|
|
36
|
+
now?: Date;
|
|
37
|
+
};
|
|
38
|
+
export declare class PendingAuthorizedActionError extends Error {
|
|
39
|
+
readonly code: string;
|
|
40
|
+
constructor(code: string, message: string);
|
|
41
|
+
}
|
|
42
|
+
export declare function canonicalizeActionPayload(value: JsonValue): string;
|
|
43
|
+
export declare function hashActionPayload(value: JsonValue): string;
|
|
44
|
+
export declare function hashPendingDeviceSecret(secret: string): string;
|
|
45
|
+
export declare class PendingAuthorizedActionStore {
|
|
46
|
+
readonly directory: string;
|
|
47
|
+
constructor(rootDir?: string);
|
|
48
|
+
create(input: CreatePendingActionInput): PendingAuthorizedAction;
|
|
49
|
+
get(id: string): PendingAuthorizedAction | null;
|
|
50
|
+
list(): PendingAuthorizedAction[];
|
|
51
|
+
findByIdempotencyKey(idempotencyKey: string): PendingAuthorizedAction | null;
|
|
52
|
+
listRecoverable(now?: Date): PendingAuthorizedAction[];
|
|
53
|
+
readDeviceSecret(id: string): string;
|
|
54
|
+
markApproved(id: string, now?: Date): PendingAuthorizedAction;
|
|
55
|
+
markBrowserOpened(id: string, now?: Date): PendingAuthorizedAction;
|
|
56
|
+
markExecuting(id: string, now?: Date): PendingAuthorizedAction;
|
|
57
|
+
markCompleted(id: string, result: JsonValue, now?: Date): PendingAuthorizedAction;
|
|
58
|
+
markFailed(id: string, errorCode: string, now?: Date): PendingAuthorizedAction;
|
|
59
|
+
private transition;
|
|
60
|
+
private update;
|
|
61
|
+
private ensureDirectory;
|
|
62
|
+
private recordPath;
|
|
63
|
+
private secretPath;
|
|
64
|
+
private writeRecord;
|
|
65
|
+
private writeSecret;
|
|
66
|
+
private atomicWrite;
|
|
67
|
+
private removeSecret;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import d from"node:crypto";import c from"node:fs";import m from"node:path";import{resolveShennianPath as b}from"../config/index.js";const u=/^[A-Za-z0-9_-]{8,128}$/,w=/^[a-z][a-z0-9_.-]{0,63}$/,S=/^[a-f0-9]{64}$/,f=new Set(["completed","failed","expired"]);class r extends Error{code;constructor(e,t){super(t),this.code=e,this.name="PendingAuthorizedActionError"}}function I(i){return l(i)}function _(i){return d.createHash("sha256").update(I(i),"utf8").digest("hex")}function v(i){return d.createHash("sha256").update(i,"utf8").digest("hex")}class k{directory;constructor(e=b("runtime","pending-authorized-actions")){this.directory=e}create(e){if(y(e.loginIntentId,"loginIntentId"),O(e.publicLoginUrl,e.loginIntentId),!w.test(e.actionType))throw new r("invalid_action_type","Invalid authorized action type.");if(!e.idempotencyKey.trim()||e.idempotencyKey.length>191)throw new r("invalid_idempotency_key","Invalid idempotency key.");if(e.deviceSecret.length<32||e.deviceSecret.length>256)throw new r("invalid_device_secret","Invalid device secret.");const t=new Date(e.expiresAt);if(!Number.isFinite(t.getTime()))throw new r("invalid_expiry","Invalid authorization expiry.");this.ensureDirectory();const n=x(e.actionPayload),o=_(n),a=this.list().find(p=>p.idempotencyKey===e.idempotencyKey);if(a){if(a.actionType!==e.actionType||a.actionPayloadHash!==o)throw new r("idempotency_payload_conflict","The idempotency key is already bound to a different action.");return a}const s=(e.now??new Date).toISOString(),h=e.id??d.randomUUID();y(h,"id");const g={schemaVersion:1,id:h,loginIntentId:e.loginIntentId,publicLoginUrl:e.publicLoginUrl,actionType:e.actionType,canonicalActionPayload:n,actionPayloadHash:o,idempotencyKey:e.idempotencyKey,deviceSecretHash:v(e.deviceSecret),status:"waiting_user",expiresAt:t.toISOString(),createdAt:s,updatedAt:s};this.writeSecret(h,e.deviceSecret);try{this.writeRecord(g)}catch(p){throw this.removeSecret(h),p}return g}get(e){y(e,"id");try{const t=this.recordPath(e),n=c.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw new r("unsafe_pending_action","Pending action is not a regular file.");return A(JSON.parse(c.readFileSync(t,"utf8")))}catch(t){if(N(t))return null;throw t instanceof r?t:new r("corrupt_pending_action","Pending authorized action is corrupt.")}}list(){return this.ensureDirectory(),c.readdirSync(this.directory).filter(e=>e.endsWith(".json")&&!e.includes(".tmp-")).map(e=>this.get(e.slice(0,-5))).filter(e=>e!==null).sort((e,t)=>e.createdAt.localeCompare(t.createdAt))}findByIdempotencyKey(e){return this.list().find(t=>t.idempotencyKey===e)??null}listRecoverable(e=new Date){const t=[];for(const n of this.list())if(!f.has(n.status)){if(new Date(n.expiresAt)<=e){this.update(n.id,{status:"expired",errorCode:"authorization_expired"},e),this.removeSecret(n.id);continue}t.push(n)}return t}readDeviceSecret(e){const t=this.get(e);if(!t||f.has(t.status))throw new r("pending_action_unavailable","Pending action is unavailable.");let n;try{const o=this.secretPath(e),a=c.lstatSync(o);if(!a.isFile()||a.isSymbolicLink())throw new r("unsafe_device_secret","Device authorization secret is not a regular file.");n=c.readFileSync(o,"utf8").trim()}catch(o){throw o instanceof r?o:new r("device_secret_missing","Device authorization secret is missing.")}if(!$(n,t.deviceSecretHash))throw new r("device_secret_mismatch","Device authorization secret is invalid.");return n}markApproved(e,t=new Date){return this.transition(e,["waiting_user","approved"],"approved",{approvedAt:t.toISOString()},t)}markBrowserOpened(e,t=new Date){const n=this.get(e);if(!n)throw new r("pending_action_not_found","Pending action not found.");return n.browserOpenedAt||n.status!=="waiting_user"?n:this.update(e,{browserOpenedAt:t.toISOString()},t)}markExecuting(e,t=new Date){const n=this.transition(e,["approved"],"executing",{executingAt:t.toISOString()},t);return this.removeSecret(e),n}markCompleted(e,t,n=new Date){const o=this.transition(e,["executing","completed"],"completed",{completedAt:n.toISOString(),result:t,errorCode:void 0},n);return this.removeSecret(e),o}markFailed(e,t,n=new Date){const o=this.transition(e,["waiting_user","approved","executing","failed"],"failed",{failedAt:n.toISOString(),errorCode:t.slice(0,120)},n);return this.removeSecret(e),o}transition(e,t,n,o,a){const s=this.get(e);if(!s)throw new r("pending_action_not_found","Pending action not found.");if(!t.includes(s.status))throw new r("invalid_pending_action_transition",`Cannot transition ${s.status} to ${n}.`);return s.status===n&&f.has(n)?s:this.update(e,{...o,status:n},a)}update(e,t,n){const o=this.get(e);if(!o)throw new r("pending_action_not_found","Pending action not found.");const a=A({...o,...t,id:o.id,updatedAt:n.toISOString()});return this.writeRecord(a),a}ensureDirectory(){c.mkdirSync(this.directory,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.directory,448)}recordPath(e){return m.join(this.directory,`${e}.json`)}secretPath(e){return m.join(this.directory,`${e}.secret`)}writeRecord(e){this.atomicWrite(this.recordPath(e.id),`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}writeSecret(e,t){this.atomicWrite(this.secretPath(e),`${t}
|
|
3
|
+
`)}atomicWrite(e,t){this.ensureDirectory();const n=`${e}.tmp-${process.pid}-${d.randomBytes(6).toString("hex")}`;try{c.writeFileSync(n,t,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&c.chmodSync(n,384),c.renameSync(n,e),process.platform!=="win32"&&c.chmodSync(e,384)}finally{try{c.unlinkSync(n)}catch{}}}removeSecret(e){try{c.unlinkSync(this.secretPath(e))}catch{}}}function l(i){if(i===null||typeof i=="boolean"||typeof i=="string")return JSON.stringify(i);if(typeof i=="number"){if(!Number.isFinite(i))throw new r("invalid_action_payload","Numbers must be finite.");return JSON.stringify(i)}if(Array.isArray(i))return`[${i.map(l).join(",")}]`;if(typeof i!="object")throw new r("invalid_action_payload","Action payload must be JSON.");const e=Object.getPrototypeOf(i);if(e!==Object.prototype&&e!==null)throw new r("invalid_action_payload","Action payload must be plain JSON.");return`{${Object.keys(i).sort().map(t=>`${JSON.stringify(t)}:${l(i[t])}`).join(",")}}`}function x(i){return JSON.parse(l(i))}function y(i,e){if(!u.test(i))throw new r(`invalid_${e}`,`Invalid ${e}.`)}function A(i){if(!i||typeof i!="object")throw new r("corrupt_pending_action","Pending action must be an object.");const e=i;if(e.schemaVersion!==1||typeof e.id!="string"||!u.test(e.id)||typeof e.loginIntentId!="string"||!u.test(e.loginIntentId)||typeof e.publicLoginUrl!="string"||!P(e.publicLoginUrl,e.loginIntentId)||typeof e.actionType!="string"||!w.test(e.actionType)||typeof e.idempotencyKey!="string"||!e.idempotencyKey||typeof e.actionPayloadHash!="string"||!S.test(e.actionPayloadHash)||typeof e.deviceSecretHash!="string"||!S.test(e.deviceSecretHash)||!["waiting_user","approved","executing","completed","failed","expired"].includes(e.status??"")||typeof e.expiresAt!="string"||!Number.isFinite(new Date(e.expiresAt).getTime())||typeof e.createdAt!="string"||typeof e.updatedAt!="string")throw new r("corrupt_pending_action","Pending action has invalid fields.");if(e.browserOpenedAt!==void 0&&(typeof e.browserOpenedAt!="string"||!Number.isFinite(new Date(e.browserOpenedAt).getTime())))throw new r("corrupt_pending_action","Pending action has invalid browser state.");if(_(e.canonicalActionPayload)!==e.actionPayloadHash)throw new r("action_payload_tampered","Pending action payload hash mismatch.");return e}function O(i,e){if(!P(i,e))throw new r("invalid_public_login_url","Invalid Shennian authorization URL.")}function P(i,e){try{const t=new URL(i);return t.pathname!==`/authorize/${e}`||t.search||t.hash?!1:t.protocol==="https:"&&(t.hostname==="shennian.net"||t.hostname.endsWith(".shennian.net"))?!0:t.protocol==="http:"&&D(t.hostname)}catch{return!1}}function D(i){return i==="127.0.0.1"||i==="localhost"||i==="::1"||/^10\./.test(i)||/^192\.168\./.test(i)||/^172\.(1[6-9]|2\d|3[0-1])\./.test(i)}function $(i,e){const t=Buffer.from(v(i),"hex"),n=Buffer.from(e,"hex");return t.length===n.length&&d.timingSafeEqual(t,n)}function N(i){return typeof i=="object"&&i!==null&&"code"in i&&i.code==="ENOENT"}export{r as PendingAuthorizedActionError,k as PendingAuthorizedActionStore,I as canonicalizeActionPayload,_ as hashActionPayload,v as hashPendingDeviceSecret};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { RoomActivationMode, RoomMessageView } from '@shennian/wire';
|
|
2
|
+
type FetchLike = typeof fetch;
|
|
3
|
+
type BindingTarget = {
|
|
4
|
+
machineToken: string;
|
|
5
|
+
roomId: string;
|
|
6
|
+
bindingId: string;
|
|
7
|
+
};
|
|
8
|
+
export type RemoteRoomAgentStatus = {
|
|
9
|
+
bindingId: string;
|
|
10
|
+
roomId: string;
|
|
11
|
+
agentIdentityId: string;
|
|
12
|
+
agentMembershipId: string | null;
|
|
13
|
+
status: 'pending_approval' | 'active' | 'paused' | 'detached';
|
|
14
|
+
activationMode: RoomActivationMode;
|
|
15
|
+
lastDeliveredSequence: string;
|
|
16
|
+
lastActivatedSequence: string;
|
|
17
|
+
room: {
|
|
18
|
+
name: string;
|
|
19
|
+
status: string;
|
|
20
|
+
lastSequence: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type RemoteRoomJoinResult = {
|
|
24
|
+
outcome: 'joined' | 'pending_approval';
|
|
25
|
+
room: {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
webUrl: string;
|
|
29
|
+
};
|
|
30
|
+
agentIdentity: {
|
|
31
|
+
id: string;
|
|
32
|
+
displayName: string;
|
|
33
|
+
};
|
|
34
|
+
binding: {
|
|
35
|
+
id: string;
|
|
36
|
+
roomId: string;
|
|
37
|
+
agentIdentityId: string;
|
|
38
|
+
agentMembershipId: string | null;
|
|
39
|
+
status: 'pending_approval' | 'active' | 'paused';
|
|
40
|
+
activationMode: RoomActivationMode;
|
|
41
|
+
};
|
|
42
|
+
joinRequestId?: string;
|
|
43
|
+
idempotent: boolean;
|
|
44
|
+
};
|
|
45
|
+
export type RemoteRoomAttachmentCapability = {
|
|
46
|
+
attachmentId: string;
|
|
47
|
+
roomId: string;
|
|
48
|
+
safeName: string;
|
|
49
|
+
mimeType: string;
|
|
50
|
+
byteSize: string;
|
|
51
|
+
sha256: string;
|
|
52
|
+
downloadUrl: string;
|
|
53
|
+
expiresAt: string;
|
|
54
|
+
};
|
|
55
|
+
export type RemoteRoomAttachmentUpload = {
|
|
56
|
+
attachment: {
|
|
57
|
+
id: string;
|
|
58
|
+
roomId: string;
|
|
59
|
+
name: string;
|
|
60
|
+
mimeType: string;
|
|
61
|
+
byteSize: string;
|
|
62
|
+
sha256: string;
|
|
63
|
+
status: 'pending' | 'uploaded';
|
|
64
|
+
};
|
|
65
|
+
upload: {
|
|
66
|
+
method: 'PUT';
|
|
67
|
+
url: string;
|
|
68
|
+
headers: Record<string, string>;
|
|
69
|
+
expiresAt: string;
|
|
70
|
+
};
|
|
71
|
+
idempotent: boolean;
|
|
72
|
+
};
|
|
73
|
+
export type RemoteRoomCreateResult = {
|
|
74
|
+
outcome: 'created';
|
|
75
|
+
room: {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
visibility: 'private';
|
|
79
|
+
joinPolicy: 'approval';
|
|
80
|
+
webUrl: string;
|
|
81
|
+
inviteUrl: string;
|
|
82
|
+
};
|
|
83
|
+
agentIdentity: {
|
|
84
|
+
id: string;
|
|
85
|
+
displayName: string;
|
|
86
|
+
};
|
|
87
|
+
binding: {
|
|
88
|
+
id: string;
|
|
89
|
+
roomId: string;
|
|
90
|
+
agentIdentityId: string;
|
|
91
|
+
agentMembershipId: string;
|
|
92
|
+
status: 'active' | 'paused';
|
|
93
|
+
activationMode: RoomActivationMode;
|
|
94
|
+
};
|
|
95
|
+
idempotent: boolean;
|
|
96
|
+
};
|
|
97
|
+
export type RemoteOperatorRoomCreateResult = {
|
|
98
|
+
outcome: 'created';
|
|
99
|
+
room: {
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
visibility: 'private';
|
|
103
|
+
joinPolicy: 'approval';
|
|
104
|
+
webUrl: string;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
export declare class RoomActionHttpError extends Error {
|
|
108
|
+
readonly code: string;
|
|
109
|
+
constructor(code: string, message: string);
|
|
110
|
+
}
|
|
111
|
+
export declare class HttpRoomActionClient {
|
|
112
|
+
private readonly fetchImpl;
|
|
113
|
+
private readonly baseUrl;
|
|
114
|
+
constructor(serverUrl: string, fetchImpl?: FetchLike);
|
|
115
|
+
createOperatorRoom(input: {
|
|
116
|
+
machineToken: string;
|
|
117
|
+
name: string;
|
|
118
|
+
}): Promise<RemoteOperatorRoomCreateResult>;
|
|
119
|
+
resolveNativeSession(input: {
|
|
120
|
+
machineToken: string;
|
|
121
|
+
runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
|
|
122
|
+
sourceSessionKey: string;
|
|
123
|
+
}): Promise<{
|
|
124
|
+
nianSessionId: string;
|
|
125
|
+
created: boolean;
|
|
126
|
+
}>;
|
|
127
|
+
createRoom(input: {
|
|
128
|
+
machineToken: string;
|
|
129
|
+
bindingIntentId: string;
|
|
130
|
+
nianSessionId: string;
|
|
131
|
+
runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
|
|
132
|
+
sessionRegistryKey: string;
|
|
133
|
+
name: string;
|
|
134
|
+
agentAlias: string;
|
|
135
|
+
activationCapability: 'managed' | 'idle_resume' | 'notify_only';
|
|
136
|
+
activationMode: RoomActivationMode;
|
|
137
|
+
}): Promise<RemoteRoomCreateResult>;
|
|
138
|
+
joinRoom(input: {
|
|
139
|
+
machineToken: string;
|
|
140
|
+
bindingIntentId: string;
|
|
141
|
+
nianSessionId: string;
|
|
142
|
+
runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
|
|
143
|
+
sessionRegistryKey: string;
|
|
144
|
+
inviteToken: string;
|
|
145
|
+
message?: string;
|
|
146
|
+
agentAlias: string;
|
|
147
|
+
activationCapability: 'managed' | 'idle_resume' | 'notify_only';
|
|
148
|
+
activationMode: RoomActivationMode;
|
|
149
|
+
}): Promise<RemoteRoomJoinResult>;
|
|
150
|
+
status(target: BindingTarget): Promise<RemoteRoomAgentStatus>;
|
|
151
|
+
getAttachmentCapability(target: BindingTarget & {
|
|
152
|
+
attachmentId: string;
|
|
153
|
+
}): Promise<RemoteRoomAttachmentCapability>;
|
|
154
|
+
initializeAttachment(target: BindingTarget & {
|
|
155
|
+
clientUploadId: string;
|
|
156
|
+
name: string;
|
|
157
|
+
mediaKind: 'image' | 'audio' | 'file';
|
|
158
|
+
mimeType: string;
|
|
159
|
+
byteSize: number;
|
|
160
|
+
sha256: string;
|
|
161
|
+
}): Promise<RemoteRoomAttachmentUpload>;
|
|
162
|
+
completeAttachment(target: BindingTarget & {
|
|
163
|
+
attachmentId: string;
|
|
164
|
+
}): Promise<{
|
|
165
|
+
attachment: {
|
|
166
|
+
attachmentId: string;
|
|
167
|
+
mediaKind: 'image' | 'audio' | 'file';
|
|
168
|
+
name: string;
|
|
169
|
+
mimeType: string;
|
|
170
|
+
byteSize: string;
|
|
171
|
+
sha256: string;
|
|
172
|
+
status: 'ready';
|
|
173
|
+
};
|
|
174
|
+
idempotent: boolean;
|
|
175
|
+
}>;
|
|
176
|
+
read(target: BindingTarget & {
|
|
177
|
+
afterSequence: string;
|
|
178
|
+
limit: number;
|
|
179
|
+
}): Promise<{
|
|
180
|
+
messages: RoomMessageView[];
|
|
181
|
+
nextSequence: string;
|
|
182
|
+
hasMore: boolean;
|
|
183
|
+
historyFromSequence: string;
|
|
184
|
+
roomName: string;
|
|
185
|
+
}>;
|
|
186
|
+
send(target: BindingTarget & {
|
|
187
|
+
clientMessageId: string;
|
|
188
|
+
text: string;
|
|
189
|
+
mentionMembershipIds?: string[];
|
|
190
|
+
replyToMessageId?: string;
|
|
191
|
+
activationCauseMessageId?: string;
|
|
192
|
+
attachmentIds?: string[];
|
|
193
|
+
}): Promise<{
|
|
194
|
+
message: RoomMessageView;
|
|
195
|
+
idempotent: boolean;
|
|
196
|
+
}>;
|
|
197
|
+
mode(target: BindingTarget & {
|
|
198
|
+
activationMode: RoomActivationMode;
|
|
199
|
+
}): Promise<{
|
|
200
|
+
bindingId: string;
|
|
201
|
+
roomId: string;
|
|
202
|
+
status: 'active' | 'paused';
|
|
203
|
+
activationMode: RoomActivationMode;
|
|
204
|
+
idempotent: boolean;
|
|
205
|
+
}>;
|
|
206
|
+
private request;
|
|
207
|
+
}
|
|
208
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class c extends Error{code;constructor(e,o){super(o),this.code=e,this.name="RoomActionHttpError"}}class j{fetchImpl;baseUrl;constructor(e,o=fetch){this.fetchImpl=o,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/operator/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({name:e.name}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return I(t,e,this.baseUrl)}async resolveNativeSession(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/resolve-session`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);(!t||typeof t!="object")&&a();const n=t;return(typeof n.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(n.nianSessionId)||typeof n.created!="boolean")&&a(),n}async createRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,name:e.name,agentAlias:e.agentAlias,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return g(t,e,this.baseUrl)}async joinRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/join`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,inviteToken:e.inviteToken,message:e.message,agentAlias:e.agentAlias,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return v(t,e,this.baseUrl)}async status(e){const o=await this.request(e,"status");return b(o,e)}async getAttachmentCapability(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!o||typeof o!="object")&&a();const t=o;return(t.attachmentId!==e.attachmentId||t.roomId!==e.roomId||typeof t.safeName!="string"||!S(t.safeName)||typeof t.mimeType!="string"||t.mimeType.length<1||t.mimeType.length>191||typeof t.byteSize!="string"||!d(t.byteSize)||typeof t.sha256!="string"||!/^[a-f0-9]{64}$/.test(t.sha256)||!h(t.downloadUrl)||typeof t.expiresAt!="string"||!Number.isFinite(Date.parse(t.expiresAt)))&&a(),t}async initializeAttachment(e){const o=await this.request(e,"attachments/init",{method:"POST",body:JSON.stringify({clientUploadId:e.clientUploadId,name:e.name,mediaKind:e.mediaKind,mimeType:e.mimeType,byteSize:e.byteSize,sha256:e.sha256})});(!o||typeof o!="object")&&a();const t=o,n=t.attachment,r=t.upload;return(!n||typeof n.id!="string"||n.roomId!==e.roomId||n.name.length<1||n.mimeType!==e.mimeType||n.byteSize!==String(e.byteSize)||n.sha256!==e.sha256||n.status!=="pending"&&n.status!=="uploaded"||!r||r.method!=="PUT"||!h(r.url)||!r.headers||r.headers["content-type"]!==e.mimeType||r.headers["x-oss-meta-sha256"]!==e.sha256||typeof r.expiresAt!="string"||Date.parse(r.expiresAt)<=Date.now()||typeof t.idempotent!="boolean")&&a(),t}async completeAttachment(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!o||typeof o!="object")&&a();const t=o;typeof t.idempotent!="boolean"&&a();const n=t.attachment;return(!n||n.attachmentId!==e.attachmentId||n.mediaKind!=="image"&&n.mediaKind!=="audio"&&n.mediaKind!=="file"||typeof n.name!="string"||typeof n.mimeType!="string"||typeof n.byteSize!="string"||!d(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||n.status!=="ready")&&a(),{attachment:n,idempotent:t.idempotent}}async read(e){const o=new URLSearchParams({after:e.afterSequence,limit:String(e.limit)}),t=await this.request(e,`messages?${o}`);(!t||typeof t!="object")&&a();const n=t;return(!Array.isArray(n.messages)||typeof n.nextSequence!="string"||!d(n.nextSequence)||typeof n.hasMore!="boolean"||typeof n.historyFromSequence!="string"||!d(n.historyFromSequence)||typeof n.roomName!="string")&&a(),{messages:n.messages.map(s=>l(s,e.roomId)),nextSequence:n.nextSequence,hasMore:n.hasMore,historyFromSequence:n.historyFromSequence,roomName:n.roomName}}async send(e){const o=await this.request(e,"messages",{method:"POST",body:JSON.stringify({clientMessageId:e.clientMessageId,text:e.text,mentionMembershipIds:e.mentionMembershipIds,replyToMessageId:e.replyToMessageId,activationCauseMessageId:e.activationCauseMessageId,attachmentIds:e.attachmentIds})});(!o||typeof o!="object")&&a();const t=o;return typeof t.idempotent!="boolean"&&a(),{message:l(t.message,e.roomId),idempotent:t.idempotent}}async mode(e){const o=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!o||typeof o!="object")&&a();const t=o;return(t.bindingId!==e.bindingId||t.roomId!==e.roomId||t.status!=="active"&&t.status!=="paused"||!f(t.activationMode)||t.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),t}async request(e,o,t={}){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${o}`,{...t,headers:{authorization:`Bearer ${e.machineToken}`,...t.body?{"content-type":"application/json"}:{}},signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let r=`room_action_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(r=s.error)}catch{}throw new c(r,"Shennian Room action failed.")}try{return await n.json()}catch{a()}}}function b(i,e){(!i||typeof i!="object")&&a();const o=i,t=o.room;(o.bindingId!==e.bindingId||o.roomId!==e.roomId||typeof o.agentIdentityId!="string"||o.agentMembershipId!==null&&typeof o.agentMembershipId!="string"||o.status!=="pending_approval"&&o.status!=="active"&&o.status!=="paused"&&o.status!=="detached"||!f(o.activationMode)||typeof o.lastDeliveredSequence!="string"||!d(o.lastDeliveredSequence)||typeof o.lastActivatedSequence!="string"||!d(o.lastActivatedSequence)||!t||typeof t!="object")&&a(),(o.status==="pending_approval"&&o.agentMembershipId!==null||(o.status==="active"||o.status==="paused")&&typeof o.agentMembershipId!="string")&&a();const n=t;return(typeof n.name!="string"||typeof n.status!="string"||typeof n.lastSequence!="string"||!d(n.lastSequence))&&a(),o}function l(i,e){(!i||typeof i!="object")&&a();const o=i;if((typeof o.id!="string"||o.roomId!==e||typeof o.sequence!="string"||!d(o.sequence)||!o.author||typeof o.author.membershipId!="string"||!Array.isArray(o.attachments)||typeof o.createdAt!="string")&&a(),o.activationCause!=null){const t=o.activationCause;(typeof t.causeMessageId!="string"||typeof t.rootCauseMessageId!="string"||!Number.isInteger(t.hopCount)||t.hopCount<1||t.hopCount>32)&&a()}return o}function g(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room,r=t.agentIdentity,s=t.binding;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!y(n.webUrl,o,`/spaces/${n.id}`)||!w(n.inviteUrl,o)||!r||typeof r.id!="string"||r.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==r.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),t}function I(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!y(n.webUrl,o,`/spaces/${n.id}`))&&a(),t}function v(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room,r=t.agentIdentity,s=t.binding;return(t.outcome!=="joined"&&t.outcome!=="pending_approval"||!n||typeof n.id!="string"||typeof n.name!="string"||!y(n.webUrl,o,`/spaces/${n.id}`)||!r||typeof r.id!="string"||r.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==r.id||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),(t.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof t.joinRequestId!="string")||t.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||t.joinRequestId!==void 0))&&a(),t}function d(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function S(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function h(i){if(typeof i!="string")return!1;try{const e=new URL(i);return e.protocol==="https:"&&!e.username&&!e.password&&!e.hash}catch{return!1}}function f(i){return i==="auto_follow"||i==="mention_only"||i==="paused"}function a(){throw new c("invalid_room_action_response","Invalid Room action response.")}async function m(i){try{return await i.json()}catch{a()}}function p(i,e){let o=`room_action_http_${i}`;if(e&&typeof e=="object"){const t=e.error;typeof t=="string"&&/^[a-z0-9_-]{1,80}$/.test(t)&&(o=t)}return new c(o,"Shennian Room action failed.")}function y(i,e,o){return u(i,e,t=>t.pathname===o)}function w(i,e){return u(i,e,o=>/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(o.pathname))}function u(i,e,o){if(typeof i!="string")return!1;try{const t=new URL(i),n=new URL(e);return t.hostname===n.hostname&&(t.protocol==="https:"||n.protocol==="http:"&&t.protocol==="http:")&&!t.username&&!t.password&&!t.search&&!t.hash&&o(t)}catch{return!1}}export{j as HttpRoomActionClient,c as RoomActionHttpError};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
|
|
2
|
+
import { RoomInvocationTokenAuthority } from './invocation-token.js';
|
|
3
|
+
import type { RoomAttachmentCache } from './attachment-cache.js';
|
|
4
|
+
import { HttpRoomActionClient } from './room-action-client.js';
|
|
5
|
+
import { RoomSessionRegistry } from './session-registry.js';
|
|
6
|
+
import type { RoomAttachmentUploader } from './attachment-upload.js';
|
|
7
|
+
import type { RoomHostSessionObservationStore } from './host-session-observations.js';
|
|
8
|
+
type RoomMirrorWriter = {
|
|
9
|
+
append(input: {
|
|
10
|
+
messages: Awaited<ReturnType<HttpRoomActionClient['read']>>['messages'];
|
|
11
|
+
historyFromSequence: string;
|
|
12
|
+
roomName?: string | null;
|
|
13
|
+
}): unknown;
|
|
14
|
+
markAccessRevoked(): void;
|
|
15
|
+
search(input: {
|
|
16
|
+
query: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
}): unknown;
|
|
19
|
+
};
|
|
20
|
+
export declare function createRoomActionIpcHandlers(input: {
|
|
21
|
+
authority: RoomInvocationTokenAuthority;
|
|
22
|
+
registry: RoomSessionRegistry;
|
|
23
|
+
actionClient: Pick<HttpRoomActionClient, 'createRoom' | 'joinRoom' | 'read' | 'send' | 'mode' | 'status'>;
|
|
24
|
+
getMachineCredentials: () => {
|
|
25
|
+
machineId?: string;
|
|
26
|
+
machineToken?: string;
|
|
27
|
+
};
|
|
28
|
+
mirrorFactory?: (roomId: string) => RoomMirrorWriter;
|
|
29
|
+
attachmentCache?: Pick<RoomAttachmentCache, 'materialize'>;
|
|
30
|
+
attachmentUploader?: Pick<RoomAttachmentUploader, 'upload'>;
|
|
31
|
+
observations?: RoomHostSessionObservationStore;
|
|
32
|
+
}): Record<string, DaemonIpcHandler>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as R}from"./room-action-schema.js";function B(e){return{"room-action.execute":async o=>{const h=T(o),l=e.getMachineCredentials();if(!l.machineId?.trim()||!l.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const s=p(h.registryKey,"registryKey",64),A=p(h.invocationToken,"invocationToken",128),d=S(h.action),u=R(d,h.actionParameters);return e.registry.runExclusive(s,async()=>{let y;try{y=e.authority.consume({invocationToken:A,machineId:l.machineId,machineCredential:l.machineToken,registryKey:s,action:d,targetTool:"shennian_room",actionParameters:u})}catch(t){throw C(t)}const r=e.registry.getByRegistryKey(s),i=r?.binding;if(!r)throw new a("room_session_unavailable","The exact Agent session is unavailable.");if(d==="room.create"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!y.bindingIntentId)throw new a("binding_intent_missing","Room create intent is missing.");const t=u,n=await e.actionClient.createRoom({machineToken:l.machineToken,bindingIntentId:y.bindingIntentId,nianSessionId:r.nianSessionId,runtimeAdapter:r.runtimeAdapter,sessionRegistryKey:s,name:t.name,agentAlias:t.agentAlias,activationCapability:_(r.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:s,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),g(n)}if(d==="room.join"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!y.bindingIntentId)throw new a("binding_intent_missing","Room join intent is missing.");const t=u,n=await e.actionClient.joinRoom({machineToken:l.machineToken,bindingIntentId:y.bindingIntentId,nianSessionId:r.nianSessionId,runtimeAdapter:r.runtimeAdapter,sessionRegistryKey:s,inviteToken:k(t.inviteUrl),message:t.message,agentAlias:t.agentAlias,activationCapability:_(r.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:s,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),g(n)}if(!i)throw new a("room_not_bound","This Agent session is not bound to a Room.");if(d!=="room.status"&&(i.status==="detached"||i.status==="pending_approval"))throw new a(i.status==="pending_approval"?"room_binding_pending_approval":"room_binding_detached",i.status==="pending_approval"?"This Agent is waiting for Room approval.":"This Agent Room binding must be repaired before use.");const I={machineToken:l.machineToken,roomId:i.roomId,bindingId:i.bindingId};if(d==="room.status"){const t=await e.actionClient.status(I);e.registry.setBinding({registryKey:s,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:t.status,activationMode:t.activationMode}),t.status==="detached"&&e.mirrorFactory?.(t.roomId).markAccessRevoked();const n=e.registry.getByRegistryKey(s),c=n?.binding,m=e.observations?.getBySource(r);return g({...t,hostSession:{state:n?.lifecycleState??"closed",lastReportedEvent:m?.lastEventName??null,lastActiveAt:m?.lastActiveAt??null,lastIdleAt:m?.lastIdleAt??null,closedAt:m?.closedAt??null,closedConfirmed:m?.lifecycleState==="closed",activationCapability:_(r.capabilities)},localActivation:{queued:c?.activationQueue.length??0,inFlight:!!c?.activeActivation,pausedUntil:c?.loopBreaker.pausedUntil??null}})}if(d==="room.read"){const t=u;if(t.query){const b=e.mirrorFactory?.(i.roomId);if(!b)throw new a("room_mirror_unavailable","Room mirror is unavailable.");return g({source:"local_mirror",matches:b.search({query:t.query,limit:t.limit})})}const n=await e.actionClient.read({...I,afterSequence:t.afterSequence,limit:t.limit});e.mirrorFactory?.(i.roomId).append({messages:n.messages,historyFromSequence:n.historyFromSequence,roomName:n.roomName});const c=BigInt(n.nextSequence)>BigInt(i.lastDeliveredSequence)?n.nextSequence:i.lastDeliveredSequence;e.registry.advanceCursor({registryKey:s,cursor:"delivered",sequence:c});const m=[];for(const b of t.attachmentIds??[]){const v=n.messages.flatMap(w=>w.attachments).find(w=>w.attachmentId===b);if(!v||v.status!=="ready")throw new a("attachment_not_in_read_result","Requested attachment is not available in this read result.");if(!e.attachmentCache)throw new a("attachment_cache_unavailable","Room attachment cache is unavailable.");m.push({attachmentId:b,...await e.attachmentCache.materialize({...I,attachmentId:b,expected:{downloadRef:v.downloadRef,byteSize:v.byteSize,sha256:v.sha256}})})}return g({...n,materializedAttachments:m})}if(d==="room.send"){const t=u;let n;if(t.files?.length){const c=e.observations?.getBySource(r);if(!c?.workspacePath||!e.attachmentUploader)throw new a("room_attachment_upload_unavailable","The exact Agent workspace is unavailable for attachment upload.");n=await e.attachmentUploader.upload({...I,workspacePath:c.workspacePath,clientMessageId:t.clientMessageId,files:t.files})}return g(await e.actionClient.send({...I,clientMessageId:t.clientMessageId,text:t.text,mentionMembershipIds:t.mentionMembershipIds,replyToMessageId:t.replyToMessageId,attachmentIds:n,activationCauseMessageId:i.activeActivation?.causeMessageId}))}const M=u,f=await e.actionClient.mode({...I,...M});return e.registry.setBinding({registryKey:s,bindingId:i.bindingId,roomId:i.roomId,agentIdentityId:i.agentIdentityId,agentMembershipId:i.agentMembershipId,status:f.status,activationMode:f.activationMode}),g(f)})}}}function T(e){if(!e||Array.isArray(e)||typeof e!="object")throw new a("invalid_params","Room action parameters are required.");return e}function p(e,o,h){if(typeof e!="string"||!e.trim()||e.length>h)throw new a("invalid_params",`${o} is invalid.`);return e.trim()}function S(e){const o=p(e,"action",64);if(o!=="room.read"&&o!=="room.create"&&o!=="room.join"&&o!=="room.send"&&o!=="room.mode"&&o!=="room.status")throw new a("room_action_not_implemented","Room action is not available yet.");return o}function k(e){const o=new URL(e).pathname.split("/").at(-1);if(!o||!/^[A-Za-z0-9_-]{43}$/.test(o))throw new a("invalid_params","inviteUrl is invalid.");return o}function _(e){return e.canResumeIdleSession||e.canActivateWhileHostOpen?"idle_resume":"notify_only"}function C(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"&&"message"in e&&typeof e.message=="string"?new a(e.code,e.message):new a("room_invocation_failed","Room invocation failed.")}function g(e){return JSON.parse(JSON.stringify(e))}export{B as createRoomActionIpcHandlers};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RoomInvocationAction } from './invocation-token.js';
|
|
2
|
+
import type { JsonValue } from './pending-authorized-actions.js';
|
|
3
|
+
export declare function normalizeRoomActionParameters(action: Exclude<RoomInvocationAction, 'room.bind'>, value: JsonValue | undefined): JsonValue;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DaemonIpcError as p}from"../daemon-ipc/protocol.js";function I(t,n){const e=h(n);switch(t){case"room.status":return m(e,[]),{};case"room.read":{m(e,["afterSequence","limit","attachmentIds","query"]);const s=d(e.query,"query",200),r=e.afterSequence??"0",a=e.limit??50;(typeof r!="string"||!/^(0|[1-9]\d{0,19})$/.test(r))&&i("afterSequence"),(typeof a!="number"||!Number.isInteger(a)||a<1||a>100)&&i("limit");const c=u(e.attachmentIds,"attachmentIds",10);return s&&(e.afterSequence!=null||c)&&i("parameters"),s?{query:s,limit:a}:{afterSequence:r,limit:a,...c?{attachmentIds:c}:{}}}case"room.send":{m(e,["clientMessageId","text","files","mentionMembershipIds","replyToMessageId"]);const s=o(e.clientMessageId,"clientMessageId",191),r=d(e.text,"text",2e4),a=g(e.files,"files",10);!r&&!a&&i("parameters");const c=u(e.mentionMembershipIds,"mentionMembershipIds",20),l=d(e.replyToMessageId,"replyToMessageId",191);return{clientMessageId:s,text:r??"",...a?{files:a}:{},...c?{mentionMembershipIds:c}:{},...l?{replyToMessageId:l}:{}}}case"room.mode":return m(e,["activationMode"]),{activationMode:f(e.activationMode)};case"room.create":return m(e,["name","agentAlias","activationMode"]),{name:o(e.name,"name",80),agentAlias:o(e.agentAlias,"agentAlias",80),activationMode:f(e.activationMode??"auto_follow")};case"room.join":return m(e,["inviteUrl","agentAlias","message","activationMode"]),{inviteUrl:y(e.inviteUrl),agentAlias:o(e.agentAlias,"agentAlias",80),activationMode:f(e.activationMode??"auto_follow"),...d(e.message,"message",500)?{message:d(e.message,"message",500)}:{}}}}function g(t,n,e){return t==null?void 0:((!Array.isArray(t)||t.length<1||t.length>e)&&i(n),t.map(r=>((typeof r!="string"||!r.trim()||r.length>4096||/[\0\r\n]/.test(r))&&i(n),r.trim())))}function h(t){return t==null?{}:((Array.isArray(t)||typeof t!="object")&&i("parameters"),t)}function m(t,n){const e=new Set(n);Object.keys(t).some(s=>!e.has(s))&&i("parameters")}function o(t,n,e){return(typeof t!="string"||!t.trim()||t.length>e||/[\0\r]/.test(t))&&i(n),t.trim()}function d(t,n,e){if(!(t==null||t===""))return o(t,n,e)}function u(t,n,e){if(t==null)return;(!Array.isArray(t)||t.length>e)&&i(n);const s=t.map(r=>o(r,n,191));return new Set(s).size!==s.length&&i(n),s.length>0?s:void 0}function f(t){if(t==="auto"||t==="auto_follow")return"auto_follow";if(t==="mention"||t==="mention_only")return"mention_only";if(t==="paused")return"paused";i("activationMode")}function y(t){const n=o(t,"inviteUrl",2048);let e;try{e=new URL(n)}catch{i("inviteUrl")}return(e.protocol!=="https:"||e.hostname!=="shennian.net"||!/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(e.pathname)||e.username||e.password||e.search||e.hash)&&i("inviteUrl"),e.toString()}function i(t){throw new p("invalid_params",`${t} is invalid.`)}export{I as normalizeRoomActionParameters};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AuthorizedActionExecutor } from './authorized-action-coordinator.js';
|
|
2
|
+
import type { RoomHostSessionObservationStore } from './host-session-observations.js';
|
|
3
|
+
import type { HttpRoomBindingClient } from './room-binding-client.js';
|
|
4
|
+
import type { JsonValue, PendingAuthorizedAction } from './pending-authorized-actions.js';
|
|
5
|
+
import type { RoomSessionRegistry } from './session-registry.js';
|
|
6
|
+
type Credentials = {
|
|
7
|
+
machineId: string;
|
|
8
|
+
machineToken: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class RoomAuthorizedActionExecutor implements AuthorizedActionExecutor {
|
|
11
|
+
private readonly deps;
|
|
12
|
+
constructor(deps: {
|
|
13
|
+
registry: RoomSessionRegistry;
|
|
14
|
+
observations: RoomHostSessionObservationStore;
|
|
15
|
+
actionClient: Pick<HttpRoomBindingClient, 'resolveNativeSession' | 'createRoom' | 'joinRoom'>;
|
|
16
|
+
getMachineCredentials: () => {
|
|
17
|
+
machineId?: string;
|
|
18
|
+
machineToken?: string;
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
execute(action: PendingAuthorizedAction, claimed?: Credentials): Promise<JsonValue>;
|
|
22
|
+
}
|
|
23
|
+
export {};
|