@vostride/agent-qa-dashboard 0.1.14 → 0.1.15
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/{index-BL7OautO.d.cts → index-ygQLgXzT.d.cts} +6 -1
- package/dist/{index-BL7OautO.d.ts → index-ygQLgXzT.d.ts} +6 -1
- package/dist/index.cjs +201 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +204 -21
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +200 -21
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +202 -19
- package/dist/server/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DashboardDatabase } from './index-
|
|
2
|
-
export { A as AppiumManager, C as ConfigManager, L as LiveExecutionEvent, R as RunRow, S as StepRow, T as TestFileInfo, a as TestFileManager, b as TestRunHandle, c as TestRunner, d as TestValidationError, e as TestValidationResult, f as createRouter, r as readJsonBody, s as startServer } from './index-
|
|
1
|
+
import { D as DashboardDatabase } from './index-ygQLgXzT.cjs';
|
|
2
|
+
export { A as AppiumManager, C as ConfigManager, L as LiveExecutionEvent, R as RunRow, S as StepRow, T as TestFileInfo, a as TestFileManager, b as TestRunHandle, c as TestRunner, d as TestValidationError, e as TestValidationResult, f as createRouter, r as readJsonBody, s as startServer } from './index-ygQLgXzT.cjs';
|
|
3
3
|
import { Reporter, SecretRedactor, SuiteDefinition, RunArtifactReporterContext, SuiteSummary, TestDefinition, StepResult, TestResult, RunSummary, HookResultEvent } from '@vostride/agent-qa-core';
|
|
4
4
|
import 'node:http';
|
|
5
5
|
import '@vostride/agent-qa-mcp';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DashboardDatabase } from './index-
|
|
2
|
-
export { A as AppiumManager, C as ConfigManager, L as LiveExecutionEvent, R as RunRow, S as StepRow, T as TestFileInfo, a as TestFileManager, b as TestRunHandle, c as TestRunner, d as TestValidationError, e as TestValidationResult, f as createRouter, r as readJsonBody, s as startServer } from './index-
|
|
1
|
+
import { D as DashboardDatabase } from './index-ygQLgXzT.js';
|
|
2
|
+
export { A as AppiumManager, C as ConfigManager, L as LiveExecutionEvent, R as RunRow, S as StepRow, T as TestFileInfo, a as TestFileManager, b as TestRunHandle, c as TestRunner, d as TestValidationError, e as TestValidationResult, f as createRouter, r as readJsonBody, s as startServer } from './index-ygQLgXzT.js';
|
|
3
3
|
import { Reporter, SecretRedactor, SuiteDefinition, RunArtifactReporterContext, SuiteSummary, TestDefinition, StepResult, TestResult, RunSummary, HookResultEvent } from '@vostride/agent-qa-core';
|
|
4
4
|
import 'node:http';
|
|
5
5
|
import '@vostride/agent-qa-mcp';
|
package/dist/index.js
CHANGED
|
@@ -1797,7 +1797,7 @@ function resolveDashboardDbPath({
|
|
|
1797
1797
|
// src/reporter/dashboard-reporter.ts
|
|
1798
1798
|
import { copyFile as copyFile2, mkdir as mkdir2, readFile, rename as rename2, unlink as unlink2, writeFile } from "fs/promises";
|
|
1799
1799
|
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, join, relative as relative2, resolve as resolve3 } from "path";
|
|
1800
|
-
import { buildInternalRunAttributes,
|
|
1800
|
+
import { buildInternalRunAttributes, redactAuthStateValue, validateTrustedRunAttributes } from "@vostride/agent-qa-core";
|
|
1801
1801
|
function isPathInside(rootDir, candidatePath) {
|
|
1802
1802
|
const relativePath = relative2(rootDir, candidatePath);
|
|
1803
1803
|
return Boolean(relativePath) && !relativePath.startsWith("..") && !isAbsolute2(relativePath);
|
|
@@ -1917,7 +1917,7 @@ var DashboardReporter = class {
|
|
|
1917
1917
|
this.redactor = redactor;
|
|
1918
1918
|
}
|
|
1919
1919
|
redactValue(value) {
|
|
1920
|
-
return
|
|
1920
|
+
return redactAuthStateValue(value, { secretRedactor: this.redactor });
|
|
1921
1921
|
}
|
|
1922
1922
|
redactString(value) {
|
|
1923
1923
|
return value === void 0 ? void 0 : this.redactValue(value);
|
|
@@ -4221,7 +4221,7 @@ function readJsonBody(req, maxBytes = DEFAULT_MAX_BYTES) {
|
|
|
4221
4221
|
}
|
|
4222
4222
|
|
|
4223
4223
|
// src/server/routes.ts
|
|
4224
|
-
import { buildAnalyticsEvent, buildInternalRunAttributes as buildInternalRunAttributes2, captureAnalytics, mergeRunAttributes, readAuth, writeAuth, removeAuth, getAgentQaVersion, getProviderOptions, getLLMAuthProviderPlugin, listLLMAuthProviderPlugins, ModelConfigSchema, NamedLLMConfigSchema, WorkspaceSchema, ServicesSchema, RegistrySchema, UseSchema, MobileAppStateSchema, hashStepInstruction, TimeoutConfigSchema, CacheConfigSchema, HealingConfigSchema, PlannerConfigSchema, LoggingConfigSchema, LogCaptureConfigSchema, AccessibilityConfigSchema, DashboardConfigSchema, McpConfigSchema, RecordingConfigSchema, BrowserConfigSchema, AnalyticsSchema, AgentQaConfigSchema as AgentQaConfigSchema3, TestDefinitionSchema, SuiteDefinitionSchema, parseEnvFile, serializeEnvFile, runHookInSandbox, RUNTIME_IMAGE_MAP, SecretStore, SecretRedactor, validateUserRunAttributes, discoverWorkspaceFiles as discoverWorkspaceFiles4, isWorkspacePathMatch, resolveAnalyticsStandardProperties, resolveMemoryRoot as resolveMemoryRoot2, resolveWorkspaceFileTarget as resolveWorkspaceFileTarget2 } from "@vostride/agent-qa-core";
|
|
4224
|
+
import { AuthStateNameSchema, buildAnalyticsEvent, buildInternalRunAttributes as buildInternalRunAttributes2, captureAnalytics, mergeRunAttributes, readAuth, writeAuth, removeAuth, getAgentQaVersion, getProviderOptions, getLLMAuthProviderPlugin, listAuthStateMetadata, listLLMAuthProviderPlugins, ModelConfigSchema, NamedLLMConfigSchema, WorkspaceSchema, ServicesSchema, RegistrySchema, UseSchema, MobileAppStateSchema, hashStepInstruction, TimeoutConfigSchema, CacheConfigSchema, HealingConfigSchema, PlannerConfigSchema, LoggingConfigSchema, LogCaptureConfigSchema, AccessibilityConfigSchema, DashboardConfigSchema, McpConfigSchema, RecordingConfigSchema, BrowserConfigSchema, AnalyticsSchema, AgentQaConfigSchema as AgentQaConfigSchema3, TestDefinitionSchema, SuiteDefinitionSchema, parseEnvFile, serializeEnvFile, runHookInSandbox, RUNTIME_IMAGE_MAP, SecretStore, SecretRedactor, redactAuthStateValue as redactAuthStateValue2, validateUserRunAttributes, discoverWorkspaceFiles as discoverWorkspaceFiles4, isWorkspacePathMatch, resolveAnalyticsStandardProperties, resolveMemoryRoot as resolveMemoryRoot2, resolveWorkspaceFileTarget as resolveWorkspaceFileTarget2 } from "@vostride/agent-qa-core";
|
|
4225
4225
|
import { parse as parseYaml5 } from "yaml";
|
|
4226
4226
|
var LLM_PROVIDER_MODES = /* @__PURE__ */ new Set([
|
|
4227
4227
|
"openai-compatible",
|
|
@@ -4511,6 +4511,41 @@ function cors(res) {
|
|
|
4511
4511
|
function isPlainRecord(value) {
|
|
4512
4512
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4513
4513
|
}
|
|
4514
|
+
function getConfiguredAuthStateDir(config) {
|
|
4515
|
+
const services = isPlainRecord(config.services) ? config.services : void 0;
|
|
4516
|
+
const authState = services && isPlainRecord(services.authState) ? services.authState : void 0;
|
|
4517
|
+
return typeof authState?.dir === "string" ? authState.dir : void 0;
|
|
4518
|
+
}
|
|
4519
|
+
function getSessionTargetNameForAuthState(session) {
|
|
4520
|
+
if (!session || typeof session.getState !== "function") return "selected target";
|
|
4521
|
+
try {
|
|
4522
|
+
const state = session.getState();
|
|
4523
|
+
if (isPlainRecord(state) && typeof state.targetName === "string" && state.targetName.trim().length > 0) {
|
|
4524
|
+
return state.targetName.trim();
|
|
4525
|
+
}
|
|
4526
|
+
} catch {
|
|
4527
|
+
}
|
|
4528
|
+
return "selected target";
|
|
4529
|
+
}
|
|
4530
|
+
function buildAuthStateSaveErrorMessage(stateName, targetName, error) {
|
|
4531
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
4532
|
+
if (/already exists/i.test(raw)) {
|
|
4533
|
+
return {
|
|
4534
|
+
status: 409,
|
|
4535
|
+
message: `Auth state "${stateName}" for target "${targetName}" already exists. Use replace=true to replace it.`
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
if (/web Live Mode|not ready|executing|busy/i.test(raw)) {
|
|
4539
|
+
return {
|
|
4540
|
+
status: 409,
|
|
4541
|
+
message: `Could not save auth state "${stateName}" for target "${targetName}".`
|
|
4542
|
+
};
|
|
4543
|
+
}
|
|
4544
|
+
return {
|
|
4545
|
+
status: 500,
|
|
4546
|
+
message: `Could not save auth state "${stateName}" for target "${targetName}".`
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4514
4549
|
function isDashboardProductEventName(value) {
|
|
4515
4550
|
return typeof value === "string" && DASHBOARD_PRODUCT_EVENT_NAME_SET.has(value);
|
|
4516
4551
|
}
|
|
@@ -4910,7 +4945,7 @@ function sanitizeSecretsFileMetadata(value) {
|
|
|
4910
4945
|
};
|
|
4911
4946
|
}
|
|
4912
4947
|
function sanitizeArtifactPayloadForResponse(payload) {
|
|
4913
|
-
const sanitized = sanitizeSecretTemplates(payload);
|
|
4948
|
+
const sanitized = redactAuthStateValue2(sanitizeSecretTemplates(payload));
|
|
4914
4949
|
if (!isRecord2(sanitized)) return payload;
|
|
4915
4950
|
const config = sanitized.config;
|
|
4916
4951
|
if (!isRecord2(config) || !("secretsFile" in config)) return sanitized;
|
|
@@ -4929,6 +4964,9 @@ function sanitizeArtifactForResponse(artifact) {
|
|
|
4929
4964
|
payload: sanitizeArtifactPayloadForResponse(artifact.payload)
|
|
4930
4965
|
};
|
|
4931
4966
|
}
|
|
4967
|
+
function sanitizeAuthStateForResponse(value) {
|
|
4968
|
+
return redactAuthStateValue2(value);
|
|
4969
|
+
}
|
|
4932
4970
|
async function normalizeDashboardWorkspacePath(filePath, workspacePaths, kind, requireExisting = true) {
|
|
4933
4971
|
if (!workspacePaths) {
|
|
4934
4972
|
throw new Error("Workspace path resolution is required for dashboard-triggered runs");
|
|
@@ -5153,6 +5191,28 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5153
5191
|
}
|
|
5154
5192
|
const url = parseUrl(req);
|
|
5155
5193
|
const path = url.pathname;
|
|
5194
|
+
if (path === "/api/auth-states" && req.method === "GET") {
|
|
5195
|
+
if (!configManager || !deps.configPath) {
|
|
5196
|
+
json(res, { error: "Auth-state metadata not available" }, 503);
|
|
5197
|
+
return;
|
|
5198
|
+
}
|
|
5199
|
+
;
|
|
5200
|
+
(async () => {
|
|
5201
|
+
try {
|
|
5202
|
+
const config = await configManager.read();
|
|
5203
|
+
const targetName = url.searchParams.get("target") ?? void 0;
|
|
5204
|
+
const authStates = await listAuthStateMetadata({
|
|
5205
|
+
configDir: dirname8(resolve6(deps.configPath)),
|
|
5206
|
+
authStateDir: getConfiguredAuthStateDir(config),
|
|
5207
|
+
targetName
|
|
5208
|
+
});
|
|
5209
|
+
json(res, { authStates });
|
|
5210
|
+
} catch {
|
|
5211
|
+
json(res, { error: "Could not list auth states." }, 500);
|
|
5212
|
+
}
|
|
5213
|
+
})();
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5156
5216
|
if (path === "/api/analytics/events" && req.method === "POST") {
|
|
5157
5217
|
readJsonBody(req).then((body) => {
|
|
5158
5218
|
if (!isPlainRecord(body) || !isDashboardProductEventName(body.name) || Object.prototype.hasOwnProperty.call(body, "properties") && body.properties !== void 0 && !isPlainRecord(body.properties)) {
|
|
@@ -5266,7 +5326,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5266
5326
|
return;
|
|
5267
5327
|
}
|
|
5268
5328
|
const bundle = db.getRunArtifactBundle(runId);
|
|
5269
|
-
json(res, {
|
|
5329
|
+
json(res, sanitizeAuthStateForResponse({
|
|
5270
5330
|
run,
|
|
5271
5331
|
artifact: sanitizeArtifactForResponse(bundle.artifact),
|
|
5272
5332
|
children: bundle.children.map((child) => ({
|
|
@@ -5274,7 +5334,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5274
5334
|
artifact: sanitizeArtifactForResponse(child.artifact)
|
|
5275
5335
|
})),
|
|
5276
5336
|
missingSections: getArtifactMissingSections(bundle.artifact)
|
|
5277
|
-
});
|
|
5337
|
+
}));
|
|
5278
5338
|
return;
|
|
5279
5339
|
}
|
|
5280
5340
|
if (path.startsWith("/api/runs/") && path.endsWith("/accessibility") && req.method === "GET") {
|
|
@@ -5300,7 +5360,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5300
5360
|
const stepOrder = parseInt(stepOrderStr, 10);
|
|
5301
5361
|
const trace = db.getReasoningTrace(runId, stepOrder);
|
|
5302
5362
|
if (trace) {
|
|
5303
|
-
json(res, { trace });
|
|
5363
|
+
json(res, sanitizeAuthStateForResponse({ trace }));
|
|
5304
5364
|
return;
|
|
5305
5365
|
}
|
|
5306
5366
|
const steps = db.getSteps(runId);
|
|
@@ -5309,7 +5369,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5309
5369
|
notFound(res, "Step not found");
|
|
5310
5370
|
return;
|
|
5311
5371
|
}
|
|
5312
|
-
json(res, {
|
|
5372
|
+
json(res, sanitizeAuthStateForResponse({
|
|
5313
5373
|
trace: {
|
|
5314
5374
|
id: null,
|
|
5315
5375
|
stepId: step.id,
|
|
@@ -5330,7 +5390,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5330
5390
|
screenStateAfter: null,
|
|
5331
5391
|
createdAt: step.createdAt
|
|
5332
5392
|
}
|
|
5333
|
-
});
|
|
5393
|
+
}));
|
|
5334
5394
|
return;
|
|
5335
5395
|
}
|
|
5336
5396
|
if (path.startsWith("/api/runs/") && path.endsWith("/execution-logs") && req.method === "GET") {
|
|
@@ -5350,11 +5410,11 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5350
5410
|
for (const rid of allRunIds) {
|
|
5351
5411
|
allLogs.push(...db.getExecutionLogs({ runId: rid, stepId, type }));
|
|
5352
5412
|
}
|
|
5353
|
-
json(res, { logs: allLogs });
|
|
5413
|
+
json(res, sanitizeAuthStateForResponse({ logs: allLogs }));
|
|
5354
5414
|
return;
|
|
5355
5415
|
}
|
|
5356
5416
|
const logs = db.getExecutionLogs({ runId: id, stepId, type });
|
|
5357
|
-
json(res, { logs });
|
|
5417
|
+
json(res, sanitizeAuthStateForResponse({ logs }));
|
|
5358
5418
|
return;
|
|
5359
5419
|
}
|
|
5360
5420
|
if (path.startsWith("/api/runs/") && path.endsWith("/logs") && req.method === "GET") {
|
|
@@ -5371,7 +5431,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5371
5431
|
const limit = url.searchParams.has("limit") ? parseInt(url.searchParams.get("limit"), 10) : 500;
|
|
5372
5432
|
const offset = url.searchParams.has("offset") ? parseInt(url.searchParams.get("offset"), 10) : 0;
|
|
5373
5433
|
const logs = db.getLogs({ runId: id, stepId, level, source, limit, offset });
|
|
5374
|
-
json(res, { logs, total: logs.length });
|
|
5434
|
+
json(res, sanitizeAuthStateForResponse({ logs, total: logs.length }));
|
|
5375
5435
|
return;
|
|
5376
5436
|
}
|
|
5377
5437
|
if (path.startsWith("/api/runs/") && path.endsWith("/steps") && req.method === "GET") {
|
|
@@ -5383,7 +5443,7 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5383
5443
|
return;
|
|
5384
5444
|
}
|
|
5385
5445
|
const steps = db.getSteps(id);
|
|
5386
|
-
json(res, { steps });
|
|
5446
|
+
json(res, sanitizeAuthStateForResponse({ steps }));
|
|
5387
5447
|
return;
|
|
5388
5448
|
}
|
|
5389
5449
|
if (path.startsWith("/api/runs/") && path.endsWith("/cancel") && req.method === "POST") {
|
|
@@ -5457,9 +5517,9 @@ function createRouter(dbOrDeps, artifactsDir) {
|
|
|
5457
5517
|
const attempts = db.getRunsByParent(id);
|
|
5458
5518
|
if (run.suiteId && !run.parentRunId) {
|
|
5459
5519
|
const tests = db.getRunsByParent(id);
|
|
5460
|
-
json(res, { run, steps, attempts, tests });
|
|
5520
|
+
json(res, sanitizeAuthStateForResponse({ run, steps, attempts, tests }));
|
|
5461
5521
|
} else {
|
|
5462
|
-
json(res, { run, steps, attempts });
|
|
5522
|
+
json(res, sanitizeAuthStateForResponse({ run, steps, attempts }));
|
|
5463
5523
|
}
|
|
5464
5524
|
return;
|
|
5465
5525
|
}
|
|
@@ -7072,7 +7132,7 @@ data: ${JSON.stringify(event)}
|
|
|
7072
7132
|
const networkLogs = summarizeHookNetworkLogs(
|
|
7073
7133
|
result.networkLogs
|
|
7074
7134
|
);
|
|
7075
|
-
json(res, {
|
|
7135
|
+
json(res, sanitizeAuthStateForResponse({
|
|
7076
7136
|
success: result.success,
|
|
7077
7137
|
status: result.success ? "passed" : "failed",
|
|
7078
7138
|
executedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -7090,9 +7150,9 @@ data: ${JSON.stringify(event)}
|
|
|
7090
7150
|
networkLogsAvailable: networkLogs.length > 0,
|
|
7091
7151
|
networkLogs
|
|
7092
7152
|
}
|
|
7093
|
-
});
|
|
7153
|
+
}));
|
|
7094
7154
|
} catch (err) {
|
|
7095
|
-
json(res, { error: err instanceof Error ? err.message : "Failed to run hook" }, 500);
|
|
7155
|
+
json(res, sanitizeAuthStateForResponse({ error: err instanceof Error ? err.message : "Failed to run hook" }), 500);
|
|
7096
7156
|
}
|
|
7097
7157
|
}).catch(() => json(res, { error: "Invalid request body" }, 400));
|
|
7098
7158
|
return;
|
|
@@ -7341,6 +7401,45 @@ data: ${JSON.stringify(event)}
|
|
|
7341
7401
|
})();
|
|
7342
7402
|
return;
|
|
7343
7403
|
}
|
|
7404
|
+
const liveEditorAuthStateMatch = path.match(/^\/api\/live-editor\/sessions\/([^/]+)\/auth-state$/);
|
|
7405
|
+
if (liveEditorAuthStateMatch && req.method === "POST") {
|
|
7406
|
+
if (!sessionManager) {
|
|
7407
|
+
json(res, { error: "Live editor not available" }, 503);
|
|
7408
|
+
return;
|
|
7409
|
+
}
|
|
7410
|
+
;
|
|
7411
|
+
(async () => {
|
|
7412
|
+
let stateName = "";
|
|
7413
|
+
let session;
|
|
7414
|
+
try {
|
|
7415
|
+
const body = await readJsonBody(req);
|
|
7416
|
+
const parsedName = AuthStateNameSchema.safeParse(typeof body?.name === "string" ? body.name : "");
|
|
7417
|
+
if (!parsedName.success) {
|
|
7418
|
+
json(res, { error: "Auth state name must be a lowercase slug." }, 400);
|
|
7419
|
+
return;
|
|
7420
|
+
}
|
|
7421
|
+
stateName = parsedName.data;
|
|
7422
|
+
session = sessionManager.getSession(decodeURIComponent(liveEditorAuthStateMatch[1]));
|
|
7423
|
+
if (!session) {
|
|
7424
|
+
notFound(res, "Session not found");
|
|
7425
|
+
return;
|
|
7426
|
+
}
|
|
7427
|
+
if (typeof session.captureWebAuthState !== "function") {
|
|
7428
|
+
json(res, {
|
|
7429
|
+
error: `Could not save auth state "${stateName}" for target "${getSessionTargetNameForAuthState(session)}".`
|
|
7430
|
+
}, 500);
|
|
7431
|
+
return;
|
|
7432
|
+
}
|
|
7433
|
+
const authState = await session.captureWebAuthState(stateName, { replace: body?.replace === true });
|
|
7434
|
+
json(res, { authState });
|
|
7435
|
+
} catch (err) {
|
|
7436
|
+
const targetName = getSessionTargetNameForAuthState(session);
|
|
7437
|
+
const response = buildAuthStateSaveErrorMessage(stateName || "unknown", targetName, err);
|
|
7438
|
+
json(res, { error: response.message }, response.status);
|
|
7439
|
+
}
|
|
7440
|
+
})();
|
|
7441
|
+
return;
|
|
7442
|
+
}
|
|
7344
7443
|
if (path === "/api/live-editor/sessions" && req.method === "POST") {
|
|
7345
7444
|
if (!sessionManager) {
|
|
7346
7445
|
json(res, { error: "Live editor not available" }, 503);
|
|
@@ -8548,7 +8647,9 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
8548
8647
|
|
|
8549
8648
|
// src/live-editor/live-session.ts
|
|
8550
8649
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
8650
|
+
import { dirname as dirname12, resolve as resolvePath } from "path";
|
|
8551
8651
|
import {
|
|
8652
|
+
AUTH_STATE_SCHEMA_VERSION,
|
|
8552
8653
|
executeStep,
|
|
8553
8654
|
createModel,
|
|
8554
8655
|
getProviderOptions as getProviderOptions2,
|
|
@@ -8562,8 +8663,11 @@ import {
|
|
|
8562
8663
|
runHooks,
|
|
8563
8664
|
runHookInSandbox as runHookInSandbox2,
|
|
8564
8665
|
MobileSetupError as MobileSetupError2,
|
|
8565
|
-
|
|
8566
|
-
|
|
8666
|
+
readAuthStateMetadata,
|
|
8667
|
+
redactAuthStateValue as redactAuthStateValue3,
|
|
8668
|
+
resolveAuthStatePaths,
|
|
8669
|
+
resolveSecretTemplatesInValue,
|
|
8670
|
+
writeAuthStateFiles
|
|
8567
8671
|
} from "@vostride/agent-qa-core";
|
|
8568
8672
|
|
|
8569
8673
|
// src/live-editor/mobile-bootstrap.ts
|
|
@@ -8718,6 +8822,26 @@ function coerceRunJSResult(value) {
|
|
|
8718
8822
|
}
|
|
8719
8823
|
return JSON.stringify(value);
|
|
8720
8824
|
}
|
|
8825
|
+
function getWebStorageStateContext(adapter) {
|
|
8826
|
+
if (!adapter || !("getPage" in adapter)) return null;
|
|
8827
|
+
const page = adapter.getPage?.();
|
|
8828
|
+
if (!page || typeof page !== "object") return null;
|
|
8829
|
+
const contextFactory = page.context;
|
|
8830
|
+
if (typeof contextFactory !== "function") return null;
|
|
8831
|
+
const context = contextFactory.call(page);
|
|
8832
|
+
if (!context || typeof context !== "object") return null;
|
|
8833
|
+
const storageState = context.storageState;
|
|
8834
|
+
if (typeof storageState !== "function") return null;
|
|
8835
|
+
return context;
|
|
8836
|
+
}
|
|
8837
|
+
function getConfiguredAuthStateDir2(config) {
|
|
8838
|
+
const services = config.services;
|
|
8839
|
+
if (!services || typeof services !== "object") return void 0;
|
|
8840
|
+
const authState = services.authState;
|
|
8841
|
+
if (!authState || typeof authState !== "object") return void 0;
|
|
8842
|
+
const dir = authState.dir;
|
|
8843
|
+
return typeof dir === "string" ? dir : void 0;
|
|
8844
|
+
}
|
|
8721
8845
|
function toLiveSubActionData(result) {
|
|
8722
8846
|
return result.trace?.subActions?.map((subAction) => ({
|
|
8723
8847
|
index: subAction.index,
|
|
@@ -8795,6 +8919,7 @@ var LiveSession = class {
|
|
|
8795
8919
|
previousSteps = [];
|
|
8796
8920
|
currentStep = null;
|
|
8797
8921
|
platform = "web";
|
|
8922
|
+
targetName = null;
|
|
8798
8923
|
currentUrl = null;
|
|
8799
8924
|
executing = false;
|
|
8800
8925
|
idleTimer = null;
|
|
@@ -8832,7 +8957,7 @@ var LiveSession = class {
|
|
|
8832
8957
|
this.pendingMessages.push(message);
|
|
8833
8958
|
}
|
|
8834
8959
|
redact(value) {
|
|
8835
|
-
return
|
|
8960
|
+
return redactAuthStateValue3(value, { secretRedactor: this.secretRedactor });
|
|
8836
8961
|
}
|
|
8837
8962
|
resolveSecrets(value) {
|
|
8838
8963
|
return resolveSecretTemplatesInValue(value, this.secretStore);
|
|
@@ -9062,6 +9187,7 @@ var LiveSession = class {
|
|
|
9062
9187
|
}
|
|
9063
9188
|
async initialize(config) {
|
|
9064
9189
|
this.platform = config.platform;
|
|
9190
|
+
this.targetName = typeof config.targetName === "string" && config.targetName.trim().length > 0 ? config.targetName.trim() : null;
|
|
9065
9191
|
this._modelName = config.llmConfig.model ?? "unknown";
|
|
9066
9192
|
this.variableStore = new VariableStore();
|
|
9067
9193
|
this.secretStore = config.secretStore;
|
|
@@ -9552,6 +9678,62 @@ ${errorLines.join("\n")}`;
|
|
|
9552
9678
|
this.messageSink = originalSink;
|
|
9553
9679
|
}
|
|
9554
9680
|
}
|
|
9681
|
+
async captureWebAuthState(name, options = {}) {
|
|
9682
|
+
if (this.platform !== "web") {
|
|
9683
|
+
throw new Error("Auth-state capture is only available for web Live Mode sessions.");
|
|
9684
|
+
}
|
|
9685
|
+
if (!this.readyForInteraction || !this.adapter) {
|
|
9686
|
+
throw new Error(this.terminalError ?? "Live session is not ready for auth-state capture.");
|
|
9687
|
+
}
|
|
9688
|
+
if (this.adapter.platform !== "web") {
|
|
9689
|
+
throw new Error("Auth-state capture is only available for web Live Mode sessions.");
|
|
9690
|
+
}
|
|
9691
|
+
if (this.executing) {
|
|
9692
|
+
throw new Error("Cannot save auth state while the Live Mode session is executing.");
|
|
9693
|
+
}
|
|
9694
|
+
if (!this.targetName) {
|
|
9695
|
+
throw new Error("Cannot save auth state because this Live Mode session has no configured target.");
|
|
9696
|
+
}
|
|
9697
|
+
if (!this.deps.configManager || !this.deps.configPath) {
|
|
9698
|
+
throw new Error("Cannot save auth state because workspace config is unavailable.");
|
|
9699
|
+
}
|
|
9700
|
+
const context = getWebStorageStateContext(this.adapter);
|
|
9701
|
+
if (!context) {
|
|
9702
|
+
throw new Error("Cannot save auth state because the active browser context is unavailable.");
|
|
9703
|
+
}
|
|
9704
|
+
const config = await this.deps.configManager.read();
|
|
9705
|
+
const authStateDir = getConfiguredAuthStateDir2(config);
|
|
9706
|
+
const paths = resolveAuthStatePaths({
|
|
9707
|
+
configDir: dirname12(resolvePath(this.deps.configPath)),
|
|
9708
|
+
authStateDir,
|
|
9709
|
+
targetName: this.targetName,
|
|
9710
|
+
stateName: name,
|
|
9711
|
+
target: { platform: "web" }
|
|
9712
|
+
});
|
|
9713
|
+
let existing = null;
|
|
9714
|
+
try {
|
|
9715
|
+
existing = await readAuthStateMetadata(paths);
|
|
9716
|
+
} catch {
|
|
9717
|
+
existing = null;
|
|
9718
|
+
}
|
|
9719
|
+
if (existing && options.replace !== true) {
|
|
9720
|
+
throw new Error(`Auth state "${paths.stateName}" for target "${paths.targetName}" already exists. Use replace=true to replace it.`);
|
|
9721
|
+
}
|
|
9722
|
+
const payload = await context.storageState({ indexedDB: true });
|
|
9723
|
+
const metadata = {
|
|
9724
|
+
version: AUTH_STATE_SCHEMA_VERSION,
|
|
9725
|
+
kind: "web",
|
|
9726
|
+
target: paths.targetName,
|
|
9727
|
+
name: paths.stateName,
|
|
9728
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9729
|
+
};
|
|
9730
|
+
try {
|
|
9731
|
+
await writeAuthStateFiles(paths, { payload, metadata });
|
|
9732
|
+
} catch {
|
|
9733
|
+
throw new Error(`Could not save auth state "${paths.stateName}" for target "${paths.targetName}".`);
|
|
9734
|
+
}
|
|
9735
|
+
return metadata;
|
|
9736
|
+
}
|
|
9555
9737
|
cancelStep() {
|
|
9556
9738
|
this.abortController?.abort();
|
|
9557
9739
|
}
|
|
@@ -9612,6 +9794,7 @@ ${errorLines.join("\n")}`;
|
|
|
9612
9794
|
return {
|
|
9613
9795
|
sessionId: this.sessionId,
|
|
9614
9796
|
platform: this.adapter?.platform ?? this.platform,
|
|
9797
|
+
targetName: this.targetName,
|
|
9615
9798
|
status: this.status === "terminated" ? "idle" : this.status === "executing" ? "executing" : this.status,
|
|
9616
9799
|
currentStep: this.currentStep,
|
|
9617
9800
|
currentUrl: this.currentUrl,
|