@relayplane/proxy 1.8.34 → 1.8.37
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 +9 -7
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/lifecycle-telemetry.d.ts +29 -0
- package/dist/lifecycle-telemetry.d.ts.map +1 -0
- package/dist/lifecycle-telemetry.js +166 -0
- package/dist/lifecycle-telemetry.js.map +1 -0
- package/dist/osmosis-store.d.ts +4 -1
- package/dist/osmosis-store.d.ts.map +1 -1
- package/dist/osmosis-store.js +18 -4
- package/dist/osmosis-store.js.map +1 -1
- package/dist/session-tracker.d.ts +61 -0
- package/dist/session-tracker.d.ts.map +1 -0
- package/dist/session-tracker.js +262 -0
- package/dist/session-tracker.js.map +1 -0
- package/dist/standalone-proxy.d.ts.map +1 -1
- package/dist/standalone-proxy.js +212 -5
- package/dist/standalone-proxy.js.map +1 -1
- package/package.json +13 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"osmosis-store.js","sourceRoot":"","sources":["../src/osmosis-store.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"osmosis-store.js","sourceRoot":"","sources":["../src/osmosis-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Intelligence — X-Claude-Code-Session-Id tracking
|
|
3
|
+
*
|
|
4
|
+
* Stores per-session aggregates in ~/.relayplane/sessions.db (SQLite via better-sqlite3).
|
|
5
|
+
* Falls back to an in-memory Map if SQLite is unavailable (e.g. no native bindings).
|
|
6
|
+
*
|
|
7
|
+
* Extracts the X-Claude-Code-Session-Id header from every request;
|
|
8
|
+
* if missing, synthesises a session ID from sha256(hour + model).slice(0,16)
|
|
9
|
+
* prefixed with "syn_".
|
|
10
|
+
*
|
|
11
|
+
* All writes are fire-and-forget; errors are silently swallowed.
|
|
12
|
+
*/
|
|
13
|
+
import type * as http from 'node:http';
|
|
14
|
+
export interface SessionEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
started_at: number;
|
|
17
|
+
last_seen_at: number;
|
|
18
|
+
total_cost_usd: number;
|
|
19
|
+
total_tokens_in: number;
|
|
20
|
+
total_tokens_out: number;
|
|
21
|
+
request_count: number;
|
|
22
|
+
session_source: 'claude-code' | 'synthetic';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Extract or synthesise a session ID from an incoming HTTP request.
|
|
26
|
+
*
|
|
27
|
+
* - If the X-Claude-Code-Session-Id header is present, use it (source = 'claude-code').
|
|
28
|
+
* - Otherwise, generate a synthetic ID: sha256(`${hourBucket}:${model}`).slice(0,16)
|
|
29
|
+
* prefixed with "syn_" (source = 'synthetic').
|
|
30
|
+
*/
|
|
31
|
+
export declare function getSessionId(req: Pick<http.IncomingMessage, 'headers'>, model?: string): {
|
|
32
|
+
sessionId: string;
|
|
33
|
+
sessionSource: 'claude-code' | 'synthetic';
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Upsert a session record, accumulating cost and token counts.
|
|
37
|
+
* Fire-and-forget; never throws. Falls back to in-memory store if SQLite unavailable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function upsertSession(sessionId: string, sessionSource: 'claude-code' | 'synthetic', costUsd: number, tokensIn: number, tokensOut: number): void;
|
|
40
|
+
export interface SessionQueryOptions {
|
|
41
|
+
limit?: number;
|
|
42
|
+
days?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Query sessions from the last N days, most-recent first, paginated.
|
|
46
|
+
* Falls back to in-memory store if SQLite unavailable.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getSessions(options?: SessionQueryOptions): SessionEntry[];
|
|
49
|
+
/**
|
|
50
|
+
* Return sessions active in the last 5 minutes.
|
|
51
|
+
* Falls back to in-memory store if SQLite unavailable.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getActiveSessions(): SessionEntry[];
|
|
54
|
+
/**
|
|
55
|
+
* Mark stale sessions complete — staleness is computed dynamically via
|
|
56
|
+
* last_seen_at filters, so this is a no-op exposed for API symmetry.
|
|
57
|
+
*/
|
|
58
|
+
export declare function markStaleSessionsComplete(): void;
|
|
59
|
+
/** Exposed for testing — reset singleton state. */
|
|
60
|
+
export declare function _resetStore(): void;
|
|
61
|
+
//# sourceMappingURL=session-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-tracker.d.ts","sourceRoot":"","sources":["../src/session-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,aAAa,GAAG,WAAW,CAAC;CAC7C;AAoED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,EAC1C,KAAK,CAAC,EAAE,MAAM,GACb;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,aAAa,GAAG,WAAW,CAAA;CAAE,CAanE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,aAAa,GAAG,WAAW,EAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAwCN;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,EAAE,CA4B7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAwBlD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED,mDAAmD;AACnD,wBAAgB,WAAW,IAAI,IAAI,CAWlC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Intelligence — X-Claude-Code-Session-Id tracking
|
|
4
|
+
*
|
|
5
|
+
* Stores per-session aggregates in ~/.relayplane/sessions.db (SQLite via better-sqlite3).
|
|
6
|
+
* Falls back to an in-memory Map if SQLite is unavailable (e.g. no native bindings).
|
|
7
|
+
*
|
|
8
|
+
* Extracts the X-Claude-Code-Session-Id header from every request;
|
|
9
|
+
* if missing, synthesises a session ID from sha256(hour + model).slice(0,16)
|
|
10
|
+
* prefixed with "syn_".
|
|
11
|
+
*
|
|
12
|
+
* All writes are fire-and-forget; errors are silently swallowed.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.getSessionId = getSessionId;
|
|
49
|
+
exports.upsertSession = upsertSession;
|
|
50
|
+
exports.getSessions = getSessions;
|
|
51
|
+
exports.getActiveSessions = getActiveSessions;
|
|
52
|
+
exports.markStaleSessionsComplete = markStaleSessionsComplete;
|
|
53
|
+
exports._resetStore = _resetStore;
|
|
54
|
+
const crypto = __importStar(require("node:crypto"));
|
|
55
|
+
const fs = __importStar(require("node:fs"));
|
|
56
|
+
const os = __importStar(require("node:os"));
|
|
57
|
+
const path = __importStar(require("node:path"));
|
|
58
|
+
const SESSIONS_SCHEMA_SQL = `
|
|
59
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
started_at INTEGER NOT NULL,
|
|
62
|
+
last_seen_at INTEGER NOT NULL,
|
|
63
|
+
total_cost_usd REAL NOT NULL DEFAULT 0,
|
|
64
|
+
total_tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
65
|
+
total_tokens_out INTEGER NOT NULL DEFAULT 0,
|
|
66
|
+
request_count INTEGER NOT NULL DEFAULT 1,
|
|
67
|
+
session_source TEXT NOT NULL DEFAULT 'synthetic'
|
|
68
|
+
);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS sessions_last_seen ON sessions(last_seen_at);
|
|
70
|
+
`;
|
|
71
|
+
let _db = undefined;
|
|
72
|
+
let _upsertStmt = null;
|
|
73
|
+
/** In-memory fallback when SQLite is unavailable. */
|
|
74
|
+
const _memStore = new Map();
|
|
75
|
+
function getRelayplaneDir() {
|
|
76
|
+
const override = process.env['RELAYPLANE_HOME_OVERRIDE'];
|
|
77
|
+
const base = override ?? os.homedir();
|
|
78
|
+
return path.join(base, '.relayplane');
|
|
79
|
+
}
|
|
80
|
+
function ensureDir(dir) {
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
function initDb() {
|
|
84
|
+
try {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
86
|
+
const Database = require('better-sqlite3');
|
|
87
|
+
const dir = getRelayplaneDir();
|
|
88
|
+
ensureDir(dir);
|
|
89
|
+
const dbPath = path.join(dir, 'sessions.db');
|
|
90
|
+
const db = new Database(dbPath);
|
|
91
|
+
db.pragma('journal_mode = WAL');
|
|
92
|
+
db.exec(SESSIONS_SCHEMA_SQL);
|
|
93
|
+
return db;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function getDb() {
|
|
100
|
+
if (_db !== undefined)
|
|
101
|
+
return _db;
|
|
102
|
+
_db = initDb();
|
|
103
|
+
if (_db) {
|
|
104
|
+
_upsertStmt = _db.prepare(`
|
|
105
|
+
INSERT INTO sessions
|
|
106
|
+
(id, started_at, last_seen_at, total_cost_usd, total_tokens_in, total_tokens_out, request_count, session_source)
|
|
107
|
+
VALUES
|
|
108
|
+
(@id, @started_at, @last_seen_at, @total_cost_usd, @total_tokens_in, @total_tokens_out, 1, @session_source)
|
|
109
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
110
|
+
last_seen_at = excluded.last_seen_at,
|
|
111
|
+
total_cost_usd = total_cost_usd + excluded.total_cost_usd,
|
|
112
|
+
total_tokens_in = total_tokens_in + excluded.total_tokens_in,
|
|
113
|
+
total_tokens_out = total_tokens_out + excluded.total_tokens_out,
|
|
114
|
+
request_count = request_count + 1
|
|
115
|
+
`);
|
|
116
|
+
}
|
|
117
|
+
return _db;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Extract or synthesise a session ID from an incoming HTTP request.
|
|
121
|
+
*
|
|
122
|
+
* - If the X-Claude-Code-Session-Id header is present, use it (source = 'claude-code').
|
|
123
|
+
* - Otherwise, generate a synthetic ID: sha256(`${hourBucket}:${model}`).slice(0,16)
|
|
124
|
+
* prefixed with "syn_" (source = 'synthetic').
|
|
125
|
+
*/
|
|
126
|
+
function getSessionId(req, model) {
|
|
127
|
+
const headerVal = req.headers['x-claude-code-session-id'];
|
|
128
|
+
const raw = Array.isArray(headerVal) ? headerVal[0] : headerVal;
|
|
129
|
+
if (raw && raw.trim()) {
|
|
130
|
+
return { sessionId: raw.trim(), sessionSource: 'claude-code' };
|
|
131
|
+
}
|
|
132
|
+
// Synthetic: keyed by UTC hour bucket + model
|
|
133
|
+
const now = new Date();
|
|
134
|
+
const hourBucket = `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}-${now.getUTCHours()}`;
|
|
135
|
+
const key = `${hourBucket}:${model ?? 'unknown'}`;
|
|
136
|
+
const hash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 16);
|
|
137
|
+
return { sessionId: `syn_${hash}`, sessionSource: 'synthetic' };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Upsert a session record, accumulating cost and token counts.
|
|
141
|
+
* Fire-and-forget; never throws. Falls back to in-memory store if SQLite unavailable.
|
|
142
|
+
*/
|
|
143
|
+
function upsertSession(sessionId, sessionSource, costUsd, tokensIn, tokensOut) {
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
try {
|
|
146
|
+
const db = getDb();
|
|
147
|
+
if (db && _upsertStmt) {
|
|
148
|
+
_upsertStmt.run({
|
|
149
|
+
id: sessionId,
|
|
150
|
+
started_at: now,
|
|
151
|
+
last_seen_at: now,
|
|
152
|
+
total_cost_usd: costUsd,
|
|
153
|
+
total_tokens_in: tokensIn,
|
|
154
|
+
total_tokens_out: tokensOut,
|
|
155
|
+
session_source: sessionSource,
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// fall through to in-memory
|
|
162
|
+
}
|
|
163
|
+
// In-memory fallback
|
|
164
|
+
const existing = _memStore.get(sessionId);
|
|
165
|
+
if (existing) {
|
|
166
|
+
existing.last_seen_at = now;
|
|
167
|
+
existing.total_cost_usd += costUsd;
|
|
168
|
+
existing.total_tokens_in += tokensIn;
|
|
169
|
+
existing.total_tokens_out += tokensOut;
|
|
170
|
+
existing.request_count += 1;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
_memStore.set(sessionId, {
|
|
174
|
+
id: sessionId,
|
|
175
|
+
started_at: now,
|
|
176
|
+
last_seen_at: now,
|
|
177
|
+
total_cost_usd: costUsd,
|
|
178
|
+
total_tokens_in: tokensIn,
|
|
179
|
+
total_tokens_out: tokensOut,
|
|
180
|
+
request_count: 1,
|
|
181
|
+
session_source: sessionSource,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Query sessions from the last N days, most-recent first, paginated.
|
|
187
|
+
* Falls back to in-memory store if SQLite unavailable.
|
|
188
|
+
*/
|
|
189
|
+
function getSessions(options = {}) {
|
|
190
|
+
const limit = options.limit ?? 20;
|
|
191
|
+
const days = options.days ?? 7;
|
|
192
|
+
const cutoff = Date.now() - days * 86400000;
|
|
193
|
+
try {
|
|
194
|
+
const db = getDb();
|
|
195
|
+
if (db) {
|
|
196
|
+
const rows = db
|
|
197
|
+
.prepare(`SELECT id, started_at, last_seen_at, total_cost_usd, total_tokens_in, total_tokens_out, request_count, session_source
|
|
198
|
+
FROM sessions
|
|
199
|
+
WHERE last_seen_at >= ?
|
|
200
|
+
ORDER BY last_seen_at DESC
|
|
201
|
+
LIMIT ?`)
|
|
202
|
+
.all(cutoff, limit);
|
|
203
|
+
return rows;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// fall through to in-memory
|
|
208
|
+
}
|
|
209
|
+
// In-memory fallback
|
|
210
|
+
return Array.from(_memStore.values())
|
|
211
|
+
.filter(s => s.last_seen_at >= cutoff)
|
|
212
|
+
.sort((a, b) => b.last_seen_at - a.last_seen_at)
|
|
213
|
+
.slice(0, limit);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Return sessions active in the last 5 minutes.
|
|
217
|
+
* Falls back to in-memory store if SQLite unavailable.
|
|
218
|
+
*/
|
|
219
|
+
function getActiveSessions() {
|
|
220
|
+
const cutoff = Date.now() - 5 * 60 * 1000;
|
|
221
|
+
try {
|
|
222
|
+
const db = getDb();
|
|
223
|
+
if (db) {
|
|
224
|
+
const rows = db
|
|
225
|
+
.prepare(`SELECT id, started_at, last_seen_at, total_cost_usd, total_tokens_in, total_tokens_out, request_count, session_source
|
|
226
|
+
FROM sessions
|
|
227
|
+
WHERE last_seen_at >= ?
|
|
228
|
+
ORDER BY last_seen_at DESC`)
|
|
229
|
+
.all(cutoff);
|
|
230
|
+
return rows;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// fall through to in-memory
|
|
235
|
+
}
|
|
236
|
+
// In-memory fallback
|
|
237
|
+
return Array.from(_memStore.values())
|
|
238
|
+
.filter(s => s.last_seen_at >= cutoff)
|
|
239
|
+
.sort((a, b) => b.last_seen_at - a.last_seen_at);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Mark stale sessions complete — staleness is computed dynamically via
|
|
243
|
+
* last_seen_at filters, so this is a no-op exposed for API symmetry.
|
|
244
|
+
*/
|
|
245
|
+
function markStaleSessionsComplete() {
|
|
246
|
+
// No-op: staleness determined dynamically in getActiveSessions()
|
|
247
|
+
}
|
|
248
|
+
/** Exposed for testing — reset singleton state. */
|
|
249
|
+
function _resetStore() {
|
|
250
|
+
if (_db) {
|
|
251
|
+
try {
|
|
252
|
+
_db.close();
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
/* ignore */
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
_db = undefined;
|
|
259
|
+
_upsertStmt = null;
|
|
260
|
+
_memStore.clear();
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=session-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-tracker.js","sourceRoot":"","sources":["../src/session-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAY,MAAM,kBAAkB,CAAC;AAE3E,KAAK,QAAQ,GAAG,YAAY,GACxB,YAAY,GACZ,UAAU,GACV,MAAM,GACN,SAAS,GACT,UAAU,GACV,WAAW,GACX,YAAY,GACZ,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"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"}
|