wave-agent-sdk 0.16.9 → 0.16.12
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/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +18 -0
- package/dist/constants/toolLimits.d.ts +2 -0
- package/dist/constants/toolLimits.d.ts.map +1 -1
- package/dist/constants/toolLimits.js +2 -0
- package/dist/managers/aiManager.d.ts +5 -0
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +21 -0
- package/dist/managers/hookManager.d.ts +6 -3
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +36 -13
- package/dist/managers/mcpManager.d.ts +4 -28
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +10 -127
- package/dist/services/authService.d.ts +33 -1
- package/dist/services/authService.d.ts.map +1 -1
- package/dist/services/authService.js +212 -11
- package/dist/services/configurationService.d.ts +1 -0
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +48 -6
- package/dist/services/hook.d.ts +4 -0
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +10 -0
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +11 -0
- package/dist/services/interactionService.d.ts.map +1 -1
- package/dist/services/interactionService.js +0 -12
- package/dist/services/remoteSettingsService.d.ts +21 -0
- package/dist/services/remoteSettingsService.d.ts.map +1 -0
- package/dist/services/remoteSettingsService.js +280 -0
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +58 -32
- package/dist/tools/types.d.ts +4 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/agent.d.ts +7 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/auth.d.ts +12 -0
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +20 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +5 -1
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -0
- package/dist/types/mcp.d.ts +1 -1
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +9 -8
- package/dist/utils/gitUtils.d.ts +18 -1
- package/dist/utils/gitUtils.d.ts.map +1 -1
- package/dist/utils/gitUtils.js +120 -49
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +6 -1
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +4 -2
- package/dist/utils/toolResultStorage.d.ts +46 -0
- package/dist/utils/toolResultStorage.d.ts.map +1 -0
- package/dist/utils/toolResultStorage.js +90 -0
- package/dist/utils/worktreeUtils.d.ts.map +1 -1
- package/dist/utils/worktreeUtils.js +58 -0
- package/package.json +3 -3
- package/src/agent.ts +20 -0
- package/src/constants/toolLimits.ts +3 -0
- package/src/managers/aiManager.ts +37 -0
- package/src/managers/hookManager.ts +42 -17
- package/src/managers/mcpManager.ts +10 -178
- package/src/services/authService.ts +243 -16
- package/src/services/configurationService.ts +58 -6
- package/src/services/hook.ts +15 -0
- package/src/services/initializationService.ts +13 -0
- package/src/services/interactionService.ts +0 -18
- package/src/services/remoteSettingsService.ts +315 -0
- package/src/tools/bashTool.ts +70 -38
- package/src/tools/types.ts +4 -0
- package/src/types/agent.ts +7 -0
- package/src/types/auth.ts +10 -0
- package/src/types/configuration.ts +23 -0
- package/src/types/hooks.ts +7 -1
- package/src/types/mcp.ts +1 -1
- package/src/utils/containerSetup.ts +8 -8
- package/src/utils/gitUtils.ts +123 -48
- package/src/utils/mcpUtils.ts +12 -1
- package/src/utils/openaiClient.ts +5 -2
- package/src/utils/toolResultStorage.ts +117 -0
- package/src/utils/worktreeUtils.ts +63 -0
|
@@ -9,6 +9,9 @@ export declare class AuthService {
|
|
|
9
9
|
private static instance;
|
|
10
10
|
private _serverUrl;
|
|
11
11
|
private onAuthChangeCallbacks;
|
|
12
|
+
private _refreshPromise;
|
|
13
|
+
private _authFileMtime;
|
|
14
|
+
private static readonly REFRESH_BUFFER_MS;
|
|
12
15
|
static getInstance(): AuthService;
|
|
13
16
|
/**
|
|
14
17
|
* Set server URL programmatically (e.g. from AgentOptions.serverUrl).
|
|
@@ -37,15 +40,44 @@ export declare class AuthService {
|
|
|
37
40
|
}): Promise<string>;
|
|
38
41
|
/**
|
|
39
42
|
* Exchange a short-lived authorization code for a JWT token.
|
|
40
|
-
* Returns
|
|
43
|
+
* Returns token, optional refresh token, optional expiresIn, and user info.
|
|
41
44
|
*/
|
|
42
45
|
private exchangeCode;
|
|
43
46
|
private startLocalAuthServer;
|
|
44
47
|
private openBrowser;
|
|
45
48
|
isSSOAuthenticated(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Check if the current token is expired or within the refresh buffer.
|
|
51
|
+
* Returns false if no expiry info (backward compat — treated as never-expiring).
|
|
52
|
+
*/
|
|
53
|
+
isTokenExpired(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Check if the token needs refresh and refresh it if possible.
|
|
56
|
+
* Deduplicates concurrent refresh calls (401 dedup).
|
|
57
|
+
*/
|
|
58
|
+
checkAndRefreshTokenIfNeeded(): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Refresh the access token using the stored refresh token.
|
|
61
|
+
* Returns true on success, false on failure.
|
|
62
|
+
*/
|
|
63
|
+
private refreshToken;
|
|
64
|
+
/**
|
|
65
|
+
* Check if another process has refreshed the token on disk.
|
|
66
|
+
* Returns true if a fresh token was found and loaded.
|
|
67
|
+
*/
|
|
68
|
+
/** @internal Check if another process has refreshed the token on disk */
|
|
69
|
+
tryReadRefreshedTokenFromDisk(): boolean;
|
|
46
70
|
getAuthUser(): AuthUser | undefined;
|
|
47
71
|
}
|
|
48
72
|
export declare const authService: AuthService;
|
|
73
|
+
/**
|
|
74
|
+
* Create a fetch wrapper that handles SSO token refresh transparently.
|
|
75
|
+
*
|
|
76
|
+
* 1. Proactive refresh: calls checkAndRefreshTokenIfNeeded() before each request
|
|
77
|
+
* 2. Updates Authorization header with fresh token
|
|
78
|
+
* 3. Reactive 401/403 recovery: tries disk refresh then force refresh, retries once
|
|
79
|
+
*/
|
|
80
|
+
export declare function createAuthAwareFetch(innerFetch: typeof fetch): typeof fetch;
|
|
49
81
|
/**
|
|
50
82
|
* Get or create a persistent anonymous ID for telemetry.
|
|
51
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAiB,MAAM,kBAAkB,CAAC;AAQ5E,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IACrC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,qBAAqB,CACxB;IACL,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAiB;IAE1D,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;IASvE,OAAO,CAAC,gBAAgB;IAUxB,WAAW,IAAI,MAAM;IAKrB,QAAQ,IAAI,UAAU;IAmBtB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAgBlC,SAAS,IAAI,IAAI;IAgBjB,WAAW,IAAI,MAAM,GAAG,SAAS;IAKjC,YAAY,IAAI,MAAM;IAUhB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,6DAA6D;QAC7D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,mGAAmG;QACnG,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,oFAAoF;QACpF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCnB;;;OAGG;YACW,YAAY;IAyB1B,OAAO,CAAC,oBAAoB;YAoGd,WAAW;IAmBzB,kBAAkB,IAAI,OAAO;IAW7B;;;OAGG;IACH,cAAc,IAAI,OAAO;IAgBzB;;;OAGG;IACG,4BAA4B,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBtD;;;OAGG;YACW,YAAY;IA4D1B;;;OAGG;IACH,yEAAyE;IACzE,6BAA6B,IAAI,OAAO;IAwBxC,WAAW,IAAI,QAAQ,GAAG,SAAS;CAIpC;AAED,eAAO,MAAM,WAAW,aAA4B,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,KAAK,GAAG,OAAO,KAAK,CA+C3E;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAsC/C;AAED,4DAA4D;AAC5D,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles SSO authentication via the admin server.
|
|
5
5
|
* Manages auth token storage in ~/.wave/auth.json.
|
|
6
6
|
*/
|
|
7
|
-
import { readFileSync, writeFileSync, existsSync, chmodSync, rmSync, mkdirSync, } from "fs";
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, chmodSync, rmSync, mkdirSync, statSync, } from "fs";
|
|
8
8
|
import * as path from "path";
|
|
9
9
|
import * as os from "os";
|
|
10
10
|
import { randomBytes } from "crypto";
|
|
@@ -12,12 +12,15 @@ import { createServer } from "http";
|
|
|
12
12
|
import { URL } from "url";
|
|
13
13
|
import { execFile } from "child_process";
|
|
14
14
|
import { promisify } from "util";
|
|
15
|
+
import { logger } from "../utils/globalLogger.js";
|
|
15
16
|
/** Persistent anonymous ID for telemetry fallback when SSO is not authenticated. */
|
|
16
17
|
let _anonymousId;
|
|
17
18
|
const execFileAsync = promisify(execFile);
|
|
18
19
|
export class AuthService {
|
|
19
20
|
constructor() {
|
|
20
21
|
this.onAuthChangeCallbacks = [];
|
|
22
|
+
this._refreshPromise = null;
|
|
23
|
+
this._authFileMtime = 0;
|
|
21
24
|
}
|
|
22
25
|
static getInstance() {
|
|
23
26
|
if (!AuthService.instance) {
|
|
@@ -63,6 +66,13 @@ export class AuthService {
|
|
|
63
66
|
}
|
|
64
67
|
try {
|
|
65
68
|
const content = readFileSync(authPath, "utf-8");
|
|
69
|
+
// Best-effort mtime tracking for multi-process detection
|
|
70
|
+
try {
|
|
71
|
+
this._authFileMtime = statSync(authPath).mtimeMs;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// ignore stat errors
|
|
75
|
+
}
|
|
66
76
|
return JSON.parse(content);
|
|
67
77
|
}
|
|
68
78
|
catch {
|
|
@@ -77,10 +87,19 @@ export class AuthService {
|
|
|
77
87
|
}
|
|
78
88
|
writeFileSync(authPath, JSON.stringify(config, null, 2), "utf-8");
|
|
79
89
|
chmodSync(authPath, 0o600);
|
|
90
|
+
// Update mtime after write
|
|
91
|
+
try {
|
|
92
|
+
this._authFileMtime = statSync(authPath).mtimeMs;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// ignore stat errors
|
|
96
|
+
}
|
|
80
97
|
}
|
|
81
98
|
clearAuth() {
|
|
82
99
|
const config = this.loadAuth();
|
|
83
100
|
delete config.SSO_TOKEN;
|
|
101
|
+
delete config.SSO_REFRESH_TOKEN;
|
|
102
|
+
delete config.SSO_TOKEN_EXPIRES_AT;
|
|
84
103
|
if (Object.keys(config).length === 0) {
|
|
85
104
|
const authPath = this.getAuthPath();
|
|
86
105
|
if (existsSync(authPath)) {
|
|
@@ -111,33 +130,41 @@ export class AuthService {
|
|
|
111
130
|
readToken: options?.readToken,
|
|
112
131
|
});
|
|
113
132
|
// Exchange authorization code for JWT (includes user info)
|
|
114
|
-
const { token, user } = await this.exchangeCode(serverUrl, code);
|
|
133
|
+
const { token, refreshToken, expiresIn, user } = await this.exchangeCode(serverUrl, code);
|
|
115
134
|
// Save the token and user info (preserve existing keys)
|
|
116
135
|
const existing = this.loadAuth();
|
|
117
|
-
this.saveAuth({
|
|
136
|
+
this.saveAuth({
|
|
137
|
+
...existing,
|
|
138
|
+
SSO_TOKEN: token,
|
|
139
|
+
SSO_REFRESH_TOKEN: refreshToken,
|
|
140
|
+
SSO_TOKEN_EXPIRES_AT: expiresIn
|
|
141
|
+
? Date.now() + expiresIn * 1000
|
|
142
|
+
: undefined,
|
|
143
|
+
user,
|
|
144
|
+
});
|
|
118
145
|
this.notifyAuthChange("login");
|
|
119
146
|
return token;
|
|
120
147
|
}
|
|
121
148
|
/**
|
|
122
149
|
* Exchange a short-lived authorization code for a JWT token.
|
|
123
|
-
* Returns
|
|
150
|
+
* Returns token, optional refresh token, optional expiresIn, and user info.
|
|
124
151
|
*/
|
|
125
152
|
async exchangeCode(serverUrl, code) {
|
|
126
|
-
const exchangeUrl = `${serverUrl}/api/auth/
|
|
153
|
+
const exchangeUrl = `${serverUrl}/api/auth/token`;
|
|
154
|
+
logger.info(`[Auth] Exchanging authorization code at ${exchangeUrl}`);
|
|
127
155
|
const response = await fetch(exchangeUrl, {
|
|
128
156
|
method: "POST",
|
|
129
157
|
headers: { "Content-Type": "application/json" },
|
|
130
|
-
body: JSON.stringify({ code }),
|
|
158
|
+
body: JSON.stringify({ grant_type: "authorization_code", code }),
|
|
131
159
|
});
|
|
132
160
|
if (!response.ok) {
|
|
133
161
|
const text = await response.text();
|
|
162
|
+
logger.info(`[Auth] Authorization code exchange failed (${response.status}): ${text}`);
|
|
134
163
|
throw new Error(`Token exchange failed (${response.status}): ${text}`);
|
|
135
164
|
}
|
|
136
165
|
const data = (await response.json());
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
user: { id: data.user.id, email: data.user.email },
|
|
140
|
-
};
|
|
166
|
+
logger.info("[Auth] Authorization code exchanged successfully");
|
|
167
|
+
return data;
|
|
141
168
|
}
|
|
142
169
|
startLocalAuthServer(serverUrl, options) {
|
|
143
170
|
return new Promise((resolve, reject) => {
|
|
@@ -233,14 +260,188 @@ export class AuthService {
|
|
|
233
260
|
await execFileAsync(command, args);
|
|
234
261
|
}
|
|
235
262
|
isSSOAuthenticated() {
|
|
236
|
-
|
|
263
|
+
const config = this.loadAuth();
|
|
264
|
+
if (!config.SSO_TOKEN)
|
|
265
|
+
return false;
|
|
266
|
+
if (config.SSO_TOKEN_EXPIRES_AT &&
|
|
267
|
+
Date.now() >= config.SSO_TOKEN_EXPIRES_AT)
|
|
268
|
+
return false;
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Check if the current token is expired or within the refresh buffer.
|
|
273
|
+
* Returns false if no expiry info (backward compat — treated as never-expiring).
|
|
274
|
+
*/
|
|
275
|
+
isTokenExpired() {
|
|
276
|
+
const config = this.loadAuth();
|
|
277
|
+
if (!config.SSO_TOKEN_EXPIRES_AT)
|
|
278
|
+
return false;
|
|
279
|
+
const expiresAt = config.SSO_TOKEN_EXPIRES_AT;
|
|
280
|
+
const bufferMs = AuthService.REFRESH_BUFFER_MS;
|
|
281
|
+
const now = Date.now();
|
|
282
|
+
const remaining = expiresAt - now;
|
|
283
|
+
const expired = now >= expiresAt - bufferMs;
|
|
284
|
+
if (expired) {
|
|
285
|
+
logger.info(`[Auth] Token expired or within refresh buffer: remaining=${Math.round(remaining / 1000)}s, buffer=${bufferMs / 1000}s`);
|
|
286
|
+
}
|
|
287
|
+
return expired;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Check if the token needs refresh and refresh it if possible.
|
|
291
|
+
* Deduplicates concurrent refresh calls (401 dedup).
|
|
292
|
+
*/
|
|
293
|
+
async checkAndRefreshTokenIfNeeded() {
|
|
294
|
+
if (!this.isTokenExpired())
|
|
295
|
+
return true;
|
|
296
|
+
// Dedup: if a refresh is already in-flight, reuse the same promise
|
|
297
|
+
if (this._refreshPromise) {
|
|
298
|
+
logger.info("[Auth] Token refresh already in-flight, reusing existing promise");
|
|
299
|
+
return this._refreshPromise;
|
|
300
|
+
}
|
|
301
|
+
logger.info("[Auth] Starting token refresh");
|
|
302
|
+
this._refreshPromise = this.refreshToken();
|
|
303
|
+
try {
|
|
304
|
+
return await this._refreshPromise;
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
this._refreshPromise = null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Refresh the access token using the stored refresh token.
|
|
312
|
+
* Returns true on success, false on failure.
|
|
313
|
+
*/
|
|
314
|
+
async refreshToken() {
|
|
315
|
+
const config = this.loadAuth();
|
|
316
|
+
if (!config.SSO_REFRESH_TOKEN) {
|
|
317
|
+
logger.info("[Auth] No refresh token available, cannot refresh");
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
const serverUrl = this.getServerUrl();
|
|
321
|
+
try {
|
|
322
|
+
logger.info(`[Auth] Refreshing token via ${serverUrl}/api/auth/token`);
|
|
323
|
+
const response = await fetch(`${serverUrl}/api/auth/token`, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: { "Content-Type": "application/json" },
|
|
326
|
+
body: JSON.stringify({
|
|
327
|
+
grant_type: "refresh_token",
|
|
328
|
+
refresh_token: config.SSO_REFRESH_TOKEN,
|
|
329
|
+
}),
|
|
330
|
+
});
|
|
331
|
+
if (response.status === 400 || response.status === 401) {
|
|
332
|
+
// Refresh token revoked — clear auth
|
|
333
|
+
logger.info(`[Auth] Refresh token rejected (${response.status}), clearing auth`);
|
|
334
|
+
this.clearAuth();
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
logger.info(`[Auth] Token refresh failed with status ${response.status}`);
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
const data = (await response.json());
|
|
342
|
+
const newExpiresAt = data.expiresIn
|
|
343
|
+
? Date.now() + data.expiresIn * 1000
|
|
344
|
+
: undefined;
|
|
345
|
+
this.saveAuth({
|
|
346
|
+
...config,
|
|
347
|
+
SSO_TOKEN: data.token,
|
|
348
|
+
SSO_REFRESH_TOKEN: data.refreshToken ?? config.SSO_REFRESH_TOKEN,
|
|
349
|
+
SSO_TOKEN_EXPIRES_AT: newExpiresAt,
|
|
350
|
+
user: data.user
|
|
351
|
+
? { id: data.user.id, email: data.user.email }
|
|
352
|
+
: config.user,
|
|
353
|
+
});
|
|
354
|
+
logger.info(`[Auth] Token refreshed successfully, new token expires at ${newExpiresAt ? new Date(newExpiresAt).toISOString() : "never"}`);
|
|
355
|
+
this.notifyAuthChange("login");
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
// Network error — don't clear auth (might be transient)
|
|
360
|
+
logger.info(`[Auth] Token refresh failed with network error: ${err}`);
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Check if another process has refreshed the token on disk.
|
|
366
|
+
* Returns true if a fresh token was found and loaded.
|
|
367
|
+
*/
|
|
368
|
+
/** @internal Check if another process has refreshed the token on disk */
|
|
369
|
+
tryReadRefreshedTokenFromDisk() {
|
|
370
|
+
try {
|
|
371
|
+
const authPath = this.getAuthPath();
|
|
372
|
+
if (!existsSync(authPath))
|
|
373
|
+
return false;
|
|
374
|
+
const stat = statSync(authPath);
|
|
375
|
+
if (stat.mtimeMs <= this._authFileMtime)
|
|
376
|
+
return false;
|
|
377
|
+
// File was modified by another process — check if token is fresh
|
|
378
|
+
const config = this.loadAuth();
|
|
379
|
+
if (config.SSO_TOKEN_EXPIRES_AT &&
|
|
380
|
+
Date.now() < config.SSO_TOKEN_EXPIRES_AT) {
|
|
381
|
+
logger.info(`[Auth] Detected token refreshed by another process (auth.json mtime changed)`);
|
|
382
|
+
this._authFileMtime = stat.mtimeMs;
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
237
390
|
}
|
|
238
391
|
getAuthUser() {
|
|
239
392
|
const config = this.loadAuth();
|
|
240
393
|
return config.user;
|
|
241
394
|
}
|
|
242
395
|
}
|
|
396
|
+
AuthService.REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
243
397
|
export const authService = AuthService.getInstance();
|
|
398
|
+
/**
|
|
399
|
+
* Create a fetch wrapper that handles SSO token refresh transparently.
|
|
400
|
+
*
|
|
401
|
+
* 1. Proactive refresh: calls checkAndRefreshTokenIfNeeded() before each request
|
|
402
|
+
* 2. Updates Authorization header with fresh token
|
|
403
|
+
* 3. Reactive 401/403 recovery: tries disk refresh then force refresh, retries once
|
|
404
|
+
*/
|
|
405
|
+
export function createAuthAwareFetch(innerFetch) {
|
|
406
|
+
return async (input, init) => {
|
|
407
|
+
// Proactive refresh
|
|
408
|
+
await authService.checkAndRefreshTokenIfNeeded();
|
|
409
|
+
// Update Authorization header with fresh token
|
|
410
|
+
const freshToken = authService.getSSOToken();
|
|
411
|
+
const headers = new Headers(init?.headers);
|
|
412
|
+
if (freshToken) {
|
|
413
|
+
headers.set("Authorization", `Bearer ${freshToken}`);
|
|
414
|
+
}
|
|
415
|
+
const modifiedInit = { ...init, headers };
|
|
416
|
+
const response = await innerFetch(input, modifiedInit);
|
|
417
|
+
// Reactive 401/403 recovery (single retry)
|
|
418
|
+
if (response.status === 401 || response.status === 403) {
|
|
419
|
+
logger.info(`[Auth] Received ${response.status}, attempting token recovery`);
|
|
420
|
+
// Try disk refresh first (another process may have refreshed)
|
|
421
|
+
if (authService.tryReadRefreshedTokenFromDisk()) {
|
|
422
|
+
const retryToken = authService.getSSOToken();
|
|
423
|
+
const retryHeaders = new Headers(init?.headers);
|
|
424
|
+
if (retryToken) {
|
|
425
|
+
retryHeaders.set("Authorization", `Bearer ${retryToken}`);
|
|
426
|
+
}
|
|
427
|
+
logger.info("[Auth] Retrying request with disk-refreshed token");
|
|
428
|
+
return innerFetch(input, { ...init, headers: retryHeaders });
|
|
429
|
+
}
|
|
430
|
+
// Try force refresh
|
|
431
|
+
if (await authService.checkAndRefreshTokenIfNeeded()) {
|
|
432
|
+
const retryToken = authService.getSSOToken();
|
|
433
|
+
if (retryToken) {
|
|
434
|
+
const retryHeaders = new Headers(init?.headers);
|
|
435
|
+
retryHeaders.set("Authorization", `Bearer ${retryToken}`);
|
|
436
|
+
logger.info("[Auth] Retrying request with force-refreshed token");
|
|
437
|
+
return innerFetch(input, { ...init, headers: retryHeaders });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
logger.info("[Auth] Token recovery failed, returning original response");
|
|
441
|
+
}
|
|
442
|
+
return response;
|
|
443
|
+
};
|
|
444
|
+
}
|
|
244
445
|
/**
|
|
245
446
|
* Get or create a persistent anonymous ID for telemetry.
|
|
246
447
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configurationService.d.ts","sourceRoot":"","sources":["../../src/services/configurationService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EACV,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,EACL,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,aAAa,EACb,WAAW,EAGX,cAAc,EACd,YAAY,EACb,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"configurationService.d.ts","sourceRoot":"","sources":["../../src/services/configurationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,8 @@ import { isValidEnvironmentVars, } from "../types/environment.js";
|
|
|
14
14
|
import { ConfigurationError, CONFIG_ERRORS, } from "../types/index.js";
|
|
15
15
|
import { DEFAULT_WAVE_MAX_INPUT_TOKENS, DEFAULT_WAVE_MAX_OUTPUT_TOKENS, } from "../utils/constants.js";
|
|
16
16
|
import { parseCustomHeaders } from "../utils/stringUtils.js";
|
|
17
|
+
import { getRemoteSettingsSync, mergeRemoteSettings, } from "./remoteSettingsService.js";
|
|
18
|
+
import { createAuthAwareFetch } from "./authService.js";
|
|
17
19
|
/**
|
|
18
20
|
* Default ConfigurationService implementation
|
|
19
21
|
*
|
|
@@ -62,17 +64,22 @@ export class ConfigurationService {
|
|
|
62
64
|
warnings: validation.warnings,
|
|
63
65
|
};
|
|
64
66
|
}
|
|
67
|
+
// Merge remote settings (highest priority: Remote > Local > Project > User)
|
|
68
|
+
const remoteSettings = getRemoteSettingsSync();
|
|
69
|
+
const finalConfig = remoteSettings
|
|
70
|
+
? mergeRemoteSettings(mergedConfig, remoteSettings)
|
|
71
|
+
: mergedConfig;
|
|
65
72
|
// Success case
|
|
66
|
-
this.currentConfiguration =
|
|
73
|
+
this.currentConfiguration = finalConfig;
|
|
67
74
|
// Set environment variables from merged config and inject system variables
|
|
68
75
|
const env = {
|
|
69
|
-
...(
|
|
76
|
+
...(finalConfig.env || {}),
|
|
70
77
|
WAVE_PROJECT_DIR: workdir,
|
|
71
78
|
};
|
|
72
79
|
this.setEnvironmentVars(env);
|
|
73
|
-
|
|
80
|
+
finalConfig.env = env;
|
|
74
81
|
return {
|
|
75
|
-
configuration:
|
|
82
|
+
configuration: finalConfig,
|
|
76
83
|
success: true,
|
|
77
84
|
sourcePath: "merged configuration",
|
|
78
85
|
warnings: validation.warnings,
|
|
@@ -321,6 +328,8 @@ export class ConfigurationService {
|
|
|
321
328
|
const ssoToken = this.readSSOToken();
|
|
322
329
|
const serverUrl = this.options.serverUrl || process.env.WAVE_SERVER_URL;
|
|
323
330
|
if (ssoToken && serverUrl) {
|
|
331
|
+
const baseFetch = fetch ?? this.options.fetch ?? globalThis.fetch;
|
|
332
|
+
const authAwareFetch = createAuthAwareFetch(baseFetch);
|
|
324
333
|
return {
|
|
325
334
|
apiKey: ssoToken,
|
|
326
335
|
baseURL: `${serverUrl}/api/v1`,
|
|
@@ -328,7 +337,7 @@ export class ConfigurationService {
|
|
|
328
337
|
? defaultHeaders
|
|
329
338
|
: undefined,
|
|
330
339
|
fetchOptions: fetchOptions ?? this.options.fetchOptions,
|
|
331
|
-
fetch:
|
|
340
|
+
fetch: authAwareFetch,
|
|
332
341
|
};
|
|
333
342
|
}
|
|
334
343
|
// Resolve API key: override > options > env (settings.json) > process.env
|
|
@@ -394,7 +403,10 @@ export class ConfigurationService {
|
|
|
394
403
|
*/
|
|
395
404
|
resolveModelConfig(model, fastModel, maxTokens, permissionMode) {
|
|
396
405
|
// Resolve agent model: override > options > process.env (includes settings.json env)
|
|
397
|
-
const resolvedAgentModel = model ||
|
|
406
|
+
const resolvedAgentModel = model ||
|
|
407
|
+
this.options.model ||
|
|
408
|
+
process.env.WAVE_MODEL ||
|
|
409
|
+
this.currentConfiguration?.model;
|
|
398
410
|
// Resolve fast model: override > options > process.env (includes settings.json env)
|
|
399
411
|
const resolvedFastModel = fastModel || this.options.fastModel || process.env.WAVE_FAST_MODEL;
|
|
400
412
|
// Validate required fields
|
|
@@ -547,6 +559,26 @@ export class ConfigurationService {
|
|
|
547
559
|
*/
|
|
548
560
|
setModel(model) {
|
|
549
561
|
this.options.model = model;
|
|
562
|
+
this.persistModelToSettings(model);
|
|
563
|
+
}
|
|
564
|
+
async persistModelToSettings(model) {
|
|
565
|
+
const configPath = getUserConfigPaths()[0]; // ~/.wave/settings.json
|
|
566
|
+
const configDir = path.dirname(configPath);
|
|
567
|
+
if (!existsSync(configDir)) {
|
|
568
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
569
|
+
}
|
|
570
|
+
let config = {};
|
|
571
|
+
if (existsSync(configPath)) {
|
|
572
|
+
try {
|
|
573
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
574
|
+
config = JSON.parse(content);
|
|
575
|
+
}
|
|
576
|
+
catch {
|
|
577
|
+
// Start fresh if corrupted
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
config.model = model;
|
|
581
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
550
582
|
}
|
|
551
583
|
/**
|
|
552
584
|
* Get all configured models from settings.json and environment
|
|
@@ -558,6 +590,10 @@ export class ConfigurationService {
|
|
|
558
590
|
if (currentModel) {
|
|
559
591
|
models.add(currentModel);
|
|
560
592
|
}
|
|
593
|
+
// Persisted model from settings (includes remote-merged)
|
|
594
|
+
if (this.currentConfiguration?.model) {
|
|
595
|
+
models.add(this.currentConfiguration.model);
|
|
596
|
+
}
|
|
561
597
|
// Add models from merged configuration
|
|
562
598
|
if (this.currentConfiguration?.models) {
|
|
563
599
|
Object.keys(this.currentConfiguration.models).forEach((model) => {
|
|
@@ -894,6 +930,7 @@ export function loadWaveConfigFromFile(filePath) {
|
|
|
894
930
|
permissions: config.permissions || undefined,
|
|
895
931
|
enabledPlugins: config.enabledPlugins || undefined,
|
|
896
932
|
language: config.language || undefined,
|
|
933
|
+
model: config.model || undefined,
|
|
897
934
|
autoMemoryEnabled: config.autoMemoryEnabled !== undefined
|
|
898
935
|
? config.autoMemoryEnabled
|
|
899
936
|
: undefined,
|
|
@@ -1007,6 +1044,10 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1007
1044
|
if (config.language !== undefined) {
|
|
1008
1045
|
mergedConfig.language = config.language;
|
|
1009
1046
|
}
|
|
1047
|
+
// Merge model (last one wins)
|
|
1048
|
+
if (config.model !== undefined) {
|
|
1049
|
+
mergedConfig.model = config.model;
|
|
1050
|
+
}
|
|
1010
1051
|
// Merge autoMemoryEnabled (last one wins)
|
|
1011
1052
|
if (config.autoMemoryEnabled !== undefined) {
|
|
1012
1053
|
mergedConfig.autoMemoryEnabled = config.autoMemoryEnabled;
|
|
@@ -1049,6 +1090,7 @@ export function loadMergedWaveConfig(workdir) {
|
|
|
1049
1090
|
? mergedConfig.enabledPlugins
|
|
1050
1091
|
: undefined,
|
|
1051
1092
|
language: mergedConfig.language,
|
|
1093
|
+
model: mergedConfig.model,
|
|
1052
1094
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1053
1095
|
marketplaces: mergedConfig.marketplaces &&
|
|
1054
1096
|
Object.keys(mergedConfig.marketplaces).length > 0
|
package/dist/services/hook.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export declare function executeCommand(command: string, context: HookExecutionCo
|
|
|
14
14
|
* Execute multiple commands in sequence
|
|
15
15
|
*/
|
|
16
16
|
export declare function executeCommands(commands: string[], context: HookExecutionContext | ExtendedHookExecutionContext, options?: HookExecutionOptions): Promise<HookExecutionResult[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Execute a CwdChanged hook
|
|
19
|
+
*/
|
|
20
|
+
export declare function executeCwdChangedHooks(oldCwd: string, newCwd: string, context: ExtendedHookExecutionContext): Promise<HookExecutionResult[]>;
|
|
17
21
|
/**
|
|
18
22
|
* Validate command safety (basic checks)
|
|
19
23
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/services/hook.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EAElC,MAAM,mBAAmB,CAAC;AA2F3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA6I9B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAchC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsBtD"}
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/services/hook.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EAElC,MAAM,mBAAmB,CAAC;AA2F3B;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA6I9B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,EAC5D,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAchC;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAMhC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsBtD"}
|
package/dist/services/hook.js
CHANGED
|
@@ -223,6 +223,16 @@ export async function executeCommands(commands, context, options) {
|
|
|
223
223
|
}
|
|
224
224
|
return results;
|
|
225
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Execute a CwdChanged hook
|
|
228
|
+
*/
|
|
229
|
+
export async function executeCwdChangedHooks(oldCwd, newCwd, context) {
|
|
230
|
+
// CwdChanged hooks are executed through HookManager.executeCwdChangedHooks()
|
|
231
|
+
void context;
|
|
232
|
+
void oldCwd;
|
|
233
|
+
void newCwd;
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
226
236
|
/**
|
|
227
237
|
* Validate command safety (basic checks)
|
|
228
238
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initializationService.d.ts","sourceRoot":"","sources":["../../src/services/initializationService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"initializationService.d.ts","sourceRoot":"","sources":["../../src/services/initializationService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,WAAW,CAAC;IACxB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,wBAAwB,EAAE,MAAM,IAAI,CAAC;CACtC;AAED,qBAAa,qBAAqB;WACZ,UAAU,CAC5B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;KACtB,GACA,OAAO,CAAC,IAAI,CAAC;CAiSjB"}
|
|
@@ -2,6 +2,7 @@ import { handleSessionRestoration } from "./session.js";
|
|
|
2
2
|
import { setGlobalLogger } from "../utils/globalLogger.js";
|
|
3
3
|
import { LspManager } from "../managers/lspManager.js";
|
|
4
4
|
import { USER_MEMORY_FILE } from "../utils/constants.js";
|
|
5
|
+
import { remoteSettingsService } from "./remoteSettingsService.js";
|
|
5
6
|
export class InitializationService {
|
|
6
7
|
static async initialize(context, options) {
|
|
7
8
|
const { skillManager, subagentManager, container, toolManager, pluginManager, options: agentOptions, slashCommandManager, logger, mcpManager, workdir, lspManager, configurationService, hookManager, messageManager, memoryRuleManager, liveConfigManager, taskManager, resolveAndValidateConfig, } = context;
|
|
@@ -162,6 +163,16 @@ export class InitializationService {
|
|
|
162
163
|
logger?.error("Failed to initialize live configuration reload:", error);
|
|
163
164
|
// Don't throw error to prevent app startup failure - continue without live reload
|
|
164
165
|
}
|
|
166
|
+
// Initialize remote settings (fetch server-managed config)
|
|
167
|
+
try {
|
|
168
|
+
const phaseStart = performance.now();
|
|
169
|
+
await remoteSettingsService.initialize();
|
|
170
|
+
logger?.debug(`Initialization Phase [Remote Settings] took ${(performance.now() - phaseStart).toFixed(2)}ms`);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
logger?.error("Failed to initialize remote settings:", error);
|
|
174
|
+
// Don't throw error to prevent app startup failure - continue without remote settings
|
|
175
|
+
}
|
|
165
176
|
// Memory is lazy-cached on first getCombinedMemoryContent call
|
|
166
177
|
// No explicit loading needed during initialization
|
|
167
178
|
// Handle session restoration or set provided messages
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactionService.d.ts","sourceRoot":"","sources":["../../src/services/interactionService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"interactionService.d.ts","sourceRoot":"","sources":["../../src/services/interactionService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,qBAAa,kBAAkB;WACT,WAAW,CAC7B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACjD,OAAO,CAAC,IAAI,CAAC;WA6FI,cAAc,CAChC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;CAiDjB"}
|
|
@@ -21,18 +21,6 @@ export class InteractionService {
|
|
|
21
21
|
// If command doesn't exist, continue as normal message processing
|
|
22
22
|
// Don't add to history, let normal message processing logic below handle it
|
|
23
23
|
}
|
|
24
|
-
// Inject pending notifications from background tasks
|
|
25
|
-
const notificationQueue = context.aiManager["container"].has("NotificationQueue")
|
|
26
|
-
? context.aiManager["container"].get("NotificationQueue")
|
|
27
|
-
: undefined;
|
|
28
|
-
if (notificationQueue && notificationQueue.hasPending()) {
|
|
29
|
-
const notifications = notificationQueue.dequeueAll();
|
|
30
|
-
for (const notification of notifications) {
|
|
31
|
-
messageManager.addUserMessage({
|
|
32
|
-
content: notification,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
24
|
// Handle normal AI message
|
|
37
25
|
// Add user message first, will automatically sync to UI
|
|
38
26
|
messageManager.addUserMessage({
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RemoteSettingsFetchResult } from "../types/configuration.js";
|
|
2
|
+
import type { WaveConfiguration } from "../types/configuration.js";
|
|
3
|
+
export declare function initialize(): void;
|
|
4
|
+
export declare function getRemoteSettingsSync(): WaveConfiguration | null;
|
|
5
|
+
export declare function refresh(): Promise<RemoteSettingsFetchResult>;
|
|
6
|
+
export declare function clear(): void;
|
|
7
|
+
export declare function shutdown(): void;
|
|
8
|
+
export declare function mergeRemoteSettings(localMerged: WaveConfiguration, remote: WaveConfiguration): WaveConfiguration;
|
|
9
|
+
/**
|
|
10
|
+
* Singleton object for consumers that prefer a namespace-style import.
|
|
11
|
+
* Usage: import { remoteSettingsService } from "./remoteSettingsService.js"
|
|
12
|
+
*/
|
|
13
|
+
export declare const remoteSettingsService: {
|
|
14
|
+
readonly initialize: typeof initialize;
|
|
15
|
+
readonly getRemoteSettingsSync: typeof getRemoteSettingsSync;
|
|
16
|
+
readonly refresh: typeof refresh;
|
|
17
|
+
readonly clear: typeof clear;
|
|
18
|
+
readonly shutdown: typeof shutdown;
|
|
19
|
+
readonly mergeRemoteSettings: typeof mergeRemoteSettings;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=remoteSettingsService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteSettingsService.d.ts","sourceRoot":"","sources":["../../src/services/remoteSettingsService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AA2JnE,wBAAgB,UAAU,IAAI,IAAI,CASjC;AAED,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAEhE;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,yBAAyB,CAAC,CAKlE;AAED,wBAAgB,KAAK,IAAI,IAAI,CAO5B;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAK/B;AAqCD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,iBAAiB,GACxB,iBAAiB,CA4DnB;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;;;;CAOxB,CAAC"}
|