@reclaimprotocol/agent 0.1.24 → 0.1.26
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 +5 -2
- package/lib/cdp/claims.d.ts +21 -0
- package/lib/cdp/claims.js +129 -0
- package/lib/cdp/client.d.ts +0 -10
- package/lib/cdp/container.d.ts +85 -0
- package/lib/cdp/container.js +481 -0
- package/lib/live/tunnel.d.ts +6 -3
- package/lib/live/tunnel.js +57 -17
- package/lib/mcp/instructions.js +2 -1
- package/lib/mcp/start.js +58 -3
- package/lib/mcp/tools/agent/attach.d.ts +22 -2
- package/lib/mcp/tools/agent/attach.js +197 -55
- package/lib/mcp/tools/agent/build.d.ts +5 -0
- package/lib/mcp/tools/agent/build.js +9 -2
- package/lib/mcp/tools/agent/index.d.ts +1 -2
- package/lib/mcp/tools/agent/index.js +1 -1
- package/lib/mcp/tools/agent/live-view.d.ts +3 -7
- package/lib/mcp/tools/agent/live-view.js +74 -81
- package/lib/mcp/tools/agent/session.d.ts +14 -0
- package/lib/mcp/tools/agent/session.js +14 -8
- package/lib/old/client.d.ts +83 -0
- package/lib/old/client.js +46 -0
- package/lib/old/tools/build.js +2 -0
- package/lib/old/tools/session-logs.d.ts +3 -0
- package/lib/old/tools/session-logs.js +310 -0
- package/package.json +2 -2
- package/skill/SKILL.common.md +115 -38
- package/skill/SKILL.old.md +28 -2
- package/skill/builder-mode.md +9 -7
- package/skill/interception.md +3 -2
- package/skill/old-mode.md +14 -10
- package/src/cdp/claims.ts +153 -0
- package/src/cdp/client.ts +0 -8
- package/src/cdp/container.ts +593 -0
- package/src/live/tunnel.ts +71 -20
- package/src/mcp/instructions.ts +2 -1
- package/src/mcp/start.ts +65 -2
- package/src/mcp/tools/agent/attach.ts +229 -60
- package/src/mcp/tools/agent/build.ts +10 -2
- package/src/mcp/tools/agent/index.ts +6 -2
- package/src/mcp/tools/agent/live-view.ts +77 -93
- package/src/mcp/tools/agent/session.ts +14 -8
- package/src/old/client.ts +128 -0
- package/src/old/tools/build.ts +2 -0
- package/src/old/tools/session-logs.ts +395 -0
- package/lib/cdp/browser-proxy.d.ts +0 -78
- package/lib/cdp/browser-proxy.js +0 -186
- package/lib/live/page.d.ts +0 -20
- package/lib/live/page.js +0 -134
- package/lib/live/viewer.d.ts +0 -28
- package/lib/live/viewer.js +0 -232
- package/lib/mcp/tools/agent/proxy.d.ts +0 -3
- package/lib/mcp/tools/agent/proxy.js +0 -199
- package/src/live/page.ts +0 -145
- package/src/live/viewer.ts +0 -293
|
@@ -16,12 +16,9 @@
|
|
|
16
16
|
import { badRequest, conflict } from '@hapi/boom';
|
|
17
17
|
import assert from 'node:assert';
|
|
18
18
|
import { startTunnel } from "../../../live/tunnel.js";
|
|
19
|
-
import { startLiveView } from "../../../live/viewer.js";
|
|
20
19
|
import { LOGGER } from "../../../logger.js";
|
|
21
20
|
import { defineTool } from "../../server.js";
|
|
22
|
-
|
|
23
|
-
* therefore have no hosted live view of their own. */
|
|
24
|
-
const SHAREABLE_MODES = new Set(['dedicated', 'attach', 'custom']);
|
|
21
|
+
import { SHAREABLE_MODES } from "./attach.js";
|
|
25
22
|
/** Tear down whatever share is running. Safe when none is. Exported so the
|
|
26
23
|
* session tools can call it from `dispose_browser` / `reset_session`. */
|
|
27
24
|
export async function stopLiveShare(ref) {
|
|
@@ -30,98 +27,87 @@ export async function stopLiveShare(ref) {
|
|
|
30
27
|
return false;
|
|
31
28
|
}
|
|
32
29
|
ref.current = undefined;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
30
|
+
// Only the tunnel is ours to close. The browser and its viewer belong to the
|
|
31
|
+
// container, which keeps running until the browser is disposed.
|
|
36
32
|
await share.tunnel.stop().catch((err) => {
|
|
37
33
|
LOGGER.warn({ err }, 'live view: tunnel stop failed');
|
|
38
34
|
});
|
|
39
35
|
return true;
|
|
40
36
|
}
|
|
41
|
-
export function liveViewTools(attachRef,
|
|
37
|
+
export function liveViewTools(attachRef, shareRef) {
|
|
42
38
|
return [
|
|
43
39
|
defineTool({
|
|
44
40
|
name: 'share_browser_view',
|
|
45
|
-
description: 'Put the attached
|
|
46
|
-
+ 'quick tunnel) so someone elsewhere can watch it
|
|
47
|
-
+ '
|
|
48
|
-
+ '
|
|
49
|
-
+ '
|
|
50
|
-
+ '
|
|
51
|
-
+ 'ONLY for
|
|
52
|
-
+ '
|
|
53
|
-
+ '
|
|
54
|
-
+ '`
|
|
55
|
-
+ '
|
|
56
|
-
+ 'and
|
|
57
|
-
+ '
|
|
58
|
-
+ '
|
|
59
|
-
+ '
|
|
41
|
+
description: 'Put the attached browser\'s live view behind a PUBLIC HTTPS URL (a '
|
|
42
|
+
+ 'cloudflared quick tunnel) so someone elsewhere can watch it and '
|
|
43
|
+
+ 'act in it. Use it to hand a login or a 2FA step to the person '
|
|
44
|
+
+ 'who holds the credentials, or to let a colleague watch a '
|
|
45
|
+
+ 'capture. Returns the URL — relay it to the developer VERBATIM, '
|
|
46
|
+
+ 'on its own line. '
|
|
47
|
+
+ 'ONLY for `container` mode, whose browser runs its own viewer. '
|
|
48
|
+
+ 'A `builder` browser already has a hosted live view: use the '
|
|
49
|
+
+ '`liveViewUrl` attach_browser returned. A local browser '
|
|
50
|
+
+ '(`dedicated`, `attach`, `custom`) cannot be shared at all — no '
|
|
51
|
+
+ 'tunnel can fix that, because there is no viewer behind it. Tell '
|
|
52
|
+
+ 'them so, and offer to re-attach: `container` is free and needs '
|
|
53
|
+
+ 'Docker, `builder` needs no install but is chargeable. '
|
|
54
|
+
+ 'THE URL IS THE ONLY CREDENTIAL: the random hostname is the whole '
|
|
55
|
+
+ 'secret, with no token or login behind it, so anyone who learns '
|
|
56
|
+
+ 'it can see and drive this browser as the signed-in user. Share '
|
|
57
|
+
+ 'it only with the person who needs it, and call stop_browser_view '
|
|
58
|
+
+ 'the moment they are done. '
|
|
60
59
|
+ 'Needs cloudflared: an existing install on PATH is used as-is, '
|
|
61
60
|
+ 'otherwise it is downloaded once to ~/.reclaim/bin on first '
|
|
62
61
|
+ 'share (never at install time). Set RECLAIM_CLOUDFLARED_PATH to '
|
|
63
62
|
+ 'point at your own copy instead.',
|
|
64
|
-
inputSchema: {
|
|
65
|
-
|
|
66
|
-
properties: {
|
|
67
|
-
tabId: {
|
|
68
|
-
type: 'string',
|
|
69
|
-
description: 'Tab to stream, from list_tabs. Defaults to the tab the '
|
|
70
|
-
+ 'active capture is on, else the first tab.',
|
|
71
|
-
},
|
|
72
|
-
interactive: {
|
|
73
|
-
type: 'boolean',
|
|
74
|
-
description: 'Let the viewer click and type in the page. Default true. '
|
|
75
|
-
+ 'Set false to stream a read-only view.',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
}, async ({ tabId, interactive }) => {
|
|
63
|
+
inputSchema: { type: 'object', properties: {} },
|
|
64
|
+
}, async () => {
|
|
80
65
|
const attached = attachRef.current;
|
|
81
66
|
assert(attached, badRequest('No browser attached. Call attach_browser first.'));
|
|
82
67
|
assert(SHAREABLE_MODES.has(attached.mode), badRequest(`share_browser_view does not apply to \`${attached.mode}\` mode. `
|
|
83
|
-
+
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
+ (attached.liveViewUrl
|
|
69
|
+
? 'That browser already has a hosted live view — use the '
|
|
70
|
+
+ `\`liveViewUrl\` from attach_browser: ${attached.liveViewUrl}`
|
|
71
|
+
: 'That browser has no live view to put behind a URL. To let '
|
|
72
|
+
+ 'someone else in, re-attach with mode "container" (the '
|
|
73
|
+
+ 'Reclaim runtime in Docker — free, needs Docker) or '
|
|
74
|
+
+ '"builder" (hosted, no install, chargeable)')
|
|
86
75
|
+ '.'));
|
|
87
76
|
assert(!shareRef.current, conflict('A live view is already running at '
|
|
88
77
|
+ `${shareRef.current?.url}. Call stop_browser_view first, or `
|
|
89
78
|
+ 'reuse that URL.'));
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
let tunnel;
|
|
103
|
-
try {
|
|
104
|
-
tunnel = await startTunnel(view.port);
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
// Don't leave the loopback server behind if the tunnel failed.
|
|
108
|
-
await view.stop().catch(() => { });
|
|
109
|
-
throw err;
|
|
110
|
-
}
|
|
111
|
-
const url = `${tunnel.url}${view.path}`;
|
|
79
|
+
// The container publishes its viewer on loopback and its full CDP
|
|
80
|
+
// port beside it. Only the VIEWER is ever tunnelled: the CDP port is
|
|
81
|
+
// unauthenticated, total control of the browser, and putting it on a
|
|
82
|
+
// public URL would hand that to anyone with the link.
|
|
83
|
+
const viewerPort = attached.viewerPort;
|
|
84
|
+
assert(viewerPort, badRequest('This browser reports no live-view port. Re-attach with mode '
|
|
85
|
+
+ '"container".'));
|
|
86
|
+
const tunnel = await startTunnel(viewerPort);
|
|
87
|
+
// The path is the runtime's own viewer page; `magnify=1` is what makes
|
|
88
|
+
// it lay out for the device that opens it.
|
|
89
|
+
const url = `${tunnel.url}/liveview.html?magnify=1`;
|
|
112
90
|
const share = {
|
|
113
|
-
|
|
91
|
+
tunnel, url,
|
|
114
92
|
};
|
|
115
93
|
shareRef.current = share;
|
|
116
|
-
LOGGER.info({ mode: attached.mode
|
|
94
|
+
LOGGER.info({ mode: attached.mode }, 'live view shared');
|
|
117
95
|
return {
|
|
118
96
|
url,
|
|
119
|
-
interactive: wantsInput,
|
|
120
|
-
tabId: target,
|
|
121
97
|
...(share.tunnel.binary.source === 'downloaded'
|
|
122
98
|
? { cloudflaredInstalled: true }
|
|
123
99
|
: {}),
|
|
124
|
-
...(share.tunnel.ready
|
|
100
|
+
...(share.tunnel.ready
|
|
101
|
+
? {}
|
|
102
|
+
: {
|
|
103
|
+
propagating: true,
|
|
104
|
+
// cloudflared's own last words. Without them, "the URL does
|
|
105
|
+
// not connect" is unactionable: the tunnel, the local server
|
|
106
|
+
// and this machine's egress all look identical from outside.
|
|
107
|
+
...(share.tunnel.diagnostics
|
|
108
|
+
? { cloudflaredLog: share.tunnel.diagnostics }
|
|
109
|
+
: {}),
|
|
110
|
+
}),
|
|
125
111
|
_notes: [
|
|
126
112
|
...(share.tunnel.binary.source === 'downloaded'
|
|
127
113
|
? ['cloudflared was not installed, so it was downloaded once '
|
|
@@ -129,24 +115,31 @@ export function liveViewTools(attachRef, captures, shareRef) {
|
|
|
129
115
|
: []),
|
|
130
116
|
...(share.tunnel.ready
|
|
131
117
|
? []
|
|
132
|
-
: ['The tunnel
|
|
133
|
-
+ '
|
|
118
|
+
: ['The tunnel never confirmed a connection to Cloudflare, so '
|
|
119
|
+
+ 'the URL may not resolve yet. Give it ~10s and reload '
|
|
120
|
+
+ 'first. If it still fails, say so and offer to retry '
|
|
121
|
+
+ '(stop_browser_view then share_browser_view) — a quick '
|
|
122
|
+
+ 'tunnel does sometimes fail to register. '
|
|
123
|
+
+ '`cloudflaredLog` has its output if it keeps happening.']),
|
|
134
124
|
'Relay `url` to the developer verbatim, on its own line.',
|
|
135
|
-
'The URL is a credential: it needs no login, and anyone with '
|
|
136
|
-
+ '
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
125
|
+
'The URL is a credential: it needs no login, and anyone with it '
|
|
126
|
+
+ 'can see this browser and act in it as the signed-in user.',
|
|
127
|
+
'It shows the whole browser, so a sign-in that opens a separate '
|
|
128
|
+
+ 'window is visible too — unlike a single-tab stream.',
|
|
129
|
+
'Opening it on a phone lays the page out for that phone, with '
|
|
130
|
+
+ 'touch, pinch-zoom and the soft keyboard.',
|
|
131
|
+
'Call stop_browser_view when they are done. That closes the '
|
|
132
|
+
+ 'tunnel only — the browser keeps running.',
|
|
140
133
|
],
|
|
141
134
|
};
|
|
142
135
|
}),
|
|
143
136
|
defineTool({
|
|
144
137
|
name: 'stop_browser_view',
|
|
145
|
-
description: 'Close the public
|
|
146
|
-
+ '
|
|
147
|
-
+ '
|
|
148
|
-
+ '
|
|
149
|
-
+ 'keeps running.',
|
|
138
|
+
description: 'Close the public URL opened by share_browser_view: shuts the '
|
|
139
|
+
+ 'tunnel down, so the URL stops resolving for whoever still has '
|
|
140
|
+
+ 'it. Call it as soon as the person you shared with is finished. '
|
|
141
|
+
+ 'Nothing else changes — the browser stays attached, its own live '
|
|
142
|
+
+ 'view keeps serving locally, and any capture keeps running.',
|
|
150
143
|
inputSchema: { type: 'object', properties: {} },
|
|
151
144
|
}, async () => ({ stopped: await stopLiveShare(shareRef) })),
|
|
152
145
|
];
|
|
@@ -7,3 +7,17 @@ export declare function sessionTools(attachRef: {
|
|
|
7
7
|
current?: AttachState;
|
|
8
8
|
config: ConfigStore;
|
|
9
9
|
}, captures: CaptureStore, liveShare: LiveShareRef): RegisteredTool[];
|
|
10
|
+
/**
|
|
11
|
+
* Release everything a session holds: the public tunnel, the captures, and the
|
|
12
|
+
* browser itself.
|
|
13
|
+
*
|
|
14
|
+
* Exported because a session can end WITHOUT anyone calling `dispose_browser` —
|
|
15
|
+
* the developer closes their agent, the client kills the server, the transport
|
|
16
|
+
* drops. Nothing runs on its own then, so `start.ts` runs this. Without it a
|
|
17
|
+
* container keeps running and, worse, a public URL to a live browser outlives
|
|
18
|
+
* the session that created it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resetAll(attachRef: {
|
|
21
|
+
current?: AttachState;
|
|
22
|
+
config: ConfigStore;
|
|
23
|
+
}, captures: CaptureStore, liveShare: LiveShareRef): Promise<void>;
|
|
@@ -24,11 +24,7 @@ export function sessionTools(attachRef, captures, liveShare) {
|
|
|
24
24
|
// URL can recover it instead of opening a second tunnel.
|
|
25
25
|
...(liveShare.current
|
|
26
26
|
? {
|
|
27
|
-
sharedView: {
|
|
28
|
-
url: liveShare.current.url,
|
|
29
|
-
interactive: liveShare.current.interactive,
|
|
30
|
-
viewers: liveShare.current.view.viewers(),
|
|
31
|
-
},
|
|
27
|
+
sharedView: { url: liveShare.current.url },
|
|
32
28
|
}
|
|
33
29
|
: {}),
|
|
34
30
|
})),
|
|
@@ -55,9 +51,19 @@ export function sessionTools(attachRef, captures, liveShare) {
|
|
|
55
51
|
}),
|
|
56
52
|
];
|
|
57
53
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Release everything a session holds: the public tunnel, the captures, and the
|
|
56
|
+
* browser itself.
|
|
57
|
+
*
|
|
58
|
+
* Exported because a session can end WITHOUT anyone calling `dispose_browser` —
|
|
59
|
+
* the developer closes their agent, the client kills the server, the transport
|
|
60
|
+
* drops. Nothing runs on its own then, so `start.ts` runs this. Without it a
|
|
61
|
+
* container keeps running and, worse, a public URL to a live browser outlives
|
|
62
|
+
* the session that created it.
|
|
63
|
+
*/
|
|
64
|
+
export async function resetAll(attachRef, captures, liveShare) {
|
|
65
|
+
// Before the captures: a public URL must never outlive the browser it points
|
|
66
|
+
// at.
|
|
61
67
|
await stopLiveShare(liveShare);
|
|
62
68
|
await resetCaptures(captures);
|
|
63
69
|
await disposeAttached(attachRef);
|
package/lib/old/client.d.ts
CHANGED
|
@@ -94,6 +94,70 @@ export interface LinkNonce {
|
|
|
94
94
|
nonce: string;
|
|
95
95
|
expiresInSeconds: number;
|
|
96
96
|
}
|
|
97
|
+
/** Log-level filter accepted by the devtools backend's session-logs route.
|
|
98
|
+
* It validates against this exact list and rejects anything else with a 400,
|
|
99
|
+
* including the comma-separated lists the log-stream service itself would
|
|
100
|
+
* accept. Each value is hierarchical at the log-stream end: `fine` means
|
|
101
|
+
* FINE+CONFIG+INFO+WARNING+SEVERE, `info` means INFO+WARNING+SEVERE, and so
|
|
102
|
+
* on. `finer`/`finest` (the PII tiers) are not reachable through this route
|
|
103
|
+
* at all. */
|
|
104
|
+
export type SessionLogLevel = 'fine' | 'config' | 'info' | 'warning' | 'severe' | 'unknown';
|
|
105
|
+
/** Filters for {@link ReclaimOldClient.getSessionLogs}. Every one is optional
|
|
106
|
+
* and forwarded verbatim; the session id itself is the route's path param, so
|
|
107
|
+
* `providerId`/`deviceId` narrow WITHIN one session rather than searching
|
|
108
|
+
* across sessions. */
|
|
109
|
+
export interface SessionLogsQuery {
|
|
110
|
+
deviceId?: string;
|
|
111
|
+
source?: string;
|
|
112
|
+
/** The client-side logger NAME (log-stream's `log_type` column, the SDK's
|
|
113
|
+
* `LogEntry.type`) — not the event type. */
|
|
114
|
+
logType?: string;
|
|
115
|
+
logLevel?: SessionLogLevel;
|
|
116
|
+
providerId?: string;
|
|
117
|
+
/** ISO-8601. Omitting BOTH bounds makes the log-stream service default to
|
|
118
|
+
* the last 3 days. */
|
|
119
|
+
startTime?: string;
|
|
120
|
+
endTime?: string;
|
|
121
|
+
/** Substring match against the log line (`LIKE %…%`). */
|
|
122
|
+
logLine?: string;
|
|
123
|
+
/** Exact `LogEventType` name, for example `REQUEST_MATCHED`. */
|
|
124
|
+
eventType?: string;
|
|
125
|
+
/** Capped at 1000 by the log-stream service. */
|
|
126
|
+
limit?: number;
|
|
127
|
+
offset?: number;
|
|
128
|
+
includeCount?: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** One raw log line, in the log-stream service's snake_case ClickHouse shape,
|
|
131
|
+
* passed through unchanged by the devtools backend. `event_type` is the SDK's
|
|
132
|
+
* `LogEventType` milestone marker (`REQUEST_MATCHED`,
|
|
133
|
+
* `CLAIM_CREATION_STARTED`, `PROOF_GENERATED`, …); it is `''` on the majority
|
|
134
|
+
* of lines, which carry no event. */
|
|
135
|
+
export interface SessionLogRow {
|
|
136
|
+
timestamp: string;
|
|
137
|
+
request_id: string;
|
|
138
|
+
session_id: string;
|
|
139
|
+
device_id: string;
|
|
140
|
+
source: string;
|
|
141
|
+
app_id: string;
|
|
142
|
+
provider_id: string;
|
|
143
|
+
log_type: string;
|
|
144
|
+
log_level: string;
|
|
145
|
+
log_line: string;
|
|
146
|
+
event_type?: string;
|
|
147
|
+
metadata?: string;
|
|
148
|
+
}
|
|
149
|
+
/** `GET /api/logger/:sessionId`'s `data` envelope. The backend queries every
|
|
150
|
+
* region that holds the session, concatenates the pages, sorts them
|
|
151
|
+
* newest-first and re-slices to `limit`, so `data` is one page and
|
|
152
|
+
* `totalCount` is the match count across all of them. */
|
|
153
|
+
export interface SessionLogsPage {
|
|
154
|
+
data: SessionLogRow[];
|
|
155
|
+
count: number;
|
|
156
|
+
totalCount?: number;
|
|
157
|
+
/** Regions whose log-stream query failed — the page is INCOMPLETE when
|
|
158
|
+
* this is present. */
|
|
159
|
+
failedRegions?: string[];
|
|
160
|
+
}
|
|
97
161
|
/**
|
|
98
162
|
* Minimal REST client for the OLD devtools Express backend (base `/api`).
|
|
99
163
|
* Deliberately NOT a clone of `@reclaimprotocol/client`'s operationId
|
|
@@ -133,6 +197,25 @@ export declare class ReclaimOldClient {
|
|
|
133
197
|
* `#request`). Attaches the caller identity when one is set; queried
|
|
134
198
|
* anonymously otherwise. */
|
|
135
199
|
getSessionAnalyticsLogs(sessionId: string): Promise<unknown>;
|
|
200
|
+
/**
|
|
201
|
+
* Raw log LINES for a verification session — the SDK's own diagnostic
|
|
202
|
+
* logging, the thing the dashboard's session "logs" tab renders, and a
|
|
203
|
+
* strictly richer signal than `getSessionAnalyticsLogs`'s milestone events.
|
|
204
|
+
*
|
|
205
|
+
* This is the devtools backend (`GET /api/logger/:sessionId`), NOT the
|
|
206
|
+
* analytics-logs host — hence `#request`, and hence the same identity and
|
|
207
|
+
* ownership rules as every other authed call here: the session's app must
|
|
208
|
+
* be one you created (admins excepted), or the route 404s.
|
|
209
|
+
*
|
|
210
|
+
* The backend fans the query out to the per-region log-stream services,
|
|
211
|
+
* which read the ClickHouse `logs` table. Two defaults applied down there
|
|
212
|
+
* bite hard if you don't override them:
|
|
213
|
+
* - no `startTime`/`endTime` → only the last 3 days are searched;
|
|
214
|
+
* - no `logLevel` → INFO and above only, so every FINE/CONFIG line is
|
|
215
|
+
* invisible.
|
|
216
|
+
* Entries are dropped entirely after 30 days (table TTL).
|
|
217
|
+
*/
|
|
218
|
+
getSessionLogs(sessionId: string, query?: SessionLogsQuery): Promise<SessionLogsPage>;
|
|
136
219
|
/** Mint a brand-new, unassociated (sandboxed) app — no auth required. Link
|
|
137
220
|
* it to an account afterwards with `getLinkNonce` + `linkAppToAccount`, or
|
|
138
221
|
* use it as-is within the sandbox session limit. */
|
package/lib/old/client.js
CHANGED
|
@@ -89,6 +89,52 @@ export class ReclaimOldClient {
|
|
|
89
89
|
}
|
|
90
90
|
return json;
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Raw log LINES for a verification session — the SDK's own diagnostic
|
|
94
|
+
* logging, the thing the dashboard's session "logs" tab renders, and a
|
|
95
|
+
* strictly richer signal than `getSessionAnalyticsLogs`'s milestone events.
|
|
96
|
+
*
|
|
97
|
+
* This is the devtools backend (`GET /api/logger/:sessionId`), NOT the
|
|
98
|
+
* analytics-logs host — hence `#request`, and hence the same identity and
|
|
99
|
+
* ownership rules as every other authed call here: the session's app must
|
|
100
|
+
* be one you created (admins excepted), or the route 404s.
|
|
101
|
+
*
|
|
102
|
+
* The backend fans the query out to the per-region log-stream services,
|
|
103
|
+
* which read the ClickHouse `logs` table. Two defaults applied down there
|
|
104
|
+
* bite hard if you don't override them:
|
|
105
|
+
* - no `startTime`/`endTime` → only the last 3 days are searched;
|
|
106
|
+
* - no `logLevel` → INFO and above only, so every FINE/CONFIG line is
|
|
107
|
+
* invisible.
|
|
108
|
+
* Entries are dropped entirely after 30 days (table TTL).
|
|
109
|
+
*/
|
|
110
|
+
async getSessionLogs(sessionId, query = {}) {
|
|
111
|
+
const res = await __classPrivateFieldGet(this, _ReclaimOldClient_instances, "m", _ReclaimOldClient_request).call(this, 'GET', `/logger/${encodeURIComponent(sessionId)}`, {
|
|
112
|
+
auth: true,
|
|
113
|
+
query: {
|
|
114
|
+
deviceId: query.deviceId,
|
|
115
|
+
source: query.source,
|
|
116
|
+
logType: query.logType,
|
|
117
|
+
logLevel: query.logLevel,
|
|
118
|
+
providerId: query.providerId,
|
|
119
|
+
startTime: query.startTime,
|
|
120
|
+
endTime: query.endTime,
|
|
121
|
+
logLine: query.logLine,
|
|
122
|
+
eventType: query.eventType,
|
|
123
|
+
limit: query.limit,
|
|
124
|
+
offset: query.offset,
|
|
125
|
+
includeCount: query.includeCount === undefined
|
|
126
|
+
? undefined
|
|
127
|
+
: String(query.includeCount),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const page = res.data;
|
|
131
|
+
return {
|
|
132
|
+
data: page?.data ?? [],
|
|
133
|
+
count: page?.count ?? page?.data?.length ?? 0,
|
|
134
|
+
totalCount: page?.totalCount,
|
|
135
|
+
failedRegions: page?.failedRegions,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
92
138
|
/** Mint a brand-new, unassociated (sandboxed) app — no auth required. Link
|
|
93
139
|
* it to an account afterwards with `getLinkNonce` + `linkAppToAccount`, or
|
|
94
140
|
* use it as-is within the sandbox session limit. */
|
package/lib/old/tools/build.js
CHANGED
|
@@ -8,6 +8,7 @@ import { getMeProvidersTool } from "./me-providers.js";
|
|
|
8
8
|
import { createVersionFromCaptureTool } from "./publish.js";
|
|
9
9
|
import { scaffoldDemoServerTool } from "./scaffold-demo-server.js";
|
|
10
10
|
import { sessionAnalyticsLogsTool } from "./session-analytics-logs.js";
|
|
11
|
+
import { sessionLogsTool } from "./session-logs.js";
|
|
11
12
|
import { updateProviderMetadataTool } from "./update-provider-metadata.js";
|
|
12
13
|
import { verifySdkSetupTool } from "./verify-sdk-setup.js";
|
|
13
14
|
/**
|
|
@@ -32,6 +33,7 @@ export function buildOldBackendTools(client, sdkClient, attachRef) {
|
|
|
32
33
|
getProviderInfoTool(sdkClient),
|
|
33
34
|
updateProviderMetadataTool(client),
|
|
34
35
|
sessionAnalyticsLogsTool(client),
|
|
36
|
+
sessionLogsTool(client),
|
|
35
37
|
issueAppCredentialsTool(client),
|
|
36
38
|
linkAppTool(client),
|
|
37
39
|
checkAppStatusTool(client),
|