@qwen-code/channel-base 0.26.5 → 0.26.7
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 +4 -1
- package/dist/ChannelBase.d.ts +28 -2
- package/dist/ChannelBase.d.ts.map +1 -1
- package/dist/ChannelBase.js +99 -16
- package/dist/ChannelBase.js.map +1 -1
- package/dist/GroupGate.d.ts +9 -1
- package/dist/GroupGate.d.ts.map +1 -1
- package/dist/GroupGate.js +21 -3
- package/dist/GroupGate.js.map +1 -1
- package/dist/PollingChannelBase.d.ts +26 -0
- package/dist/PollingChannelBase.d.ts.map +1 -1
- package/dist/PollingChannelBase.js +102 -16
- package/dist/PollingChannelBase.js.map +1 -1
- package/dist/bound-session-delivery.d.ts +27 -0
- package/dist/bound-session-delivery.d.ts.map +1 -0
- package/dist/bound-session-delivery.js +39 -0
- package/dist/bound-session-delivery.js.map +1 -0
- package/dist/channel-events.d.ts +82 -0
- package/dist/channel-events.d.ts.map +1 -0
- package/dist/channel-events.js +404 -0
- package/dist/channel-events.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/GroupGate.d.ts
CHANGED
|
@@ -10,7 +10,15 @@ export declare class GroupGate {
|
|
|
10
10
|
private policy;
|
|
11
11
|
private groups;
|
|
12
12
|
private pairingStore;
|
|
13
|
-
|
|
13
|
+
private normalizeGroupId;
|
|
14
|
+
constructor(policy?: GroupPolicy, groups?: Record<string, GroupConfig>, pairingStore?: PairingStore, normalizeGroupId?: (groupId: string) => string);
|
|
15
|
+
/**
|
|
16
|
+
* Configured groups are matched verbatim first. Platforms whose group IDs
|
|
17
|
+
* are case-insensitive (GitHub repository full names) also match a
|
|
18
|
+
* normalized key, so the operator's spelling admits the canonical ID the
|
|
19
|
+
* platform reports.
|
|
20
|
+
*/
|
|
21
|
+
private groupConfigFor;
|
|
14
22
|
/**
|
|
15
23
|
* Full group check: policy + allowlist + pairing + mention gating.
|
|
16
24
|
* Evaluation order:
|
package/dist/GroupGate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupGate.d.ts","sourceRoot":"","sources":["../src/GroupGate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EACV,0BAA0B,EAC1B,YAAY,EACb,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EACH,UAAU,GACV,iBAAiB,GACjB,kBAAkB,GAClB,0BAA0B,GAC1B,kBAAkB,CAAC;IACvB,mGAAmG;IACnG,OAAO,CAAC,EAAE,0BAA0B,CAAC;CACtC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,YAAY,CAAsB;
|
|
1
|
+
{"version":3,"file":"GroupGate.d.ts","sourceRoot":"","sources":["../src/GroupGate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EACV,0BAA0B,EAC1B,YAAY,EACb,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EACH,UAAU,GACV,iBAAiB,GACjB,kBAAkB,GAClB,0BAA0B,GAC1B,kBAAkB,CAAC;IACvB,mGAAmG;IACnG,OAAO,CAAC,EAAE,0BAA0B,CAAC;CACtC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAA8B;gBAGpD,MAAM,GAAE,WAAwB,EAChC,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAM,EACxC,YAAY,CAAC,EAAE,YAAY,EAC3B,gBAAgB,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAA6B;IAQtE;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAStB;;;;;;;;;;;;;;OAcG;IACH,KAAK,CACH,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE;QAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;KAAO,GAC/C,gBAAgB;IAoDnB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CAG1C;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAClC,IAAI,CAMN"}
|
package/dist/GroupGate.js
CHANGED
|
@@ -2,10 +2,28 @@ export class GroupGate {
|
|
|
2
2
|
policy;
|
|
3
3
|
groups;
|
|
4
4
|
pairingStore;
|
|
5
|
-
|
|
5
|
+
normalizeGroupId;
|
|
6
|
+
constructor(policy = 'disabled', groups = {}, pairingStore, normalizeGroupId = (groupId) => groupId) {
|
|
6
7
|
this.policy = policy;
|
|
7
8
|
this.groups = groups;
|
|
8
9
|
this.pairingStore = pairingStore ?? null;
|
|
10
|
+
this.normalizeGroupId = normalizeGroupId;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Configured groups are matched verbatim first. Platforms whose group IDs
|
|
14
|
+
* are case-insensitive (GitHub repository full names) also match a
|
|
15
|
+
* normalized key, so the operator's spelling admits the canonical ID the
|
|
16
|
+
* platform reports.
|
|
17
|
+
*/
|
|
18
|
+
groupConfigFor(groupId) {
|
|
19
|
+
if (Object.hasOwn(this.groups, groupId))
|
|
20
|
+
return this.groups[groupId];
|
|
21
|
+
const normalized = this.normalizeGroupId(groupId);
|
|
22
|
+
for (const [key, group] of Object.entries(this.groups)) {
|
|
23
|
+
if (this.normalizeGroupId(key) === normalized)
|
|
24
|
+
return group;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
9
27
|
}
|
|
10
28
|
/**
|
|
11
29
|
* Full group check: policy + allowlist + pairing + mention gating.
|
|
@@ -32,7 +50,7 @@ export class GroupGate {
|
|
|
32
50
|
if (this.policy === 'allowlist') {
|
|
33
51
|
// In allowlist mode, "*" is only a default config — not a wildcard allow.
|
|
34
52
|
// The group must be explicitly listed by ID.
|
|
35
|
-
if (!this.
|
|
53
|
+
if (!this.groupConfigFor(envelope.chatId)) {
|
|
36
54
|
return { allowed: false, reason: 'not_allowlisted' };
|
|
37
55
|
}
|
|
38
56
|
}
|
|
@@ -49,7 +67,7 @@ export class GroupGate {
|
|
|
49
67
|
pairing: result ?? { rejected: 'cap_reached' },
|
|
50
68
|
};
|
|
51
69
|
}
|
|
52
|
-
const requireMention = this.
|
|
70
|
+
const requireMention = this.groupConfigFor(envelope.chatId)?.requireMention ??
|
|
53
71
|
this.groups['*']?.requireMention ??
|
|
54
72
|
true;
|
|
55
73
|
if (requireMention && !envelope.isMentioned && !envelope.isReplyToBot) {
|
package/dist/GroupGate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupGate.js","sourceRoot":"","sources":["../src/GroupGate.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,SAAS;IACZ,MAAM,CAAc;IACpB,MAAM,CAA8B;IACpC,YAAY,CAAsB;
|
|
1
|
+
{"version":3,"file":"GroupGate.js","sourceRoot":"","sources":["../src/GroupGate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import type { ChannelAgentBridge } from './ChannelAgentBridge.js';
|
|
2
2
|
import type { ChannelBaseOptions } from './ChannelBase.js';
|
|
3
3
|
import { ChannelBase } from './ChannelBase.js';
|
|
4
|
+
import type { ChannelEventDetail } from './channel-events.js';
|
|
4
5
|
import type { ChannelConfig } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Inclusive bounds the poll loop enforces at runtime. Node clamps a timeout
|
|
8
|
+
* above its 32-bit range to 1ms and rounds a sub-millisecond one to ~1ms, so
|
|
9
|
+
* either end left open turns the loop into a hot loop that burns the platform's
|
|
10
|
+
* rate quota. The descriptor vocabulary has only an exclusive lower bound, so
|
|
11
|
+
* the inclusive floor a field documents is enforced here; a plugin declares
|
|
12
|
+
* `maximum: MAX_POLL_INTERVAL_MS` so the two cannot drift apart.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MIN_POLL_INTERVAL_MS = 1;
|
|
15
|
+
export declare const MAX_POLL_INTERVAL_MS = 86400000;
|
|
5
16
|
export declare abstract class PollingChannelBase<Cursor> extends ChannelBase {
|
|
6
17
|
protected cursor: Cursor;
|
|
7
18
|
private abortController;
|
|
8
19
|
private running;
|
|
9
20
|
private consecutiveErrors;
|
|
21
|
+
private pollEventDetail;
|
|
22
|
+
/**
|
|
23
|
+
* Attaches adapter-specific counters (for example how many notifications a
|
|
24
|
+
* poll returned) to the poll event the base class records when the poll
|
|
25
|
+
* settles. The value is consumed once, by the poll it belongs to.
|
|
26
|
+
*/
|
|
27
|
+
protected setPollEventDetail(detail: ChannelEventDetail | undefined): void;
|
|
10
28
|
protected abortableSleep(ms: number): Promise<void>;
|
|
11
29
|
constructor(name: string, config: ChannelConfig, bridge: ChannelAgentBridge, options?: ChannelBaseOptions);
|
|
12
30
|
protected abstract pollOnce(): Promise<void>;
|
|
@@ -19,5 +37,13 @@ export declare abstract class PollingChannelBase<Cursor> extends ChannelBase {
|
|
|
19
37
|
private runLoop;
|
|
20
38
|
private loadCursorFromDisk;
|
|
21
39
|
private cursorPath;
|
|
40
|
+
/**
|
|
41
|
+
* Pre-scoping layout: one file directly under `channels/`, shared by every
|
|
42
|
+
* workspace running a channel of this name. Read only, so an upgrade keeps
|
|
43
|
+
* its cursor; the next save lands in the scoped path and the legacy file
|
|
44
|
+
* stays for other workspaces (and older versions) to read.
|
|
45
|
+
*/
|
|
46
|
+
private legacyCursorPath;
|
|
47
|
+
private cursorFileName;
|
|
22
48
|
}
|
|
23
49
|
//# sourceMappingURL=PollingChannelBase.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PollingChannelBase.d.ts","sourceRoot":"","sources":["../src/PollingChannelBase.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PollingChannelBase.d.ts","sourceRoot":"","sources":["../src/PollingChannelBase.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,oBAAoB,WAAa,CAAC;AAE/C,8BAAsB,kBAAkB,CAAC,MAAM,CAAE,SAAQ,WAAW;IAClE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAAiC;IAExD;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,IAAI;IAI1E,SAAS,CAAC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gBAoBjD,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,kBAAkB,EAC1B,OAAO,CAAC,EAAE,kBAAkB;IAM9B,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,mBAAmB,IAAI,MAAM;IAEhD,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAMxD,SAAS,KAAK,YAAY,IAAI,MAAM,CAwBnC;IAED,SAAS,CAAC,UAAU,IAAI,IAAI;IAmB5B,SAAS,CAAC,aAAa,IAAI,IAAI;IAQ/B,SAAS,CAAC,YAAY,IAAI,IAAI;YAKhB,OAAO;IAqDrB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,UAAU;IASlB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,cAAc;CAQvB"}
|
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import { ChannelBase } from './ChannelBase.js';
|
|
6
|
-
import { getGlobalQwenDir } from './paths.js';
|
|
6
|
+
import { getGlobalQwenDir, getWorkspaceScopeDirName } from './paths.js';
|
|
7
|
+
import { sanitizeLogText } from './sanitize.js';
|
|
7
8
|
const INITIAL_BACKOFF = 2_000;
|
|
8
9
|
const MAX_BACKOFF = 30_000;
|
|
10
|
+
/**
|
|
11
|
+
* Inclusive bounds the poll loop enforces at runtime. Node clamps a timeout
|
|
12
|
+
* above its 32-bit range to 1ms and rounds a sub-millisecond one to ~1ms, so
|
|
13
|
+
* either end left open turns the loop into a hot loop that burns the platform's
|
|
14
|
+
* rate quota. The descriptor vocabulary has only an exclusive lower bound, so
|
|
15
|
+
* the inclusive floor a field documents is enforced here; a plugin declares
|
|
16
|
+
* `maximum: MAX_POLL_INTERVAL_MS` so the two cannot drift apart.
|
|
17
|
+
*/
|
|
18
|
+
export const MIN_POLL_INTERVAL_MS = 1;
|
|
19
|
+
export const MAX_POLL_INTERVAL_MS = 86_400_000;
|
|
9
20
|
export class PollingChannelBase extends ChannelBase {
|
|
10
21
|
cursor;
|
|
11
22
|
abortController = new AbortController();
|
|
12
23
|
running = false;
|
|
13
24
|
consecutiveErrors = 0;
|
|
25
|
+
pollEventDetail;
|
|
26
|
+
/**
|
|
27
|
+
* Attaches adapter-specific counters (for example how many notifications a
|
|
28
|
+
* poll returned) to the poll event the base class records when the poll
|
|
29
|
+
* settles. The value is consumed once, by the poll it belongs to.
|
|
30
|
+
*/
|
|
31
|
+
setPollEventDetail(detail) {
|
|
32
|
+
this.pollEventDetail = detail;
|
|
33
|
+
}
|
|
14
34
|
abortableSleep(ms) {
|
|
15
35
|
const signal = this.abortController.signal;
|
|
16
36
|
return new Promise((resolve) => {
|
|
@@ -43,15 +63,38 @@ export class PollingChannelBase extends ChannelBase {
|
|
|
43
63
|
if (typeof configured === 'number' &&
|
|
44
64
|
Number.isFinite(configured) &&
|
|
45
65
|
configured > 0) {
|
|
46
|
-
|
|
66
|
+
// Node clamps a timeout above its 32-bit range to 1ms, which would turn
|
|
67
|
+
// the poll loop into a hot loop that burns the platform's rate quota.
|
|
68
|
+
// Clamp rather than fall back: an interval between one day and that limit
|
|
69
|
+
// was honoured before this ceiling existed and is not a hot loop, so
|
|
70
|
+
// replacing it with 60s would multiply the poll rate by orders of
|
|
71
|
+
// magnitude. One day matches the ceiling Email enforces and GitHub
|
|
72
|
+
// declares.
|
|
73
|
+
//
|
|
74
|
+
// The low end needs the same treatment from the other direction: a
|
|
75
|
+
// fractional interval such as 0.5 passes the descriptor's exclusive
|
|
76
|
+
// lower bound and the store, and setTimeout rounds it to ~1ms.
|
|
77
|
+
return Math.min(Math.max(configured, MIN_POLL_INTERVAL_MS), MAX_POLL_INTERVAL_MS);
|
|
47
78
|
}
|
|
48
79
|
return 60_000;
|
|
49
80
|
}
|
|
50
81
|
saveCursor() {
|
|
51
82
|
const path = this.cursorPath();
|
|
52
|
-
|
|
83
|
+
const dir = dirname(path);
|
|
84
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
85
|
+
try {
|
|
86
|
+
chmodSync(dir, 0o700);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// A directory we do not own stays as created; the write below still
|
|
90
|
+
// decides whether this channel can use it.
|
|
91
|
+
}
|
|
53
92
|
const tmp = `${path}.tmp`;
|
|
54
|
-
writeFileSync(tmp, JSON.stringify(this.cursor) + '\n',
|
|
93
|
+
writeFileSync(tmp, JSON.stringify(this.cursor) + '\n', {
|
|
94
|
+
encoding: 'utf-8',
|
|
95
|
+
mode: 0o600,
|
|
96
|
+
});
|
|
97
|
+
chmodSync(tmp, 0o600);
|
|
55
98
|
renameSync(tmp, path);
|
|
56
99
|
}
|
|
57
100
|
startPollLoop() {
|
|
@@ -69,15 +112,42 @@ export class PollingChannelBase extends ChannelBase {
|
|
|
69
112
|
async runLoop() {
|
|
70
113
|
const signal = this.abortController.signal;
|
|
71
114
|
while (this.running && !signal.aborted) {
|
|
115
|
+
const startedAt = Date.now();
|
|
116
|
+
// The adapter populates the detail while its poll runs, so it is reset
|
|
117
|
+
// here and consumed after the poll settles — reading it up front would
|
|
118
|
+
// record the previous poll's counters against this poll's timestamp.
|
|
119
|
+
this.pollEventDetail = undefined;
|
|
72
120
|
try {
|
|
73
121
|
await this.pollOnce();
|
|
74
122
|
this.saveCursor();
|
|
75
123
|
this.consecutiveErrors = 0;
|
|
124
|
+
const detail = this.pollEventDetail;
|
|
125
|
+
this.pollEventDetail = undefined;
|
|
126
|
+
this.recordChannelEvent({
|
|
127
|
+
type: 'poll',
|
|
128
|
+
outcome: 'ok',
|
|
129
|
+
durationMs: Date.now() - startedAt,
|
|
130
|
+
...(detail ? { detail } : {}),
|
|
131
|
+
});
|
|
76
132
|
}
|
|
77
133
|
catch (err) {
|
|
78
134
|
this.consecutiveErrors++;
|
|
79
135
|
const backoff = Math.min(INITIAL_BACKOFF * 2 ** (this.consecutiveErrors - 1), MAX_BACKOFF);
|
|
80
|
-
|
|
136
|
+
const detail = this.pollEventDetail;
|
|
137
|
+
this.pollEventDetail = undefined;
|
|
138
|
+
const message = sanitizeLogText(err instanceof Error ? err.message : String(err), 200);
|
|
139
|
+
process.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${message}\n`);
|
|
140
|
+
this.recordChannelEvent({
|
|
141
|
+
type: 'poll',
|
|
142
|
+
outcome: 'failed',
|
|
143
|
+
durationMs: Date.now() - startedAt,
|
|
144
|
+
error: message,
|
|
145
|
+
detail: {
|
|
146
|
+
...(detail ?? {}),
|
|
147
|
+
consecutiveErrors: this.consecutiveErrors,
|
|
148
|
+
backoffMs: backoff,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
81
151
|
await this.abortableSleep(backoff);
|
|
82
152
|
continue;
|
|
83
153
|
}
|
|
@@ -85,23 +155,39 @@ export class PollingChannelBase extends ChannelBase {
|
|
|
85
155
|
}
|
|
86
156
|
}
|
|
87
157
|
loadCursorFromDisk() {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
158
|
+
for (const path of [this.cursorPath(), this.legacyCursorPath()]) {
|
|
159
|
+
try {
|
|
160
|
+
const raw = readFileSync(path, 'utf-8').trim();
|
|
161
|
+
if (!raw)
|
|
162
|
+
return null;
|
|
163
|
+
return this.validateCursor(JSON.parse(raw));
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (err.code !== 'ENOENT')
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
96
169
|
}
|
|
170
|
+
return null;
|
|
97
171
|
}
|
|
98
172
|
cursorPath() {
|
|
173
|
+
return join(getGlobalQwenDir(), 'channels', getWorkspaceScopeDirName(this.config.cwd), this.cursorFileName());
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Pre-scoping layout: one file directly under `channels/`, shared by every
|
|
177
|
+
* workspace running a channel of this name. Read only, so an upgrade keeps
|
|
178
|
+
* its cursor; the next save lands in the scoped path and the legacy file
|
|
179
|
+
* stays for other workspaces (and older versions) to read.
|
|
180
|
+
*/
|
|
181
|
+
legacyCursorPath() {
|
|
182
|
+
return join(getGlobalQwenDir(), 'channels', this.cursorFileName());
|
|
183
|
+
}
|
|
184
|
+
cursorFileName() {
|
|
99
185
|
const encoded = this.name.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 200);
|
|
100
186
|
const hash = createHash('sha256')
|
|
101
187
|
.update(this.name)
|
|
102
188
|
.digest('hex')
|
|
103
189
|
.slice(0, 16);
|
|
104
|
-
return
|
|
190
|
+
return `${encoded}-${hash}-poll-cursor.json`;
|
|
105
191
|
}
|
|
106
192
|
}
|
|
107
193
|
//# sourceMappingURL=PollingChannelBase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PollingChannelBase.js","sourceRoot":"","sources":["../src/PollingChannelBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"PollingChannelBase.js","sourceRoot":"","sources":["../src/PollingChannelBase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery failures for prompts injected into PR-bound sessions the channel
|
|
3
|
+
* does not own. The cases drive different adapter behavior: a gone session
|
|
4
|
+
* falls back to the normal channel path; a full queue or a temporarily
|
|
5
|
+
* unavailable session/workspace is backpressure that defers the event
|
|
6
|
+
* without consuming the failure budget.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BoundSessionGoneError extends Error {
|
|
9
|
+
readonly sessionId: string;
|
|
10
|
+
constructor(sessionId: string);
|
|
11
|
+
}
|
|
12
|
+
export declare class BoundSessionQueueFullError extends Error {
|
|
13
|
+
readonly sessionId: string;
|
|
14
|
+
constructor(sessionId: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The session or its owning workspace is temporarily unavailable (workspace
|
|
18
|
+
* draining/restarting, session limit reached, restore in progress). Like a
|
|
19
|
+
* full queue this is retry-later backpressure, not a delivery failure: the
|
|
20
|
+
* daemon even sends Retry-After for it. Burning the attempt budget on these
|
|
21
|
+
* steady-state conditions would silently lose the event after three polls.
|
|
22
|
+
*/
|
|
23
|
+
export declare class BoundSessionUnavailableError extends Error {
|
|
24
|
+
readonly sessionId: string;
|
|
25
|
+
constructor(sessionId: string, detail?: string);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=bound-session-delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bound-session-delivery.d.ts","sourceRoot":"","sources":["../src/bound-session-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,qBAAa,qBAAsB,SAAQ,KAAK;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED;;;;;;GAMG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IAEnD,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM,EAC1B,MAAM,CAAC,EAAE,MAAM;CAOlB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery failures for prompts injected into PR-bound sessions the channel
|
|
3
|
+
* does not own. The cases drive different adapter behavior: a gone session
|
|
4
|
+
* falls back to the normal channel path; a full queue or a temporarily
|
|
5
|
+
* unavailable session/workspace is backpressure that defers the event
|
|
6
|
+
* without consuming the failure budget.
|
|
7
|
+
*/
|
|
8
|
+
export class BoundSessionGoneError extends Error {
|
|
9
|
+
sessionId;
|
|
10
|
+
constructor(sessionId) {
|
|
11
|
+
super(`Bound session ${sessionId} no longer exists`);
|
|
12
|
+
this.sessionId = sessionId;
|
|
13
|
+
this.name = 'BoundSessionGoneError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class BoundSessionQueueFullError extends Error {
|
|
17
|
+
sessionId;
|
|
18
|
+
constructor(sessionId) {
|
|
19
|
+
super(`Prompt queue of bound session ${sessionId} is full`);
|
|
20
|
+
this.sessionId = sessionId;
|
|
21
|
+
this.name = 'BoundSessionQueueFullError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The session or its owning workspace is temporarily unavailable (workspace
|
|
26
|
+
* draining/restarting, session limit reached, restore in progress). Like a
|
|
27
|
+
* full queue this is retry-later backpressure, not a delivery failure: the
|
|
28
|
+
* daemon even sends Retry-After for it. Burning the attempt budget on these
|
|
29
|
+
* steady-state conditions would silently lose the event after three polls.
|
|
30
|
+
*/
|
|
31
|
+
export class BoundSessionUnavailableError extends Error {
|
|
32
|
+
sessionId;
|
|
33
|
+
constructor(sessionId, detail) {
|
|
34
|
+
super(`Bound session ${sessionId} is temporarily unavailable${detail ? `: ${detail}` : ''}`);
|
|
35
|
+
this.sessionId = sessionId;
|
|
36
|
+
this.name = 'BoundSessionUnavailableError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=bound-session-delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bound-session-delivery.js","sourceRoot":"","sources":["../src/bound-session-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACzB;IAArB,YAAqB,SAAiB;QACpC,KAAK,CAAC,iBAAiB,SAAS,mBAAmB,CAAC,CAAC;QADlC,cAAS,GAAT,SAAS,CAAQ;QAEpC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC9B;IAArB,YAAqB,SAAiB;QACpC,KAAK,CAAC,iCAAiC,SAAS,UAAU,CAAC,CAAC;QADzC,cAAS,GAAT,SAAS,CAAQ;QAEpC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAE1C;IADX,YACW,SAAiB,EAC1B,MAAe;QAEf,KAAK,CACH,iBAAiB,SAAS,8BAA8B,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACtF,CAAC;QALO,cAAS,GAAT,SAAS,CAAQ;QAM1B,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* One bounded, append-only record of what a Channel did: an inbound message it
|
|
8
|
+
* accepted or skipped, a publication outcome, an inbound task transition, or a
|
|
9
|
+
* poll result. Records carry routing decisions and identifiers only — never
|
|
10
|
+
* message text — so the journal cannot become a second copy of conversation
|
|
11
|
+
* history.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CHANNEL_EVENT_TYPES: readonly ["inbound", "outbound", "task", "poll"];
|
|
14
|
+
export type ChannelEventType = (typeof CHANNEL_EVENT_TYPES)[number];
|
|
15
|
+
export declare const CHANNEL_EVENT_MAX_TEXT = 200;
|
|
16
|
+
export declare const CHANNEL_EVENT_MAX_DETAIL_KEYS = 12;
|
|
17
|
+
export declare const CHANNEL_EVENT_MAX_READ = 500;
|
|
18
|
+
export type ChannelEventDetail = Record<string, string | number | boolean>;
|
|
19
|
+
export interface ChannelEventRecord {
|
|
20
|
+
at: string;
|
|
21
|
+
channel: string;
|
|
22
|
+
type: ChannelEventType;
|
|
23
|
+
outcome: string;
|
|
24
|
+
reason?: string;
|
|
25
|
+
triggerKind?: string;
|
|
26
|
+
repository?: string;
|
|
27
|
+
thread?: string;
|
|
28
|
+
number?: number;
|
|
29
|
+
actor?: string;
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
messageId?: string;
|
|
32
|
+
durationMs?: number;
|
|
33
|
+
error?: string;
|
|
34
|
+
detail?: ChannelEventDetail;
|
|
35
|
+
}
|
|
36
|
+
export type ChannelEventInput = Omit<ChannelEventRecord, 'at' | 'channel'> & {
|
|
37
|
+
at?: string;
|
|
38
|
+
};
|
|
39
|
+
export interface ChannelEventQuery {
|
|
40
|
+
/** Workspace-scope directories already resolved by the caller. */
|
|
41
|
+
scopeDirs: readonly string[];
|
|
42
|
+
channels?: readonly string[];
|
|
43
|
+
types?: readonly ChannelEventType[];
|
|
44
|
+
since?: number;
|
|
45
|
+
limit?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ChannelEventQueryResult {
|
|
48
|
+
events: ChannelEventRecord[];
|
|
49
|
+
/**
|
|
50
|
+
* True when older events are missing: the record limit was reached, a
|
|
51
|
+
* journal was left out by the file cap or the byte budget, or a journal was
|
|
52
|
+
* taller than the tail window.
|
|
53
|
+
*/
|
|
54
|
+
truncated: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Rebuilds a record from untrusted input (a journal line, or an event handed to
|
|
58
|
+
* `appendChannelEvent`). Returns undefined when the required fields are missing
|
|
59
|
+
* or invalid, so a corrupt line can never surface as a half-rendered row.
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeChannelEventRecord(value: unknown): ChannelEventRecord | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Directory holding a Channel's workspace-scoped state files.
|
|
64
|
+
*/
|
|
65
|
+
export declare function channelEventScopeDir(cwd: string): string;
|
|
66
|
+
export declare function channelEventFilePath(channelName: string, cwd: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Appends one event to a Channel's journal. A failure is reported once on
|
|
69
|
+
* stderr and swallowed: diagnostics must never break message handling.
|
|
70
|
+
*/
|
|
71
|
+
export declare function appendChannelEvent(channelName: string, cwd: string, input: ChannelEventInput): void;
|
|
72
|
+
/**
|
|
73
|
+
* Reads the newest events across the given workspace scopes. Every candidate
|
|
74
|
+
* path is a file found inside an already-resolved scope directory, so a query
|
|
75
|
+
* value can never select a path.
|
|
76
|
+
*
|
|
77
|
+
* The read is asynchronous and chunked: this runs inside the daemon that also
|
|
78
|
+
* serves sessions, so a synchronous multi-megabyte scan would stall every
|
|
79
|
+
* other request for as long as it takes.
|
|
80
|
+
*/
|
|
81
|
+
export declare function readChannelEvents(query: ChannelEventQuery): Promise<ChannelEventQueryResult>;
|
|
82
|
+
//# sourceMappingURL=channel-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-events.d.ts","sourceRoot":"","sources":["../src/channel-events.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,kDAKtB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAChD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AA2B1C,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,SAAS,CAAC,GAAG;IAC3E,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAuCD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,GACb,kBAAkB,GAAG,SAAS,CA8ChC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAWD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7E;AAsBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,iBAAiB,GACvB,IAAI,CA6BN;AAkHD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAmElC"}
|