@kontextmind/kxm 0.7.0 → 0.7.5
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/.claude-plugin/marketplace.json +1 -1
- package/.kxm/roles/writer.yaml +2 -0
- package/docs/README.md +3 -0
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
- package/docs/agent-skills.md +19 -2
- package/docs/browser-automation.md +116 -0
- package/docs/configuration.md +10 -1
- package/docs/getting-started.md +21 -0
- package/docs/kb/how-credentials-retrieved-safely.md +31 -0
- package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
- package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
- package/docs/kb/how-to-resume-after-mfa.md +28 -0
- package/docs/kb/how-to-take-over-session.md +32 -0
- package/docs/kb/why-authentication-disappeared.md +32 -0
- package/docs/kb/why-automation-opened-different-browser.md +32 -0
- package/docs/kb/why-session-viewer-cannot-control.md +31 -0
- package/docs/operations.md +24 -0
- package/docs/prompts/browser-annotate-feedback.md +41 -0
- package/docs/prompts/browser-diagnose-recover.md +38 -0
- package/docs/prompts/browser-explore.md +42 -0
- package/docs/prompts/browser-repro-fix.md +48 -0
- package/docs/prompts/browser-start.md +41 -0
- package/docs/prompts/browser-takeover.md +50 -0
- package/docs/skills/repo-work-delivery.md +5 -0
- package/docs/skills.md +2 -0
- package/docs/troubleshooting.md +22 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +2257 -439
- package/plugins/kxm/dist/core.js +57 -0
- package/plugins/kxm/dist/extension.js +40 -3
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +572 -17
- package/plugins/kxm/dist/server.js +129 -4
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +136 -13
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/hints.json +30 -0
- package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
- package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +9 -2
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/browser.ts +603 -0
- package/plugins/kxm/src/cli.ts +507 -9
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/external-effects.ts +1 -1
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/protocol.ts +111 -0
- package/plugins/kxm/src/role.ts +335 -0
- package/plugins/kxm/src/runtime.ts +1 -0
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/vnext-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +38 -1
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +16 -0
- package/plugins/kxm/src/vnext-harness.ts +4 -2
- package/plugins/kxm/src/vnext-oneshot-process.ts +21 -4
- package/plugins/kxm/src/vnext-oneshot-producer.ts +18 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +1 -1
- package/plugins/kxm/src/workflow-tui.ts +1 -1
- package/plugins/kxm/src/workflow.ts +144 -0
- package/scripts/kxm-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +145 -3
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -0
|
@@ -15122,6 +15122,19 @@ var OUTPUT_LIMIT = 8 * 1024 * 1024;
|
|
|
15122
15122
|
var KILL_GRACE_MS = 250;
|
|
15123
15123
|
var DRAIN_GRACE_MS = 500;
|
|
15124
15124
|
var REAP_GRACE_MS = 1e3;
|
|
15125
|
+
function killProcessTree(child, signal = "SIGKILL") {
|
|
15126
|
+
if (process.platform !== "win32" && child.pid) {
|
|
15127
|
+
try {
|
|
15128
|
+
process.kill(-child.pid, signal);
|
|
15129
|
+
return;
|
|
15130
|
+
} catch {
|
|
15131
|
+
}
|
|
15132
|
+
}
|
|
15133
|
+
try {
|
|
15134
|
+
child.kill(signal);
|
|
15135
|
+
} catch {
|
|
15136
|
+
}
|
|
15137
|
+
}
|
|
15125
15138
|
function defaultSpawn(command, args, options) {
|
|
15126
15139
|
if (options.signal?.aborted) {
|
|
15127
15140
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
@@ -15158,11 +15171,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15158
15171
|
resolve8({ stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), code, signal, started, observedChildExit, terminationRequested: stopping, ...error ? { error } : {} });
|
|
15159
15172
|
};
|
|
15160
15173
|
const kill = (requested) => {
|
|
15161
|
-
|
|
15162
|
-
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, requested);
|
|
15163
|
-
else child.kill(requested);
|
|
15164
|
-
} catch {
|
|
15165
|
-
}
|
|
15174
|
+
killProcessTree(child, requested);
|
|
15166
15175
|
};
|
|
15167
15176
|
const stop = (reason) => {
|
|
15168
15177
|
if (finished) return;
|
|
@@ -15489,7 +15498,9 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
15489
15498
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
15490
15499
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15491
15500
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15492
|
-
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"])
|
|
15501
|
+
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
15502
|
+
agy: Object.freeze(["--mode", "plan", "--sandbox", "--disable-slash-commands"]),
|
|
15503
|
+
kimi: Object.freeze(["--plan"])
|
|
15493
15504
|
});
|
|
15494
15505
|
function oneShotReadOnlyArgs(harness) {
|
|
15495
15506
|
return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
|
|
@@ -15543,7 +15554,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15543
15554
|
authArgs: ["provider", "list"],
|
|
15544
15555
|
update: { self: ["upgrade"] },
|
|
15545
15556
|
oneShot: {
|
|
15546
|
-
argv: ["--output-format", "stream-json", "-p"],
|
|
15557
|
+
argv: [...oneShotReadOnlyArgs("kimi"), "--output-format", "stream-json", "-p"],
|
|
15547
15558
|
promptVia: "arg",
|
|
15548
15559
|
outputFormat: "stream-json",
|
|
15549
15560
|
usageParser: parseKimiOneShotUsage
|
|
@@ -15606,7 +15617,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15606
15617
|
authArgs: ["models"],
|
|
15607
15618
|
update: { self: ["update"] },
|
|
15608
15619
|
oneShot: {
|
|
15609
|
-
argv: ["--output-format", "json", "-p"],
|
|
15620
|
+
argv: [...oneShotReadOnlyArgs("agy"), "--output-format", "json", "-p"],
|
|
15610
15621
|
promptVia: "arg",
|
|
15611
15622
|
outputFormat: "json",
|
|
15612
15623
|
usageParser: parseAgyOneShotUsage
|
|
@@ -17060,7 +17071,7 @@ function validateModelReferences(models, issues) {
|
|
|
17060
17071
|
};
|
|
17061
17072
|
for (const id of models.keys()) walk(id, []);
|
|
17062
17073
|
}
|
|
17063
|
-
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry) {
|
|
17074
|
+
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot) {
|
|
17064
17075
|
const issues = [];
|
|
17065
17076
|
const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
|
|
17066
17077
|
const repositoryIds = /* @__PURE__ */ new Set();
|
|
@@ -17128,6 +17139,33 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17128
17139
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
17129
17140
|
if (!workflows.has(defaultWorkflow)) issues.push(issue2("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
17130
17141
|
for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
|
|
17142
|
+
if (projectRoot) {
|
|
17143
|
+
const writerRolePath = join(projectRoot, ".kxm", "roles", "writer.yaml");
|
|
17144
|
+
const implementerAgent = agents.get("implementer") ?? agents.get("writer");
|
|
17145
|
+
if (existsSync2(writerRolePath) && implementerAgent) {
|
|
17146
|
+
try {
|
|
17147
|
+
const rawRole = parseRestrictedYaml2(readFileSync(writerRolePath, "utf8"));
|
|
17148
|
+
const roleObj = objectValue(rawRole);
|
|
17149
|
+
const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
|
|
17150
|
+
const enabledRosterModels = rosterEntries.filter((entry) => entry.enabled !== false).map((entry) => stringValue(entry.model)).filter((m) => Boolean(m));
|
|
17151
|
+
const agentModelObj = objectValue(implementerAgent.value.model);
|
|
17152
|
+
const agentModelStr = stringValue(implementerAgent.value.model);
|
|
17153
|
+
const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
|
|
17154
|
+
const agentModel = agentModelObj ? stringValue(agentModelObj.model) : agentModelStr;
|
|
17155
|
+
const canonicalAgentModel = agentProvider && agentModel ? `${agentProvider}/${agentModel}` : agentModel;
|
|
17156
|
+
if (enabledRosterModels.length > 0 && canonicalAgentModel) {
|
|
17157
|
+
const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
|
|
17158
|
+
if (!matches) {
|
|
17159
|
+
issues.push(issue2("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
|
|
17160
|
+
}
|
|
17161
|
+
}
|
|
17162
|
+
} catch (error) {
|
|
17163
|
+
if (error instanceof VnextConfigError) {
|
|
17164
|
+
issues.push(...error.issues);
|
|
17165
|
+
}
|
|
17166
|
+
}
|
|
17167
|
+
}
|
|
17168
|
+
}
|
|
17131
17169
|
return sortIssues2(issues);
|
|
17132
17170
|
}
|
|
17133
17171
|
function vnextCanonicalJson(value) {
|
|
@@ -17308,7 +17346,7 @@ function loadVnextProject(projectRoot, options = {}) {
|
|
|
17308
17346
|
}
|
|
17309
17347
|
}
|
|
17310
17348
|
if (loadIssues.length > 0) throw new VnextConfigError(loadIssues);
|
|
17311
|
-
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry);
|
|
17349
|
+
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root);
|
|
17312
17350
|
if (issues.length > 0) throw new VnextConfigError(issues);
|
|
17313
17351
|
const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits3(left.logicalPath, right.logicalPath));
|
|
17314
17352
|
return {
|
|
@@ -17420,7 +17458,47 @@ import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:pa
|
|
|
17420
17458
|
|
|
17421
17459
|
// plugins/kxm/src/vnext-bindings.ts
|
|
17422
17460
|
import { homedir } from "node:os";
|
|
17423
|
-
|
|
17461
|
+
|
|
17462
|
+
// plugins/kxm/src/sqlite.ts
|
|
17463
|
+
import { createRequire } from "node:module";
|
|
17464
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
17465
|
+
function loadNative() {
|
|
17466
|
+
try {
|
|
17467
|
+
const mod = requireFromHere("node:sqlite");
|
|
17468
|
+
if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
|
|
17469
|
+
} catch {
|
|
17470
|
+
}
|
|
17471
|
+
try {
|
|
17472
|
+
const mod = requireFromHere("bun:sqlite");
|
|
17473
|
+
const Ctor = mod?.DatabaseSync ?? mod?.Database;
|
|
17474
|
+
if (Ctor) return { Ctor, bun: true };
|
|
17475
|
+
} catch {
|
|
17476
|
+
}
|
|
17477
|
+
throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
|
|
17478
|
+
}
|
|
17479
|
+
var native = loadNative();
|
|
17480
|
+
var DatabaseSync = class {
|
|
17481
|
+
inner;
|
|
17482
|
+
constructor(path, options) {
|
|
17483
|
+
let normalized = options;
|
|
17484
|
+
if (native.bun && options) {
|
|
17485
|
+
const { readOnly, ...rest } = options;
|
|
17486
|
+
normalized = readOnly === void 0 ? rest : { ...rest, readonly: readOnly };
|
|
17487
|
+
}
|
|
17488
|
+
this.inner = normalized === void 0 ? new native.Ctor(path) : new native.Ctor(path, normalized);
|
|
17489
|
+
}
|
|
17490
|
+
prepare(sql) {
|
|
17491
|
+
return this.inner.prepare(sql);
|
|
17492
|
+
}
|
|
17493
|
+
exec(sql) {
|
|
17494
|
+
return this.inner.exec(sql);
|
|
17495
|
+
}
|
|
17496
|
+
close() {
|
|
17497
|
+
this.inner.close();
|
|
17498
|
+
}
|
|
17499
|
+
};
|
|
17500
|
+
|
|
17501
|
+
// plugins/kxm/src/vnext-bindings.ts
|
|
17424
17502
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
17425
17503
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
17426
17504
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
@@ -17464,7 +17542,6 @@ import {
|
|
|
17464
17542
|
writeFileSync
|
|
17465
17543
|
} from "node:fs";
|
|
17466
17544
|
import { basename as basename2, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
|
|
17467
|
-
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
17468
17545
|
function databaseError(code, file, message) {
|
|
17469
17546
|
const issue3 = { phase: "semantic", code, file, message };
|
|
17470
17547
|
return new VnextConfigError([issue3]);
|
|
@@ -17544,7 +17621,7 @@ function openDatabase(file, description, spec) {
|
|
|
17544
17621
|
}
|
|
17545
17622
|
}
|
|
17546
17623
|
}
|
|
17547
|
-
const database = new
|
|
17624
|
+
const database = new DatabaseSync(file);
|
|
17548
17625
|
let transaction = false;
|
|
17549
17626
|
try {
|
|
17550
17627
|
database.exec(`PRAGMA busy_timeout = ${spec.timeoutMs ?? 5e3}`);
|
|
@@ -17648,7 +17725,7 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
|
17648
17725
|
if (existsSync3(resolvedTarget)) {
|
|
17649
17726
|
unlinkSync(resolvedTarget);
|
|
17650
17727
|
}
|
|
17651
|
-
const sourceDb = new
|
|
17728
|
+
const sourceDb = new DatabaseSync(resolvedSource);
|
|
17652
17729
|
let schemaVersion = 0;
|
|
17653
17730
|
try {
|
|
17654
17731
|
sourceDb.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -17663,7 +17740,7 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
|
17663
17740
|
} finally {
|
|
17664
17741
|
sourceDb.close();
|
|
17665
17742
|
}
|
|
17666
|
-
const targetDb = new
|
|
17743
|
+
const targetDb = new DatabaseSync(resolvedTarget);
|
|
17667
17744
|
try {
|
|
17668
17745
|
targetDb.exec("PRAGMA busy_timeout = 5000");
|
|
17669
17746
|
if (!checkIntegrity(targetDb)) {
|
|
@@ -17695,7 +17772,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
17695
17772
|
if (!backupStat || !backupStat.isFile() || backupStat.isSymbolicLink()) {
|
|
17696
17773
|
throw databaseError("runtime_path_invalid", resolvedBackup, `backup database ${resolvedBackup} must be a regular file, not a link or directory`);
|
|
17697
17774
|
}
|
|
17698
|
-
const backupDb = new
|
|
17775
|
+
const backupDb = new DatabaseSync(resolvedBackup);
|
|
17699
17776
|
let schemaVersion = 0;
|
|
17700
17777
|
try {
|
|
17701
17778
|
backupDb.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -17735,7 +17812,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
17735
17812
|
chmodSync(resolvedTarget, 384);
|
|
17736
17813
|
} catch {
|
|
17737
17814
|
}
|
|
17738
|
-
const targetDb = new
|
|
17815
|
+
const targetDb = new DatabaseSync(resolvedTarget);
|
|
17739
17816
|
try {
|
|
17740
17817
|
targetDb.exec("PRAGMA busy_timeout = 5000");
|
|
17741
17818
|
if (!checkIntegrity(targetDb)) {
|
|
@@ -22912,6 +22989,32 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
22912
22989
|
// plugins/kxm/src/vnext-engine-command.ts
|
|
22913
22990
|
import { spawn as spawn2 } from "node:child_process";
|
|
22914
22991
|
import { createHash as createHash9 } from "node:crypto";
|
|
22992
|
+
|
|
22993
|
+
// plugins/kxm/src/safety-integrity.ts
|
|
22994
|
+
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
22995
|
+
/^\s*rm\s+.*-[a-zA-Z]*r[a-zA-Z]*f/i,
|
|
22996
|
+
/^\s*rm\s+.*-[a-zA-Z]*f[a-zA-Z]*r/i,
|
|
22997
|
+
/^\s*rm\s+.*(-[a-zA-Z]*r[a-zA-Z]*\s+.*-[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*\s+.*-[a-zA-Z]*r[a-zA-Z]*)/i,
|
|
22998
|
+
/^\s*git\s+reset\s+--hard/i,
|
|
22999
|
+
/^\s*git\s+clean\s+-[a-zA-Z]*f/i,
|
|
23000
|
+
/^\s*git\s+checkout\s+--\s+/i,
|
|
23001
|
+
/^\s*git\s+restore\s+(\.|\*|--staged\s+(\.|\*))/i
|
|
23002
|
+
]);
|
|
23003
|
+
function assertCommandSeatbelt(command) {
|
|
23004
|
+
for (const pattern of DESTRUCTIVE_COMMAND_PATTERNS) {
|
|
23005
|
+
if (pattern.test(command)) {
|
|
23006
|
+
throw new Error(
|
|
23007
|
+
`Command blocked by KXM safety seatbelt: "${command}". Destructive workspace mutations require explicit operator override.`
|
|
23008
|
+
);
|
|
23009
|
+
}
|
|
23010
|
+
}
|
|
23011
|
+
}
|
|
23012
|
+
var INSECURE_SSH_HOST_KEY_PATTERNS = Object.freeze([
|
|
23013
|
+
/StrictHostKeyChecking=(accept-new|no|off)/i,
|
|
23014
|
+
/UserKnownHostsFile=\/dev\/null/i
|
|
23015
|
+
]);
|
|
23016
|
+
|
|
23017
|
+
// plugins/kxm/src/vnext-engine-command.ts
|
|
22915
23018
|
var MAX_DIRECT_TIMER_MS = 2147483647;
|
|
22916
23019
|
var COMMAND_TERM_GRACE_MS = 2e3;
|
|
22917
23020
|
var COMMAND_FINAL_WAIT_MS = 2e3;
|
|
@@ -23028,6 +23131,7 @@ var CommandObserver = class {
|
|
|
23028
23131
|
if (!Number.isInteger(this.definition.timeoutMs) || this.definition.timeoutMs < 1 || this.definition.timeoutMs > MAX_DIRECT_TIMER_MS) {
|
|
23029
23132
|
throw runtimeError("run_events_illegal", "command", "command timeoutMs exceeds the direct timer bound");
|
|
23030
23133
|
}
|
|
23134
|
+
assertCommandSeatbelt(this.definition.argv.join(" "));
|
|
23031
23135
|
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23032
23136
|
let child;
|
|
23033
23137
|
try {
|
|
@@ -24153,6 +24257,7 @@ async function stepLocked(context, runId, producer, token) {
|
|
|
24153
24257
|
return drivePanel(context, prepared.panel, producer);
|
|
24154
24258
|
}
|
|
24155
24259
|
async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
24260
|
+
assertCommandSeatbelt(definition.argv.join(" "));
|
|
24156
24261
|
const storePath = context.eventStore.path;
|
|
24157
24262
|
const observer = createCommandObserver({
|
|
24158
24263
|
definition,
|
|
@@ -25930,6 +26035,24 @@ function createVnextOneShotProducer(options = {}) {
|
|
|
25930
26035
|
"--output-format",
|
|
25931
26036
|
"json"
|
|
25932
26037
|
];
|
|
26038
|
+
} else if (harness === "agy") {
|
|
26039
|
+
args = [
|
|
26040
|
+
"-m",
|
|
26041
|
+
resolved.model,
|
|
26042
|
+
...permissionArgs,
|
|
26043
|
+
"--output-format",
|
|
26044
|
+
"json",
|
|
26045
|
+
"-p"
|
|
26046
|
+
];
|
|
26047
|
+
} else if (harness === "kimi") {
|
|
26048
|
+
args = [
|
|
26049
|
+
"-m",
|
|
26050
|
+
resolved.model,
|
|
26051
|
+
...permissionArgs,
|
|
26052
|
+
"--output-format",
|
|
26053
|
+
"stream-json",
|
|
26054
|
+
"-p"
|
|
26055
|
+
];
|
|
25933
26056
|
} else {
|
|
25934
26057
|
throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
|
|
25935
26058
|
}
|
|
@@ -27627,6 +27750,429 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options) {
|
|
|
27627
27750
|
reason: `Unknown promotion policy: ${String(policy)}`
|
|
27628
27751
|
};
|
|
27629
27752
|
}
|
|
27753
|
+
|
|
27754
|
+
// plugins/kxm/src/browser.ts
|
|
27755
|
+
import { execSync } from "node:child_process";
|
|
27756
|
+
var VIEWPORT_PRESETS = Object.freeze({
|
|
27757
|
+
"mobile-sm": { name: "Mobile Small (SE)", category: "mobile", width: 375, height: 667, isMobile: true, hasTouch: true },
|
|
27758
|
+
"mobile": { name: "Mobile (iPhone 16 / 15 Pro)", category: "mobile", width: 393, height: 852, isMobile: true, hasTouch: true },
|
|
27759
|
+
"mobile-lg": { name: "Mobile Large (Pro Max)", category: "mobile", width: 430, height: 932, isMobile: true, hasTouch: true },
|
|
27760
|
+
"pixel": { name: "Google Pixel 8/9", category: "mobile", width: 412, height: 924, isMobile: true, hasTouch: true },
|
|
27761
|
+
"galaxy": { name: "Samsung Galaxy S24", category: "mobile", width: 360, height: 780, isMobile: true, hasTouch: true },
|
|
27762
|
+
"tablet": { name: "Tablet (iPad Air / Mini)", category: "tablet", width: 820, height: 1180, isMobile: true, hasTouch: true },
|
|
27763
|
+
"tablet-lg": { name: "Tablet Large (iPad Pro 12.9)", category: "tablet", width: 1024, height: 1366, isMobile: true, hasTouch: true },
|
|
27764
|
+
"laptop": { name: "Standard Laptop", category: "laptop", width: 1366, height: 768 },
|
|
27765
|
+
"macbook-13": { name: "MacBook Air 13", category: "laptop", width: 1440, height: 900 },
|
|
27766
|
+
"macbook-16": { name: "MacBook Pro 16", category: "laptop", width: 1728, height: 1117 },
|
|
27767
|
+
"desktop": { name: "Desktop FHD (1080p)", category: "desktop", width: 1920, height: 1080 },
|
|
27768
|
+
"desktop-2k": { name: "Desktop QHD (1440p)", category: "desktop", width: 2560, height: 1440 },
|
|
27769
|
+
"desktop-4k": { name: "Desktop 4K UHD", category: "desktop", width: 3840, height: 2160 }
|
|
27770
|
+
});
|
|
27771
|
+
function resolveViewportDimensions(presetOrDims) {
|
|
27772
|
+
if (!presetOrDims) {
|
|
27773
|
+
return { width: 1920, height: 1080 };
|
|
27774
|
+
}
|
|
27775
|
+
if (typeof presetOrDims === "string") {
|
|
27776
|
+
const matched = VIEWPORT_PRESETS[presetOrDims.toLowerCase()];
|
|
27777
|
+
if (matched) {
|
|
27778
|
+
return { width: matched.width, height: matched.height };
|
|
27779
|
+
}
|
|
27780
|
+
return { width: 1920, height: 1080 };
|
|
27781
|
+
}
|
|
27782
|
+
return presetOrDims;
|
|
27783
|
+
}
|
|
27784
|
+
function resolvePassCliApiKey(execFn = (cmd) => execSync(cmd, { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 5e3 })) {
|
|
27785
|
+
if (typeof process === "undefined" || process.env.USE_PASS_CLI === "false") {
|
|
27786
|
+
return void 0;
|
|
27787
|
+
}
|
|
27788
|
+
if (process.env.PASS_CLI_OUTPUT_MOCK) {
|
|
27789
|
+
try {
|
|
27790
|
+
const parsed = JSON.parse(process.env.PASS_CLI_OUTPUT_MOCK);
|
|
27791
|
+
const extraFields = parsed?.item?.content?.extra_fields || [];
|
|
27792
|
+
const customSections = parsed?.item?.content?.content?.Custom?.sections || [];
|
|
27793
|
+
const sectionFields = customSections.flatMap((s) => s.section_fields || []);
|
|
27794
|
+
const allFields = [...extraFields, ...sectionFields];
|
|
27795
|
+
return allFields.find((f) => f.name === "STEEL_API_KEY")?.content?.Hidden;
|
|
27796
|
+
} catch {
|
|
27797
|
+
return void 0;
|
|
27798
|
+
}
|
|
27799
|
+
}
|
|
27800
|
+
try {
|
|
27801
|
+
const output = execFn(
|
|
27802
|
+
'pass-cli item view --vault-name "AI Provider Keys" --item-title "Steel Browser (KontextMind DOKS)" --output json'
|
|
27803
|
+
);
|
|
27804
|
+
const parsed = JSON.parse(output);
|
|
27805
|
+
const extraFields = parsed?.item?.content?.extra_fields || [];
|
|
27806
|
+
const customSections = parsed?.item?.content?.content?.Custom?.sections || [];
|
|
27807
|
+
const sectionFields = customSections.flatMap((s) => s.section_fields || []);
|
|
27808
|
+
const allFields = [...extraFields, ...sectionFields];
|
|
27809
|
+
return allFields.find((f) => f.name === "STEEL_API_KEY")?.content?.Hidden;
|
|
27810
|
+
} catch {
|
|
27811
|
+
return void 0;
|
|
27812
|
+
}
|
|
27813
|
+
}
|
|
27814
|
+
function resolveSteelConfig(overrides) {
|
|
27815
|
+
const apiUrl = overrides?.apiUrl || process.env.STEEL_API_URL || "https://steel.kontextmind.com";
|
|
27816
|
+
const apiKey = overrides?.apiKey || process.env.STEEL_API_KEY || resolvePassCliApiKey();
|
|
27817
|
+
const uiUrl = overrides?.uiUrl || (overrides?.apiUrl ? `${overrides.apiUrl.replace(/\/$/, "")}/ui` : void 0) || process.env.STEEL_UI_URL || `${apiUrl.replace(/\/$/, "")}/ui`;
|
|
27818
|
+
return {
|
|
27819
|
+
apiUrl: apiUrl.replace(/\/$/, ""),
|
|
27820
|
+
apiKey,
|
|
27821
|
+
uiUrl,
|
|
27822
|
+
timeoutMs: overrides?.timeoutMs || 3e5
|
|
27823
|
+
// 5 minutes default
|
|
27824
|
+
};
|
|
27825
|
+
}
|
|
27826
|
+
function formatCDPEndpoint(session, config) {
|
|
27827
|
+
const baseApi = config.apiUrl;
|
|
27828
|
+
const urlObj = new URL(baseApi);
|
|
27829
|
+
const isSecure = urlObj.protocol === "https:";
|
|
27830
|
+
const wsProtocol = isSecure ? "wss:" : "ws:";
|
|
27831
|
+
const host = urlObj.host;
|
|
27832
|
+
const searchParams = new URLSearchParams();
|
|
27833
|
+
searchParams.set("sessionId", session.id);
|
|
27834
|
+
if (config.apiKey) {
|
|
27835
|
+
searchParams.set("apiKey", config.apiKey);
|
|
27836
|
+
}
|
|
27837
|
+
return `${wsProtocol}//${host}/v1/devtools?${searchParams.toString()}`;
|
|
27838
|
+
}
|
|
27839
|
+
function sanitizeLogOutput(input) {
|
|
27840
|
+
if (typeof input === "string") {
|
|
27841
|
+
return input.replace(/apiKey=[^&]+/g, "apiKey=[REDACTED]").replace(/steel_[a-f0-9]+/g, "steel_[REDACTED]");
|
|
27842
|
+
}
|
|
27843
|
+
if (Array.isArray(input)) {
|
|
27844
|
+
return input.map(sanitizeLogOutput);
|
|
27845
|
+
}
|
|
27846
|
+
if (input !== null && typeof input === "object") {
|
|
27847
|
+
const copy = {};
|
|
27848
|
+
for (const [k, v] of Object.entries(input)) {
|
|
27849
|
+
if (/key|secret|token|auth|password/i.test(k) && typeof v === "string") {
|
|
27850
|
+
copy[k] = "[REDACTED]";
|
|
27851
|
+
} else {
|
|
27852
|
+
copy[k] = sanitizeLogOutput(v);
|
|
27853
|
+
}
|
|
27854
|
+
}
|
|
27855
|
+
return copy;
|
|
27856
|
+
}
|
|
27857
|
+
return input;
|
|
27858
|
+
}
|
|
27859
|
+
function createAnnotationFeedback(feedback) {
|
|
27860
|
+
return {
|
|
27861
|
+
...feedback,
|
|
27862
|
+
capturedAt: feedback.capturedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
27863
|
+
};
|
|
27864
|
+
}
|
|
27865
|
+
function formatAnnotationFeedbackPrompt(feedback) {
|
|
27866
|
+
let output = `## Visual Feedback & Section Annotation
|
|
27867
|
+
|
|
27868
|
+
`;
|
|
27869
|
+
output += `- **Target URL**: ${feedback.url}
|
|
27870
|
+
`;
|
|
27871
|
+
if (feedback.sessionId) {
|
|
27872
|
+
output += `- **Session ID**: ${feedback.sessionId}
|
|
27873
|
+
`;
|
|
27874
|
+
}
|
|
27875
|
+
if (feedback.sectionSelector) {
|
|
27876
|
+
output += `- **Section Target Selector**: \`${feedback.sectionSelector}\`
|
|
27877
|
+
`;
|
|
27878
|
+
}
|
|
27879
|
+
if (feedback.screenshotPath) {
|
|
27880
|
+
output += `- **Screenshot Artifact**: \`${feedback.screenshotPath}\`
|
|
27881
|
+
`;
|
|
27882
|
+
}
|
|
27883
|
+
output += `- **Captured At**: ${feedback.capturedAt}
|
|
27884
|
+
|
|
27885
|
+
`;
|
|
27886
|
+
output += `### Summary
|
|
27887
|
+
${feedback.overallSummary}
|
|
27888
|
+
|
|
27889
|
+
`;
|
|
27890
|
+
if (feedback.annotations.length > 0) {
|
|
27891
|
+
output += `### Annotated Elements & Notes
|
|
27892
|
+
|
|
27893
|
+
`;
|
|
27894
|
+
feedback.annotations.forEach((item, idx) => {
|
|
27895
|
+
output += `${idx + 1}. **${item.label}**`;
|
|
27896
|
+
if (item.severity) {
|
|
27897
|
+
output += ` [${item.severity.toUpperCase()}]`;
|
|
27898
|
+
}
|
|
27899
|
+
output += `
|
|
27900
|
+
- **Note**: ${item.note}
|
|
27901
|
+
`;
|
|
27902
|
+
if (item.selector) {
|
|
27903
|
+
output += ` - **Selector**: \`${item.selector}\`
|
|
27904
|
+
`;
|
|
27905
|
+
}
|
|
27906
|
+
if (item.boundingBox) {
|
|
27907
|
+
output += ` - **Region (Box)**: x=${item.boundingBox.x}, y=${item.boundingBox.y}, w=${item.boundingBox.width}, h=${item.boundingBox.height}
|
|
27908
|
+
`;
|
|
27909
|
+
}
|
|
27910
|
+
});
|
|
27911
|
+
output += `
|
|
27912
|
+
`;
|
|
27913
|
+
}
|
|
27914
|
+
if (feedback.requestedChanges.length > 0) {
|
|
27915
|
+
output += `### Actionable Change List
|
|
27916
|
+
|
|
27917
|
+
`;
|
|
27918
|
+
feedback.requestedChanges.forEach((change, idx) => {
|
|
27919
|
+
output += `- [ ] ${change}
|
|
27920
|
+
`;
|
|
27921
|
+
});
|
|
27922
|
+
}
|
|
27923
|
+
return output;
|
|
27924
|
+
}
|
|
27925
|
+
var SteelClient = class {
|
|
27926
|
+
config;
|
|
27927
|
+
activeSessions = /* @__PURE__ */ new Map();
|
|
27928
|
+
constructor(config) {
|
|
27929
|
+
this.config = resolveSteelConfig(config);
|
|
27930
|
+
}
|
|
27931
|
+
getConfig() {
|
|
27932
|
+
return { ...this.config };
|
|
27933
|
+
}
|
|
27934
|
+
headers() {
|
|
27935
|
+
const h = {
|
|
27936
|
+
"Content-Type": "application/json"
|
|
27937
|
+
};
|
|
27938
|
+
if (this.config.apiKey) {
|
|
27939
|
+
h["x-steel-api-key"] = this.config.apiKey;
|
|
27940
|
+
}
|
|
27941
|
+
return h;
|
|
27942
|
+
}
|
|
27943
|
+
/**
|
|
27944
|
+
* Launch a new Steel browser session on DOKS.
|
|
27945
|
+
*/
|
|
27946
|
+
async createSession(options) {
|
|
27947
|
+
const timeoutMs = options?.timeoutMs ?? this.config.timeoutMs ?? 3e5;
|
|
27948
|
+
const body = {
|
|
27949
|
+
timeout: timeoutMs
|
|
27950
|
+
};
|
|
27951
|
+
const dimensions = options?.dimensions || (options?.viewportPreset ? resolveViewportDimensions(options.viewportPreset) : void 0);
|
|
27952
|
+
if (dimensions) {
|
|
27953
|
+
body.dimensions = dimensions;
|
|
27954
|
+
}
|
|
27955
|
+
if (options?.userAgent) {
|
|
27956
|
+
body.userAgent = options.userAgent;
|
|
27957
|
+
}
|
|
27958
|
+
if (options?.proxy) {
|
|
27959
|
+
body.proxy = options.proxy;
|
|
27960
|
+
}
|
|
27961
|
+
const res = await fetch(`${this.config.apiUrl}/v1/sessions`, {
|
|
27962
|
+
method: "POST",
|
|
27963
|
+
headers: this.headers(),
|
|
27964
|
+
body: JSON.stringify(body)
|
|
27965
|
+
});
|
|
27966
|
+
if (!res.ok) {
|
|
27967
|
+
const errText = await res.text();
|
|
27968
|
+
throw new Error(`Failed to create Steel session (${res.status}): ${errText}`);
|
|
27969
|
+
}
|
|
27970
|
+
const data = await res.json();
|
|
27971
|
+
const session = {
|
|
27972
|
+
id: data.id,
|
|
27973
|
+
createdAt: data.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
27974
|
+
status: "live",
|
|
27975
|
+
state: "AGENT_CONTROL",
|
|
27976
|
+
websocketUrl: data.websocketUrl || "",
|
|
27977
|
+
debugUrl: data.debugUrl || "",
|
|
27978
|
+
debuggerUrl: data.debuggerUrl || "",
|
|
27979
|
+
sessionViewerUrl: data.sessionViewerUrl || `${this.config.uiUrl}?sessionId=${data.id}`,
|
|
27980
|
+
timeoutMs,
|
|
27981
|
+
lastActiveAt: Date.now(),
|
|
27982
|
+
activeController: "agent",
|
|
27983
|
+
userAgent: data.userAgent
|
|
27984
|
+
};
|
|
27985
|
+
this.activeSessions.set(session.id, session);
|
|
27986
|
+
return session;
|
|
27987
|
+
}
|
|
27988
|
+
/**
|
|
27989
|
+
* Get details of an existing session.
|
|
27990
|
+
*/
|
|
27991
|
+
async getSession(sessionId) {
|
|
27992
|
+
const res = await fetch(`${this.config.apiUrl}/v1/sessions/${encodeURIComponent(sessionId)}`, {
|
|
27993
|
+
method: "GET",
|
|
27994
|
+
headers: this.headers()
|
|
27995
|
+
});
|
|
27996
|
+
if (res.status === 404) {
|
|
27997
|
+
const cached = this.activeSessions.get(sessionId);
|
|
27998
|
+
if (cached) {
|
|
27999
|
+
cached.state = "EXPIRED";
|
|
28000
|
+
cached.status = "released";
|
|
28001
|
+
cached.activeController = "none";
|
|
28002
|
+
}
|
|
28003
|
+
return null;
|
|
28004
|
+
}
|
|
28005
|
+
if (!res.ok) {
|
|
28006
|
+
throw new Error(`Failed to fetch Steel session (${res.status})`);
|
|
28007
|
+
}
|
|
28008
|
+
const data = await res.json();
|
|
28009
|
+
const existing = this.activeSessions.get(sessionId);
|
|
28010
|
+
const session = {
|
|
28011
|
+
id: data.id,
|
|
28012
|
+
createdAt: data.createdAt,
|
|
28013
|
+
status: data.status,
|
|
28014
|
+
state: existing?.state ?? (data.status === "live" ? "AGENT_CONTROL" : "RELEASED"),
|
|
28015
|
+
websocketUrl: data.websocketUrl || existing?.websocketUrl || "",
|
|
28016
|
+
debugUrl: data.debugUrl || existing?.debugUrl || "",
|
|
28017
|
+
debuggerUrl: data.debuggerUrl || existing?.debuggerUrl || "",
|
|
28018
|
+
sessionViewerUrl: existing?.sessionViewerUrl || `${this.config.uiUrl}?sessionId=${data.id}`,
|
|
28019
|
+
timeoutMs: data.timeout || existing?.timeoutMs || 3e5,
|
|
28020
|
+
lastActiveAt: Date.now(),
|
|
28021
|
+
activeController: existing?.activeController ?? (data.status === "live" ? "agent" : "none"),
|
|
28022
|
+
userAgent: data.userAgent
|
|
28023
|
+
};
|
|
28024
|
+
this.activeSessions.set(session.id, session);
|
|
28025
|
+
return session;
|
|
28026
|
+
}
|
|
28027
|
+
/**
|
|
28028
|
+
* Request human takeover for MFA, login, or consent.
|
|
28029
|
+
* Pauses agent automation and sets state to HUMAN_CONTROL.
|
|
28030
|
+
*/
|
|
28031
|
+
requestHumanTakeover(sessionId, reason) {
|
|
28032
|
+
const session = this.activeSessions.get(sessionId);
|
|
28033
|
+
if (!session) {
|
|
28034
|
+
throw new Error(`Session ${sessionId} not tracked or already released`);
|
|
28035
|
+
}
|
|
28036
|
+
if (session.state === "RELEASED" || session.state === "EXPIRED" || session.state === "FAILED") {
|
|
28037
|
+
throw new Error(`Cannot initiate takeover on session in state ${session.state}`);
|
|
28038
|
+
}
|
|
28039
|
+
session.state = "HUMAN_CONTROL";
|
|
28040
|
+
session.activeController = "human";
|
|
28041
|
+
session.takeoverReason = reason;
|
|
28042
|
+
session.lastActiveAt = Date.now();
|
|
28043
|
+
const takeoverUrl = `${this.config.uiUrl}?sessionId=${encodeURIComponent(sessionId)}`;
|
|
28044
|
+
const instructions = `[HUMAN TAKEOVER REQUIRED]
|
|
28045
|
+
Reason: ${reason}
|
|
28046
|
+
Session ID: ${session.id}
|
|
28047
|
+
Takeover URL: ${takeoverUrl}
|
|
28048
|
+
|
|
28049
|
+
Instructions for Operator:
|
|
28050
|
+
1. Open the URL above to access the session UI.
|
|
28051
|
+
2. Perform the required authentication / MFA / consent action.
|
|
28052
|
+
3. Return to the terminal and signal completion. Automation is paused until you confirm.`;
|
|
28053
|
+
return {
|
|
28054
|
+
session,
|
|
28055
|
+
takeoverUrl,
|
|
28056
|
+
instructions
|
|
28057
|
+
};
|
|
28058
|
+
}
|
|
28059
|
+
/**
|
|
28060
|
+
* Signal that human takeover is complete.
|
|
28061
|
+
* Moves state to VERIFY_AUTHENTICATION before transitioning back to AGENT_CONTROL.
|
|
28062
|
+
*/
|
|
28063
|
+
signalHumanComplete(sessionId) {
|
|
28064
|
+
const session = this.activeSessions.get(sessionId);
|
|
28065
|
+
if (!session) {
|
|
28066
|
+
throw new Error(`Session ${sessionId} not tracked or already released`);
|
|
28067
|
+
}
|
|
28068
|
+
if (session.state !== "HUMAN_CONTROL") {
|
|
28069
|
+
throw new Error(`Session ${sessionId} is not in HUMAN_CONTROL state (currently ${session.state})`);
|
|
28070
|
+
}
|
|
28071
|
+
session.state = "VERIFY_AUTHENTICATION";
|
|
28072
|
+
session.activeController = "agent";
|
|
28073
|
+
session.lastActiveAt = Date.now();
|
|
28074
|
+
return {
|
|
28075
|
+
session,
|
|
28076
|
+
state: session.state
|
|
28077
|
+
};
|
|
28078
|
+
}
|
|
28079
|
+
/**
|
|
28080
|
+
* Confirm authentication verification passed and restore AGENT_CONTROL.
|
|
28081
|
+
*/
|
|
28082
|
+
confirmAuthenticationVerified(sessionId) {
|
|
28083
|
+
const session = this.activeSessions.get(sessionId);
|
|
28084
|
+
if (!session) {
|
|
28085
|
+
throw new Error(`Session ${sessionId} not tracked or already released`);
|
|
28086
|
+
}
|
|
28087
|
+
if (session.state !== "VERIFY_AUTHENTICATION") {
|
|
28088
|
+
throw new Error(`Session ${sessionId} is not in VERIFY_AUTHENTICATION state (currently ${session.state})`);
|
|
28089
|
+
}
|
|
28090
|
+
session.state = "AGENT_CONTROL";
|
|
28091
|
+
session.activeController = "agent";
|
|
28092
|
+
session.takeoverReason = void 0;
|
|
28093
|
+
session.lastActiveAt = Date.now();
|
|
28094
|
+
return session;
|
|
28095
|
+
}
|
|
28096
|
+
/**
|
|
28097
|
+
* Gracefully release a session.
|
|
28098
|
+
*/
|
|
28099
|
+
async releaseSession(sessionId) {
|
|
28100
|
+
try {
|
|
28101
|
+
const res = await fetch(`${this.config.apiUrl}/v1/sessions/${encodeURIComponent(sessionId)}/release`, {
|
|
28102
|
+
method: "POST",
|
|
28103
|
+
headers: this.headers()
|
|
28104
|
+
});
|
|
28105
|
+
const session = this.activeSessions.get(sessionId);
|
|
28106
|
+
if (session) {
|
|
28107
|
+
session.status = "released";
|
|
28108
|
+
session.state = "RELEASED";
|
|
28109
|
+
session.activeController = "none";
|
|
28110
|
+
}
|
|
28111
|
+
this.activeSessions.delete(sessionId);
|
|
28112
|
+
return res.ok;
|
|
28113
|
+
} catch {
|
|
28114
|
+
this.activeSessions.delete(sessionId);
|
|
28115
|
+
return false;
|
|
28116
|
+
}
|
|
28117
|
+
}
|
|
28118
|
+
/**
|
|
28119
|
+
* Perform a direct stateless scrape without manual session management.
|
|
28120
|
+
*/
|
|
28121
|
+
async scrape(url) {
|
|
28122
|
+
const res = await fetch(`${this.config.apiUrl}/v1/scrape`, {
|
|
28123
|
+
method: "POST",
|
|
28124
|
+
headers: this.headers(),
|
|
28125
|
+
body: JSON.stringify({ url })
|
|
28126
|
+
});
|
|
28127
|
+
if (!res.ok) {
|
|
28128
|
+
const err = await res.text();
|
|
28129
|
+
throw new Error(`Scrape failed (${res.status}): ${err}`);
|
|
28130
|
+
}
|
|
28131
|
+
return res.json();
|
|
28132
|
+
}
|
|
28133
|
+
/**
|
|
28134
|
+
* Perform a direct screenshot action.
|
|
28135
|
+
*/
|
|
28136
|
+
async screenshot(url, fullPage = false) {
|
|
28137
|
+
const res = await fetch(`${this.config.apiUrl}/v1/screenshot`, {
|
|
28138
|
+
method: "POST",
|
|
28139
|
+
headers: this.headers(),
|
|
28140
|
+
body: JSON.stringify({ url, fullPage })
|
|
28141
|
+
});
|
|
28142
|
+
if (!res.ok) {
|
|
28143
|
+
const err = await res.text();
|
|
28144
|
+
throw new Error(`Screenshot failed (${res.status}): ${err}`);
|
|
28145
|
+
}
|
|
28146
|
+
return res.json();
|
|
28147
|
+
}
|
|
28148
|
+
/**
|
|
28149
|
+
* Detect and list orphaned or timed-out active sessions.
|
|
28150
|
+
*/
|
|
28151
|
+
async checkOrphanedSessions(maxIdleMs = 6e5) {
|
|
28152
|
+
const res = await fetch(`${this.config.apiUrl}/v1/sessions`, {
|
|
28153
|
+
method: "GET",
|
|
28154
|
+
headers: this.headers()
|
|
28155
|
+
});
|
|
28156
|
+
if (!res.ok) {
|
|
28157
|
+
return [];
|
|
28158
|
+
}
|
|
28159
|
+
const data = await res.json();
|
|
28160
|
+
const remoteSessions = data.sessions || [];
|
|
28161
|
+
const now = Date.now();
|
|
28162
|
+
const orphaned = [];
|
|
28163
|
+
for (const rs of remoteSessions) {
|
|
28164
|
+
if (rs.status === "live" || rs.status === "idle") {
|
|
28165
|
+
const tracked = this.activeSessions.get(rs.id);
|
|
28166
|
+
if (!tracked && rs.duration > maxIdleMs) {
|
|
28167
|
+
orphaned.push(rs.id);
|
|
28168
|
+
} else if (tracked && now - tracked.lastActiveAt > maxIdleMs && tracked.state !== "HUMAN_CONTROL") {
|
|
28169
|
+
orphaned.push(rs.id);
|
|
28170
|
+
}
|
|
28171
|
+
}
|
|
28172
|
+
}
|
|
28173
|
+
return orphaned;
|
|
28174
|
+
}
|
|
28175
|
+
};
|
|
27630
28176
|
export {
|
|
27631
28177
|
BUILTIN_HARNESSES,
|
|
27632
28178
|
BUILTIN_HARNESS_IDS,
|
|
@@ -27642,6 +28188,8 @@ export {
|
|
|
27642
28188
|
PI_NATIVE_BRAKE_PROVIDERS,
|
|
27643
28189
|
PiSession,
|
|
27644
28190
|
SAFE_HARNESS_COMMAND_ID,
|
|
28191
|
+
SteelClient,
|
|
28192
|
+
VIEWPORT_PRESETS,
|
|
27645
28193
|
VNEXT_ABSENT_MEMORY_REVISION,
|
|
27646
28194
|
VNEXT_EVENT_STORE_SCHEMA_VERSION,
|
|
27647
28195
|
VNEXT_REGISTRY_SCHEMA_VERSION,
|
|
@@ -27663,6 +28211,7 @@ export {
|
|
|
27663
28211
|
closeVnextRuntimeContext,
|
|
27664
28212
|
computeGateEvidenceOutcome,
|
|
27665
28213
|
computeVnextMemoryRevision,
|
|
28214
|
+
createAnnotationFeedback,
|
|
27666
28215
|
createBackup,
|
|
27667
28216
|
createLogger,
|
|
27668
28217
|
createVnextOneShotProducer,
|
|
@@ -27677,6 +28226,8 @@ export {
|
|
|
27677
28226
|
fileSha256,
|
|
27678
28227
|
findWinNpmInnerExe,
|
|
27679
28228
|
foldStoredVnextRun,
|
|
28229
|
+
formatAnnotationFeedbackPrompt,
|
|
28230
|
+
formatCDPEndpoint,
|
|
27680
28231
|
formatHarnessInventory,
|
|
27681
28232
|
formatHarnessUpdate,
|
|
27682
28233
|
formatImprovementReport,
|
|
@@ -27721,11 +28272,15 @@ export {
|
|
|
27721
28272
|
redactLogValue,
|
|
27722
28273
|
registerVnextRuntimeCloseHook,
|
|
27723
28274
|
resolveDispatchStatus,
|
|
28275
|
+
resolvePassCliApiKey,
|
|
28276
|
+
resolveSteelConfig,
|
|
28277
|
+
resolveViewportDimensions,
|
|
27724
28278
|
restoreBackup,
|
|
27725
28279
|
restoreDatabaseFile,
|
|
27726
28280
|
rotateLogFiles,
|
|
27727
28281
|
runHarnessUpdate,
|
|
27728
28282
|
runtimeError,
|
|
28283
|
+
sanitizeLogOutput,
|
|
27729
28284
|
startVnextRuntimeSupervisor,
|
|
27730
28285
|
tableColumns,
|
|
27731
28286
|
userTables,
|