@eddyskywalker/dsh-chatgpt-subscription 0.1.5 → 0.1.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/CHANGELOG.md +11 -0
- package/README.md +32 -15
- package/lib/client.js +34 -249
- package/lib/client.js.map +1 -1
- package/lib/index.js +392 -16
- package/lib/types/client/CodexSubscriptionSection.d.ts +3 -1
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +17 -5
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/host/bash-prompt-scheduling-compat.d.ts +51 -0
- package/lib/types/host/bash-prompt-scheduling-compat.d.ts.map +1 -0
- package/lib/types/host/oauth-service.d.ts.map +1 -1
- package/lib/types/host/platform-token-store.d.ts +3 -0
- package/lib/types/host/platform-token-store.d.ts.map +1 -0
- package/lib/types/host/subagent-report-scheduling-compat.d.ts +21 -0
- package/lib/types/host/subagent-report-scheduling-compat.d.ts.map +1 -0
- package/lib/types/host/token-store-linux.d.ts +20 -0
- package/lib/types/host/token-store-linux.d.ts.map +1 -0
- package/lib/types/host/token-store-windows.d.ts +4 -0
- package/lib/types/host/token-store-windows.d.ts.map +1 -1
- package/lib/types/host/token-store.d.ts +6 -0
- package/lib/types/host/token-store.d.ts.map +1 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/contracts.d.ts +7 -4
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/package.json +3 -1
- package/lib/types/client/process-folding.d.ts +0 -5
- package/lib/types/client/process-folding.d.ts.map +0 -1
package/lib/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CallId, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
2
2
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import http from "node:http";
|
|
4
|
+
import { constants } from "node:fs";
|
|
5
|
+
import { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
4
6
|
import { homedir } from "node:os";
|
|
5
7
|
import { dirname, join } from "node:path";
|
|
6
8
|
import { spawn } from "node:child_process";
|
|
@@ -122,6 +124,260 @@ var CodexChatGptAdapter = class extends LlmAdapter {
|
|
|
122
124
|
}
|
|
123
125
|
};
|
|
124
126
|
//#endregion
|
|
127
|
+
//#region src/host/subagent-report-scheduling-compat.ts
|
|
128
|
+
/**
|
|
129
|
+
* DSH_COMPAT_REMOVE(subagent-report-settlement-dedup)
|
|
130
|
+
*
|
|
131
|
+
* Temporary compatibility shim for DSH 0.1.0-rc.6. A continuable child is told
|
|
132
|
+
* to report its result before finishing, while DSH also unconditionally sends
|
|
133
|
+
* the same closing output in a `subagent-settled` notice. The report is often
|
|
134
|
+
* still queued when the settlement reaches the parent, so the parent sees the
|
|
135
|
+
* result once and the equivalent report remains as duplicate next-turn work.
|
|
136
|
+
*
|
|
137
|
+
* Remove this module, its installation in `src/index.ts`, and its focused test
|
|
138
|
+
* once upstream coalesces an equivalent final report with settlement delivery.
|
|
139
|
+
*/
|
|
140
|
+
const DSH_SUBAGENT_REPORT_DEDUP_COMPAT_MARKER = "__dshChatgptSubscriptionSubagentReportDedupCompatV1";
|
|
141
|
+
function sourceOf(message) {
|
|
142
|
+
return message.source;
|
|
143
|
+
}
|
|
144
|
+
function isTextBlock(value, text) {
|
|
145
|
+
return typeof value === "object" && value !== null && value.type === "text" && value.text === text;
|
|
146
|
+
}
|
|
147
|
+
function sameValue(left, right) {
|
|
148
|
+
if (left === right) return true;
|
|
149
|
+
if (Array.isArray(left) || Array.isArray(right)) return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => sameValue(value, right[index]));
|
|
150
|
+
if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
|
|
151
|
+
const leftRecord = left;
|
|
152
|
+
const rightRecord = right;
|
|
153
|
+
const leftKeys = Object.keys(leftRecord).sort();
|
|
154
|
+
const rightKeys = Object.keys(rightRecord).sort();
|
|
155
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && sameValue(leftRecord[key], rightRecord[key]));
|
|
156
|
+
}
|
|
157
|
+
function duplicatePendingReports(agent, settlement) {
|
|
158
|
+
const settlementSource = sourceOf(settlement);
|
|
159
|
+
if (settlementSource.kind !== "subagent-settled" || settlementSource.senderSessionId === void 0) return [];
|
|
160
|
+
if (settlement.content.length < 2 || !isTextBlock(settlement.content[1], "Its closing message:")) return [];
|
|
161
|
+
const closingContent = settlement.content.slice(2);
|
|
162
|
+
return [...agent.inbox.nextStep, ...agent.inbox.nextTurn].filter((pending) => {
|
|
163
|
+
const pendingSource = sourceOf(pending);
|
|
164
|
+
return pendingSource.kind === "subagent-report" && pendingSource.senderSessionId === settlementSource.senderSessionId && sameValue(pending.content.slice(1), closingContent);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function errorMessage(error) {
|
|
168
|
+
return error instanceof Error ? error.message : String(error);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Discard only an exact, same-child report duplicate immediately before DSH
|
|
172
|
+
* delivers the corresponding settlement notice. Partial reports, reports with
|
|
173
|
+
* different content, and all unrelated inbox work remain untouched.
|
|
174
|
+
*/
|
|
175
|
+
function installSubagentReportDedupCompat(ctx) {
|
|
176
|
+
const patches = /* @__PURE__ */ new Map();
|
|
177
|
+
const patch = (agent) => {
|
|
178
|
+
if (patches.has(agent)) return;
|
|
179
|
+
const shared = agent.followup[DSH_SUBAGENT_REPORT_DEDUP_COMPAT_MARKER];
|
|
180
|
+
if (shared?.wrappers.followup === agent.followup && shared.wrappers.steer === agent.steer && shared.wrappers.inject === agent.inject) {
|
|
181
|
+
shared.owners += 1;
|
|
182
|
+
patches.set(agent, shared);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const originals = {
|
|
186
|
+
followup: agent.followup,
|
|
187
|
+
steer: agent.steer,
|
|
188
|
+
inject: agent.inject
|
|
189
|
+
};
|
|
190
|
+
let record;
|
|
191
|
+
const deliver = (name, message) => {
|
|
192
|
+
for (const duplicate of duplicatePendingReports(agent, message)) try {
|
|
193
|
+
agent.inbox.remove(duplicate.id);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
ctx.logger.warn("[dsh-chatgpt-subscription] Could not discard a duplicate DSH subagent report: " + errorMessage(error));
|
|
196
|
+
}
|
|
197
|
+
originals[name].call(agent, message);
|
|
198
|
+
};
|
|
199
|
+
const wrappers = {
|
|
200
|
+
followup(message) {
|
|
201
|
+
deliver("followup", message);
|
|
202
|
+
},
|
|
203
|
+
steer(message) {
|
|
204
|
+
deliver("steer", message);
|
|
205
|
+
},
|
|
206
|
+
inject(message) {
|
|
207
|
+
deliver("inject", message);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
record = {
|
|
211
|
+
originals,
|
|
212
|
+
wrappers,
|
|
213
|
+
owners: 1
|
|
214
|
+
};
|
|
215
|
+
for (const wrapper of Object.values(wrappers)) Object.defineProperty(wrapper, DSH_SUBAGENT_REPORT_DEDUP_COMPAT_MARKER, { value: record });
|
|
216
|
+
try {
|
|
217
|
+
agent.followup = wrappers.followup;
|
|
218
|
+
agent.steer = wrappers.steer;
|
|
219
|
+
agent.inject = wrappers.inject;
|
|
220
|
+
patches.set(agent, record);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
record.owners = 0;
|
|
223
|
+
for (const name of [
|
|
224
|
+
"followup",
|
|
225
|
+
"steer",
|
|
226
|
+
"inject"
|
|
227
|
+
]) if (agent[name] === wrappers[name]) try {
|
|
228
|
+
agent[name] = originals[name];
|
|
229
|
+
} catch {}
|
|
230
|
+
ctx.logger.warn("[dsh-chatgpt-subscription] Could not install temporary DSH subagent dedup compatibility: " + errorMessage(error));
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const unpatch = (agent) => {
|
|
234
|
+
const record = patches.get(agent);
|
|
235
|
+
if (!record) return;
|
|
236
|
+
patches.delete(agent);
|
|
237
|
+
record.owners -= 1;
|
|
238
|
+
if (record.owners > 0) return;
|
|
239
|
+
for (const name of [
|
|
240
|
+
"followup",
|
|
241
|
+
"steer",
|
|
242
|
+
"inject"
|
|
243
|
+
]) {
|
|
244
|
+
if (agent[name] !== record.wrappers[name]) continue;
|
|
245
|
+
try {
|
|
246
|
+
agent[name] = record.originals[name];
|
|
247
|
+
} catch (error) {
|
|
248
|
+
ctx.logger.warn("[dsh-chatgpt-subscription] Could not remove temporary DSH subagent dedup compatibility: " + errorMessage(error));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
for (const agent of ctx.agents.list()) patch(agent);
|
|
253
|
+
const disposeCreated = ctx.on("agent/created", ({ agent }) => patch(agent));
|
|
254
|
+
const disposeDisposed = ctx.on("agent/disposed", ({ agent }) => unpatch(agent));
|
|
255
|
+
return () => {
|
|
256
|
+
disposeDisposed();
|
|
257
|
+
disposeCreated();
|
|
258
|
+
for (const agent of [...patches.keys()]) unpatch(agent);
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/host/bash-prompt-scheduling-compat.ts
|
|
263
|
+
/**
|
|
264
|
+
* DSH_COMPAT_REMOVE(persistent-bash-prompt-mismatch)
|
|
265
|
+
*
|
|
266
|
+
* Temporary compatibility shim for DSH 0.1.0-rc.6. The persistent bash tool
|
|
267
|
+
* (`@deepseek-ai/dsh-tool-bash-persistent`) configures the bash shell PS1 to
|
|
268
|
+
* `__DSH_PERSISTENT_BASH_PROMPT__ `, while the underlying PTY backend
|
|
269
|
+
* (`@deepseek-ai/dsh-terminal-bash`) only checks for a hardcoded `dsh> `
|
|
270
|
+
* prompt with a 6-character truncation limit.
|
|
271
|
+
*
|
|
272
|
+
* When the prompt does not match, `promptTextSeen` remains false and the PTY
|
|
273
|
+
* session falls back to the 3.5s idle silence timeout (`idleSilenceMs: 3000ms`
|
|
274
|
+
* + `handoffGraceMs: 500ms`) on every command execution.
|
|
275
|
+
*
|
|
276
|
+
* This compatibility module patches the PTY session `onData` handler so that
|
|
277
|
+
* both `dsh> ` and `__DSH_PERSISTENT_BASH_PROMPT__ ` (and its trimmed variants)
|
|
278
|
+
* satisfy `promptTextSeen`, restoring instant 50ms readiness settling.
|
|
279
|
+
*
|
|
280
|
+
* Remove this module, its installation in `src/index.ts`, and its focused test
|
|
281
|
+
* once upstream aligns `CONTROLLED_PROMPT` with persistent bash tools.
|
|
282
|
+
*/
|
|
283
|
+
const DSH_BASH_PROMPT_COMPAT_MARKER = "__dshChatgptSubscriptionBashPromptCompatV1";
|
|
284
|
+
const KNOWN_CONTROLLED_PROMPTS = [
|
|
285
|
+
"dsh> ",
|
|
286
|
+
"dsh>",
|
|
287
|
+
"__DSH_PERSISTENT_BASH_PROMPT__ ",
|
|
288
|
+
"__DSH_PERSISTENT_BASH_PROMPT__"
|
|
289
|
+
];
|
|
290
|
+
const MAX_PROMPT_BUFFER_LENGTH = 64;
|
|
291
|
+
function isKnownControlledPrompt(tail) {
|
|
292
|
+
if (!tail) return false;
|
|
293
|
+
const cleaned = tail.replaceAll(/[\x00-\x1f\x7f]/g, "").trim();
|
|
294
|
+
for (const known of KNOWN_CONTROLLED_PROMPTS) if (cleaned === known.replaceAll(/[\x00-\x1f\x7f]/g, "").trim()) return true;
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
function patchSessionOnData(target) {
|
|
298
|
+
const obj = target.prototype ?? target;
|
|
299
|
+
if (!obj || typeof obj.onData !== "function") return () => {};
|
|
300
|
+
const existing = obj.onData[DSH_BASH_PROMPT_COMPAT_MARKER];
|
|
301
|
+
if (existing) {
|
|
302
|
+
existing.owners += 1;
|
|
303
|
+
return () => {
|
|
304
|
+
existing.owners -= 1;
|
|
305
|
+
if (existing.owners <= 0 && obj.onData["__dshChatgptSubscriptionBashPromptCompatV1"] === existing) obj.onData = existing.originalOnData;
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const originalOnData = obj.onData;
|
|
309
|
+
const record = {
|
|
310
|
+
originalOnData,
|
|
311
|
+
owners: 1
|
|
312
|
+
};
|
|
313
|
+
function wrapper(data) {
|
|
314
|
+
const sanitized = this.sanitizer.push(data);
|
|
315
|
+
this.appendOutput(sanitized.text);
|
|
316
|
+
if (sanitized.prompt) {
|
|
317
|
+
this.promptSeen = true;
|
|
318
|
+
this.promptTail = "";
|
|
319
|
+
this.lastOutputAt = Date.now();
|
|
320
|
+
}
|
|
321
|
+
if (this.promptSeen && sanitized.promptTail !== void 0) {
|
|
322
|
+
const remaining = Math.max(0, MAX_PROMPT_BUFFER_LENGTH - this.promptTail.length);
|
|
323
|
+
this.promptTail += sanitized.promptTail.slice(0, remaining);
|
|
324
|
+
if (sanitized.promptTail.length > remaining) this.promptTail = `${this.promptTail}\0`;
|
|
325
|
+
this.promptTextSeen = isKnownControlledPrompt(this.promptTail);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
Object.defineProperty(wrapper, DSH_BASH_PROMPT_COMPAT_MARKER, { value: record });
|
|
329
|
+
obj.onData = wrapper;
|
|
330
|
+
return () => {
|
|
331
|
+
record.owners -= 1;
|
|
332
|
+
if (record.owners <= 0 && obj.onData === wrapper) obj.onData = originalOnData;
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Installs the persistent bash prompt compatibility patch onto registered and
|
|
337
|
+
* future PTY backends.
|
|
338
|
+
*/
|
|
339
|
+
function installBashPromptCompat(ctx) {
|
|
340
|
+
const disposers = [];
|
|
341
|
+
const patchBackend = (backend) => {
|
|
342
|
+
if (!backend) return;
|
|
343
|
+
if (typeof backend.createSession === "function") {
|
|
344
|
+
const originalCreate = backend.createSession;
|
|
345
|
+
backend.createSession = function(terminal, config) {
|
|
346
|
+
const session = originalCreate.call(this, terminal, config);
|
|
347
|
+
if (session) patchSessionOnData(session);
|
|
348
|
+
return session;
|
|
349
|
+
};
|
|
350
|
+
disposers.push(() => {
|
|
351
|
+
if (backend.createSession) backend.createSession = originalCreate;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
const terminals = ctx.terminals;
|
|
356
|
+
if (terminals?.backends) {
|
|
357
|
+
for (const backend of terminals.backends.values()) patchBackend(backend);
|
|
358
|
+
if (typeof terminals.registerBackend === "function") {
|
|
359
|
+
const originalRegister = terminals.registerBackend;
|
|
360
|
+
terminals.registerBackend = function(backend) {
|
|
361
|
+
patchBackend(backend);
|
|
362
|
+
return originalRegister.call(this, backend);
|
|
363
|
+
};
|
|
364
|
+
disposers.push(() => {
|
|
365
|
+
if (terminals.registerBackend) terminals.registerBackend = originalRegister;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return () => {
|
|
370
|
+
while (disposers.length > 0) {
|
|
371
|
+
const dispose = disposers.pop();
|
|
372
|
+
try {
|
|
373
|
+
dispose?.();
|
|
374
|
+
} catch (error) {
|
|
375
|
+
ctx.logger.warn("[dsh-chatgpt-subscription] Could not remove bash prompt compatibility: " + (error instanceof Error ? error.message : String(error)));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
//#endregion
|
|
125
381
|
//#region src/compat.ts
|
|
126
382
|
/**
|
|
127
383
|
* Compatibility constants for the ChatGPT-backed Codex flow. The backend and
|
|
@@ -293,13 +549,16 @@ var OAuthService = class {
|
|
|
293
549
|
return this.statusFromCredentials(credentials);
|
|
294
550
|
} catch {
|
|
295
551
|
return {
|
|
296
|
-
...this.statusFromCredentials(null),
|
|
552
|
+
...this.statusFromCredentials(null, false),
|
|
297
553
|
error: publicError(new OAuthServiceError("storage-failed", "Secure credential storage could not be read."))
|
|
298
554
|
};
|
|
299
555
|
}
|
|
300
556
|
}
|
|
301
557
|
async startLogin() {
|
|
302
558
|
this.assertAvailable();
|
|
559
|
+
await this.store.load().catch(() => {
|
|
560
|
+
throw new OAuthServiceError("storage-failed", "Secure credential storage is unavailable. Fix its ownership or permissions before signing in.");
|
|
561
|
+
});
|
|
303
562
|
if (this.activeLogin !== null) throw new OAuthServiceError("login-active", "A ChatGPT sign-in is already in progress.");
|
|
304
563
|
this.lastLoginError = void 0;
|
|
305
564
|
const loginId = this.random(24).toString("base64url");
|
|
@@ -452,14 +711,14 @@ var OAuthService = class {
|
|
|
452
711
|
if (stored === null) throw new OAuthServiceError("not-authenticated", "Sign in with ChatGPT first.");
|
|
453
712
|
return stored;
|
|
454
713
|
}
|
|
455
|
-
statusFromCredentials(credentials) {
|
|
714
|
+
statusFromCredentials(credentials, storageAvailable = true) {
|
|
456
715
|
const active = this.activeLogin;
|
|
457
716
|
if (credentials === null) return {
|
|
458
717
|
authenticated: false,
|
|
459
718
|
account: null,
|
|
460
719
|
storage: {
|
|
461
|
-
|
|
462
|
-
|
|
720
|
+
...this.store.storage,
|
|
721
|
+
available: storageAvailable
|
|
463
722
|
},
|
|
464
723
|
login: {
|
|
465
724
|
active: active !== null,
|
|
@@ -478,8 +737,8 @@ var OAuthService = class {
|
|
|
478
737
|
tokenExpiresAt: Math.floor(credentials.expiresAt / 1e3)
|
|
479
738
|
},
|
|
480
739
|
storage: {
|
|
481
|
-
|
|
482
|
-
|
|
740
|
+
...this.store.storage,
|
|
741
|
+
available: storageAvailable
|
|
483
742
|
},
|
|
484
743
|
login: {
|
|
485
744
|
active: active !== null,
|
|
@@ -718,7 +977,7 @@ async function buildResponsesPayload(options, attachments, localRawImages = {})
|
|
|
718
977
|
}
|
|
719
978
|
function runCodeInstruction(tools) {
|
|
720
979
|
if (!tools?.some((tool) => tool.name === "run_code")) return void 0;
|
|
721
|
-
return "run_code compatibility rule: its code is parsed as strict JavaScript/TypeScript before execution. On Windows,
|
|
980
|
+
return "run_code compatibility rule: its code is parsed as strict JavaScript/TypeScript before execution. Shell commands are nested string data: JavaScript template literals may consume ${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them. On Windows, avoid embedding PowerShell containing $, ${...}, backslashes, or here-strings in template literals; String.raw does not disable ${...} interpolation. On Linux, prefer ordinary quoted strings or write a script file before invoking bash/sh, especially for commands containing backticks or ${...}. Prefer arrays of ordinary quoted strings joined with \"\\n\", escaping backslashes, or use a file-write tool for large scripts.";
|
|
722
981
|
}
|
|
723
982
|
function localRawImageInstruction(stats) {
|
|
724
983
|
if (stats.failed === 0) return void 0;
|
|
@@ -728,18 +987,25 @@ function supportsImageInput(options) {
|
|
|
728
987
|
return options.provider === "codex-chatgpt" && options.model.toLowerCase().startsWith("gpt-");
|
|
729
988
|
}
|
|
730
989
|
function toolDescriptionForCodex(name, description) {
|
|
731
|
-
if (name === "run_code") return `${description}\n\nCompatibility: code is strict JavaScript/TypeScript
|
|
990
|
+
if (name === "run_code") return `${description}\n\nCompatibility: code is strict JavaScript/TypeScript and nested shell commands are string data. Template literals may consume \${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them. Prefer ordinary quoted string arrays joined with "\\n", or write a script file with a dedicated file tool before invoking the shell.`;
|
|
732
991
|
if (isCommandTool(name)) return `${description}\n\n${commandToolCompatibilityText(name)}`;
|
|
733
992
|
return description;
|
|
734
993
|
}
|
|
735
994
|
function commandToolInstruction(tools) {
|
|
736
995
|
const names = tools?.filter((tool) => isCommandTool(tool.name)).map((tool) => tool.name);
|
|
737
996
|
if (!names?.length) return void 0;
|
|
738
|
-
|
|
997
|
+
const uniqueNames = [...new Set(names)];
|
|
998
|
+
const normalized = uniqueNames.map((name) => name.toLowerCase());
|
|
999
|
+
const shellGuidance = [
|
|
1000
|
+
normalized.some((name) => name === "pwsh" || name.includes("powershell")) ? "For pwsh/PowerShell, use native PowerShell syntax and native Windows paths." : void 0,
|
|
1001
|
+
normalized.includes("bash") ? "For bash, use native POSIX paths and Bash syntax in a fresh non-interactive process." : void 0,
|
|
1002
|
+
normalized.some((name) => name === "sh" || name === "shell") ? "For sh/generic shell, prefer portable POSIX syntax and avoid Bash-only arrays, [[ ... ]], process substitution, and source." : void 0
|
|
1003
|
+
].filter((value) => value !== void 0).join(" ");
|
|
1004
|
+
return `Command tool compatibility rule (${uniqueNames.join(", ")}): each command call runs in a fresh process, so do not rely on cd, aliases, functions, or variables from previous calls; set workdir when the tool supports it. ${shellGuidance} For deletion or move operations, first resolve and verify exact absolute target paths, then operate on those literal paths only; avoid dynamically deleting paths built from home-directory expansion, wildcards, command substitution, or another shell's output. Treat [auto-mode hard deny] and similar policy denials as non-retriable; choose a safer non-destructive inspection or report the limitation instead of repeating the same command or adding sandbox escalation. If downloads fail with TLS credential or connection-closed errors, treat that as an environment/network failure and use local sources or report the limitation instead of cycling through equivalent download commands.`;
|
|
739
1005
|
}
|
|
740
1006
|
function commandToolCompatibilityText(name) {
|
|
741
1007
|
const shell = name.toLowerCase();
|
|
742
|
-
return `Compatibility: command execution is stateless between calls.${shell === "pwsh" || shell.includes("powershell") ? " Use native PowerShell syntax and native Windows paths
|
|
1008
|
+
return `Compatibility: command execution is stateless between calls.${shell === "pwsh" || shell.includes("powershell") ? " Use native PowerShell syntax and native Windows paths." : shell === "bash" ? " Use Bash syntax and native POSIX paths." : " Use portable POSIX syntax and native POSIX paths; avoid Bash-only arrays, [[ ... ]], process substitution, and source."} Prefer workdir over cd because every call starts a fresh process. For destructive operations, verify exact absolute targets first and use literal paths; policy hard-deny results require a safer command shape, not sandbox escalation.`;
|
|
743
1009
|
}
|
|
744
1010
|
function sandboxToolInstruction(tools, sandboxRetryTools) {
|
|
745
1011
|
if (!tools?.some((tool) => hasSandboxControls(tool.parameters))) return void 0;
|
|
@@ -761,7 +1027,7 @@ function toolParametersForCodex(toolName, parameters, allowSandboxRetry) {
|
|
|
761
1027
|
const code = record$2(properties.code);
|
|
762
1028
|
if (code !== null) {
|
|
763
1029
|
const current = typeof code.description === "string" ? code.description.trim() : "";
|
|
764
|
-
const compatibility = "Strict JavaScript/TypeScript source.
|
|
1030
|
+
const compatibility = "Strict JavaScript/TypeScript source. Nested shell commands are string data: template literals may consume ${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them; String.raw still performs ${...} interpolation. Prefer ordinary quoted string arrays joined with \"\\n\", or write a script file with a dedicated file tool.";
|
|
765
1031
|
code.description = current ? `${current}\n\n${compatibility}` : compatibility;
|
|
766
1032
|
}
|
|
767
1033
|
}
|
|
@@ -784,7 +1050,7 @@ function appendPropertyDescription(value, addition) {
|
|
|
784
1050
|
}
|
|
785
1051
|
function isCommandTool(name) {
|
|
786
1052
|
const normalized = name.toLowerCase();
|
|
787
|
-
return normalized === "pwsh" || normalized === "powershell" || normalized === "bash" || normalized === "shell";
|
|
1053
|
+
return normalized === "pwsh" || normalized === "powershell" || normalized === "bash" || normalized === "sh" || normalized === "shell";
|
|
788
1054
|
}
|
|
789
1055
|
function hasSandboxControls(parameters) {
|
|
790
1056
|
const properties = record$2(parameters.properties);
|
|
@@ -828,7 +1094,7 @@ function appendMissingToolCalls(input, knownToolCalls, message) {
|
|
|
828
1094
|
}
|
|
829
1095
|
function runCodeErrorOutput(output) {
|
|
830
1096
|
if (!isRunCodeParserError(output)) return output;
|
|
831
|
-
return `${output}\n\nCompatibility hint: run_code failed while parsing strict JavaScript/TypeScript, before the nested tool ran.
|
|
1097
|
+
return `${output}\n\nCompatibility hint: run_code failed while parsing strict JavaScript/TypeScript, before the nested tool ran. Shell commands are nested string data; template literals can consume \${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them, and String.raw does not prevent \${...} interpolation. Build the script from ordinary quoted strings joined with "\\n", or write a script file with a dedicated file tool and then invoke the shell.`;
|
|
832
1098
|
}
|
|
833
1099
|
function isRunCodeParserError(output) {
|
|
834
1100
|
return /(?:Legacy octal escape is not permitted in strict mode|Unexpected token|Invalid or unexpected token|Unterminated template|Expected ['"]?\}['"]?)/i.test(output);
|
|
@@ -1833,6 +2099,100 @@ function parseStoredCredentials(value) {
|
|
|
1833
2099
|
};
|
|
1834
2100
|
}
|
|
1835
2101
|
//#endregion
|
|
2102
|
+
//#region src/host/token-store-linux.ts
|
|
2103
|
+
const DIRECTORY_MODE = 448;
|
|
2104
|
+
const FILE_MODE = 384;
|
|
2105
|
+
function defaultLinuxCredentialPath() {
|
|
2106
|
+
return join(process.env.DSH_HOME?.trim() || join(homedir(), ".dsh"), "storages", "dsh-chatgpt-subscription", "oauth.json");
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Linux credential storage protected by owner-only filesystem permissions.
|
|
2110
|
+
* The payload is not encrypted at rest, so callers must report that distinction
|
|
2111
|
+
* instead of presenting this store as equivalent to Windows DPAPI.
|
|
2112
|
+
*/
|
|
2113
|
+
var LinuxFileTokenStore = class {
|
|
2114
|
+
path;
|
|
2115
|
+
storage = {
|
|
2116
|
+
kind: "linux-file",
|
|
2117
|
+
encrypted: false
|
|
2118
|
+
};
|
|
2119
|
+
noFollow = constants.O_NOFOLLOW;
|
|
2120
|
+
constructor(path = defaultLinuxCredentialPath()) {
|
|
2121
|
+
this.path = path;
|
|
2122
|
+
if (process.platform !== "linux") throw new Error("Linux credential storage requires Linux");
|
|
2123
|
+
if (this.noFollow === void 0) throw new Error("Linux credential storage requires O_NOFOLLOW support");
|
|
2124
|
+
if (dirname(path) === path) throw new Error("invalid Linux credential path");
|
|
2125
|
+
}
|
|
2126
|
+
async load() {
|
|
2127
|
+
let handle;
|
|
2128
|
+
try {
|
|
2129
|
+
handle = await open(this.path, constants.O_RDONLY | this.noFollow);
|
|
2130
|
+
} catch (error) {
|
|
2131
|
+
if (isMissing(error)) return null;
|
|
2132
|
+
throw new Error("Linux credential read failed", { cause: error });
|
|
2133
|
+
}
|
|
2134
|
+
try {
|
|
2135
|
+
const stats = await handle.stat();
|
|
2136
|
+
if (!stats.isFile()) throw new Error("credential path is not a regular file");
|
|
2137
|
+
if (typeof process.getuid === "function" && stats.uid !== process.getuid()) throw new Error("credential file is owned by another user");
|
|
2138
|
+
if ((stats.mode & 511) !== FILE_MODE) throw new Error("credential file permissions must be 0600");
|
|
2139
|
+
const payload = await handle.readFile({ encoding: "utf8" });
|
|
2140
|
+
return parseStoredCredentials(JSON.parse(payload));
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
throw new Error("Linux credential payload is invalid or insecure", { cause: error });
|
|
2143
|
+
} finally {
|
|
2144
|
+
await handle.close();
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
async save(value) {
|
|
2148
|
+
const directory = dirname(this.path);
|
|
2149
|
+
const temporary = `${this.path}.tmp-${randomUUID()}`;
|
|
2150
|
+
try {
|
|
2151
|
+
const existing = await lstat(this.path);
|
|
2152
|
+
if (existing.isSymbolicLink() || !existing.isFile()) throw new Error("credential path is not a regular file");
|
|
2153
|
+
assertOwnedByCurrentUser(existing.uid, "credential file");
|
|
2154
|
+
} catch (error) {
|
|
2155
|
+
if (!isMissing(error)) throw new Error("Linux credential write failed", { cause: error });
|
|
2156
|
+
}
|
|
2157
|
+
await mkdir(directory, {
|
|
2158
|
+
recursive: true,
|
|
2159
|
+
mode: DIRECTORY_MODE
|
|
2160
|
+
});
|
|
2161
|
+
const directoryStats = await stat(directory);
|
|
2162
|
+
if (!directoryStats.isDirectory()) throw new Error("Linux credential directory is invalid");
|
|
2163
|
+
assertOwnedByCurrentUser(directoryStats.uid, "credential directory");
|
|
2164
|
+
await chmod(directory, DIRECTORY_MODE);
|
|
2165
|
+
let handle;
|
|
2166
|
+
try {
|
|
2167
|
+
handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, FILE_MODE);
|
|
2168
|
+
await handle.writeFile(JSON.stringify(value), { encoding: "utf8" });
|
|
2169
|
+
await handle.sync();
|
|
2170
|
+
await handle.close();
|
|
2171
|
+
handle = void 0;
|
|
2172
|
+
await rename(temporary, this.path);
|
|
2173
|
+
await chmod(this.path, FILE_MODE);
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
await handle?.close().catch(() => void 0);
|
|
2176
|
+
await unlink(temporary).catch(() => void 0);
|
|
2177
|
+
throw new Error("Linux credential write failed", { cause: error });
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async clear() {
|
|
2181
|
+
try {
|
|
2182
|
+
await unlink(this.path);
|
|
2183
|
+
} catch (error) {
|
|
2184
|
+
if (isMissing(error)) return;
|
|
2185
|
+
throw new Error("Linux credential deletion failed", { cause: error });
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
function assertOwnedByCurrentUser(owner, label) {
|
|
2190
|
+
if (typeof process.getuid === "function" && owner !== process.getuid()) throw new Error(`${label} is owned by another user`);
|
|
2191
|
+
}
|
|
2192
|
+
function isMissing(error) {
|
|
2193
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2194
|
+
}
|
|
2195
|
+
//#endregion
|
|
1836
2196
|
//#region src/host/token-store-windows.ts
|
|
1837
2197
|
const PROTECT_SCRIPT = String.raw`
|
|
1838
2198
|
$ErrorActionPreference = 'Stop'
|
|
@@ -1866,6 +2226,10 @@ function defaultDpapiCredentialPath() {
|
|
|
1866
2226
|
}
|
|
1867
2227
|
var WindowsDpapiTokenStore = class {
|
|
1868
2228
|
path;
|
|
2229
|
+
storage = {
|
|
2230
|
+
kind: "windows-dpapi",
|
|
2231
|
+
encrypted: true
|
|
2232
|
+
};
|
|
1869
2233
|
constructor(path = defaultDpapiCredentialPath()) {
|
|
1870
2234
|
this.path = path;
|
|
1871
2235
|
if (process.platform !== "win32") throw new Error("Windows DPAPI storage requires Windows");
|
|
@@ -1938,14 +2302,22 @@ function runPowerShell(script, path, stdin) {
|
|
|
1938
2302
|
});
|
|
1939
2303
|
}
|
|
1940
2304
|
//#endregion
|
|
2305
|
+
//#region src/host/platform-token-store.ts
|
|
2306
|
+
function createPlatformTokenStore(platform = process.platform) {
|
|
2307
|
+
if (platform === "win32") return new WindowsDpapiTokenStore();
|
|
2308
|
+
if (platform === "linux") return new LinuxFileTokenStore();
|
|
2309
|
+
throw new Error(`Unsupported platform ${platform}; dsh-chatgpt-subscription supports Windows and Linux.`);
|
|
2310
|
+
}
|
|
2311
|
+
//#endregion
|
|
1941
2312
|
//#region src/index.ts
|
|
1942
2313
|
const inject = [
|
|
1943
2314
|
"webServer",
|
|
1944
2315
|
"llm",
|
|
1945
|
-
"attachments"
|
|
2316
|
+
"attachments",
|
|
2317
|
+
"agents"
|
|
1946
2318
|
];
|
|
1947
2319
|
function apply(ctx) {
|
|
1948
|
-
const oauth = new OAuthService(
|
|
2320
|
+
const oauth = new OAuthService(createPlatformTokenStore(), { logger: ctx.logger });
|
|
1949
2321
|
const usage = new UsageService(oauth);
|
|
1950
2322
|
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
1951
2323
|
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
@@ -1954,7 +2326,11 @@ function apply(ctx) {
|
|
|
1954
2326
|
ctx.effect(() => {
|
|
1955
2327
|
const disposeRoutes = registerRoutes(ctx, oauth, usage);
|
|
1956
2328
|
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
|
|
2329
|
+
const disposeSubagentReportCompat = installSubagentReportDedupCompat(ctx);
|
|
2330
|
+
const disposeBashPromptCompat = installBashPromptCompat(ctx);
|
|
1957
2331
|
return () => {
|
|
2332
|
+
disposeBashPromptCompat();
|
|
2333
|
+
disposeSubagentReportCompat();
|
|
1958
2334
|
disposeAdapter();
|
|
1959
2335
|
disposeRoutes();
|
|
1960
2336
|
oauth.dispose();
|
|
@@ -1965,4 +2341,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
1965
2341
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
1966
2342
|
}
|
|
1967
2343
|
//#endregion
|
|
1968
|
-
export { CodexChatGptAdapter, OAuthService, ResponsesClient, UsageService, apply, inject, mapCodexUsage, parseResponsesStream };
|
|
2344
|
+
export { CodexChatGptAdapter, LinuxFileTokenStore, OAuthService, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createPlatformTokenStore, inject, mapCodexUsage, parseResponsesStream };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
-
import type { QuotaWindowDto } from '../shared/contracts.ts';
|
|
2
|
+
import type { CredentialStorageDto, QuotaWindowDto } from '../shared/contracts.ts';
|
|
3
3
|
import { NS } from './locales.ts';
|
|
4
4
|
type Props = PropsRuntime<'settings.section'> & PropsLocale<typeof NS>;
|
|
5
5
|
type Translate = Props['t'];
|
|
6
6
|
export declare function CodexSubscriptionSection({ t }: Props): React.JSX.Element;
|
|
7
|
+
export declare function storageLabel(storage: CredentialStorageDto | undefined, t: Translate): string;
|
|
8
|
+
export declare function storageNotice(storage: CredentialStorageDto | undefined, t: Translate): string;
|
|
7
9
|
export declare function QuotaBar({ label, window, t }: {
|
|
8
10
|
label: string;
|
|
9
11
|
window: QuotaWindowDto;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodexSubscriptionSection.d.ts","sourceRoot":"","sources":["../../../src/client/CodexSubscriptionSection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"CodexSubscriptionSection.d.ts","sourceRoot":"","sources":["../../../src/client/CodexSubscriptionSection.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACjF,OAAO,KAAK,EAAE,oBAAoB,EAAmC,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEnH,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAEjC,KAAK,KAAK,GAAG,YAAY,CAAC,kBAAkB,CAAC,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;AAEtE,KAAK,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;AAI3B,wBAAgB,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAoMxE;AAiBD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAM5F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAM7F;AAUD,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAczH;AAMD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAQxE;AAWD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAK3E,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAK3E,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAG/D,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0BAA0B,EAAE,SAAS,CAAA;KACtC;CACF;AAED,eAAO,MAAM,MAAM,UAAsB,CAAA;AAEzC,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAU9C"}
|
|
@@ -9,8 +9,14 @@ export declare const zh: {
|
|
|
9
9
|
readonly accountId: "账号 ID";
|
|
10
10
|
readonly expires: "令牌到期";
|
|
11
11
|
readonly storage: "凭据存储";
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
12
|
+
readonly storageWindows: "Windows DPAPI(当前用户加密)";
|
|
13
|
+
readonly storageLinuxFile: "Linux 用户私有文件(权限 0600)";
|
|
14
|
+
readonly storageMemory: "仅 Host 内存(不持久化)";
|
|
15
|
+
readonly storageUnavailable: "凭据存储不可用";
|
|
16
|
+
readonly securityWindows: "令牌 Windows CurrentUser DPAPI 加密。";
|
|
17
|
+
readonly securityLinuxFile: "令牌仅写入 Host 上当前 Linux 用户可读的 0600 文件。";
|
|
18
|
+
readonly securityMemory: "令牌仅保留在 Host 内存中,Host 退出后丢失。";
|
|
19
|
+
readonly securityUnavailable: "Host 无法安全访问凭据存储;请修复文件所有者或权限后重试。";
|
|
14
20
|
readonly signIn: "使用 ChatGPT 登录";
|
|
15
21
|
readonly signInAgain: "重新登录";
|
|
16
22
|
readonly cancel: "取消登录";
|
|
@@ -60,8 +66,14 @@ export declare const dictionaries: {
|
|
|
60
66
|
readonly accountId: "账号 ID";
|
|
61
67
|
readonly expires: "令牌到期";
|
|
62
68
|
readonly storage: "凭据存储";
|
|
63
|
-
readonly
|
|
64
|
-
readonly
|
|
69
|
+
readonly storageWindows: "Windows DPAPI(当前用户加密)";
|
|
70
|
+
readonly storageLinuxFile: "Linux 用户私有文件(权限 0600)";
|
|
71
|
+
readonly storageMemory: "仅 Host 内存(不持久化)";
|
|
72
|
+
readonly storageUnavailable: "凭据存储不可用";
|
|
73
|
+
readonly securityWindows: "令牌由 Host 使用 Windows CurrentUser DPAPI 加密,不会进入浏览器、settings.yaml 或日志。";
|
|
74
|
+
readonly securityLinuxFile: "令牌仅写入 Host 上当前 Linux 用户可读的 0600 文件,但不会额外加密;同 UID 进程、root、备份和磁盘快照仍可能读取。";
|
|
75
|
+
readonly securityMemory: "令牌仅保留在 Host 内存中,Host 退出后丢失。";
|
|
76
|
+
readonly securityUnavailable: "Host 无法安全访问凭据存储;请修复文件所有者或权限后重试。";
|
|
65
77
|
readonly signIn: "使用 ChatGPT 登录";
|
|
66
78
|
readonly signInAgain: "重新登录";
|
|
67
79
|
readonly cancel: "取消登录";
|
|
@@ -98,6 +110,6 @@ export declare const dictionaries: {
|
|
|
98
110
|
readonly retry: "重试";
|
|
99
111
|
readonly unknown: "未知";
|
|
100
112
|
};
|
|
101
|
-
en: Record<"
|
|
113
|
+
en: Record<"quota" | "account" | "storage" | "stale" | "pending" | "title" | "intro" | "signedOut" | "signedIn" | "plan" | "accountId" | "expires" | "storageWindows" | "storageLinuxFile" | "storageMemory" | "storageUnavailable" | "securityWindows" | "securityLinuxFile" | "securityMemory" | "securityUnavailable" | "signIn" | "signInAgain" | "cancel" | "signOut" | "refreshToken" | "popupBlocked" | "continueLogin" | "loading" | "connection" | "provider" | "connectionState" | "connected" | "untested" | "testConnection" | "testing" | "latency" | "models" | "quotaIntro" | "refreshQuota" | "refreshing" | "noQuota" | "quotaSignedOut" | "updated" | "primary" | "secondary" | "limitWindow" | "used" | "remaining" | "exhausted" | "resets" | "retry" | "unknown", string>;
|
|
102
114
|
};
|
|
103
115
|
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAG,0BAAmC,CAAA;AAErD,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAG,0BAAmC,CAAA;AAErD,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDL,CAAA;AAEV,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,OAAO,EAAE,EAAE,MAAM,CAqD9C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AACvC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/client/styles.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/client/styles.ts"],"names":[],"mappings":"AAiDA,wBAAgB,aAAa,IAAI,MAAM,IAAI,CAQ1C"}
|