@getpaseo/server 0.5.0-beta.5 → 0.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/dist/server/server/agent/agent-manager.d.ts +1 -0
- package/dist/server/server/agent/agent-manager.js +42 -17
- package/dist/server/server/agent/agent-sdk-types.d.ts +5 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +1 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js +54 -5
- package/dist/server/services/quota-fetcher/providers/claude.d.ts +0 -2
- package/dist/server/services/quota-fetcher/providers/claude.js +6 -50
- package/dist/server/services/quota-fetcher/providers/codex.d.ts +0 -2
- package/dist/server/services/quota-fetcher/providers/codex.js +7 -54
- package/dist/server/services/quota-fetcher/providers/kimi.d.ts +0 -4
- package/dist/server/services/quota-fetcher/providers/kimi.js +5 -99
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-28615b837fff299849f3363652b95f0f.js → desktop-attachment-bridge-d452c6e787c8d41aa2e1cfafbb6e477b.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-28615b837fff299849f3363652b95f0f.js.br → desktop-attachment-bridge-d452c6e787c8d41aa2e1cfafbb6e477b.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-d452c6e787c8d41aa2e1cfafbb6e477b.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-99d20cdd7788af37bc5fbd91e0bb49e6.js → desktop-attachment-store-4804f15e0899aeb03f110d60408577f6.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-4804f15e0899aeb03f110d60408577f6.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-99d20cdd7788af37bc5fbd91e0bb49e6.js.gz → desktop-attachment-store-4804f15e0899aeb03f110d60408577f6.js.gz} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-f38dc1ee96a16e9953f300e24744fb3c.js → index-d7eec3768e89ac8be8b5256d2a46412e.js} +226 -224
- package/dist/server/web-ui/_expo/static/js/web/index-d7eec3768e89ac8be8b5256d2a46412e.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-d7eec3768e89ac8be8b5256d2a46412e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-2bc098e7b3216f2c0cf93eeeafaac48e.js → indexeddb-attachment-store-e846e4521dbd9112f5a8aca5b678843c.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-e846e4521dbd9112f5a8aca5b678843c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-e846e4521dbd9112f5a8aca5b678843c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-021ee430e5ecd819e1e2016ed79c78d3.js → native-file-attachment-store-dae92151d814845947244302dbb2a21b.js} +3 -3
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-dae92151d814845947244302dbb2a21b.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-dae92151d814845947244302dbb2a21b.js.gz +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/package.json +6 -6
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-28615b837fff299849f3363652b95f0f.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-99d20cdd7788af37bc5fbd91e0bb49e6.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-f38dc1ee96a16e9953f300e24744fb3c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-f38dc1ee96a16e9953f300e24744fb3c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-2bc098e7b3216f2c0cf93eeeafaac48e.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-2bc098e7b3216f2c0cf93eeeafaac48e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-021ee430e5ecd819e1e2016ed79c78d3.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-021ee430e5ecd819e1e2016ed79c78d3.js.gz +0 -0
|
@@ -376,6 +376,7 @@ export declare class AgentManager {
|
|
|
376
376
|
tryRunOutOfBand(agentId: string, prompt: AgentPromptInput, options?: AgentRunOptions): boolean;
|
|
377
377
|
appendTimelineItem(agentId: string, item: AgentTimelineItem): Promise<void>;
|
|
378
378
|
emitLiveTimelineItem(agentId: string, item: AgentTimelineItem): Promise<void>;
|
|
379
|
+
private startPendingForegroundTurn;
|
|
379
380
|
streamAgent(agentId: string, prompt: AgentPromptInput, options?: AgentRunOptions): AsyncGenerator<AgentStreamEvent>;
|
|
380
381
|
private finalizeForegroundTurn;
|
|
381
382
|
private openActiveTurn;
|
|
@@ -9,7 +9,7 @@ import { buildArchivedAgentRecord } from "./agent-archive.js";
|
|
|
9
9
|
import { InMemoryAgentTimelineStore, } from "./agent-timeline-store.js";
|
|
10
10
|
import { AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS, AgentStreamCoalescer, } from "./agent-stream-coalescer.js";
|
|
11
11
|
import { limitAgentTimelineItemContent } from "./agent-timeline-content.js";
|
|
12
|
-
import { AgentRunState } from "./agent-run-state.js";
|
|
12
|
+
import { AgentRunState, } from "./agent-run-state.js";
|
|
13
13
|
import { invokeRewindCapability } from "./rewind/rewind.js";
|
|
14
14
|
import { isSystemInjectedEnvelope } from "./agent-prompt.js";
|
|
15
15
|
import { stripInternalPaseoMcpServer, withRuntimePaseoMcpServer } from "./runtime-mcp-config.js";
|
|
@@ -1397,6 +1397,31 @@ export class AgentManager {
|
|
|
1397
1397
|
provider: agent.provider,
|
|
1398
1398
|
});
|
|
1399
1399
|
}
|
|
1400
|
+
async startPendingForegroundTurn(params) {
|
|
1401
|
+
const { agent, agentId, pendingRun, prompt, options } = params;
|
|
1402
|
+
try {
|
|
1403
|
+
const result = await agent.session.startTurn(prompt, options);
|
|
1404
|
+
if (pendingRun.settled) {
|
|
1405
|
+
throw new Error(`Agent ${agentId} run was canceled before its turn started`);
|
|
1406
|
+
}
|
|
1407
|
+
return result.turnId;
|
|
1408
|
+
}
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
if (pendingRun.settled) {
|
|
1411
|
+
throw error;
|
|
1412
|
+
}
|
|
1413
|
+
agent.pendingReplacement = false;
|
|
1414
|
+
const errorMsg = error instanceof Error ? error.message : "Failed to start turn";
|
|
1415
|
+
await this.handleStreamEvent(agent, {
|
|
1416
|
+
type: "turn_failed",
|
|
1417
|
+
provider: agent.provider,
|
|
1418
|
+
error: errorMsg,
|
|
1419
|
+
});
|
|
1420
|
+
this.finalizeForegroundTurn(agent);
|
|
1421
|
+
this.runs.settleForegroundRun(agentId, pendingRun.token);
|
|
1422
|
+
throw error;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1400
1425
|
streamAgent(agentId, prompt, options) {
|
|
1401
1426
|
const existingAgent = this.requireSessionAgent(agentId);
|
|
1402
1427
|
this.logger.trace({
|
|
@@ -1428,22 +1453,13 @@ export class AgentManager {
|
|
|
1428
1453
|
const streamForwarder = async function* streamForwarder() {
|
|
1429
1454
|
let turnId;
|
|
1430
1455
|
let turnStream = null;
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
await this.handleStreamEvent(agent, {
|
|
1439
|
-
type: "turn_failed",
|
|
1440
|
-
provider: agent.provider,
|
|
1441
|
-
error: errorMsg,
|
|
1442
|
-
});
|
|
1443
|
-
this.finalizeForegroundTurn(agent);
|
|
1444
|
-
this.runs.settleForegroundRun(agentId, pendingRun.token);
|
|
1445
|
-
throw error;
|
|
1446
|
-
}
|
|
1456
|
+
turnId = await this.startPendingForegroundTurn({
|
|
1457
|
+
agent,
|
|
1458
|
+
agentId,
|
|
1459
|
+
pendingRun,
|
|
1460
|
+
prompt,
|
|
1461
|
+
options,
|
|
1462
|
+
});
|
|
1447
1463
|
if (isReplacement) {
|
|
1448
1464
|
agent.pendingReplacement = false;
|
|
1449
1465
|
}
|
|
@@ -1859,6 +1875,15 @@ export class AgentManager {
|
|
|
1859
1875
|
});
|
|
1860
1876
|
await run.settledPromise;
|
|
1861
1877
|
}
|
|
1878
|
+
else if (settlement === "timed_out" && run.kind === "foreground") {
|
|
1879
|
+
this.logger.warn({ agentId, kind: run.kind }, "cancelAgentRun: acknowledged pending turn still active after timeout, clearing it");
|
|
1880
|
+
this.runs.settleForegroundRun(agentId, run.token);
|
|
1881
|
+
if (!agent.pendingReplacement) {
|
|
1882
|
+
agent.lifecycle = "idle";
|
|
1883
|
+
this.touchUpdatedAt(agent);
|
|
1884
|
+
this.emitState(agent);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1862
1887
|
else if (settlement === "timed_out" && run.kind === "autonomous") {
|
|
1863
1888
|
this.logger.warn({ agentId, kind: run.kind }, "cancelAgentRun: acknowledged turn still active after timeout, force-canceling");
|
|
1864
1889
|
await this.dispatchSessionEvent(agent, {
|
|
@@ -577,6 +577,11 @@ export interface AgentSession {
|
|
|
577
577
|
getPendingPermissions(): AgentPermissionRequest[];
|
|
578
578
|
respondToPermission(requestId: string, response: AgentPermissionResponse): Promise<AgentPermissionResult | void>;
|
|
579
579
|
describePersistence(): AgentPersistenceHandle | null;
|
|
580
|
+
/**
|
|
581
|
+
* Resolve once every foreground turn that predates this call can no longer run or become active.
|
|
582
|
+
* Calling while already idle is a successful no-op. Reject only when foreground ownership is
|
|
583
|
+
* still uncertain.
|
|
584
|
+
*/
|
|
580
585
|
interrupt(): Promise<void>;
|
|
581
586
|
/** Release live runtime resources without archiving or deleting the durable native session. */
|
|
582
587
|
close(): Promise<void>;
|
|
@@ -127,6 +127,7 @@ export declare class CodexAppServerAgentSession implements AgentSession {
|
|
|
127
127
|
private currentThreadId;
|
|
128
128
|
private currentTurnId;
|
|
129
129
|
private pendingForegroundTurnIdentification;
|
|
130
|
+
private pendingForegroundStart;
|
|
130
131
|
private client;
|
|
131
132
|
private readonly subscribers;
|
|
132
133
|
private nextTurnOrdinal;
|
|
@@ -587,6 +587,11 @@ function isDefinitiveCodexSteerRejection(error) {
|
|
|
587
587
|
/^expected active turn id `[^`]+` but found `[^`]+`$/.test(error.message) ||
|
|
588
588
|
error.message === "active turn uses a different output schema");
|
|
589
589
|
}
|
|
590
|
+
function isCodexAlreadyIdleInterrupt(error) {
|
|
591
|
+
return (error instanceof CodexAppServerRpcError &&
|
|
592
|
+
error.code === -32600 &&
|
|
593
|
+
error.message === "no active turn to interrupt");
|
|
594
|
+
}
|
|
590
595
|
const CodexModelListResponseSchema = z.object({
|
|
591
596
|
data: z
|
|
592
597
|
.array(z.object({
|
|
@@ -2363,6 +2368,7 @@ export class CodexAppServerAgentSession {
|
|
|
2363
2368
|
this.currentThreadId = null;
|
|
2364
2369
|
this.currentTurnId = null;
|
|
2365
2370
|
this.pendingForegroundTurnIdentification = null;
|
|
2371
|
+
this.pendingForegroundStart = null;
|
|
2366
2372
|
this.client = null;
|
|
2367
2373
|
this.subscribers = new Set();
|
|
2368
2374
|
this.nextTurnOrdinal = 0;
|
|
@@ -3056,9 +3062,18 @@ export class CodexAppServerAgentSession {
|
|
|
3056
3062
|
});
|
|
3057
3063
|
}
|
|
3058
3064
|
async startTurn(prompt, options) {
|
|
3059
|
-
if (this.activeForegroundTurnId) {
|
|
3065
|
+
if (this.activeForegroundTurnId || this.pendingForegroundStart) {
|
|
3060
3066
|
throw new Error("A foreground turn is already active");
|
|
3061
3067
|
}
|
|
3068
|
+
let resolveStart;
|
|
3069
|
+
const pendingStart = {
|
|
3070
|
+
promise: new Promise((resolve) => {
|
|
3071
|
+
resolveStart = resolve;
|
|
3072
|
+
}),
|
|
3073
|
+
resolve: () => resolveStart(),
|
|
3074
|
+
cancelRequested: false,
|
|
3075
|
+
};
|
|
3076
|
+
this.pendingForegroundStart = pendingStart;
|
|
3062
3077
|
this.dismissPendingPlanApprovals("Dismissed by a new prompt");
|
|
3063
3078
|
try {
|
|
3064
3079
|
await this.connect();
|
|
@@ -3099,6 +3114,9 @@ export class CodexAppServerAgentSession {
|
|
|
3099
3114
|
hasDeveloperInstructions: turnStart.hasDeveloperInstructions,
|
|
3100
3115
|
hasCodexConfig: turnStart.hasCodexConfig,
|
|
3101
3116
|
});
|
|
3117
|
+
if (pendingStart.cancelRequested) {
|
|
3118
|
+
throw new Error("Codex turn start was interrupted before reaching Codex");
|
|
3119
|
+
}
|
|
3102
3120
|
await this.client.request("turn/start", turnStart.params, TURN_START_TIMEOUT_MS);
|
|
3103
3121
|
return { turnId };
|
|
3104
3122
|
}
|
|
@@ -3109,6 +3127,12 @@ export class CodexAppServerAgentSession {
|
|
|
3109
3127
|
this.activeClientMessageId = null;
|
|
3110
3128
|
throw error;
|
|
3111
3129
|
}
|
|
3130
|
+
finally {
|
|
3131
|
+
if (this.pendingForegroundStart === pendingStart) {
|
|
3132
|
+
this.pendingForegroundStart = null;
|
|
3133
|
+
}
|
|
3134
|
+
pendingStart.resolve();
|
|
3135
|
+
}
|
|
3112
3136
|
}
|
|
3113
3137
|
async steerActiveTurn(prompt, options) {
|
|
3114
3138
|
const client = this.client;
|
|
@@ -3518,7 +3542,17 @@ export class CodexAppServerAgentSession {
|
|
|
3518
3542
|
});
|
|
3519
3543
|
}
|
|
3520
3544
|
async interrupt() {
|
|
3545
|
+
const pendingStart = this.pendingForegroundStart;
|
|
3546
|
+
if (pendingStart) {
|
|
3547
|
+
pendingStart.cancelRequested = true;
|
|
3548
|
+
await pendingStart.promise;
|
|
3549
|
+
}
|
|
3521
3550
|
if (!this.client || !this.currentThreadId) {
|
|
3551
|
+
if (!this.activeForegroundTurnId &&
|
|
3552
|
+
!this.currentTurnId &&
|
|
3553
|
+
!this.pendingForegroundTurnIdentification) {
|
|
3554
|
+
return;
|
|
3555
|
+
}
|
|
3522
3556
|
throw new Error("Cannot interrupt Codex before the active thread is initialized");
|
|
3523
3557
|
}
|
|
3524
3558
|
let turnId = this.currentTurnId;
|
|
@@ -3531,13 +3565,28 @@ export class CodexAppServerAgentSession {
|
|
|
3531
3565
|
pendingIdentification?.foregroundTurnId === foregroundTurnId) {
|
|
3532
3566
|
turnId = await pendingIdentification.promise;
|
|
3533
3567
|
}
|
|
3568
|
+
if (!turnId && !this.activeForegroundTurnId && !this.currentTurnId) {
|
|
3569
|
+
return;
|
|
3570
|
+
}
|
|
3534
3571
|
if (!turnId || (foregroundTurnId && this.activeForegroundTurnId !== foregroundTurnId)) {
|
|
3535
3572
|
throw new Error("Cannot interrupt Codex before turn/started identifies the active turn");
|
|
3536
3573
|
}
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3574
|
+
try {
|
|
3575
|
+
await this.client.request("turn/interrupt", {
|
|
3576
|
+
threadId: this.currentThreadId,
|
|
3577
|
+
turnId,
|
|
3578
|
+
}, INTERRUPT_TIMEOUT_MS);
|
|
3579
|
+
}
|
|
3580
|
+
catch (error) {
|
|
3581
|
+
if (!isCodexAlreadyIdleInterrupt(error)) {
|
|
3582
|
+
throw error;
|
|
3583
|
+
}
|
|
3584
|
+
this.activeForegroundTurnId = null;
|
|
3585
|
+
this.activeClientMessageId = null;
|
|
3586
|
+
this.currentTurnId = null;
|
|
3587
|
+
this.pendingForegroundTurnIdentification?.resolve(null);
|
|
3588
|
+
this.pendingForegroundTurnIdentification = null;
|
|
3589
|
+
}
|
|
3541
3590
|
}
|
|
3542
3591
|
async close() {
|
|
3543
3592
|
this.closed = true;
|
|
@@ -31,8 +31,6 @@ export declare class ClaudeQuotaProvider implements ProviderUsageFetcher {
|
|
|
31
31
|
private scopedLimitsFromResponse;
|
|
32
32
|
private readCredentials;
|
|
33
33
|
private callClaudeApi;
|
|
34
|
-
private refreshClaudeToken;
|
|
35
|
-
private saveClaudeCredentials;
|
|
36
34
|
}
|
|
37
35
|
export {};
|
|
38
36
|
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -8,7 +8,6 @@ import { ApiNumberSchema, fetchProviderApi, toneFromUsedPct, unavailableUsage, w
|
|
|
8
8
|
const execFileAsync = promisify(execFile);
|
|
9
9
|
const CLAUDE_KEYCHAIN_TIMEOUT_MS = 2000;
|
|
10
10
|
const CLAUDE_OAUTH_BETA = "oauth-2025-04-20";
|
|
11
|
-
const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
12
11
|
const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
13
12
|
const ClaudeCredentialsSchema = z.object({
|
|
14
13
|
claudeAiOauth: z
|
|
@@ -51,10 +50,6 @@ const ClaudeUsageResponseSchema = z.object({
|
|
|
51
50
|
})
|
|
52
51
|
.nullish(),
|
|
53
52
|
});
|
|
54
|
-
const ClaudeTokenRefreshSchema = z.object({
|
|
55
|
-
access_token: z.string().optional(),
|
|
56
|
-
refresh_token: z.string().optional(),
|
|
57
|
-
});
|
|
58
53
|
const SCOPED_WEEKLY_KIND = "weekly_scoped";
|
|
59
54
|
function buildClaudePlan(subscriptionType, rateLimitTier) {
|
|
60
55
|
if (!subscriptionType)
|
|
@@ -270,26 +265,12 @@ export class ClaudeQuotaProvider {
|
|
|
270
265
|
if (!credentials) {
|
|
271
266
|
return unavailableUsage(this);
|
|
272
267
|
}
|
|
273
|
-
const { oauth
|
|
268
|
+
const { oauth } = credentials;
|
|
274
269
|
const plan = buildClaudePlan(oauth.subscriptionType, oauth.rateLimitTier);
|
|
275
|
-
|
|
270
|
+
const resp = await this.callClaudeApi(oauth.accessToken);
|
|
276
271
|
if (resp === "NEEDS_AUTH") {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
const refreshed = await this.refreshClaudeToken(oauth.refreshToken);
|
|
281
|
-
if (!refreshed?.access_token) {
|
|
282
|
-
return unavailableUsage(this);
|
|
283
|
-
}
|
|
284
|
-
await this.saveClaudeCredentials(filePath, {
|
|
285
|
-
...oauth,
|
|
286
|
-
accessToken: refreshed.access_token,
|
|
287
|
-
refreshToken: refreshed.refresh_token ?? oauth.refreshToken,
|
|
288
|
-
});
|
|
289
|
-
resp = await this.callClaudeApi(refreshed.access_token);
|
|
290
|
-
if (resp === "NEEDS_AUTH") {
|
|
291
|
-
return unavailableUsage(this);
|
|
292
|
-
}
|
|
272
|
+
// Read-only on credentials; the Claude CLI owns refresh. See docs/providers.md.
|
|
273
|
+
return unavailableUsage(this);
|
|
293
274
|
}
|
|
294
275
|
const scoped = reconcileScopedLimits(legacyScopedLimits(resp), this.scopedLimitsFromResponse(resp.limits));
|
|
295
276
|
const windows = [...unscopedWindows(resp), ...scopedWindows(scoped)];
|
|
@@ -353,7 +334,7 @@ export class ClaudeQuotaProvider {
|
|
|
353
334
|
const creds = ClaudeCredentialsSchema.parse(JSON.parse(await fs.readFile(credPath, "utf8")));
|
|
354
335
|
const oauth = creds.claudeAiOauth;
|
|
355
336
|
if (oauth?.accessToken) {
|
|
356
|
-
return { oauth: { ...oauth, accessToken: oauth.accessToken }
|
|
337
|
+
return { oauth: { ...oauth, accessToken: oauth.accessToken } };
|
|
357
338
|
}
|
|
358
339
|
}
|
|
359
340
|
catch {
|
|
@@ -364,7 +345,7 @@ export class ClaudeQuotaProvider {
|
|
|
364
345
|
const creds = ClaudeCredentialsSchema.safeParse(await this.readKeychainCredentials());
|
|
365
346
|
const oauth = creds.success ? creds.data.claudeAiOauth : undefined;
|
|
366
347
|
if (oauth?.accessToken) {
|
|
367
|
-
return { oauth: { ...oauth, accessToken: oauth.accessToken }
|
|
348
|
+
return { oauth: { ...oauth, accessToken: oauth.accessToken } };
|
|
368
349
|
}
|
|
369
350
|
}
|
|
370
351
|
return null;
|
|
@@ -383,30 +364,5 @@ export class ClaudeQuotaProvider {
|
|
|
383
364
|
throw new Error(`Claude usage API returned ${res.status}`);
|
|
384
365
|
return ClaudeUsageResponseSchema.parse(await res.json());
|
|
385
366
|
}
|
|
386
|
-
async refreshClaudeToken(refreshToken) {
|
|
387
|
-
const res = await fetchProviderApi(this.fetchApi, "https://platform.claude.com/v1/oauth/token", {
|
|
388
|
-
method: "POST",
|
|
389
|
-
headers: { "Content-Type": "application/json" },
|
|
390
|
-
body: JSON.stringify({
|
|
391
|
-
grant_type: "refresh_token",
|
|
392
|
-
refresh_token: refreshToken,
|
|
393
|
-
client_id: CLAUDE_CLIENT_ID,
|
|
394
|
-
scope: "user:profile user:inference user:sessions:claude_code user:mcp_servers",
|
|
395
|
-
}),
|
|
396
|
-
});
|
|
397
|
-
if (!res.ok)
|
|
398
|
-
return null;
|
|
399
|
-
return ClaudeTokenRefreshSchema.parse(await res.json());
|
|
400
|
-
}
|
|
401
|
-
async saveClaudeCredentials(credPath, oauth) {
|
|
402
|
-
try {
|
|
403
|
-
const existing = ClaudeCredentialsSchema.parse(JSON.parse(await fs.readFile(credPath, "utf8")));
|
|
404
|
-
existing.claudeAiOauth = oauth;
|
|
405
|
-
await fs.writeFile(credPath, JSON.stringify(existing, null, 2), { mode: 0o600 });
|
|
406
|
-
}
|
|
407
|
-
catch {
|
|
408
|
-
// Non-fatal; Claude Code can refresh again on its own next time.
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
367
|
}
|
|
412
368
|
//# sourceMappingURL=claude.js.map
|
|
@@ -3,7 +3,6 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { ApiNumberSchema, balanceToneFromRemaining, toneFromUsedPct, fetchProviderApi, unavailableUsage, windowFromUsedPct, } from "../usage.js";
|
|
6
|
-
const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
7
6
|
const CodexAuthSchema = z.object({
|
|
8
7
|
tokens: z
|
|
9
8
|
.object({
|
|
@@ -39,10 +38,6 @@ const CodexUsageResponseSchema = z.object({
|
|
|
39
38
|
})
|
|
40
39
|
.nullish(),
|
|
41
40
|
});
|
|
42
|
-
const CodexTokenRefreshSchema = z.object({
|
|
43
|
-
access_token: z.string().optional(),
|
|
44
|
-
refresh_token: z.string().optional(),
|
|
45
|
-
});
|
|
46
41
|
function codexWindow(window) {
|
|
47
42
|
if (!window)
|
|
48
43
|
return null;
|
|
@@ -59,27 +54,16 @@ export class CodexQuotaProvider {
|
|
|
59
54
|
this.fetchApi = options.fetch ?? fetch;
|
|
60
55
|
}
|
|
61
56
|
async fetchUsage() {
|
|
62
|
-
const
|
|
63
|
-
const auth = authRecord?.auth;
|
|
57
|
+
const auth = await this.readCodexAuth();
|
|
64
58
|
const accessToken = auth?.tokens?.access_token;
|
|
65
|
-
if (!
|
|
59
|
+
if (!auth || !accessToken) {
|
|
66
60
|
return unavailableUsage(this);
|
|
67
61
|
}
|
|
68
|
-
const {
|
|
69
|
-
|
|
62
|
+
const { account_id } = auth.tokens ?? {};
|
|
63
|
+
const resp = await this.callCodexApi(accessToken, account_id);
|
|
70
64
|
if (resp === "NEEDS_AUTH") {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
const refreshed = await this.refreshCodexToken(refresh_token);
|
|
75
|
-
if (!refreshed?.access_token) {
|
|
76
|
-
return unavailableUsage(this);
|
|
77
|
-
}
|
|
78
|
-
await this.saveCodexAuth(authRecord.path, auth, refreshed);
|
|
79
|
-
resp = await this.callCodexApi(refreshed.access_token, account_id);
|
|
80
|
-
if (resp === "NEEDS_AUTH") {
|
|
81
|
-
return unavailableUsage(this);
|
|
82
|
-
}
|
|
65
|
+
// Read-only on credentials; the Codex CLI owns refresh. See docs/providers.md.
|
|
66
|
+
return unavailableUsage(this);
|
|
83
67
|
}
|
|
84
68
|
return this.toUsage(resp);
|
|
85
69
|
}
|
|
@@ -148,7 +132,7 @@ export class CodexQuotaProvider {
|
|
|
148
132
|
try {
|
|
149
133
|
const auth = CodexAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
|
|
150
134
|
if (auth.tokens?.access_token)
|
|
151
|
-
return
|
|
135
|
+
return auth;
|
|
152
136
|
}
|
|
153
137
|
catch {
|
|
154
138
|
continue;
|
|
@@ -176,36 +160,5 @@ export class CodexQuotaProvider {
|
|
|
176
160
|
return "NEEDS_AUTH";
|
|
177
161
|
return CodexUsageResponseSchema.parse(JSON.parse(text));
|
|
178
162
|
}
|
|
179
|
-
async refreshCodexToken(refreshToken) {
|
|
180
|
-
const params = new URLSearchParams({
|
|
181
|
-
grant_type: "refresh_token",
|
|
182
|
-
client_id: CODEX_CLIENT_ID,
|
|
183
|
-
refresh_token: refreshToken,
|
|
184
|
-
});
|
|
185
|
-
const res = await fetchProviderApi(this.fetchApi, "https://auth.openai.com/oauth/token", {
|
|
186
|
-
method: "POST",
|
|
187
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
188
|
-
body: params.toString(),
|
|
189
|
-
});
|
|
190
|
-
if (!res.ok)
|
|
191
|
-
return null;
|
|
192
|
-
return CodexTokenRefreshSchema.parse(await res.json());
|
|
193
|
-
}
|
|
194
|
-
async saveCodexAuth(authPath, original, refreshed) {
|
|
195
|
-
try {
|
|
196
|
-
const updated = {
|
|
197
|
-
...original,
|
|
198
|
-
tokens: {
|
|
199
|
-
...original.tokens,
|
|
200
|
-
access_token: refreshed.access_token ?? original.tokens?.access_token,
|
|
201
|
-
refresh_token: refreshed.refresh_token ?? original.tokens?.refresh_token,
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
await fs.writeFile(authPath, JSON.stringify(updated, null, 2), { mode: 0o600 });
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
// Non-fatal; the next call can refresh again.
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
163
|
}
|
|
211
164
|
//# sourceMappingURL=codex.js.map
|
|
@@ -14,11 +14,7 @@ export declare class KimiQuotaProvider implements ProviderUsageFetcher {
|
|
|
14
14
|
private readonly homeDir?;
|
|
15
15
|
constructor(options: KimiQuotaProviderOptions);
|
|
16
16
|
fetchUsage(): Promise<ProviderUsage>;
|
|
17
|
-
private fetchUsageResponse;
|
|
18
17
|
private callUsageApi;
|
|
19
|
-
private refreshToken;
|
|
20
|
-
private saveRefreshedCredentials;
|
|
21
|
-
private credentialsUnchangedSince;
|
|
22
18
|
private readCredentials;
|
|
23
19
|
private credentialPaths;
|
|
24
20
|
private readCredentialFile;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { existsSync, promises as fs } from "node:fs";
|
|
3
2
|
import { homedir } from "node:os";
|
|
4
|
-
import {
|
|
3
|
+
import { join } from "node:path";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { ApiNumberSchema, ApiOptionalStringSchema, fetchProviderApi, toneFromUsedPct, unavailableUsage, windowFromUsedPct, } from "../usage.js";
|
|
7
|
-
const KIMI_CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
|
|
8
6
|
const KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
9
|
-
const KIMI_TOKEN_URL = "https://auth.kimi.com/api/oauth/token";
|
|
10
7
|
const KimiUsageFieldsSchema = z.object({
|
|
11
8
|
limit: ApiOptionalStringSchema,
|
|
12
9
|
used: ApiOptionalStringSchema,
|
|
@@ -191,13 +188,6 @@ const KimiAuthSchema = z
|
|
|
191
188
|
token_type: z.string().nullish(),
|
|
192
189
|
})
|
|
193
190
|
.passthrough();
|
|
194
|
-
const KimiTokenRefreshSchema = z.object({
|
|
195
|
-
access_token: z.string(),
|
|
196
|
-
refresh_token: z.string().nullish(),
|
|
197
|
-
expires_in: ApiNumberSchema.nullish(),
|
|
198
|
-
scope: z.string().nullish(),
|
|
199
|
-
token_type: z.string().nullish(),
|
|
200
|
-
});
|
|
201
191
|
export class KimiQuotaProvider {
|
|
202
192
|
constructor(options) {
|
|
203
193
|
this.providerId = "kimi";
|
|
@@ -210,8 +200,9 @@ export class KimiQuotaProvider {
|
|
|
210
200
|
const credentials = await this.readCredentials();
|
|
211
201
|
if (!credentials)
|
|
212
202
|
return unavailableUsage(this);
|
|
213
|
-
const res = await this.
|
|
203
|
+
const res = await this.callUsageApi(credentials.access_token);
|
|
214
204
|
if (!res.ok) {
|
|
205
|
+
// Read-only on credentials; the Kimi CLI owns refresh. See docs/providers.md.
|
|
215
206
|
this.logger.debug({ status: res.status }, "Kimi usage fetch failed");
|
|
216
207
|
return unavailableUsage(this);
|
|
217
208
|
}
|
|
@@ -227,24 +218,6 @@ export class KimiQuotaProvider {
|
|
|
227
218
|
error: null,
|
|
228
219
|
};
|
|
229
220
|
}
|
|
230
|
-
async fetchUsageResponse(record) {
|
|
231
|
-
const { credentials, filePath } = record;
|
|
232
|
-
const res = await this.callUsageApi(credentials.access_token);
|
|
233
|
-
if (res.status !== 401 || !filePath || !credentials.refresh_token)
|
|
234
|
-
return res;
|
|
235
|
-
const latest = await this.readCredentialFile(filePath);
|
|
236
|
-
const retried = latest?.access_token && latest.access_token !== credentials.access_token
|
|
237
|
-
? await this.callUsageApi(latest.access_token)
|
|
238
|
-
: res;
|
|
239
|
-
if (retried.status !== 401)
|
|
240
|
-
return retried;
|
|
241
|
-
const refreshTokenUsed = latest?.refresh_token ?? credentials.refresh_token;
|
|
242
|
-
const refreshed = await this.refreshToken(refreshTokenUsed);
|
|
243
|
-
if (!refreshed)
|
|
244
|
-
return retried;
|
|
245
|
-
await this.saveRefreshedCredentials({ filePath, refreshTokenUsed, refreshed });
|
|
246
|
-
return this.callUsageApi(refreshed.access_token);
|
|
247
|
-
}
|
|
248
221
|
async callUsageApi(token) {
|
|
249
222
|
return fetchProviderApi(this.fetchApi, KIMI_USAGE_URL, {
|
|
250
223
|
headers: {
|
|
@@ -253,82 +226,15 @@ export class KimiQuotaProvider {
|
|
|
253
226
|
},
|
|
254
227
|
});
|
|
255
228
|
}
|
|
256
|
-
async refreshToken(refreshToken) {
|
|
257
|
-
const body = new URLSearchParams({
|
|
258
|
-
client_id: KIMI_CLIENT_ID,
|
|
259
|
-
grant_type: "refresh_token",
|
|
260
|
-
refresh_token: refreshToken,
|
|
261
|
-
});
|
|
262
|
-
const res = await fetchProviderApi(this.fetchApi, KIMI_TOKEN_URL, {
|
|
263
|
-
method: "POST",
|
|
264
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
265
|
-
body,
|
|
266
|
-
});
|
|
267
|
-
if (!res.ok) {
|
|
268
|
-
this.logger.debug({ status: res.status }, "Kimi token refresh failed");
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
const parsed = KimiTokenRefreshSchema.safeParse(await res.json());
|
|
272
|
-
return parsed.success ? parsed.data : null;
|
|
273
|
-
}
|
|
274
|
-
async saveRefreshedCredentials(options) {
|
|
275
|
-
const { filePath, refreshTokenUsed, refreshed } = options;
|
|
276
|
-
const existing = await this.readCredentialFile(filePath);
|
|
277
|
-
if (!existing)
|
|
278
|
-
return;
|
|
279
|
-
// The Kimi CLI owns this file too. If its refresh token no longer matches the one we
|
|
280
|
-
// just spent, the CLI rotated the credentials while our refresh was in flight and its
|
|
281
|
-
// copy is newer than ours — writing our merge would strand the CLI on dead tokens.
|
|
282
|
-
if (existing.refresh_token !== refreshTokenUsed) {
|
|
283
|
-
this.logger.debug("Kimi credentials rotated during refresh; keeping the file on disk");
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const expiresIn = refreshed.expires_in ?? existing.expires_in;
|
|
287
|
-
const merged = {
|
|
288
|
-
...existing,
|
|
289
|
-
access_token: refreshed.access_token,
|
|
290
|
-
refresh_token: refreshed.refresh_token ?? existing.refresh_token,
|
|
291
|
-
expires_in: expiresIn,
|
|
292
|
-
expires_at: expiresIn == null ? existing.expires_at : Math.floor(Date.now() / 1000) + expiresIn,
|
|
293
|
-
scope: refreshed.scope ?? existing.scope,
|
|
294
|
-
token_type: refreshed.token_type ?? existing.token_type,
|
|
295
|
-
};
|
|
296
|
-
const tempPath = join(dirname(filePath), `.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
297
|
-
try {
|
|
298
|
-
await fs.writeFile(tempPath, JSON.stringify(merged, null, 2), { mode: 0o600 });
|
|
299
|
-
// writeFile above is async I/O that gives the CLI a window to rotate the file on
|
|
300
|
-
// its own. Re-read right before the rename and confirm nothing changed since the
|
|
301
|
-
// guard above — the check-then-write gap this closes is what the file-level guard
|
|
302
|
-
// couldn't cover, since it ran before the write, not right before the replace.
|
|
303
|
-
const beforeRename = await this.readCredentialFile(filePath);
|
|
304
|
-
if (!this.credentialsUnchangedSince(existing, beforeRename)) {
|
|
305
|
-
this.logger.debug("Kimi credentials rotated during refresh; discarding stale merge");
|
|
306
|
-
await fs.rm(tempPath, { force: true }).catch(() => undefined);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
await fs.rename(tempPath, filePath);
|
|
310
|
-
}
|
|
311
|
-
catch (error) {
|
|
312
|
-
this.logger.warn({ err: error }, "Failed to persist refreshed Kimi credentials");
|
|
313
|
-
await fs.rm(tempPath, { force: true }).catch(() => undefined);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
credentialsUnchangedSince(baseline, candidate) {
|
|
317
|
-
return (candidate?.access_token === baseline.access_token &&
|
|
318
|
-
candidate?.refresh_token === baseline.refresh_token);
|
|
319
|
-
}
|
|
320
229
|
async readCredentials() {
|
|
321
230
|
const environmentToken = process.env["KIMI_TOKEN"] || process.env["KIMI_API_KEY"];
|
|
322
231
|
if (environmentToken) {
|
|
323
|
-
return {
|
|
232
|
+
return { access_token: environmentToken };
|
|
324
233
|
}
|
|
325
234
|
for (const path of this.credentialPaths()) {
|
|
326
235
|
const credentials = await this.readCredentialFile(path);
|
|
327
236
|
if (credentials?.access_token) {
|
|
328
|
-
return {
|
|
329
|
-
credentials: { ...credentials, access_token: credentials.access_token },
|
|
330
|
-
filePath: path,
|
|
331
|
-
};
|
|
237
|
+
return { ...credentials, access_token: credentials.access_token };
|
|
332
238
|
}
|
|
333
239
|
}
|
|
334
240
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentBridge=function(){const l=(0,s.createDesktopPreviewUrlResolver)({reader:(0,s.createDesktopFileReader)(),objectUrls:(0,s.createBrowserObjectUrlMinter)()});return{copyFile:t.copyDesktopAttachmentFile,writeBase64:t.writeDesktopAttachmentBase64,writeBytes:t.writeDesktopAttachmentBytes,deleteFile:t.deleteDesktopAttachmentFile,garbageCollect:t.garbageCollectDesktopAttachmentFiles,readFileBase64:s.readDesktopFileBase64,resolvePreviewUrl:t=>l.resolve(t),releasePreviewUrl:t=>l.release(t)}};var t=r(d[0]),s=r(d[1])},4943,[4284,4285]);
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},
|
|
1
|
+
__d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.createDesktopAttachmentStore=function(n){return{storageType:"desktop-file",async save(s){const o=s.id??(0,t.generateAttachmentId)(),l=s.fileName??null;if("file_uri"===s.source.kind)return await c(n,{id:o,uri:s.source.uri,mimeType:s.mimeType,fileName:l});if("data_url"===s.source.kind){const c=(0,t.parseDataUrl)(s.source.dataUrl),p=(0,t.normalizeMimeType)(s.mimeType??c.mimeType);return await y(n,{id:o,base64:c.base64,mimeType:p,fileName:l})}if("bytes"===s.source.kind){const c=(0,t.normalizeMimeType)(s.mimeType);return await p(n,{id:o,bytes:s.source.bytes,mimeType:c,fileName:l})}const f=(0,t.normalizeMimeType)(s.mimeType??s.source.blob.type),u=await(0,t.blobToBase64)(s.source.blob);return await y(n,{id:o,base64:u,mimeType:f,fileName:l})},encodeBase64:async({attachment:t})=>(f(t),await n.readFileBase64(t.storageKey)),resolvePreviewUrl:async({attachment:t})=>(f(t),await n.resolvePreviewUrl(t)),async releasePreviewUrl({attachment:t,url:s}){f(t),await n.releasePreviewUrl({url:s})},async delete({attachment:t}){f(t),await n.deleteFile({path:t.storageKey})},async garbageCollect({referencedIds:t}){await n.garbageCollect({referencedIds:Array.from(t)})}}};var t=r(d[0]);const n={"image/png":".png","image/jpeg":".jpg","image/jpg":".jpg","image/gif":".gif","image/webp":".webp","image/avif":".avif","image/heic":".heic","image/heif":".heif","image/tiff":".tiff","image/bmp":".bmp","image/svg+xml":".svg"};function s(t){const n=t.replace(/\\/g,"/").split("/"),s=n[n.length-1];return s&&s.length>0?s:null}function o(s){const o=(0,t.getFileExtensionFromName)(s.fileName);if(o)return o;const l=(0,t.getFileExtensionFromName)(s.sourcePath);return l||(n[s.mimeType]??".img")}function l(t){return{id:t.id,mimeType:t.mimeType,storageType:"desktop-file",storageKey:t.storagePath,fileName:t.fileName??null,byteSize:t.byteSize,createdAt:Date.now()}}async function c(n,c){const y=(0,t.fileUriToPath)(c.uri),p=c.fileName??s(y),f=(0,t.normalizeMimeType)(c.mimeType),u=o({fileName:p,sourcePath:y,mimeType:f}),T=await n.copyFile({attachmentId:c.id,sourcePath:y,extension:u});return l({id:c.id,mimeType:f,storagePath:T.path,byteSize:T.byteSize,fileName:p})}async function y(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBase64({attachmentId:n.id,base64:n.base64,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}async function p(t,n){const s=o({fileName:n.fileName,mimeType:n.mimeType}),c=await t.writeBytes({attachmentId:n.id,bytes:n.bytes,extension:s});return l({id:n.id,mimeType:n.mimeType,storagePath:c.path,byteSize:c.byteSize,fileName:n.fileName})}function f(t){if("desktop-file"!==t.storageType)throw new Error(`Unsupported desktop attachment storage type '${t.storageType}'.`)}},4942,[4278]);
|
|
Binary file
|