@krischoichoi/channel-core 0.0.0-stage → 0.6.1
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/LICENSE +21 -0
- package/README.md +103 -2
- package/lib/access.d.ts +194 -0
- package/lib/access.d.ts.map +1 -0
- package/lib/access.js +122 -0
- package/lib/access.js.map +1 -0
- package/lib/account.d.ts +47 -0
- package/lib/account.d.ts.map +1 -0
- package/lib/account.js +20 -0
- package/lib/account.js.map +1 -0
- package/lib/adapter.d.ts +119 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +6 -0
- package/lib/adapter.js.map +1 -0
- package/lib/capabilities.d.ts +124 -0
- package/lib/capabilities.d.ts.map +1 -0
- package/lib/capabilities.js +34 -0
- package/lib/capabilities.js.map +1 -0
- package/lib/context.d.ts +26 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +2 -0
- package/lib/context.js.map +1 -0
- package/lib/define.d.ts +67 -0
- package/lib/define.d.ts.map +1 -0
- package/lib/define.js +36 -0
- package/lib/define.js.map +1 -0
- package/lib/errors.d.ts +43 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +86 -0
- package/lib/errors.js.map +1 -0
- package/lib/events.d.ts +131 -0
- package/lib/events.d.ts.map +1 -0
- package/lib/events.js +2 -0
- package/lib/events.js.map +1 -0
- package/lib/file-secret-store.d.ts +14 -0
- package/lib/file-secret-store.d.ts.map +1 -0
- package/lib/file-secret-store.js +82 -0
- package/lib/file-secret-store.js.map +1 -0
- package/lib/file-storage.d.ts +14 -0
- package/lib/file-storage.d.ts.map +1 -0
- package/lib/file-storage.js +76 -0
- package/lib/file-storage.js.map +1 -0
- package/lib/health.d.ts +21 -0
- package/lib/health.d.ts.map +1 -0
- package/lib/health.js +6 -0
- package/lib/health.js.map +1 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +35 -0
- package/lib/index.js.map +1 -0
- package/lib/media/bounded-response.d.ts +85 -0
- package/lib/media/bounded-response.d.ts.map +1 -0
- package/lib/media/bounded-response.js +192 -0
- package/lib/media/bounded-response.js.map +1 -0
- package/lib/media/hydration.d.ts +82 -0
- package/lib/media/hydration.d.ts.map +1 -0
- package/lib/media/hydration.js +94 -0
- package/lib/media/hydration.js.map +1 -0
- package/lib/media/index.d.ts +17 -0
- package/lib/media/index.d.ts.map +1 -0
- package/lib/media/index.js +17 -0
- package/lib/media/index.js.map +1 -0
- package/lib/media/mime-hint.d.ts +5 -0
- package/lib/media/mime-hint.d.ts.map +1 -0
- package/lib/media/mime-hint.js +29 -0
- package/lib/media/mime-hint.js.map +1 -0
- package/lib/media/remote-policy.d.ts +79 -0
- package/lib/media/remote-policy.d.ts.map +1 -0
- package/lib/media/remote-policy.js +230 -0
- package/lib/media/remote-policy.js.map +1 -0
- package/lib/media/secure-fetcher.d.ts +85 -0
- package/lib/media/secure-fetcher.d.ts.map +1 -0
- package/lib/media/secure-fetcher.js +143 -0
- package/lib/media/secure-fetcher.js.map +1 -0
- package/lib/messages.d.ts +187 -0
- package/lib/messages.d.ts.map +1 -0
- package/lib/messages.js +20 -0
- package/lib/messages.js.map +1 -0
- package/lib/mount.d.ts +51 -0
- package/lib/mount.d.ts.map +1 -0
- package/lib/mount.js +44 -0
- package/lib/mount.js.map +1 -0
- package/lib/paths.d.ts +4 -0
- package/lib/paths.d.ts.map +1 -0
- package/lib/paths.js +12 -0
- package/lib/paths.js.map +1 -0
- package/lib/plugin.d.ts +18 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +36 -0
- package/lib/plugin.js.map +1 -0
- package/lib/registry.d.ts +11 -0
- package/lib/registry.d.ts.map +1 -0
- package/lib/registry.js +36 -0
- package/lib/registry.js.map +1 -0
- package/lib/reply.d.ts +37 -0
- package/lib/reply.d.ts.map +1 -0
- package/lib/reply.js +46 -0
- package/lib/reply.js.map +1 -0
- package/lib/runtime-resources.d.ts +19 -0
- package/lib/runtime-resources.d.ts.map +1 -0
- package/lib/runtime-resources.js +2 -0
- package/lib/runtime-resources.js.map +1 -0
- package/lib/schema.d.ts +124 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +143 -0
- package/lib/schema.js.map +1 -0
- package/lib/secrets.d.ts +28 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +28 -0
- package/lib/secrets.js.map +1 -0
- package/lib/service.d.ts +71 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +97 -0
- package/lib/service.js.map +1 -0
- package/lib/storage.d.ts +18 -0
- package/lib/storage.d.ts.map +1 -0
- package/lib/storage.js +19 -0
- package/lib/storage.js.map +1 -0
- package/lib/volatile.d.ts +32 -0
- package/lib/volatile.d.ts.map +1 -0
- package/lib/volatile.js +46 -0
- package/lib/volatile.js.map +1 -0
- package/package.json +44 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wsz987
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @krischoichoi/channel-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stable cross-channel contract and Cordis `ChannelService` for DeepSeek Harness Channels.
|
|
4
|
+
|
|
5
|
+
`channel-core` is the foundation of the `dsh-channels` monorepo. It defines the
|
|
6
|
+
**Channel Contract** that every adapter implements, and the shared runtime
|
|
7
|
+
service mounted at `ctx.channels`. It never imports Harness Agent APIs and never
|
|
8
|
+
depends on a concrete messaging platform.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @krischoichoi/channel-core
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
As a Cordis plugin:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
- id: channels-service
|
|
20
|
+
name: '@krischoichoi/channel-core/plugin'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What's inside
|
|
24
|
+
|
|
25
|
+
| Export | Purpose |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `ChannelAdapter` / `defineChannelAdapter` | The adapter contract and the third-party authoring helper |
|
|
28
|
+
| `ChannelService` (`ctx.channels`) | Adapter registry, typed event bus, shared secrets/storage resources |
|
|
29
|
+
| `ChannelCapabilities` | Capability negotiation (`text`, `image`, `file`, `streaming`, …) |
|
|
30
|
+
| `ChannelTarget` / `OutboundMessage` / `SendResult` / `ReplyHandle` | Structured messaging types |
|
|
31
|
+
| `ChannelEvent` / `AuthChallenge` / `ChannelHealth` | Event, auth and health surfaces |
|
|
32
|
+
| `ChannelError` / `isChannelError` | Stable machine-readable error hierarchy |
|
|
33
|
+
| `mountChannelAdapter` | Transactional mount lifecycle (register → start → stop → unregister) |
|
|
34
|
+
| `ChannelRuntimeResources` | Durable secrets and storage shared by all mounted adapters |
|
|
35
|
+
| media helpers | `SecureRemoteMediaFetcher`, `RemotePolicy`, bounded response readers |
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { defineChannelAdapter } from '@krischoichoi/channel-core';
|
|
41
|
+
|
|
42
|
+
export default defineChannelAdapter({
|
|
43
|
+
id: 'my-channel',
|
|
44
|
+
capabilities: {
|
|
45
|
+
text: true,
|
|
46
|
+
image: false,
|
|
47
|
+
file: false,
|
|
48
|
+
audio: false,
|
|
49
|
+
video: false,
|
|
50
|
+
markdown: false,
|
|
51
|
+
cards: false,
|
|
52
|
+
reactions: false,
|
|
53
|
+
threads: false,
|
|
54
|
+
streaming: 'buffered', // 'native' | 'edit' | 'buffered'
|
|
55
|
+
},
|
|
56
|
+
async start(ctx) {
|
|
57
|
+
// connect the platform, then emit messages with ctx.emit('message', ...)
|
|
58
|
+
},
|
|
59
|
+
async stop() {
|
|
60
|
+
// idempotent cleanup
|
|
61
|
+
},
|
|
62
|
+
async send(target, message) {
|
|
63
|
+
// send one OutboundMessage
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
In production the plugin wires file-backed resources automatically; in tests the
|
|
69
|
+
service falls back to in-memory stores:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
73
|
+
import { ChannelService } from '@krischoichoi/channel-core';
|
|
74
|
+
|
|
75
|
+
const ctx = new Context();
|
|
76
|
+
const channels = new ChannelService(ctx);
|
|
77
|
+
|
|
78
|
+
channels.register(adapter);
|
|
79
|
+
channels.on((event) => console.log(event.type));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Data directory
|
|
83
|
+
|
|
84
|
+
The plugin resolves the durable data directory in this order:
|
|
85
|
+
|
|
86
|
+
1. `DSH_CHANNELS_DATA_DIR` (explicit override)
|
|
87
|
+
2. `<Harness home>/dsh-channels` (`$DSH_HOME`, else `~/.dsh`)
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm --filter @krischoichoi/channel-core build
|
|
93
|
+
pnpm --filter @krischoichoi/channel-core typecheck
|
|
94
|
+
pnpm --filter @krischoichoi/channel-core test
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Related
|
|
98
|
+
|
|
99
|
+
- [Repository root](../../README.md)
|
|
100
|
+
- [Adapter authoring guide](../../docs/adapter-authoring.md)
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
[MIT](../../LICENSE)
|
package/lib/access.d.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Access Policy — the shared, versioned, cross-package contract for
|
|
3
|
+
* inbound access control.
|
|
4
|
+
*
|
|
5
|
+
* This module defines ONLY stable cross-package semantics:
|
|
6
|
+
*
|
|
7
|
+
* - the `ChannelAccessPolicy` type + zod schema
|
|
8
|
+
* - the versioned storage-key codec (`accessPolicyStorageKey`)
|
|
9
|
+
* - `MessageActivation` (the `activation` fact on a received message)
|
|
10
|
+
* - the reserved owner-claim command constant + parser (`/dsh-claim`)
|
|
11
|
+
*
|
|
12
|
+
* It does NOT implement policy persistence orchestration, the authorization
|
|
13
|
+
* decision, Owner Claim session lifecycle, platform identity parsing, or any
|
|
14
|
+
* Web concerns. Those live in channel-control / channel-harness / channel-web,
|
|
15
|
+
* all of which share ONLY this contract.
|
|
16
|
+
*
|
|
17
|
+
* Policy model:
|
|
18
|
+
* - Groups support disabled, named allowlist, or global access through an
|
|
19
|
+
* explicit `defaultGroupRule`.
|
|
20
|
+
* - `allowFrom: []` means DENY ALL, never "open".
|
|
21
|
+
* - `requireMention` is an ACTIVATION fact, not authorization, and only
|
|
22
|
+
* applies in group conversations.
|
|
23
|
+
*/
|
|
24
|
+
import { z } from 'zod';
|
|
25
|
+
import type { MessageId } from './account.js';
|
|
26
|
+
import type { MessagePart } from './messages.js';
|
|
27
|
+
/** User-facing preset label. Runtime enforcement never branches on it. */
|
|
28
|
+
export type AccessPreset = 'owner-only' | 'allowlist' | 'custom';
|
|
29
|
+
export type DirectMessagePolicy = 'disabled' | 'allowlist' | 'open';
|
|
30
|
+
export type GroupPolicy = 'disabled' | 'allowlist' | 'open';
|
|
31
|
+
/**
|
|
32
|
+
* Within an already explicitly-allowed group:
|
|
33
|
+
* - `allowlist`: `sender.id` must be present in `rule.allowFrom`
|
|
34
|
+
* - `open`: any sender in this NAMED group is authorized
|
|
35
|
+
*/
|
|
36
|
+
export type GroupSenderPolicy = 'allowlist' | 'open';
|
|
37
|
+
export interface GroupAccessRule {
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
senderPolicy: GroupSenderPolicy;
|
|
40
|
+
/** canonical sender.id exact-match allowlist. */
|
|
41
|
+
allowFrom: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Only configurable to `true` when `descriptor.mentions === true`.
|
|
44
|
+
* `undefined !== true`, so a channel without reliable activation facts
|
|
45
|
+
* can never be required to mention (no fail-open).
|
|
46
|
+
*/
|
|
47
|
+
requireMention: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface ChannelAccessPolicy {
|
|
50
|
+
/**
|
|
51
|
+
* Persistent schema version. Future semantic changes bump this version
|
|
52
|
+
* instead of silently re-interpreting old JSON. Unknown versions are
|
|
53
|
+
* treated as invalid (fail closed).
|
|
54
|
+
*/
|
|
55
|
+
version: 1;
|
|
56
|
+
/**
|
|
57
|
+
* Web UX / materialization source only. Runtime enforcement must NOT branch
|
|
58
|
+
* on preset.
|
|
59
|
+
*/
|
|
60
|
+
preset: AccessPreset;
|
|
61
|
+
/** The local operator's canonical sender.id (optional until claimed). */
|
|
62
|
+
ownerId?: string;
|
|
63
|
+
/** Private-chat rule. */
|
|
64
|
+
dmPolicy: DirectMessagePolicy;
|
|
65
|
+
/** DM canonical sender.id allowlist (V1 only exists for dmPolicy=allowlist). */
|
|
66
|
+
allowFrom: string[];
|
|
67
|
+
/** Whether groups are disabled, explicitly named, or governed by a default rule. */
|
|
68
|
+
groupPolicy: GroupPolicy;
|
|
69
|
+
/** canonical conversation.id -> rule. */
|
|
70
|
+
groups: Record<string, GroupAccessRule>;
|
|
71
|
+
/** Required only when groupPolicy=open; applies to every inbound group. */
|
|
72
|
+
defaultGroupRule?: GroupAccessRule;
|
|
73
|
+
}
|
|
74
|
+
export declare const accessPresetSchema: z.ZodEnum<{
|
|
75
|
+
"owner-only": "owner-only";
|
|
76
|
+
allowlist: "allowlist";
|
|
77
|
+
custom: "custom";
|
|
78
|
+
}>;
|
|
79
|
+
export declare const directMessagePolicySchema: z.ZodEnum<{
|
|
80
|
+
allowlist: "allowlist";
|
|
81
|
+
disabled: "disabled";
|
|
82
|
+
open: "open";
|
|
83
|
+
}>;
|
|
84
|
+
export declare const groupPolicySchema: z.ZodEnum<{
|
|
85
|
+
allowlist: "allowlist";
|
|
86
|
+
disabled: "disabled";
|
|
87
|
+
open: "open";
|
|
88
|
+
}>;
|
|
89
|
+
export declare const groupSenderPolicySchema: z.ZodEnum<{
|
|
90
|
+
allowlist: "allowlist";
|
|
91
|
+
open: "open";
|
|
92
|
+
}>;
|
|
93
|
+
export declare const groupAccessRuleSchema: z.ZodObject<{
|
|
94
|
+
enabled: z.ZodBoolean;
|
|
95
|
+
senderPolicy: z.ZodEnum<{
|
|
96
|
+
allowlist: "allowlist";
|
|
97
|
+
open: "open";
|
|
98
|
+
}>;
|
|
99
|
+
allowFrom: z.ZodArray<z.ZodString>;
|
|
100
|
+
requireMention: z.ZodBoolean;
|
|
101
|
+
}, z.core.$strip>;
|
|
102
|
+
/**
|
|
103
|
+
* Strict access-policy schema. Matches the persisted `access:policy:v1:*` JSON.
|
|
104
|
+
* Every field is enforced; unknown keys are rejected (a policy JSON must be
|
|
105
|
+
* exactly what we understand). `ownerId` is optional (pre-claim).
|
|
106
|
+
*/
|
|
107
|
+
export declare const channelAccessPolicySchema: z.ZodObject<{
|
|
108
|
+
version: z.ZodLiteral<1>;
|
|
109
|
+
preset: z.ZodEnum<{
|
|
110
|
+
"owner-only": "owner-only";
|
|
111
|
+
allowlist: "allowlist";
|
|
112
|
+
custom: "custom";
|
|
113
|
+
}>;
|
|
114
|
+
ownerId: z.ZodOptional<z.ZodString>;
|
|
115
|
+
dmPolicy: z.ZodEnum<{
|
|
116
|
+
allowlist: "allowlist";
|
|
117
|
+
disabled: "disabled";
|
|
118
|
+
open: "open";
|
|
119
|
+
}>;
|
|
120
|
+
allowFrom: z.ZodArray<z.ZodString>;
|
|
121
|
+
groupPolicy: z.ZodEnum<{
|
|
122
|
+
allowlist: "allowlist";
|
|
123
|
+
disabled: "disabled";
|
|
124
|
+
open: "open";
|
|
125
|
+
}>;
|
|
126
|
+
groups: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
127
|
+
enabled: z.ZodBoolean;
|
|
128
|
+
senderPolicy: z.ZodEnum<{
|
|
129
|
+
allowlist: "allowlist";
|
|
130
|
+
open: "open";
|
|
131
|
+
}>;
|
|
132
|
+
allowFrom: z.ZodArray<z.ZodString>;
|
|
133
|
+
requireMention: z.ZodBoolean;
|
|
134
|
+
}, z.core.$strip>>;
|
|
135
|
+
defaultGroupRule: z.ZodOptional<z.ZodObject<{
|
|
136
|
+
enabled: z.ZodBoolean;
|
|
137
|
+
senderPolicy: z.ZodEnum<{
|
|
138
|
+
allowlist: "allowlist";
|
|
139
|
+
open: "open";
|
|
140
|
+
}>;
|
|
141
|
+
allowFrom: z.ZodArray<z.ZodString>;
|
|
142
|
+
requireMention: z.ZodBoolean;
|
|
143
|
+
}, z.core.$strip>>;
|
|
144
|
+
}, z.core.$strict>;
|
|
145
|
+
/**
|
|
146
|
+
* Shared channel-domain KV namespace for an access policy. Reused by both the
|
|
147
|
+
* channel-control writer and the channel-harness reader so neither hard-codes
|
|
148
|
+
* the other's key format. E.g. `access:policy:v1:telegram:main`.
|
|
149
|
+
*/
|
|
150
|
+
export declare function accessPolicyStorageKey(channelId: string, accountId: string): string;
|
|
151
|
+
/**
|
|
152
|
+
* The single reserved control-plane command. It is the ONLY inbound message
|
|
153
|
+
* that may be observed by the Control Plane before any access policy exists,
|
|
154
|
+
* and it MUST never reach the model / command dispatcher / Session / Binding.
|
|
155
|
+
*
|
|
156
|
+
* Format: `/dsh-claim <challengeCode>`
|
|
157
|
+
*/
|
|
158
|
+
export declare const OWNER_CLAIM_COMMAND = "/dsh-claim";
|
|
159
|
+
export interface ParsedOwnerClaimCommand {
|
|
160
|
+
command: '/dsh-claim';
|
|
161
|
+
/** 8+ character one-time challenge code sent to the bot in a DM. */
|
|
162
|
+
code?: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* True when `text` looks like the reserved claim command (exact slash at byte
|
|
166
|
+
* zero — shared with the official parseCommand convention). Both the Harness
|
|
167
|
+
* reserved-claim gate and the Control owner-claim observer use this same rule.
|
|
168
|
+
*/
|
|
169
|
+
export declare function isReservedClaimCommand(text: string): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Parse a `/dsh-claim ...` line into its challenge code, or `undefined` when
|
|
172
|
+
* the text is not a claim command. Only the first whitespace-delimited token
|
|
173
|
+
* after the command is treated as the code; the rest is ignored.
|
|
174
|
+
*/
|
|
175
|
+
export declare function parseOwnerClaimCommand(text: string): ParsedOwnerClaimCommand | undefined;
|
|
176
|
+
export interface MessageActivation {
|
|
177
|
+
/**
|
|
178
|
+
* Whether this message reliably, explicitly mentions the current Bot.
|
|
179
|
+
* `undefined`/`false` means we have no reliable fact — a rule requiring
|
|
180
|
+
* mention must NOT be treated as activated.
|
|
181
|
+
*/
|
|
182
|
+
mentionedBot?: boolean;
|
|
183
|
+
/** Reserved for a future activation policy; V1 does not use it. */
|
|
184
|
+
repliedToBot?: boolean;
|
|
185
|
+
}
|
|
186
|
+
export interface AccessMessageRef {
|
|
187
|
+
id: MessageId;
|
|
188
|
+
content: MessagePart[];
|
|
189
|
+
replyTo?: MessageId;
|
|
190
|
+
createdAt?: number;
|
|
191
|
+
activation?: MessageActivation;
|
|
192
|
+
}
|
|
193
|
+
export type { MessageId };
|
|
194
|
+
//# sourceMappingURL=access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAMjD,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjE,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,iBAAiB,CAAC;IAChC,iDAAiD;IACjD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,EAAE,CAAC,CAAC;IACX;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,gFAAgF;IAChF,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oFAAoF;IACpF,WAAW,EAAE,WAAW,CAAC;IACzB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,eAAe,CAAC;CACpC;AAaD,eAAO,MAAM,kBAAkB;;;;EAAgD,CAAC;AAChF,eAAO,MAAM,yBAAyB;;;;EAA4C,CAAC;AACnF,eAAO,MAAM,iBAAiB;;;;EAA4C,CAAC;AAC3E,eAAO,MAAM,uBAAuB;;;EAAgC,CAAC;AAErE,eAAO,MAAM,qBAAqB;;;;;;;;iBAKhC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0BlC,CAAC;AAML;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnF;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,YAAY,CAAC;IACtB,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAKxF;AAMD,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
package/lib/access.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Access Policy — the shared, versioned, cross-package contract for
|
|
3
|
+
* inbound access control.
|
|
4
|
+
*
|
|
5
|
+
* This module defines ONLY stable cross-package semantics:
|
|
6
|
+
*
|
|
7
|
+
* - the `ChannelAccessPolicy` type + zod schema
|
|
8
|
+
* - the versioned storage-key codec (`accessPolicyStorageKey`)
|
|
9
|
+
* - `MessageActivation` (the `activation` fact on a received message)
|
|
10
|
+
* - the reserved owner-claim command constant + parser (`/dsh-claim`)
|
|
11
|
+
*
|
|
12
|
+
* It does NOT implement policy persistence orchestration, the authorization
|
|
13
|
+
* decision, Owner Claim session lifecycle, platform identity parsing, or any
|
|
14
|
+
* Web concerns. Those live in channel-control / channel-harness / channel-web,
|
|
15
|
+
* all of which share ONLY this contract.
|
|
16
|
+
*
|
|
17
|
+
* Policy model:
|
|
18
|
+
* - Groups support disabled, named allowlist, or global access through an
|
|
19
|
+
* explicit `defaultGroupRule`.
|
|
20
|
+
* - `allowFrom: []` means DENY ALL, never "open".
|
|
21
|
+
* - `requireMention` is an ACTIVATION fact, not authorization, and only
|
|
22
|
+
* applies in group conversations.
|
|
23
|
+
*/
|
|
24
|
+
import { z } from 'zod';
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Zod schema (shared trust boundary — validation.ts / harness resolver reuse it)
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
const idSchema = z
|
|
29
|
+
.string()
|
|
30
|
+
.min(1)
|
|
31
|
+
// Trim leading/trailing whitespace is the ONLY allowed normalization. No
|
|
32
|
+
// lowercase, no fuzzy matching, no username resolution. IDs are opaque.
|
|
33
|
+
.trim();
|
|
34
|
+
export const accessPresetSchema = z.enum(['owner-only', 'allowlist', 'custom']);
|
|
35
|
+
export const directMessagePolicySchema = z.enum(['disabled', 'allowlist', 'open']);
|
|
36
|
+
export const groupPolicySchema = z.enum(['disabled', 'allowlist', 'open']);
|
|
37
|
+
export const groupSenderPolicySchema = z.enum(['allowlist', 'open']);
|
|
38
|
+
export const groupAccessRuleSchema = z.object({
|
|
39
|
+
enabled: z.boolean(),
|
|
40
|
+
senderPolicy: groupSenderPolicySchema,
|
|
41
|
+
allowFrom: z.array(idSchema),
|
|
42
|
+
requireMention: z.boolean(),
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Strict access-policy schema. Matches the persisted `access:policy:v1:*` JSON.
|
|
46
|
+
* Every field is enforced; unknown keys are rejected (a policy JSON must be
|
|
47
|
+
* exactly what we understand). `ownerId` is optional (pre-claim).
|
|
48
|
+
*/
|
|
49
|
+
export const channelAccessPolicySchema = z
|
|
50
|
+
.object({
|
|
51
|
+
version: z.literal(1),
|
|
52
|
+
preset: accessPresetSchema,
|
|
53
|
+
ownerId: idSchema.optional(),
|
|
54
|
+
dmPolicy: directMessagePolicySchema,
|
|
55
|
+
allowFrom: z.array(idSchema),
|
|
56
|
+
groupPolicy: groupPolicySchema,
|
|
57
|
+
groups: z.record(z.string().trim(), groupAccessRuleSchema),
|
|
58
|
+
defaultGroupRule: groupAccessRuleSchema.optional(),
|
|
59
|
+
})
|
|
60
|
+
.strict()
|
|
61
|
+
.superRefine((policy, ctx) => {
|
|
62
|
+
if (policy.groupPolicy === 'open') {
|
|
63
|
+
if (!policy.defaultGroupRule) {
|
|
64
|
+
ctx.addIssue({ code: 'custom', path: ['defaultGroupRule'], message: 'required when groupPolicy is open' });
|
|
65
|
+
}
|
|
66
|
+
if (Object.keys(policy.groups).length > 0) {
|
|
67
|
+
ctx.addIssue({ code: 'custom', path: ['groups'], message: 'must be empty when groupPolicy is open' });
|
|
68
|
+
}
|
|
69
|
+
if (policy.defaultGroupRule?.enabled !== true) {
|
|
70
|
+
ctx.addIssue({ code: 'custom', path: ['defaultGroupRule', 'enabled'], message: 'must be enabled when groupPolicy is open' });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (policy.defaultGroupRule) {
|
|
74
|
+
ctx.addIssue({ code: 'custom', path: ['defaultGroupRule'], message: 'only allowed when groupPolicy is open' });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Storage key codec
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Shared channel-domain KV namespace for an access policy. Reused by both the
|
|
82
|
+
* channel-control writer and the channel-harness reader so neither hard-codes
|
|
83
|
+
* the other's key format. E.g. `access:policy:v1:telegram:main`.
|
|
84
|
+
*/
|
|
85
|
+
export function accessPolicyStorageKey(channelId, accountId) {
|
|
86
|
+
return `access:policy:v1:${encodeURIComponent(channelId)}:${encodeURIComponent(accountId)}`;
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Reserved owner-claim command
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
/**
|
|
92
|
+
* The single reserved control-plane command. It is the ONLY inbound message
|
|
93
|
+
* that may be observed by the Control Plane before any access policy exists,
|
|
94
|
+
* and it MUST never reach the model / command dispatcher / Session / Binding.
|
|
95
|
+
*
|
|
96
|
+
* Format: `/dsh-claim <challengeCode>`
|
|
97
|
+
*/
|
|
98
|
+
export const OWNER_CLAIM_COMMAND = '/dsh-claim';
|
|
99
|
+
/**
|
|
100
|
+
* True when `text` looks like the reserved claim command (exact slash at byte
|
|
101
|
+
* zero — shared with the official parseCommand convention). Both the Harness
|
|
102
|
+
* reserved-claim gate and the Control owner-claim observer use this same rule.
|
|
103
|
+
*/
|
|
104
|
+
export function isReservedClaimCommand(text) {
|
|
105
|
+
if (!text.startsWith(OWNER_CLAIM_COMMAND))
|
|
106
|
+
return false;
|
|
107
|
+
const rest = text.slice(OWNER_CLAIM_COMMAND.length);
|
|
108
|
+
return rest.length === 0 || /^\s/.test(rest);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Parse a `/dsh-claim ...` line into its challenge code, or `undefined` when
|
|
112
|
+
* the text is not a claim command. Only the first whitespace-delimited token
|
|
113
|
+
* after the command is treated as the code; the rest is ignored.
|
|
114
|
+
*/
|
|
115
|
+
export function parseOwnerClaimCommand(text) {
|
|
116
|
+
if (!isReservedClaimCommand(text))
|
|
117
|
+
return undefined;
|
|
118
|
+
const rest = text.slice(OWNER_CLAIM_COMMAND.length).trim();
|
|
119
|
+
const code = rest.split(/\s+/)[0];
|
|
120
|
+
return { command: '/dsh-claim', code: code || undefined };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../src/access.ts"],"names":[],"mappings":"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"}
|
package/lib/account.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity types shared by every channel.
|
|
3
|
+
*
|
|
4
|
+
* All identities are branded strings so that a `ConversationId` cannot be
|
|
5
|
+
* silently passed where a `MessageId` is expected. Adapters are responsible
|
|
6
|
+
* for mapping platform-native ids into these types.
|
|
7
|
+
*/
|
|
8
|
+
/** Channel id, e.g. `'weixin'`, `'qq'`, `'dingtalk'`, `'lark'`. */
|
|
9
|
+
export type ChannelId = string & {
|
|
10
|
+
readonly __brand: 'ChannelId';
|
|
11
|
+
};
|
|
12
|
+
/** Account id within a channel, e.g. `'main'`, `'bot01'`, `'corpA'`. */
|
|
13
|
+
export type AccountId = string & {
|
|
14
|
+
readonly __brand: 'AccountId';
|
|
15
|
+
};
|
|
16
|
+
/** Conversation id within a channel account. */
|
|
17
|
+
export type ConversationId = string & {
|
|
18
|
+
readonly __brand: 'ConversationId';
|
|
19
|
+
};
|
|
20
|
+
/** Optional thread id within a conversation. */
|
|
21
|
+
export type ThreadId = string & {
|
|
22
|
+
readonly __brand: 'ThreadId';
|
|
23
|
+
};
|
|
24
|
+
/** Platform message id, used for dedup and reply correlation. */
|
|
25
|
+
export type MessageId = string & {
|
|
26
|
+
readonly __brand: 'MessageId';
|
|
27
|
+
};
|
|
28
|
+
/** Platform sender id. */
|
|
29
|
+
export type SenderId = string & {
|
|
30
|
+
readonly __brand: 'SenderId';
|
|
31
|
+
};
|
|
32
|
+
/** Key identifying one conversation binding target. */
|
|
33
|
+
export interface ChannelConversationKey {
|
|
34
|
+
channelId: ChannelId;
|
|
35
|
+
accountId: AccountId;
|
|
36
|
+
conversationId: ConversationId;
|
|
37
|
+
threadId?: ThreadId;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Canonical string form of a conversation key:
|
|
41
|
+
* `channel:account:conversation[:thread]`.
|
|
42
|
+
*
|
|
43
|
+
* Never allowed to collapse a whole account into one session — each
|
|
44
|
+
* conversation (and optionally thread) is a distinct key.
|
|
45
|
+
*/
|
|
46
|
+
export declare function conversationKey(key: ChannelConversationKey): string;
|
|
47
|
+
//# sourceMappingURL=account.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnE,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnE,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7E,gDAAgD;AAChD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAEjE,iEAAiE;AACjE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnE,0BAA0B;AAC1B,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAEjE,uDAAuD;AACvD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,sBAAsB,GAAG,MAAM,CAInE"}
|
package/lib/account.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity types shared by every channel.
|
|
3
|
+
*
|
|
4
|
+
* All identities are branded strings so that a `ConversationId` cannot be
|
|
5
|
+
* silently passed where a `MessageId` is expected. Adapters are responsible
|
|
6
|
+
* for mapping platform-native ids into these types.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Canonical string form of a conversation key:
|
|
10
|
+
* `channel:account:conversation[:thread]`.
|
|
11
|
+
*
|
|
12
|
+
* Never allowed to collapse a whole account into one session — each
|
|
13
|
+
* conversation (and optionally thread) is a distinct key.
|
|
14
|
+
*/
|
|
15
|
+
export function conversationKey(key) {
|
|
16
|
+
return key.threadId
|
|
17
|
+
? `${key.channelId}:${key.accountId}:${key.conversationId}:${key.threadId}`
|
|
18
|
+
: `${key.channelId}:${key.accountId}:${key.conversationId}`;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4BH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAA2B;IACzD,OAAO,GAAG,CAAC,QAAQ;QACjB,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,cAAc,IAAI,GAAG,CAAC,QAAQ,EAAE;QAC3E,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;AAChE,CAAC"}
|
package/lib/adapter.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ChannelAdapter` contract: the stable boundary between a messaging
|
|
3
|
+
* platform and the Channel Core.
|
|
4
|
+
*
|
|
5
|
+
* An adapter maps platform semantics to the Channel Contract and delegates
|
|
6
|
+
* all SDK/package/protocol interaction to an upstream driver. Adapters never
|
|
7
|
+
* call Harness Agent APIs.
|
|
8
|
+
*
|
|
9
|
+
* Streaming may be target-aware: an adapter's *streaming capability* can be
|
|
10
|
+
* static (`capabilities.streaming`) or depend on the concrete reply target via
|
|
11
|
+
* the optional `resolveStreamingMode(target)` hook (e.g. QQ streams natively
|
|
12
|
+
* for C2C with a triggering message id, buffering for groups — but the fields
|
|
13
|
+
* are generic and reused by Telegram reply, Slack thread reply, Lark reply and
|
|
14
|
+
* DingTalk reference).
|
|
15
|
+
*/
|
|
16
|
+
import type { ChannelAdapterContext } from './context.js';
|
|
17
|
+
import type { ChannelCapabilities, StreamingMode } from './capabilities.js';
|
|
18
|
+
import type { AuthState } from './events.js';
|
|
19
|
+
import type { ChannelConversationKey, MessageId } from './account.js';
|
|
20
|
+
import type { OutboundMessage, SendResult } from './messages.js';
|
|
21
|
+
import type { ChannelHealth } from './health.js';
|
|
22
|
+
export interface ChannelTarget extends ChannelConversationKey {
|
|
23
|
+
/**
|
|
24
|
+
* Conversation kind of the reply target. Generic capability field — reused
|
|
25
|
+
* by replies whose semantics differ between DMs and groups (e.g. QQ: C2C
|
|
26
|
+
* native streaming vs. group buffered). Not QQ-specific.
|
|
27
|
+
*/
|
|
28
|
+
conversationType?: 'dm' | 'group';
|
|
29
|
+
/**
|
|
30
|
+
* Id of the inbound message this reply answers. Lets adapters correlate a
|
|
31
|
+
* reply to the triggering message (Telegram reply, Slack thread reply, Lark
|
|
32
|
+
* reply, DingTalk reference, QQ C2C native streaming all reuse it). Not
|
|
33
|
+
* QQ-specific.
|
|
34
|
+
*/
|
|
35
|
+
replyToMessageId?: MessageId;
|
|
36
|
+
/** Raw target data the adapter needs but core does not model. */
|
|
37
|
+
raw?: unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Turn-scoped correlation id shared by every send within one Harness turn.
|
|
40
|
+
* Generic: adapters that correlate outbound sends to a single turn (e.g.
|
|
41
|
+
* Weixin run_id) read it; others ignore it.
|
|
42
|
+
*/
|
|
43
|
+
runId?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface CreateReplyOptions {
|
|
46
|
+
/** Send an initial "working on it" message immediately. */
|
|
47
|
+
placeholder?: string;
|
|
48
|
+
/** Whether reply content may contain markdown. */
|
|
49
|
+
markdown?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ReplyHandle {
|
|
52
|
+
/** Append a delta to the streaming reply. */
|
|
53
|
+
append(delta: string): Promise<void>;
|
|
54
|
+
/** Replace the whole reply content. */
|
|
55
|
+
replace(message: OutboundMessage): Promise<void>;
|
|
56
|
+
/** Finalize the reply, optionally with a final message. */
|
|
57
|
+
finish(message?: OutboundMessage): Promise<void>;
|
|
58
|
+
/** Mark the reply as failed. */
|
|
59
|
+
fail(error: unknown): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
export interface AuthChallenge {
|
|
62
|
+
/** Stable id of this challenge. */
|
|
63
|
+
id: string;
|
|
64
|
+
/** Human-readable instruction for the operator. */
|
|
65
|
+
instruction: string;
|
|
66
|
+
/** Optional QR/login URL for terminal or card rendering. */
|
|
67
|
+
qrUrl?: string;
|
|
68
|
+
/** Optional expiry timestamp. */
|
|
69
|
+
expiresAt?: number;
|
|
70
|
+
/** Adapter-specific payload; never a credential. */
|
|
71
|
+
payload?: unknown;
|
|
72
|
+
}
|
|
73
|
+
export interface AuthStatePoll {
|
|
74
|
+
state: Exclude<AuthState, 'unknown'>;
|
|
75
|
+
detail?: string;
|
|
76
|
+
}
|
|
77
|
+
export interface AuthInput {
|
|
78
|
+
kind: 'verification-code';
|
|
79
|
+
value: string;
|
|
80
|
+
}
|
|
81
|
+
export interface ChannelAdapter {
|
|
82
|
+
readonly id: string;
|
|
83
|
+
readonly capabilities: ChannelCapabilities;
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the streaming mode for one reply target. Adapters whose
|
|
86
|
+
* streaming capability depends on the target (e.g. QQ: C2C + msgId →
|
|
87
|
+
* native, group → buffered) override the static `capabilities.streaming`.
|
|
88
|
+
* Defaults to `capabilities.streaming` when absent.
|
|
89
|
+
*/
|
|
90
|
+
resolveStreamingMode?(target: ChannelTarget): StreamingMode;
|
|
91
|
+
start(ctx: ChannelAdapterContext): Promise<void>;
|
|
92
|
+
stop(): Promise<void>;
|
|
93
|
+
send(target: ChannelTarget, message: OutboundMessage): Promise<SendResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Optional in-place edit of an already-sent message (e.g. Telegram
|
|
96
|
+
* `editMessageText` / `editMessageReplyMarkup`, Lark/DingTalk card update).
|
|
97
|
+
* Enables interactive flows that rewrite a single sent message (multi-select
|
|
98
|
+
* toggling, removing stale buttons). Adapters without an edit primitive leave
|
|
99
|
+
* it undefined; the harness must degrade those flows to a non-edit strategy.
|
|
100
|
+
*/
|
|
101
|
+
edit?(target: ChannelTarget, messageId: string, message: OutboundMessage): Promise<SendResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort typing indicator. Optional: adapters with a typing API (e.g.
|
|
104
|
+
* Weixin sendtyping) implement these; the harness fires them around turn
|
|
105
|
+
* start/end and NEVER lets a failure break reply delivery.
|
|
106
|
+
*/
|
|
107
|
+
startTyping?(conversationId: string): Promise<void>;
|
|
108
|
+
stopTyping?(conversationId: string): Promise<void>;
|
|
109
|
+
startTypingForTarget?(target: ChannelTarget): Promise<void>;
|
|
110
|
+
stopTypingForTarget?(target: ChannelTarget): Promise<void>;
|
|
111
|
+
createReply?(target: ChannelTarget, options?: CreateReplyOptions): Promise<ReplyHandle>;
|
|
112
|
+
beginAuth?(): Promise<AuthChallenge>;
|
|
113
|
+
pollAuth?(challenge: AuthChallenge): Promise<AuthStatePoll>;
|
|
114
|
+
submitAuthInput?(challenge: AuthChallenge, input: AuthInput): Promise<void> | void;
|
|
115
|
+
getHealth?(): Promise<ChannelHealth>;
|
|
116
|
+
}
|
|
117
|
+
/** Emit a message event through the adapter context helper. */
|
|
118
|
+
export declare function isChannelAdapter(value: unknown): value is ChannelAdapter;
|
|
119
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/lib/adapter.js
ADDED