witnora 0.20.9 → 0.20.11
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/cli.js +14 -3
- package/dist/command-help.js +1 -0
- package/dist/gateway-exec.js +22 -3
- package/dist/gateway-service.js +11 -3
- package/dist/gateway.js +13 -6
- package/dist/onboard.js +23 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40,8 +40,8 @@ import { runOnboard } from "./onboard.js";
|
|
|
40
40
|
import { inspectRepository } from "./onboard.js";
|
|
41
41
|
import { renderReleaseEvaluation, runReleaseEvaluation } from "./release-evaluation.js";
|
|
42
42
|
import { runPrivateCapabilityDiscovery } from "./private-discovery.js";
|
|
43
|
-
import { configureManagedWorkflowHarness, doctorCustomerGateway, initializeCustomerGateway, isGatewayDoctorReady, readManagedCustomerGatewayLogs, renderGatewayDoctor, renderManagedGatewayStatus,
|
|
44
|
-
import { installCurrentGatewayService, stopCurrentGatewayOwners, uninstallCurrentGatewayService } from "./gateway-service.js";
|
|
43
|
+
import { awaitManagedCustomerGateway, configureManagedWorkflowHarness, doctorCustomerGateway, initializeCustomerGateway, isGatewayDoctorReady, readManagedCustomerGatewayLogs, renderGatewayDoctor, renderManagedGatewayStatus, runCustomerGateway, startManagedCustomerGateway, statusManagedCustomerGateway, stopManagedCustomerGateway, superviseManagedCustomerGateway, } from "./gateway.js";
|
|
44
|
+
import { installCurrentGatewayService, restartCurrentGatewayOwners, stopCurrentGatewayOwners, uninstallCurrentGatewayService } from "./gateway-service.js";
|
|
45
45
|
import { runCustomerGatewayCommand } from "./gateway-exec.js";
|
|
46
46
|
import { verifyEvidencePacketV02 } from "./evidence-v02.js";
|
|
47
47
|
process.on("uncaughtException", reportFatalError);
|
|
@@ -162,6 +162,7 @@ else if (command === "onboard") {
|
|
|
162
162
|
actionTransport: readActionTransport(readFlag("--action-transport")),
|
|
163
163
|
waitForActionPathMs: readActionTransport(readFlag("--action-transport")) && !readBoolFlag("--no-wait-for-action-path") ? 10 * 60 * 1_000 : 0,
|
|
164
164
|
openBrowser: readBoolFlag("--no-browser") ? async () => { } : undefined,
|
|
165
|
+
confirmRunDetectedAgent: readBoolFlag("--no-run-agent") ? undefined : confirmDetectedAgentRun,
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
168
|
else if (command === "mcp") {
|
|
@@ -233,7 +234,11 @@ else if (command === "gateway") {
|
|
|
233
234
|
process.stdout.write(`${await readManagedCustomerGatewayLogs({ repository: readFlag("--repo") ?? process.cwd(), dir: readFlag("--dir"), lines })}\n`);
|
|
234
235
|
}
|
|
235
236
|
else if (action === "restart") {
|
|
236
|
-
const
|
|
237
|
+
const repository = readFlag("--repo") ?? process.cwd();
|
|
238
|
+
const dir = readFlag("--dir");
|
|
239
|
+
const configHome = readFlag("--config-home");
|
|
240
|
+
const serviceOptions = { repository, gatewayDirectory: dir, cliEntry: fileURLToPath(import.meta.url), configHome };
|
|
241
|
+
const result = await restartCurrentGatewayOwners(serviceOptions, () => stopManagedCustomerGateway({ repository, dir, configHome }), () => awaitManagedCustomerGateway({ repository, dir, configHome }));
|
|
237
242
|
process.stdout.write(readBoolFlag("--json") ? `${JSON.stringify(result, null, 2)}\n` : renderManagedGatewayStatus(result));
|
|
238
243
|
}
|
|
239
244
|
else if (action === "repair") {
|
|
@@ -1045,6 +1050,12 @@ async function promptValue(label) {
|
|
|
1045
1050
|
prompt.close();
|
|
1046
1051
|
}
|
|
1047
1052
|
}
|
|
1053
|
+
async function confirmDetectedAgentRun(command) {
|
|
1054
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
1055
|
+
return false;
|
|
1056
|
+
const answer = (await promptValue(`Run ${command.join(" ")} safely once now in this terminal? [Y/n] `)).toLowerCase();
|
|
1057
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
1058
|
+
}
|
|
1048
1059
|
async function promptSecret(label) {
|
|
1049
1060
|
if (!process.stdin.isTTY || !process.stdout.isTTY || !process.stdin.setRawMode) {
|
|
1050
1061
|
throw new Error("Missing project API key. Set WITNORA_API_KEY (or legacy AGENTCERT_API_KEY) or pass --api-key in a trusted environment.");
|
package/dist/command-help.js
CHANGED
|
@@ -56,6 +56,7 @@ Options:
|
|
|
56
56
|
--repo <directory> Repository to configure (default: current directory)
|
|
57
57
|
--template <type> Override automatic repository detection
|
|
58
58
|
--action-transport <t> Test HTTP or MCP against the exact generated sandbox Action path
|
|
59
|
+
--no-run-agent Do not offer to run a detected bounded Agent command in this terminal
|
|
59
60
|
--no-browser Print the approval URL without opening it
|
|
60
61
|
`;
|
|
61
62
|
if (command === "mcp")
|
package/dist/gateway-exec.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
export async function runCustomerGatewayCommand(options) {
|
|
5
5
|
const repository = resolve(options.repository);
|
|
6
6
|
const gatewayDirectory = resolve(options.gatewayDirectory ?? join(repository, ".witnora", "gateway"));
|
|
@@ -14,8 +14,9 @@ export async function runCustomerGatewayCommand(options) {
|
|
|
14
14
|
if (!host || !Number.isInteger(port) || port < 1 || port > 65_535 || !gatewayToken) {
|
|
15
15
|
throw new Error("The local Gateway binding is incomplete. Run witnora onboard from this repository before starting the Agent workflow.");
|
|
16
16
|
}
|
|
17
|
+
const launch = await resolveLaunch(options.command, options.args ?? []);
|
|
17
18
|
return await new Promise((resolveChild, rejectChild) => {
|
|
18
|
-
const child = spawn(
|
|
19
|
+
const child = spawn(launch.command, launch.args, {
|
|
19
20
|
cwd: repository,
|
|
20
21
|
env: {
|
|
21
22
|
...process.env,
|
|
@@ -29,6 +30,24 @@ export async function runCustomerGatewayCommand(options) {
|
|
|
29
30
|
child.once("exit", (code) => resolveChild({ exitCode: code ?? 1 }));
|
|
30
31
|
});
|
|
31
32
|
}
|
|
33
|
+
async function resolveLaunch(command, args) {
|
|
34
|
+
const packageManager = basename(command).toLowerCase();
|
|
35
|
+
if (process.platform !== "win32" || packageManager !== "npm" && packageManager !== "npx")
|
|
36
|
+
return { command, args };
|
|
37
|
+
const cliName = packageManager === "npm" ? "npm-cli.js" : "npx-cli.js";
|
|
38
|
+
const candidates = [
|
|
39
|
+
packageManager === "npm" ? process.env.npm_execpath : undefined,
|
|
40
|
+
join(dirname(process.execPath), "node_modules", "npm", "bin", cliName),
|
|
41
|
+
].filter((candidate) => Boolean(candidate));
|
|
42
|
+
for (const candidate of candidates) {
|
|
43
|
+
try {
|
|
44
|
+
await access(candidate);
|
|
45
|
+
return { command: process.execPath, args: [candidate, ...args] };
|
|
46
|
+
}
|
|
47
|
+
catch { /* try the next customer-local Node installation path */ }
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`Could not locate ${packageManager} for the Agent workflow. Run the repository's direct Node or Python command instead.`);
|
|
50
|
+
}
|
|
32
51
|
async function readJson(path, label) {
|
|
33
52
|
try {
|
|
34
53
|
const value = JSON.parse(await readFile(path, "utf8"));
|
package/dist/gateway-service.js
CHANGED
|
@@ -12,9 +12,11 @@ export async function installGatewayService(input) {
|
|
|
12
12
|
// before replacing the definition so onboarding never leaves two owners
|
|
13
13
|
// racing for the same localhost port.
|
|
14
14
|
if (plan.kind === "WINDOWS_TASK") {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
if (!input.existingOwnerStopped) {
|
|
16
|
+
await input.run(plan.stop.command, plan.stop.args).catch(() => undefined);
|
|
17
|
+
await (input.removeFile ?? removeFile)(plan.serviceLease.path);
|
|
18
|
+
await (input.sleep ?? wait)(WINDOWS_PARENT_EXIT_GRACE_MS);
|
|
19
|
+
}
|
|
18
20
|
await input.writeDefinition(plan.serviceLease.path, `${plan.serviceLease.generation}\n`);
|
|
19
21
|
}
|
|
20
22
|
if (plan.launcher)
|
|
@@ -40,6 +42,7 @@ export async function installCurrentGatewayService(options) {
|
|
|
40
42
|
run: options.run ?? runCommand,
|
|
41
43
|
sleep: options.sleep,
|
|
42
44
|
removeFile: options.removeFile,
|
|
45
|
+
existingOwnerStopped: options.existingOwnerStopped,
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
export async function uninstallCurrentGatewayService(options) {
|
|
@@ -69,6 +72,11 @@ export async function restartCurrentGatewayService(options) {
|
|
|
69
72
|
await run(plan.start.command, plan.start.args);
|
|
70
73
|
return plan;
|
|
71
74
|
}
|
|
75
|
+
export async function restartCurrentGatewayOwners(options, stopManagedGateway, awaitManagedGateway) {
|
|
76
|
+
await stopCurrentGatewayOwners(options, stopManagedGateway);
|
|
77
|
+
await restartCurrentGatewayService({ ...options, existingOwnerStopped: true });
|
|
78
|
+
return awaitManagedGateway();
|
|
79
|
+
}
|
|
72
80
|
export async function stopCurrentGatewayService(options) {
|
|
73
81
|
const plan = createGatewayServicePlan(currentPlanInput(options));
|
|
74
82
|
await (options.run ?? runCommand)(plan.stop.command, plan.stop.args).catch(() => undefined);
|
package/dist/gateway.js
CHANGED
|
@@ -1424,12 +1424,19 @@ export async function stopManagedCustomerGateway(options = {}) {
|
|
|
1424
1424
|
const after = await statusManagedCustomerGateway(options);
|
|
1425
1425
|
return { ...after, stopped: !after.healthy };
|
|
1426
1426
|
}
|
|
1427
|
-
export async function
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1427
|
+
export async function awaitManagedCustomerGateway(options = {}) {
|
|
1428
|
+
const sleep = options.sleep ?? wait;
|
|
1429
|
+
const deadline = Date.now() + (options.timeoutMs ?? 12_000);
|
|
1430
|
+
let status = await statusManagedCustomerGateway(options);
|
|
1431
|
+
while (Date.now() < deadline) {
|
|
1432
|
+
if (status.state === "RUNNING_MANAGED")
|
|
1433
|
+
return { ...status, started: true };
|
|
1434
|
+
if (status.state === "RUNNING_EXTERNAL" || status.state === "CONFLICT")
|
|
1435
|
+
throw new Error(status.detail);
|
|
1436
|
+
await sleep(120);
|
|
1437
|
+
status = await statusManagedCustomerGateway(options);
|
|
1438
|
+
}
|
|
1439
|
+
throw new Error(`The operating-system Gateway supervisor did not restore a healthy managed process before the startup deadline. ${status.detail}`);
|
|
1433
1440
|
}
|
|
1434
1441
|
export async function superviseManagedCustomerGateway(options = {}) {
|
|
1435
1442
|
const inspect = options.inspect ?? (() => statusManagedCustomerGateway({ repository: options.repository, dir: options.dir }));
|
package/dist/onboard.js
CHANGED
|
@@ -13,6 +13,7 @@ import { discoverRuntimeReferences, planRuntimeSandboxModules } from "./runtime-
|
|
|
13
13
|
import { automaticRuntimeReferencesUsable, bootstrapLocalRuntime, verifyAutomaticRuntimeProbe } from "./runtime-bootstrap.js";
|
|
14
14
|
import { activateRealPathIntegrations } from "./real-path-activation.js";
|
|
15
15
|
import { installCurrentGatewayService, stopCurrentGatewayService } from "./gateway-service.js";
|
|
16
|
+
import { runCustomerGatewayCommand } from "./gateway-exec.js";
|
|
16
17
|
export async function runOnboard(options) {
|
|
17
18
|
const requestFetch = options.fetch ?? fetch;
|
|
18
19
|
const output = options.output ?? ((message) => process.stdout.write(message));
|
|
@@ -331,9 +332,23 @@ export async function runOnboard(options) {
|
|
|
331
332
|
});
|
|
332
333
|
if (actionTransportTest?.state === "WAITING_FOR_ACTION_PATH" && (options.waitForActionPathMs ?? 0) > 0) {
|
|
333
334
|
output("\nBase setup is complete. Onboarding is waiting for the Business Task and Action path; it has not claimed completion.\n");
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
let ranDetectedAgent = false;
|
|
336
|
+
if (localWorkflowCommand && options.confirmRunDetectedAgent) {
|
|
337
|
+
output(`Detected Agent command: ${localWorkflowCommand.join(" ")}\n`);
|
|
338
|
+
if (await options.confirmRunDetectedAgent(localWorkflowCommand)) {
|
|
339
|
+
output("Running this Agent safely once in the current terminal...\n");
|
|
340
|
+
const [command, ...args] = localWorkflowCommand;
|
|
341
|
+
const result = await (options.runGatewayCommand ?? runCustomerGatewayCommand)({ repository: repositoryPath, command: command, args });
|
|
342
|
+
if (result.exitCode !== 0)
|
|
343
|
+
throw new Error(`The detected Agent command exited with code ${result.exitCode}. Onboarding remains fail closed.`);
|
|
344
|
+
output("Agent run completed. Keep the browser open and confirm the Business Task; this command will finish automatically.\n");
|
|
345
|
+
ranDetectedAgent = true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (!ranDetectedAgent)
|
|
349
|
+
output(agentRunCommand
|
|
350
|
+
? `Run the Agent in its normal environment, or use this command from another terminal:\n${agentRunCommand}\n`
|
|
351
|
+
: "Run the Agent normally in its sandbox. If it has no work now, you may enter the workspace and finish Action verification after its first real run.\n");
|
|
337
352
|
output(`Keep this command open for up to ${Math.max(1, Math.ceil((options.waitForActionPathMs ?? 0) / 60_000))} minutes. It will finish automatically after you confirm the Business Task in the browser; no second onboard command is required. Press Ctrl+C to pause safely.\n`);
|
|
338
353
|
const resolution = await waitForOnboardingResolution({
|
|
339
354
|
repository: repositoryPath, server, projectId: token.projectId, apiKey: token.apiKey,
|
|
@@ -677,6 +692,11 @@ function relativeGeneratedFiles(repositoryPath, files) {
|
|
|
677
692
|
}
|
|
678
693
|
function randomSuffix() { return Math.random().toString(36).slice(2, 10); }
|
|
679
694
|
async function detectLocalWorkflowCommand(repositoryPath) {
|
|
695
|
+
const manifest = await optionalJson(join(repositoryPath, "package.json"));
|
|
696
|
+
const scripts = manifest?.scripts;
|
|
697
|
+
if (scripts && typeof scripts === "object" && !Array.isArray(scripts) && typeof scripts.agent === "string") {
|
|
698
|
+
return ["npm", "run", "agent"];
|
|
699
|
+
}
|
|
680
700
|
if (await exists(join(repositoryPath, "scripts", "run_workflow.py")))
|
|
681
701
|
return ["python", "scripts/run_workflow.py"];
|
|
682
702
|
if (await exists(join(repositoryPath, "scripts", "run-workflow.mjs")))
|