@0xmaxma/claude-gateway 1.4.8 → 1.5.0
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 +25 -2
- package/config.template.json +1 -0
- package/dist/agent/runner.d.ts +14 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +59 -0
- package/dist/agent/runner.js.map +1 -1
- package/dist/api/gateway-router.d.ts +20 -0
- package/dist/api/gateway-router.d.ts.map +1 -1
- package/dist/api/gateway-router.js +214 -0
- package/dist/api/gateway-router.js.map +1 -1
- package/dist/api/line-webhook-router.d.ts +5 -0
- package/dist/api/line-webhook-router.d.ts.map +1 -1
- package/dist/api/line-webhook-router.js +63 -0
- package/dist/api/line-webhook-router.js.map +1 -1
- package/dist/apps/agent-manager.d.ts.map +1 -1
- package/dist/apps/agent-manager.js +14 -0
- package/dist/apps/agent-manager.js.map +1 -1
- package/dist/cli-viewer/pairing-store.d.ts +117 -0
- package/dist/cli-viewer/pairing-store.d.ts.map +1 -0
- package/dist/cli-viewer/pairing-store.js +196 -0
- package/dist/cli-viewer/pairing-store.js.map +1 -0
- package/dist/cli-viewer/telegram-initdata.d.ts +21 -0
- package/dist/cli-viewer/telegram-initdata.d.ts.map +1 -0
- package/dist/cli-viewer/telegram-initdata.js +74 -0
- package/dist/cli-viewer/telegram-initdata.js.map +1 -0
- package/dist/cli-viewer/url.d.ts +13 -0
- package/dist/cli-viewer/url.d.ts.map +1 -0
- package/dist/cli-viewer/url.js +27 -0
- package/dist/cli-viewer/url.js.map +1 -0
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +7 -0
- package/dist/session/process.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/cli-viewer-ui.d.ts +37 -0
- package/dist/ui/cli-viewer-ui.d.ts.map +1 -0
- package/dist/ui/cli-viewer-ui.js +274 -0
- package/dist/ui/cli-viewer-ui.js.map +1 -0
- package/mcp/tools/discord/module.ts +99 -1
- package/mcp/tools/telegram/receiver-server.ts +41 -1
- package/package.json +1 -1
|
@@ -102,6 +102,19 @@ class AgentManager {
|
|
|
102
102
|
uid = os.userInfo().uid;
|
|
103
103
|
}
|
|
104
104
|
catch { /* use 1000 */ }
|
|
105
|
+
// Mount the agent's media directory into the container at the SAME absolute
|
|
106
|
+
// path as the host. The gateway hands the agent uploaded-image paths (and the
|
|
107
|
+
// browser-screenshot dir) as raw host paths under <agentsDir>/<agentName>/media
|
|
108
|
+
// — a sibling of workspace, so outside the /workspace mount and never
|
|
109
|
+
// translated. Mounting it at the identical path lets those raw paths resolve
|
|
110
|
+
// inside the container with zero translation. Pre-create it (like ~/.claude
|
|
111
|
+
// above) so Docker bind-mounts an existing uid-owned dir instead of a
|
|
112
|
+
// root-owned one; :rw because browser screenshots write here. realpathSync
|
|
113
|
+
// gives the Docker daemon the real source while the destination stays the
|
|
114
|
+
// host-identical path the agent is handed.
|
|
115
|
+
const agentMediaDir = path.join(this.agentsDir, agentName, 'media');
|
|
116
|
+
fs.mkdirSync(agentMediaDir, { recursive: true });
|
|
117
|
+
const agentMediaSource = fs.realpathSync(agentMediaDir);
|
|
105
118
|
// Write Dockerfile.agent: install curl + pre-create ~/.claude owned by host uid so
|
|
106
119
|
// Docker bind-mounts land inside a uid-1000-owned dir and Claude Code can freely
|
|
107
120
|
// create subdirs (session-env, todos, etc.) at runtime without cap_drop: ALL blocking chown.
|
|
@@ -129,6 +142,7 @@ class AgentManager {
|
|
|
129
142
|
`${homeDir}/.claude.json:${homeDir}/.claude.json:ro`,
|
|
130
143
|
`${homeDir}/.claude/settings.json:${homeDir}/.claude/settings.json:ro`,
|
|
131
144
|
`${workspaceDir}:/workspace`,
|
|
145
|
+
`${agentMediaSource}:${agentMediaDir}:rw`,
|
|
132
146
|
],
|
|
133
147
|
};
|
|
134
148
|
const services = (compose.services ?? {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-manager.js","sourceRoot":"","sources":["../../src/apps/agent-manager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agent-manager.js","sourceRoot":"","sources":["../../src/apps/agent-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat channel a `/cli` pairing originated from. The channel already
|
|
3
|
+
* authenticated the requesting user (allowlist / pairing gate) before a pairing
|
|
4
|
+
* is ever created, so the channel + user id recorded here is a trusted binding.
|
|
5
|
+
*/
|
|
6
|
+
export type CliChannel = 'telegram' | 'discord' | 'line';
|
|
7
|
+
/** Runtime guard for the trusted channel set (payloads cross a process boundary). */
|
|
8
|
+
export declare function isCliChannel(value: unknown): value is CliChannel;
|
|
9
|
+
/**
|
|
10
|
+
* Pairing lifecycle:
|
|
11
|
+
* pending — created by `/cli`, waiting for proof (chat-approve or initData).
|
|
12
|
+
* approved — the authenticated chat user approved (or initData verified).
|
|
13
|
+
* consumed — a browser exchanged the approval for an access session cookie.
|
|
14
|
+
* denied — the user explicitly rejected.
|
|
15
|
+
*/
|
|
16
|
+
export type CliPairingStatus = 'pending' | 'approved' | 'consumed' | 'denied';
|
|
17
|
+
export interface CliPairing {
|
|
18
|
+
/** Random, URL-safe id in the `/cli/<id>` link. NOT a secret on its own —
|
|
19
|
+
* approval on an authenticated channel is still required to unlock. */
|
|
20
|
+
pairingId: string;
|
|
21
|
+
agentId: string;
|
|
22
|
+
channel: CliChannel;
|
|
23
|
+
/** Channel user id the pairing was minted for. Approval must come from this
|
|
24
|
+
* same user, and (Telegram) initData's user id must match it. */
|
|
25
|
+
userId: string;
|
|
26
|
+
/** Short human-readable code shown both in chat and in the browser so the
|
|
27
|
+
* operator can visually confirm the browser they opened is the right one. */
|
|
28
|
+
code: string;
|
|
29
|
+
status: CliPairingStatus;
|
|
30
|
+
/** Bound to the first browser that opens the link (first-writer-wins). The
|
|
31
|
+
* access session is only ever issued to the browser holding this token, so a
|
|
32
|
+
* later opener (e.g. a leaked link) cannot ride someone else's approval. */
|
|
33
|
+
browserToken?: string;
|
|
34
|
+
/** Issued once, at consume time; carried by the `cli_session` cookie and
|
|
35
|
+
* resolved back to this (agent-scoped) pairing on every viewer request. */
|
|
36
|
+
accessToken?: string;
|
|
37
|
+
createdAt: number;
|
|
38
|
+
/** Approval window — a pending pairing past this is dead. */
|
|
39
|
+
expiresAt: number;
|
|
40
|
+
/** Access session lifetime — set at consume; the viewer is locked out after. */
|
|
41
|
+
accessExpiresAt?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface CreatedPairing {
|
|
44
|
+
pairingId: string;
|
|
45
|
+
code: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* In-memory store of `/cli` webview pairings (device-authorization flow).
|
|
49
|
+
*
|
|
50
|
+
* A single process-wide instance is shared by the runner's callback handler
|
|
51
|
+
* (which creates and approves pairings on behalf of the authenticated chat) and
|
|
52
|
+
* the gateway HTTP routes (which serve the browser side). Mirrors the singleton
|
|
53
|
+
* shape of `pty-stream-registry` so both sides reference one source of truth
|
|
54
|
+
* without threading a dependency through constructors.
|
|
55
|
+
*
|
|
56
|
+
* Security model: the pairing id in the URL is not a credential. Unlocking a
|
|
57
|
+
* viewer requires EITHER a chat approval from the pairing's own user (Discord /
|
|
58
|
+
* LINE) OR a verified Telegram initData whose user id matches — both gated by
|
|
59
|
+
* the channel's existing allowlist. The resulting access session is scoped to a
|
|
60
|
+
* single agent; it never carries admin/dashboard authority.
|
|
61
|
+
*/
|
|
62
|
+
export declare class CliPairingStore {
|
|
63
|
+
private readonly pairings;
|
|
64
|
+
/** Create a pending pairing for an authenticated chat user. */
|
|
65
|
+
create(agentId: string, channel: CliChannel, userId: string): CreatedPairing;
|
|
66
|
+
/** Raw lookup (no expiry filtering) — callers decide how to treat state. */
|
|
67
|
+
get(pairingId: string): CliPairing | undefined;
|
|
68
|
+
/** True when a pairing is still within its pre-consume approval window. */
|
|
69
|
+
private isLive;
|
|
70
|
+
/**
|
|
71
|
+
* Bind (or re-check) the browser that opened the link. First-writer-wins: the
|
|
72
|
+
* first browser to GET the link owns the pairing. A different browser (no or
|
|
73
|
+
* mismatched token) gets `'already'` — a leaked/forwarded link opened
|
|
74
|
+
* elsewhere is visibly rejected rather than silently sharing the session.
|
|
75
|
+
*/
|
|
76
|
+
bindBrowser(pairingId: string, browserToken: string): 'bound' | 'already' | 'gone';
|
|
77
|
+
/**
|
|
78
|
+
* Approve via an authenticated chat action (Discord / LINE) or a verified
|
|
79
|
+
* Telegram initData. The approving user and channel must match the pairing.
|
|
80
|
+
*/
|
|
81
|
+
approve(pairingId: string, channel: CliChannel, userId: string): 'ok' | 'mismatch' | 'gone';
|
|
82
|
+
/** Explicit rejection from the authenticated chat user. */
|
|
83
|
+
deny(pairingId: string, channel: CliChannel, userId: string): 'ok' | 'mismatch' | 'gone';
|
|
84
|
+
/**
|
|
85
|
+
* Exchange an approval for an access session. Returns the access token exactly
|
|
86
|
+
* once per pairing. `requireBrowserToken` enforces that only the bound browser
|
|
87
|
+
* (Discord / LINE device flow) may consume; the Telegram initData path proves
|
|
88
|
+
* identity cryptographically in the same request and passes it through here
|
|
89
|
+
* with the freshly-bound token.
|
|
90
|
+
*/
|
|
91
|
+
consume(pairingId: string, browserToken: string): {
|
|
92
|
+
accessToken: string;
|
|
93
|
+
} | null;
|
|
94
|
+
/**
|
|
95
|
+
* Issue an access session for the Telegram initData fast-path. Identity is
|
|
96
|
+
* already proven cryptographically (the caller verified the signed initData and
|
|
97
|
+
* that its user matches the pairing), so — unlike the Discord/LINE device flow —
|
|
98
|
+
* this does NOT depend on the first-writer browser binding: a Telegram Mini App
|
|
99
|
+
* webview may not replay the `cli_pair` cookie set on the initial page load, so
|
|
100
|
+
* we (re)bind this browser and issue. Idempotent for the same browser.
|
|
101
|
+
*/
|
|
102
|
+
issueAccessForVerifiedUser(pairingId: string, browserToken: string): {
|
|
103
|
+
accessToken: string;
|
|
104
|
+
} | null;
|
|
105
|
+
/**
|
|
106
|
+
* Resolve an access-session token to its (agent-scoped) pairing. Used by every
|
|
107
|
+
* viewer request to authorize and to learn which single agent it may touch.
|
|
108
|
+
*/
|
|
109
|
+
resolveAccess(accessToken: string): CliPairing | null;
|
|
110
|
+
/** Drop dead pairings: expired-and-unconsumed, or access sessions past TTL. */
|
|
111
|
+
prune(): void;
|
|
112
|
+
/** Test/introspection helper. */
|
|
113
|
+
size(): number;
|
|
114
|
+
}
|
|
115
|
+
/** Process-wide singleton shared by the runner callback and the gateway routes. */
|
|
116
|
+
export declare const cliPairingStore: CliPairingStore;
|
|
117
|
+
//# sourceMappingURL=pairing-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing-store.d.ts","sourceRoot":"","sources":["../../src/cli-viewer/pairing-store.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzD,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB;4EACwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB;sEACkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf;kFAC8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB;;iFAE6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;gFAC4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAOD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAE1D,+DAA+D;IAC/D,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc;IAmB5E,4EAA4E;IAC5E,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI9C,2EAA2E;IAC3E,OAAO,CAAC,MAAM;IAId;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM;IAclF;;;OAGG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM;IAQ3F,2DAA2D;IAC3D,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM;IAQxF;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAoBhF;;;;;;;OAOG;IACH,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAkBnG;;;OAGG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAcrD,+EAA+E;IAC/E,KAAK,IAAI,IAAI;IASb,iCAAiC;IACjC,IAAI,IAAI,MAAM;CAGf;AAED,mFAAmF;AACnF,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cliPairingStore = exports.CliPairingStore = void 0;
|
|
7
|
+
exports.isCliChannel = isCliChannel;
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
/** Runtime guard for the trusted channel set (payloads cross a process boundary). */
|
|
10
|
+
function isCliChannel(value) {
|
|
11
|
+
return value === 'telegram' || value === 'discord' || value === 'line';
|
|
12
|
+
}
|
|
13
|
+
/** Approval window: how long the operator has to approve after `/cli`. */
|
|
14
|
+
const PAIRING_TTL_MS = 3 * 60 * 1000;
|
|
15
|
+
/** Viewer session lifetime once unlocked. Short — re-run `/cli` to renew. */
|
|
16
|
+
const ACCESS_TTL_MS = 30 * 60 * 1000;
|
|
17
|
+
/**
|
|
18
|
+
* In-memory store of `/cli` webview pairings (device-authorization flow).
|
|
19
|
+
*
|
|
20
|
+
* A single process-wide instance is shared by the runner's callback handler
|
|
21
|
+
* (which creates and approves pairings on behalf of the authenticated chat) and
|
|
22
|
+
* the gateway HTTP routes (which serve the browser side). Mirrors the singleton
|
|
23
|
+
* shape of `pty-stream-registry` so both sides reference one source of truth
|
|
24
|
+
* without threading a dependency through constructors.
|
|
25
|
+
*
|
|
26
|
+
* Security model: the pairing id in the URL is not a credential. Unlocking a
|
|
27
|
+
* viewer requires EITHER a chat approval from the pairing's own user (Discord /
|
|
28
|
+
* LINE) OR a verified Telegram initData whose user id matches — both gated by
|
|
29
|
+
* the channel's existing allowlist. The resulting access session is scoped to a
|
|
30
|
+
* single agent; it never carries admin/dashboard authority.
|
|
31
|
+
*/
|
|
32
|
+
class CliPairingStore {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.pairings = new Map();
|
|
35
|
+
}
|
|
36
|
+
/** Create a pending pairing for an authenticated chat user. */
|
|
37
|
+
create(agentId, channel, userId) {
|
|
38
|
+
const pairingId = crypto_1.default.randomBytes(18).toString('hex');
|
|
39
|
+
// 4-digit visual-confirm code (0000–9999). Not a secret — it only helps the
|
|
40
|
+
// operator confirm the browser matches; security rests on channel approval.
|
|
41
|
+
const code = String(crypto_1.default.randomInt(0, 10000)).padStart(4, '0');
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
this.pairings.set(pairingId, {
|
|
44
|
+
pairingId,
|
|
45
|
+
agentId,
|
|
46
|
+
channel,
|
|
47
|
+
userId,
|
|
48
|
+
code,
|
|
49
|
+
status: 'pending',
|
|
50
|
+
createdAt: now,
|
|
51
|
+
expiresAt: now + PAIRING_TTL_MS,
|
|
52
|
+
});
|
|
53
|
+
return { pairingId, code };
|
|
54
|
+
}
|
|
55
|
+
/** Raw lookup (no expiry filtering) — callers decide how to treat state. */
|
|
56
|
+
get(pairingId) {
|
|
57
|
+
return this.pairings.get(pairingId);
|
|
58
|
+
}
|
|
59
|
+
/** True when a pairing is still within its pre-consume approval window. */
|
|
60
|
+
isLive(p) {
|
|
61
|
+
return !!p && p.status !== 'consumed' && p.status !== 'denied' && p.expiresAt > Date.now();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Bind (or re-check) the browser that opened the link. First-writer-wins: the
|
|
65
|
+
* first browser to GET the link owns the pairing. A different browser (no or
|
|
66
|
+
* mismatched token) gets `'already'` — a leaked/forwarded link opened
|
|
67
|
+
* elsewhere is visibly rejected rather than silently sharing the session.
|
|
68
|
+
*/
|
|
69
|
+
bindBrowser(pairingId, browserToken) {
|
|
70
|
+
const p = this.pairings.get(pairingId);
|
|
71
|
+
// A consumed pairing is still reachable by its own bound browser (the viewer
|
|
72
|
+
// keeps loading), so allow the matching token through even post-consume.
|
|
73
|
+
if (!p || p.status === 'denied' || (p.status !== 'consumed' && p.expiresAt <= Date.now())) {
|
|
74
|
+
return 'gone';
|
|
75
|
+
}
|
|
76
|
+
if (!p.browserToken) {
|
|
77
|
+
p.browserToken = browserToken;
|
|
78
|
+
return 'bound';
|
|
79
|
+
}
|
|
80
|
+
return p.browserToken === browserToken ? 'bound' : 'already';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Approve via an authenticated chat action (Discord / LINE) or a verified
|
|
84
|
+
* Telegram initData. The approving user and channel must match the pairing.
|
|
85
|
+
*/
|
|
86
|
+
approve(pairingId, channel, userId) {
|
|
87
|
+
const p = this.pairings.get(pairingId);
|
|
88
|
+
if (!this.isLive(p))
|
|
89
|
+
return 'gone';
|
|
90
|
+
if (p.channel !== channel || p.userId !== userId)
|
|
91
|
+
return 'mismatch';
|
|
92
|
+
if (p.status === 'pending')
|
|
93
|
+
p.status = 'approved';
|
|
94
|
+
return 'ok';
|
|
95
|
+
}
|
|
96
|
+
/** Explicit rejection from the authenticated chat user. */
|
|
97
|
+
deny(pairingId, channel, userId) {
|
|
98
|
+
const p = this.pairings.get(pairingId);
|
|
99
|
+
if (!this.isLive(p))
|
|
100
|
+
return 'gone';
|
|
101
|
+
if (p.channel !== channel || p.userId !== userId)
|
|
102
|
+
return 'mismatch';
|
|
103
|
+
p.status = 'denied';
|
|
104
|
+
return 'ok';
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Exchange an approval for an access session. Returns the access token exactly
|
|
108
|
+
* once per pairing. `requireBrowserToken` enforces that only the bound browser
|
|
109
|
+
* (Discord / LINE device flow) may consume; the Telegram initData path proves
|
|
110
|
+
* identity cryptographically in the same request and passes it through here
|
|
111
|
+
* with the freshly-bound token.
|
|
112
|
+
*/
|
|
113
|
+
consume(pairingId, browserToken) {
|
|
114
|
+
const p = this.pairings.get(pairingId);
|
|
115
|
+
if (!p)
|
|
116
|
+
return null;
|
|
117
|
+
// Idempotent for the owning browser: a repeat call returns the same token so
|
|
118
|
+
// a re-poll after unlock still works.
|
|
119
|
+
if (p.status === 'consumed') {
|
|
120
|
+
if (p.accessToken && p.browserToken === browserToken && (p.accessExpiresAt ?? 0) > Date.now()) {
|
|
121
|
+
return { accessToken: p.accessToken };
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (p.status !== 'approved' || p.expiresAt <= Date.now())
|
|
126
|
+
return null;
|
|
127
|
+
if (!p.browserToken || p.browserToken !== browserToken)
|
|
128
|
+
return null;
|
|
129
|
+
const accessToken = crypto_1.default.randomBytes(32).toString('hex');
|
|
130
|
+
p.accessToken = accessToken;
|
|
131
|
+
p.status = 'consumed';
|
|
132
|
+
p.accessExpiresAt = Date.now() + ACCESS_TTL_MS;
|
|
133
|
+
return { accessToken };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Issue an access session for the Telegram initData fast-path. Identity is
|
|
137
|
+
* already proven cryptographically (the caller verified the signed initData and
|
|
138
|
+
* that its user matches the pairing), so — unlike the Discord/LINE device flow —
|
|
139
|
+
* this does NOT depend on the first-writer browser binding: a Telegram Mini App
|
|
140
|
+
* webview may not replay the `cli_pair` cookie set on the initial page load, so
|
|
141
|
+
* we (re)bind this browser and issue. Idempotent for the same browser.
|
|
142
|
+
*/
|
|
143
|
+
issueAccessForVerifiedUser(pairingId, browserToken) {
|
|
144
|
+
const p = this.pairings.get(pairingId);
|
|
145
|
+
if (!p || p.status === 'denied')
|
|
146
|
+
return null;
|
|
147
|
+
if (p.status === 'consumed') {
|
|
148
|
+
if (p.accessToken && p.browserToken === browserToken && (p.accessExpiresAt ?? 0) > Date.now()) {
|
|
149
|
+
return { accessToken: p.accessToken };
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
if (p.expiresAt <= Date.now())
|
|
154
|
+
return null;
|
|
155
|
+
p.browserToken = browserToken;
|
|
156
|
+
const accessToken = crypto_1.default.randomBytes(32).toString('hex');
|
|
157
|
+
p.accessToken = accessToken;
|
|
158
|
+
p.status = 'consumed';
|
|
159
|
+
p.accessExpiresAt = Date.now() + ACCESS_TTL_MS;
|
|
160
|
+
return { accessToken };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Resolve an access-session token to its (agent-scoped) pairing. Used by every
|
|
164
|
+
* viewer request to authorize and to learn which single agent it may touch.
|
|
165
|
+
*/
|
|
166
|
+
resolveAccess(accessToken) {
|
|
167
|
+
if (!accessToken)
|
|
168
|
+
return null;
|
|
169
|
+
for (const p of this.pairings.values()) {
|
|
170
|
+
if (p.status === 'consumed' &&
|
|
171
|
+
p.accessToken === accessToken &&
|
|
172
|
+
(p.accessExpiresAt ?? 0) > Date.now()) {
|
|
173
|
+
return p;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
/** Drop dead pairings: expired-and-unconsumed, or access sessions past TTL. */
|
|
179
|
+
prune() {
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
for (const [id, p] of this.pairings) {
|
|
182
|
+
const deadPending = p.status !== 'consumed' && p.expiresAt <= now;
|
|
183
|
+
const deadAccess = p.status === 'consumed' && (p.accessExpiresAt ?? 0) <= now;
|
|
184
|
+
if (deadPending || deadAccess)
|
|
185
|
+
this.pairings.delete(id);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Test/introspection helper. */
|
|
189
|
+
size() {
|
|
190
|
+
return this.pairings.size;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.CliPairingStore = CliPairingStore;
|
|
194
|
+
/** Process-wide singleton shared by the runner callback and the gateway routes. */
|
|
195
|
+
exports.cliPairingStore = new CliPairingStore();
|
|
196
|
+
//# sourceMappingURL=pairing-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing-store.js","sourceRoot":"","sources":["../../src/cli-viewer/pairing-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface VerifiedInitData {
|
|
2
|
+
/** Telegram user id (stringified) proven by the signature. */
|
|
3
|
+
userId: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Verify a Telegram Mini App `initData` string against a bot token.
|
|
7
|
+
*
|
|
8
|
+
* Telegram signs the launch parameters so a Mini App can prove which user opened
|
|
9
|
+
* it without any secret in the URL. The check (per Telegram's spec):
|
|
10
|
+
* secret_key = HMAC_SHA256(key = "WebAppData", data = <bot token>)
|
|
11
|
+
* data_check_str = every "key=value" except `hash`, sorted, joined by "\n"
|
|
12
|
+
* expected_hash = HMAC_SHA256(key = secret_key, data = data_check_str)
|
|
13
|
+
* and `expected_hash` must equal the payload's `hash` (timing-safe).
|
|
14
|
+
*
|
|
15
|
+
* Returns the verified user id, or null if the payload is missing fields,
|
|
16
|
+
* tampered, stale, or the signature does not match. The caller is still
|
|
17
|
+
* responsible for authorizing that user for the target agent — a valid
|
|
18
|
+
* signature only proves *who* opened the Mini App, not *what* they may see.
|
|
19
|
+
*/
|
|
20
|
+
export declare function verifyTelegramInitData(initData: string, botToken: string): VerifiedInitData | null;
|
|
21
|
+
//# sourceMappingURL=telegram-initdata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-initdata.d.ts","sourceRoot":"","sources":["../../src/cli-viewer/telegram-initdata.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,gBAAgB,GAAG,IAAI,CA8CzB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.verifyTelegramInitData = verifyTelegramInitData;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
/** Max age of a Telegram initData payload we still trust (replay bound). */
|
|
9
|
+
const INITDATA_MAX_AGE_SEC = 60 * 60;
|
|
10
|
+
/**
|
|
11
|
+
* Verify a Telegram Mini App `initData` string against a bot token.
|
|
12
|
+
*
|
|
13
|
+
* Telegram signs the launch parameters so a Mini App can prove which user opened
|
|
14
|
+
* it without any secret in the URL. The check (per Telegram's spec):
|
|
15
|
+
* secret_key = HMAC_SHA256(key = "WebAppData", data = <bot token>)
|
|
16
|
+
* data_check_str = every "key=value" except `hash`, sorted, joined by "\n"
|
|
17
|
+
* expected_hash = HMAC_SHA256(key = secret_key, data = data_check_str)
|
|
18
|
+
* and `expected_hash` must equal the payload's `hash` (timing-safe).
|
|
19
|
+
*
|
|
20
|
+
* Returns the verified user id, or null if the payload is missing fields,
|
|
21
|
+
* tampered, stale, or the signature does not match. The caller is still
|
|
22
|
+
* responsible for authorizing that user for the target agent — a valid
|
|
23
|
+
* signature only proves *who* opened the Mini App, not *what* they may see.
|
|
24
|
+
*/
|
|
25
|
+
function verifyTelegramInitData(initData, botToken) {
|
|
26
|
+
if (!initData || !botToken)
|
|
27
|
+
return null;
|
|
28
|
+
let params;
|
|
29
|
+
try {
|
|
30
|
+
params = new URLSearchParams(initData);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const hash = params.get('hash');
|
|
36
|
+
if (!hash || !/^[0-9a-f]+$/i.test(hash))
|
|
37
|
+
return null;
|
|
38
|
+
params.delete('hash');
|
|
39
|
+
const dataCheckString = [...params.entries()]
|
|
40
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
41
|
+
.sort()
|
|
42
|
+
.join('\n');
|
|
43
|
+
const secretKey = crypto_1.default.createHmac('sha256', 'WebAppData').update(botToken).digest();
|
|
44
|
+
const expected = crypto_1.default.createHmac('sha256', secretKey).update(dataCheckString).digest();
|
|
45
|
+
let provided;
|
|
46
|
+
try {
|
|
47
|
+
provided = Buffer.from(hash, 'hex');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (provided.length !== expected.length || !crypto_1.default.timingSafeEqual(provided, expected)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// Reject stale payloads (replay bound). auth_date is unix seconds.
|
|
56
|
+
const authDate = Number(params.get('auth_date') ?? '');
|
|
57
|
+
if (!Number.isFinite(authDate) || authDate <= 0)
|
|
58
|
+
return null;
|
|
59
|
+
if (Date.now() / 1000 - authDate > INITDATA_MAX_AGE_SEC)
|
|
60
|
+
return null;
|
|
61
|
+
const userRaw = params.get('user');
|
|
62
|
+
if (!userRaw)
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const user = JSON.parse(userRaw);
|
|
66
|
+
if (user.id === undefined || user.id === null || user.id === '')
|
|
67
|
+
return null;
|
|
68
|
+
return { userId: String(user.id) };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=telegram-initdata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-initdata.js","sourceRoot":"","sources":["../../src/cli-viewer/telegram-initdata.ts"],"names":[],"mappings":";;;;;AAyBA,wDAiDC;AA1ED,oDAA4B;AAO5B,4EAA4E;AAC5E,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,CAAC;AAErC;;;;;;;;;;;;;;GAcG;AACH,SAAgB,sBAAsB,CACpC,QAAgB,EAChB,QAAgB;IAEhB,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAExC,IAAI,MAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEtB,MAAM,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;SAC5B,IAAI,EAAE;SACN,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,SAAS,GAAG,gBAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;IACtF,MAAM,QAAQ,GAAG,gBAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC;IAEzF,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,CAAC,gBAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mEAAmE;IACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,QAAQ,GAAG,oBAAoB;QAAE,OAAO,IAAI,CAAC;IAErE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA6B,CAAC;QAC7D,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the absolute, phone-openable URL for a `/cli` webview from the operator-
|
|
3
|
+
* configured `gateway.publicUrl`. Kept tiny and dependency-free so both the
|
|
4
|
+
* runner callback (which mints pairings) and the gateway routes can share one
|
|
5
|
+
* definition of "how a /cli link is formed".
|
|
6
|
+
*/
|
|
7
|
+
/** Trim and validate the configured public origin. Returns null when unusable
|
|
8
|
+
* (unset, blank, or not http(s)) so callers can report "not configured" rather
|
|
9
|
+
* than emit a broken link. Trailing slashes are stripped. */
|
|
10
|
+
export declare function normalizePublicUrl(publicUrl: string | undefined): string | null;
|
|
11
|
+
/** Full viewer link for a pairing, or null when no public URL is configured. */
|
|
12
|
+
export declare function buildCliUrl(publicUrl: string | undefined, pairingId: string): string | null;
|
|
13
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/cli-viewer/url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;8DAE8D;AAC9D,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAK/E;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Build the absolute, phone-openable URL for a `/cli` webview from the operator-
|
|
4
|
+
* configured `gateway.publicUrl`. Kept tiny and dependency-free so both the
|
|
5
|
+
* runner callback (which mints pairings) and the gateway routes can share one
|
|
6
|
+
* definition of "how a /cli link is formed".
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.normalizePublicUrl = normalizePublicUrl;
|
|
10
|
+
exports.buildCliUrl = buildCliUrl;
|
|
11
|
+
/** Trim and validate the configured public origin. Returns null when unusable
|
|
12
|
+
* (unset, blank, or not http(s)) so callers can report "not configured" rather
|
|
13
|
+
* than emit a broken link. Trailing slashes are stripped. */
|
|
14
|
+
function normalizePublicUrl(publicUrl) {
|
|
15
|
+
const u = (publicUrl ?? '').trim().replace(/\/+$/, '');
|
|
16
|
+
if (!u)
|
|
17
|
+
return null;
|
|
18
|
+
if (!/^https?:\/\//i.test(u))
|
|
19
|
+
return null;
|
|
20
|
+
return u;
|
|
21
|
+
}
|
|
22
|
+
/** Full viewer link for a pairing, or null when no public URL is configured. */
|
|
23
|
+
function buildCliUrl(publicUrl, pairingId) {
|
|
24
|
+
const base = normalizePublicUrl(publicUrl);
|
|
25
|
+
return base ? `${base}/cli/${pairingId}` : null;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/cli-viewer/url.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAKH,gDAKC;AAGD,kCAGC;AAdD;;8DAE8D;AAC9D,SAAgB,kBAAkB,CAAC,SAA6B;IAC9D,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,gFAAgF;AAChF,SAAgB,WAAW,CAAC,SAA6B,EAAE,SAAiB;IAC1E,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/session/process.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAYvC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,CAAC,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAMR;AAsBD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;IACzD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkC;IACjE,cAAc,SAAc;IAC5B,QAAQ,CAAC,SAAS,SAAc;IAChC,qFAAqF;IACrF,OAAO,EAAE,WAAW,GAAG,UAAU,CAAc;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IAMvB,YAAY,EAAE,MAAM,CAAwB;IAM5C,aAAa,EAAE,OAAO,CAAS;IAC/B,YAAY,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAQ;IAC1G,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,SAAS,UAAS;IAIlB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,qBAAqB,CAAK;IAMlC,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,YAAY,CAAM;IAE1B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,aAAa,CAAC,CAAyB;IAC/C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAgC;IACpD,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,CAAC,oBAAoB,CAAC,CAAS;gBAGpC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,EAC/C,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,EAC1B,MAAM,CAAC,EAAE,MAAM;IAqBjB;;;OAGG;IACH,OAAO,KAAK,SAAS,GAGpB;IAED,OAAO,CAAC,aAAa;IAMrB,wFAAwF;IACxF,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,0EAA0E;IAC1E,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,OAAO,CAAC,cAAc;IAchB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;YA8Bb,kBAAkB;IA4DhC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,cAAc;IAmFtB,8FAA8F;IAC9F,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,SAAS;IAsCjB,OAAO,CAAC,MAAM,CAAC,gBAAgB;YAOjB,YAAY;
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/session/process.ts"],"names":[],"mappings":"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"}
|
package/dist/session/process.js
CHANGED
|
@@ -625,6 +625,13 @@ class SessionProcess extends events_1.EventEmitter {
|
|
|
625
625
|
// result.usage is cumulative across all sub-calls; message_start.usage reflects a single API call's context.
|
|
626
626
|
let lastMessageStartContext = 0;
|
|
627
627
|
proc.stdout?.on('data', (data) => {
|
|
628
|
+
// Child produced output => the session is doing real work right now. Count
|
|
629
|
+
// this as activity so the idle reaper measures "time since the session last
|
|
630
|
+
// did anything", not just "time since the parent last injected a message".
|
|
631
|
+
// Without this, a self-paced loop (a /loop in dynamic mode using
|
|
632
|
+
// ScheduleWakeup) that wakes itself and works entirely inside this child
|
|
633
|
+
// process stays invisible to the idle timer and gets reaped mid-flight.
|
|
634
|
+
this.touch();
|
|
628
635
|
// Update heartbeat so the receiver's stalled detector knows Claude is active
|
|
629
636
|
if (heartbeatPath) {
|
|
630
637
|
try {
|