runwork 0.16.1 → 0.17.1
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/bundled-types/core-scheduler.d.ts +7 -0
- package/dist/index.js +1126 -259
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -411,6 +411,29 @@ class ApiClient {
|
|
|
411
411
|
const res = await this.request("/api/dev/workspaces");
|
|
412
412
|
return res.data;
|
|
413
413
|
}
|
|
414
|
+
async listWorkspaceMembers(workspaceId) {
|
|
415
|
+
const res = await this.request(`/api/workspaces/${workspaceId}/members`);
|
|
416
|
+
return res.data.members;
|
|
417
|
+
}
|
|
418
|
+
async rawApiCall(method, path, opts) {
|
|
419
|
+
const targetPath = opts?.query ? `${path}${path.includes("?") ? "&" : "?"}${opts.query}` : path;
|
|
420
|
+
const url = `${this.baseUrl}${targetPath}`;
|
|
421
|
+
const headers = { ...opts?.headers || {} };
|
|
422
|
+
if (this.apiKey) {
|
|
423
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
424
|
+
}
|
|
425
|
+
let body;
|
|
426
|
+
if (opts?.body !== undefined) {
|
|
427
|
+
body = typeof opts.body === "string" ? opts.body : JSON.stringify(opts.body);
|
|
428
|
+
if (!Object.keys(headers).some((h) => h.toLowerCase() === "content-type")) {
|
|
429
|
+
headers["Content-Type"] = "application/json";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const response = await httpFetch(url, { method: method.toUpperCase(), headers, body });
|
|
433
|
+
const contentType = response.headers.get("content-type") || "";
|
|
434
|
+
const responseBody = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
435
|
+
return { status: response.status, ok: response.ok, contentType, body: responseBody };
|
|
436
|
+
}
|
|
414
437
|
async listApps(workspaceId) {
|
|
415
438
|
const query = workspaceId ? `?workspaceId=${workspaceId}` : "";
|
|
416
439
|
const res = await this.request(`/api/dev/apps${query}`);
|
|
@@ -883,20 +906,28 @@ var init_subprocess = () => {};
|
|
|
883
906
|
|
|
884
907
|
// src/git/credentials.ts
|
|
885
908
|
import { existsSync as existsSync2 } from "fs";
|
|
886
|
-
function buildHelperValue(execPath) {
|
|
909
|
+
function buildHelperValue(execPath, scriptPath) {
|
|
887
910
|
const normalised = execPath.replace(/\\/g, "/");
|
|
911
|
+
const runtimeName = normalised.split("/").pop()?.toLowerCase() ?? "";
|
|
912
|
+
if (SCRIPT_RUNTIMES.has(runtimeName) && scriptPath) {
|
|
913
|
+
const script = scriptPath.replace(/\\/g, "/");
|
|
914
|
+
return `!"${normalised}" "${script}" git-credential-helper`;
|
|
915
|
+
}
|
|
888
916
|
return `!"${normalised}" git-credential-helper`;
|
|
889
917
|
}
|
|
890
918
|
async function configureGitCredentials(remoteUrl) {
|
|
891
919
|
const origin = new URL(remoteUrl).origin;
|
|
892
|
-
const
|
|
920
|
+
const key = `credential.${origin}.helper`;
|
|
921
|
+
const helperValue = buildHelperValue(process.execPath, process.argv[1]);
|
|
893
922
|
try {
|
|
894
|
-
|
|
895
|
-
"config",
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
923
|
+
try {
|
|
924
|
+
execFileSync("git", ["config", "--global", "--unset-all", key], { stdio: "pipe" });
|
|
925
|
+
} catch (unsetErr) {
|
|
926
|
+
if (unsetErr?.code === "ENOENT")
|
|
927
|
+
throw unsetErr;
|
|
928
|
+
}
|
|
929
|
+
execFileSync("git", ["config", "--global", "--add", key, ""], { stdio: "pipe" });
|
|
930
|
+
execFileSync("git", ["config", "--global", "--add", key, helperValue], { stdio: "pipe" });
|
|
900
931
|
} catch (err) {
|
|
901
932
|
const code = err?.code;
|
|
902
933
|
if (code === "ENOENT") {
|
|
@@ -915,14 +946,37 @@ function lookupCredentialHelper(origin) {
|
|
|
915
946
|
} catch {
|
|
916
947
|
return { status: "none" };
|
|
917
948
|
}
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
949
|
+
const keyName = `credential.${origin}.helper`;
|
|
950
|
+
let value = "";
|
|
951
|
+
let registered = false;
|
|
952
|
+
let hasReset = false;
|
|
953
|
+
let otherCount = 0;
|
|
954
|
+
for (const raw of helperConfig.split(/\r?\n/)) {
|
|
955
|
+
if (!raw.trim())
|
|
956
|
+
continue;
|
|
957
|
+
const m = raw.match(/^(\S+)(?:\s+(.*))?$/);
|
|
958
|
+
if (!m)
|
|
959
|
+
continue;
|
|
960
|
+
const key = m[1];
|
|
961
|
+
const val = (m[2] ?? "").trim();
|
|
962
|
+
if (key === keyName) {
|
|
963
|
+
if (val === "")
|
|
964
|
+
hasReset = true;
|
|
965
|
+
else if (!registered) {
|
|
966
|
+
registered = true;
|
|
967
|
+
value = val;
|
|
968
|
+
}
|
|
969
|
+
} else {
|
|
970
|
+
otherCount++;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
if (registered) {
|
|
974
|
+
return { status: "registered", value, hasReset };
|
|
975
|
+
}
|
|
976
|
+
if (otherCount === 0 && !hasReset) {
|
|
977
|
+
return { status: "none" };
|
|
924
978
|
}
|
|
925
|
-
return { status: "
|
|
979
|
+
return { status: "unscoped", otherCount };
|
|
926
980
|
}
|
|
927
981
|
function helperBinaryStatus(value) {
|
|
928
982
|
const absoluteHelperRegex = /^!"?(\/|[A-Za-z]:[\\/])/;
|
|
@@ -942,7 +996,7 @@ async function ensureGitCredentialHelper(baseUrl) {
|
|
|
942
996
|
return;
|
|
943
997
|
}
|
|
944
998
|
const lookup = lookupCredentialHelper(origin);
|
|
945
|
-
if (lookup.status === "registered" && helperBinaryStatus(lookup.value).ok) {
|
|
999
|
+
if (lookup.status === "registered" && helperBinaryStatus(lookup.value).ok && lookup.hasReset) {
|
|
946
1000
|
return;
|
|
947
1001
|
}
|
|
948
1002
|
await configureGitCredentials(baseUrl);
|
|
@@ -953,7 +1007,7 @@ async function removeGitCredentials(baseUrl) {
|
|
|
953
1007
|
execFileSync("git", [
|
|
954
1008
|
"config",
|
|
955
1009
|
"--global",
|
|
956
|
-
"--unset",
|
|
1010
|
+
"--unset-all",
|
|
957
1011
|
`credential.${origin}.helper`
|
|
958
1012
|
], { stdio: "pipe" });
|
|
959
1013
|
} catch {}
|
|
@@ -996,9 +1050,11 @@ function readStdin() {
|
|
|
996
1050
|
resolve(data);
|
|
997
1051
|
});
|
|
998
1052
|
}
|
|
1053
|
+
var SCRIPT_RUNTIMES;
|
|
999
1054
|
var init_credentials = __esm(() => {
|
|
1000
1055
|
init_subprocess();
|
|
1001
1056
|
init_store();
|
|
1057
|
+
SCRIPT_RUNTIMES = new Set(["node", "node.exe", "bun", "bun.exe"]);
|
|
1002
1058
|
});
|
|
1003
1059
|
|
|
1004
1060
|
// src/auth/login-flow.ts
|
|
@@ -1429,8 +1485,8 @@ async function resolveWorkspace(client, nameOrId) {
|
|
|
1429
1485
|
console.error(`Workspace "${nameOrId}" not found. Available: ${workspaces.map((w) => w.name).join(", ")}`);
|
|
1430
1486
|
process.exit(1);
|
|
1431
1487
|
}
|
|
1432
|
-
async function resolveApp(client, nameOrId) {
|
|
1433
|
-
const apps = await client.listApps();
|
|
1488
|
+
async function resolveApp(client, nameOrId, workspaceId) {
|
|
1489
|
+
const apps = await client.listApps(workspaceId);
|
|
1434
1490
|
if (apps.length === 0) {
|
|
1435
1491
|
console.error("No apps found.");
|
|
1436
1492
|
process.exit(1);
|
|
@@ -2422,7 +2478,7 @@ var init_clone = __esm(() => {
|
|
|
2422
2478
|
init_remote();
|
|
2423
2479
|
init_repo_config();
|
|
2424
2480
|
init_preflight();
|
|
2425
|
-
cloneCommand = new Command3("clone").description("Clone a Runwork app to local development").argument("[appId]", "App ID to clone (interactive if omitted)").argument("[directory]", "Target directory").option("--app <name-or-id>", "App name or ID (skips interactive selection)").addHelpText("after", RESTRICTED_FS_HELP).action(async (rawAppId, rawDirectory, options) => {
|
|
2481
|
+
cloneCommand = new Command3("clone").description("Clone a Runwork app to local development").argument("[appId]", "App ID to clone (interactive if omitted)").argument("[directory]", "Target directory").option("--app <name-or-id>", "App name or ID (skips interactive selection)").option("--workspace <name-or-id>", "Workspace to look up the app in (name or ID)").addHelpText("after", RESTRICTED_FS_HELP).action(async (rawAppId, rawDirectory, options) => {
|
|
2426
2482
|
requireGit("clone");
|
|
2427
2483
|
const { appId, directory } = normalizeCloneArgs(rawAppId, rawDirectory, options);
|
|
2428
2484
|
const creds = requireAuth();
|
|
@@ -2430,10 +2486,15 @@ var init_clone = __esm(() => {
|
|
|
2430
2486
|
const useJson = shouldOutputJson(undefined);
|
|
2431
2487
|
let app;
|
|
2432
2488
|
const appRef = options?.app || appId;
|
|
2489
|
+
let workspaceId;
|
|
2490
|
+
if (options?.workspace) {
|
|
2491
|
+
const ws = await resolveWorkspace(client, options.workspace);
|
|
2492
|
+
workspaceId = ws.id;
|
|
2493
|
+
}
|
|
2433
2494
|
if (appRef) {
|
|
2434
|
-
app = await resolveApp(client, appRef);
|
|
2495
|
+
app = await resolveApp(client, appRef, workspaceId);
|
|
2435
2496
|
} else {
|
|
2436
|
-
const apps = await client.listApps();
|
|
2497
|
+
const apps = await client.listApps(workspaceId);
|
|
2437
2498
|
if (apps.length === 0) {
|
|
2438
2499
|
if (useJson) {
|
|
2439
2500
|
jsonOut(buildErrorResponse("clone", "No apps found", "No apps are available to clone.", ["Create an app first with: runwork init my-app"]));
|
|
@@ -3307,13 +3368,20 @@ function resolvePollDeps(deps) {
|
|
|
3307
3368
|
}
|
|
3308
3369
|
async function pollForSession(appDir, expectedPid, expectedAppId, opts = {}) {
|
|
3309
3370
|
const intervalMs = opts.intervalMs ?? 250;
|
|
3310
|
-
const timeoutMs = opts.timeoutMs ??
|
|
3371
|
+
const timeoutMs = opts.timeoutMs ?? DETACH_READY_TIMEOUT_MS;
|
|
3372
|
+
const heartbeatMs = opts.heartbeatMs ?? DETACH_WAITING_HEARTBEAT_MS;
|
|
3311
3373
|
const d = resolvePollDeps(opts.deps);
|
|
3312
|
-
const
|
|
3374
|
+
const startedAt = d.now();
|
|
3375
|
+
const deadline = startedAt + timeoutMs;
|
|
3376
|
+
let lastHeartbeatAt = startedAt;
|
|
3313
3377
|
while (d.now() < deadline) {
|
|
3314
3378
|
if (opts.isChildAlive && !opts.isChildAlive()) {
|
|
3315
3379
|
return { result: "child-exited" };
|
|
3316
3380
|
}
|
|
3381
|
+
if (opts.onWaiting && d.now() - lastHeartbeatAt >= heartbeatMs) {
|
|
3382
|
+
lastHeartbeatAt = d.now();
|
|
3383
|
+
opts.onWaiting(d.now() - startedAt);
|
|
3384
|
+
}
|
|
3317
3385
|
const file = readSessionFile(appDir);
|
|
3318
3386
|
if (file && file.previewUrl && file.appId === expectedAppId) {
|
|
3319
3387
|
if (file.pid === expectedPid) {
|
|
@@ -3349,7 +3417,9 @@ async function runAsDetachedParent(opts) {
|
|
|
3349
3417
|
intervalMs: opts.intervalMs,
|
|
3350
3418
|
timeoutMs: opts.timeoutMs,
|
|
3351
3419
|
deps: opts.pollDeps,
|
|
3352
|
-
isChildAlive: child.isAlive
|
|
3420
|
+
isChildAlive: child.isAlive,
|
|
3421
|
+
onWaiting: opts.onWaiting,
|
|
3422
|
+
heartbeatMs: opts.heartbeatMs
|
|
3353
3423
|
});
|
|
3354
3424
|
if (outcome.result === "ready") {
|
|
3355
3425
|
return { result: "started", file: outcome.file };
|
|
@@ -3366,22 +3436,27 @@ async function runAsDetachedParent(opts) {
|
|
|
3366
3436
|
try {
|
|
3367
3437
|
removeSessionFileIfOwned(opts.appDir, child.pid);
|
|
3368
3438
|
} catch {}
|
|
3369
|
-
const childLogTail =
|
|
3439
|
+
const childLogTail = readChildLogTail(opts.appDir);
|
|
3370
3440
|
if (outcome.result === "child-exited") {
|
|
3371
3441
|
return { result: "child-exited", ourPid: child.pid, childLogTail };
|
|
3372
3442
|
}
|
|
3373
3443
|
return { result: "timeout", ourPid: child.pid, childLogTail };
|
|
3374
3444
|
}
|
|
3375
|
-
function
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3445
|
+
function readChildLogTail(appDir) {
|
|
3446
|
+
const paths = getSessionPaths(appDir);
|
|
3447
|
+
const sections = [];
|
|
3448
|
+
for (const [label, file] of [["stderr", paths.stderrLog], ["stdout", paths.stdoutLog]]) {
|
|
3449
|
+
try {
|
|
3450
|
+
if (!fs2.existsSync(file))
|
|
3451
|
+
continue;
|
|
3452
|
+
const tail = fs2.readFileSync(file, "utf-8").slice(-2000).trim();
|
|
3453
|
+
if (tail)
|
|
3454
|
+
sections.push(`--- ${label} (tail) ---
|
|
3455
|
+
${tail}`);
|
|
3456
|
+
} catch {}
|
|
3384
3457
|
}
|
|
3458
|
+
return sections.length > 0 ? sections.join(`
|
|
3459
|
+
`) : undefined;
|
|
3385
3460
|
}
|
|
3386
3461
|
function defaultSpawnDetachedChild(childArgs) {
|
|
3387
3462
|
const cwd = process.cwd();
|
|
@@ -3441,7 +3516,7 @@ function buildChildArgs(parentArgv) {
|
|
|
3441
3516
|
}
|
|
3442
3517
|
return [...stripInternalChildFlag(rest), INTERNAL_DETACHED_CHILD_FLAG];
|
|
3443
3518
|
}
|
|
3444
|
-
var INTERNAL_DETACHED_CHILD_FLAG = "--internal-detached-child", realPollDeps;
|
|
3519
|
+
var INTERNAL_DETACHED_CHILD_FLAG = "--internal-detached-child", DETACH_READY_TIMEOUT_MS = 630000, DETACH_WAITING_HEARTBEAT_MS = 15000, realPollDeps;
|
|
3445
3520
|
var init_detach = __esm(() => {
|
|
3446
3521
|
init_session();
|
|
3447
3522
|
realPollDeps = {
|
|
@@ -3450,6 +3525,104 @@ var init_detach = __esm(() => {
|
|
|
3450
3525
|
};
|
|
3451
3526
|
});
|
|
3452
3527
|
|
|
3528
|
+
// src/dev/boot-await.ts
|
|
3529
|
+
function isPermanentStartError(err) {
|
|
3530
|
+
if (!(err instanceof Error))
|
|
3531
|
+
return false;
|
|
3532
|
+
const match = err.message.match(/^API error (\d{3})/);
|
|
3533
|
+
if (!match)
|
|
3534
|
+
return false;
|
|
3535
|
+
const status = Number.parseInt(match[1], 10);
|
|
3536
|
+
return status >= 400 && status < 500;
|
|
3537
|
+
}
|
|
3538
|
+
async function awaitDevSessionReady(opts) {
|
|
3539
|
+
const now = opts.deps?.now ?? (() => Date.now());
|
|
3540
|
+
const sleep = opts.deps?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
3541
|
+
const timeoutMs = opts.timeoutMs ?? DEV_BOOT_TIMEOUT_MS;
|
|
3542
|
+
const heartbeatMs = opts.heartbeatMs ?? DEV_BOOT_HEARTBEAT_MS;
|
|
3543
|
+
const statusPollDelayMs = opts.statusPollDelayMs ?? DEV_BOOT_STATUS_POLL_DELAY_MS;
|
|
3544
|
+
const statusPollIntervalMs = opts.statusPollIntervalMs ?? DEV_BOOT_STATUS_POLL_INTERVAL_MS;
|
|
3545
|
+
const startedAt = now();
|
|
3546
|
+
const elapsed = () => now() - startedAt;
|
|
3547
|
+
const st = {
|
|
3548
|
+
startResult: null,
|
|
3549
|
+
startError: undefined,
|
|
3550
|
+
startSettled: false,
|
|
3551
|
+
permanentFailure: null,
|
|
3552
|
+
statusReady: null,
|
|
3553
|
+
statusInFlight: false
|
|
3554
|
+
};
|
|
3555
|
+
opts.start().then((session) => {
|
|
3556
|
+
st.startResult = session;
|
|
3557
|
+
st.startSettled = true;
|
|
3558
|
+
}, (err) => {
|
|
3559
|
+
st.startError = err;
|
|
3560
|
+
st.startSettled = true;
|
|
3561
|
+
if (isPermanentStartError(err)) {
|
|
3562
|
+
st.permanentFailure = err;
|
|
3563
|
+
} else {
|
|
3564
|
+
opts.onStartError?.(err);
|
|
3565
|
+
}
|
|
3566
|
+
});
|
|
3567
|
+
let lastStatusPollAt = -Infinity;
|
|
3568
|
+
let lastHeartbeatAt = startedAt;
|
|
3569
|
+
const readyResult = () => {
|
|
3570
|
+
if (st.startResult && st.startResult.previewUrl) {
|
|
3571
|
+
return { session: st.startResult, source: "start", elapsedMs: elapsed() };
|
|
3572
|
+
}
|
|
3573
|
+
if (st.statusReady) {
|
|
3574
|
+
return { session: st.statusReady, source: "status", elapsedMs: elapsed() };
|
|
3575
|
+
}
|
|
3576
|
+
return null;
|
|
3577
|
+
};
|
|
3578
|
+
while (elapsed() < timeoutMs) {
|
|
3579
|
+
if (st.permanentFailure)
|
|
3580
|
+
throw st.permanentFailure;
|
|
3581
|
+
const ready2 = readyResult();
|
|
3582
|
+
if (ready2)
|
|
3583
|
+
return ready2;
|
|
3584
|
+
const startFailedOrEmpty = st.startSettled && !(st.startResult && st.startResult.previewUrl);
|
|
3585
|
+
const pollingActive = elapsed() >= statusPollDelayMs || startFailedOrEmpty;
|
|
3586
|
+
if (pollingActive && !st.statusInFlight && now() - lastStatusPollAt >= statusPollIntervalMs) {
|
|
3587
|
+
st.statusInFlight = true;
|
|
3588
|
+
lastStatusPollAt = now();
|
|
3589
|
+
opts.pollStatus().then((session) => {
|
|
3590
|
+
st.statusInFlight = false;
|
|
3591
|
+
if (session.previewUrl)
|
|
3592
|
+
st.statusReady = session;
|
|
3593
|
+
}, () => {
|
|
3594
|
+
st.statusInFlight = false;
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
if (now() - lastHeartbeatAt >= heartbeatMs) {
|
|
3598
|
+
lastHeartbeatAt = now();
|
|
3599
|
+
opts.onWaiting?.(elapsed());
|
|
3600
|
+
}
|
|
3601
|
+
await sleep(TICK_MS);
|
|
3602
|
+
}
|
|
3603
|
+
if (st.permanentFailure)
|
|
3604
|
+
throw st.permanentFailure;
|
|
3605
|
+
const ready = readyResult();
|
|
3606
|
+
if (ready)
|
|
3607
|
+
return ready;
|
|
3608
|
+
throw new DevBootTimeoutError(elapsed(), st.startError);
|
|
3609
|
+
}
|
|
3610
|
+
var DEV_BOOT_TIMEOUT_MS = 600000, DEV_BOOT_HEARTBEAT_MS = 15000, DEV_BOOT_STATUS_POLL_DELAY_MS = 45000, DEV_BOOT_STATUS_POLL_INTERVAL_MS = 1e4, DevBootTimeoutError, TICK_MS = 250;
|
|
3611
|
+
var init_boot_await = __esm(() => {
|
|
3612
|
+
DevBootTimeoutError = class DevBootTimeoutError extends Error {
|
|
3613
|
+
elapsedMs;
|
|
3614
|
+
startError;
|
|
3615
|
+
constructor(elapsedMs, startError) {
|
|
3616
|
+
const base = `Dev session did not become ready within ${Math.round(elapsedMs / 1000)}s`;
|
|
3617
|
+
const cause = startError instanceof Error ? ` (boot call failed: ${startError.message})` : "";
|
|
3618
|
+
super(`${base}${cause}`);
|
|
3619
|
+
this.name = "DevBootTimeoutError";
|
|
3620
|
+
this.elapsedMs = elapsedMs;
|
|
3621
|
+
this.startError = startError;
|
|
3622
|
+
}
|
|
3623
|
+
};
|
|
3624
|
+
});
|
|
3625
|
+
|
|
3453
3626
|
// src/dev/stop.ts
|
|
3454
3627
|
import * as fs3 from "fs";
|
|
3455
3628
|
async function stopSession(appDir, expectedAppId, deps = {}) {
|
|
@@ -5652,6 +5825,13 @@ export interface JobRunRecord {
|
|
|
5652
5825
|
completedAt?: number;
|
|
5653
5826
|
result?: JobResult;
|
|
5654
5827
|
error?: string;
|
|
5828
|
+
/**
|
|
5829
|
+
* Stack trace of the failure. Stored because the error MESSAGE alone made
|
|
5830
|
+
* the 2026-07 "Could not serialize object of type ..." production failures
|
|
5831
|
+
* undiagnosable: the message names the workerd serializer, not the framework
|
|
5832
|
+
* call site that hit it (AGENTCLI-6).
|
|
5833
|
+
*/
|
|
5834
|
+
errorStack?: string;
|
|
5655
5835
|
}
|
|
5656
5836
|
export interface ScheduleState {
|
|
5657
5837
|
name: string;
|
|
@@ -7381,7 +7561,7 @@ function createKeyboardListener() {
|
|
|
7381
7561
|
}
|
|
7382
7562
|
|
|
7383
7563
|
// src/generated/version.ts
|
|
7384
|
-
var VERSION = "0.
|
|
7564
|
+
var VERSION = "0.17.1";
|
|
7385
7565
|
|
|
7386
7566
|
// src/commands/dev.ts
|
|
7387
7567
|
var exports_dev = {};
|
|
@@ -7515,7 +7695,64 @@ async function execDev(options) {
|
|
|
7515
7695
|
} else {
|
|
7516
7696
|
console.log(dim("Starting dev session..."));
|
|
7517
7697
|
}
|
|
7518
|
-
|
|
7698
|
+
let session;
|
|
7699
|
+
try {
|
|
7700
|
+
const boot = await awaitDevSessionReady({
|
|
7701
|
+
start: () => client.startDevSession(config.appId),
|
|
7702
|
+
pollStatus: () => client.getDevStatus(config.appId),
|
|
7703
|
+
onWaiting: (elapsedMs) => {
|
|
7704
|
+
const seconds = Math.round(elapsedMs / 1000);
|
|
7705
|
+
if (useJson) {
|
|
7706
|
+
jsonLine({
|
|
7707
|
+
event: "startup",
|
|
7708
|
+
phase: "dev_session_waiting",
|
|
7709
|
+
elapsedSeconds: seconds,
|
|
7710
|
+
timestamp: ts(),
|
|
7711
|
+
note: "Sandbox is booting. Cold boots can take a few minutes; keep waiting."
|
|
7712
|
+
});
|
|
7713
|
+
} else {
|
|
7714
|
+
console.log(dim(` Still starting the sandbox... (${seconds}s elapsed; cold boots can take a few minutes)`));
|
|
7715
|
+
}
|
|
7716
|
+
},
|
|
7717
|
+
onStartError: (err) => {
|
|
7718
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7719
|
+
if (useJson) {
|
|
7720
|
+
jsonLine({
|
|
7721
|
+
event: "startup",
|
|
7722
|
+
phase: "dev_session_retrying",
|
|
7723
|
+
timestamp: ts(),
|
|
7724
|
+
warning: `Boot call failed (${message}). The sandbox may still be booting server-side; watching status until it becomes ready.`
|
|
7725
|
+
});
|
|
7726
|
+
} else {
|
|
7727
|
+
console.warn(yellow(` Boot call failed (${message}); watching sandbox status in case the boot completes server-side...`));
|
|
7728
|
+
}
|
|
7729
|
+
}
|
|
7730
|
+
});
|
|
7731
|
+
session = boot.session;
|
|
7732
|
+
} catch (error) {
|
|
7733
|
+
const isTimeout = error instanceof DevBootTimeoutError;
|
|
7734
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7735
|
+
if (useJson) {
|
|
7736
|
+
jsonLine({
|
|
7737
|
+
event: "error",
|
|
7738
|
+
phase: "dev_session",
|
|
7739
|
+
timestamp: ts(),
|
|
7740
|
+
error: {
|
|
7741
|
+
message,
|
|
7742
|
+
diagnosis: isTimeout ? "The preview sandbox did not become ready within the boot deadline. The sandbox provider may be degraded, or the app failed to boot server-side." : "The dev session boot call failed.",
|
|
7743
|
+
suggestions: [
|
|
7744
|
+
"Re-run `runwork dev --detach --json` in about a minute; if the sandbox finished booting server-side it will be picked up quickly",
|
|
7745
|
+
"Run `runwork logs` to inspect sandbox logs for boot errors",
|
|
7746
|
+
"Run `runwork doctor` to verify auth and connectivity"
|
|
7747
|
+
]
|
|
7748
|
+
}
|
|
7749
|
+
});
|
|
7750
|
+
} else {
|
|
7751
|
+
console.error(red(`Failed to start dev session: ${message}`));
|
|
7752
|
+
console.error(dim(" Re-run `runwork dev` in about a minute; if the sandbox finished booting server-side it will be picked up quickly."));
|
|
7753
|
+
}
|
|
7754
|
+
process.exit(1);
|
|
7755
|
+
}
|
|
7519
7756
|
await populateSkill(cwd, client, config.appId);
|
|
7520
7757
|
if (useJson)
|
|
7521
7758
|
jsonLine({ event: "startup", phase: "skill_fetched", timestamp: ts() });
|
|
@@ -7804,7 +8041,21 @@ async function runDevDetachParent(opts) {
|
|
|
7804
8041
|
const outcome = await runAsDetachedParent({
|
|
7805
8042
|
appDir: cwd,
|
|
7806
8043
|
expectedAppId: config.appId,
|
|
7807
|
-
childArgs
|
|
8044
|
+
childArgs,
|
|
8045
|
+
onWaiting: (elapsedMs) => {
|
|
8046
|
+
const seconds = Math.round(elapsedMs / 1000);
|
|
8047
|
+
if (opts.json) {
|
|
8048
|
+
jsonLine({
|
|
8049
|
+
event: "waiting",
|
|
8050
|
+
phase: "sandbox_boot",
|
|
8051
|
+
elapsedSeconds: seconds,
|
|
8052
|
+
timestamp: ts(),
|
|
8053
|
+
note: "Sandbox is booting. Cold boots can take a few minutes; keep waiting for session_started."
|
|
8054
|
+
});
|
|
8055
|
+
} else {
|
|
8056
|
+
console.log(dim(` Still starting... (${seconds}s elapsed; cold boots can take a few minutes)`));
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
7808
8059
|
});
|
|
7809
8060
|
switch (outcome.result) {
|
|
7810
8061
|
case "started": {
|
|
@@ -7862,9 +8113,10 @@ async function runDevDetachParent(opts) {
|
|
|
7862
8113
|
timestamp: ts(),
|
|
7863
8114
|
error: {
|
|
7864
8115
|
message,
|
|
7865
|
-
diagnosis: "The detached child process exited (typically due to missing auth, missing git, network failure, or a
|
|
8116
|
+
diagnosis: "The detached child process exited (typically due to missing auth, missing git, network failure, a sync conflict, or a sandbox boot timeout) before it could publish a preview URL.",
|
|
7866
8117
|
suggestions: [
|
|
7867
|
-
"
|
|
8118
|
+
"Check childLogTail below; the child logs structured errors to .runwork/dev-stdout.log (stderr carries crash traces)",
|
|
8119
|
+
"If the child hit a sandbox boot timeout, re-run `runwork dev --detach --json` in about a minute; a boot that completed server-side is picked up quickly",
|
|
7868
8120
|
"Run `runwork doctor` to verify auth and connectivity",
|
|
7869
8121
|
"Try `runwork dev` (foreground) to see the failure inline"
|
|
7870
8122
|
],
|
|
@@ -7873,7 +8125,7 @@ async function runDevDetachParent(opts) {
|
|
|
7873
8125
|
});
|
|
7874
8126
|
} else {
|
|
7875
8127
|
console.error(yellow(message));
|
|
7876
|
-
console.error(dim(" Inspect .runwork/dev-stderr.log or run `runwork dev` to see what failed."));
|
|
8128
|
+
console.error(dim(" Inspect .runwork/dev-stdout.log and dev-stderr.log, or run `runwork dev` to see what failed."));
|
|
7877
8129
|
if (outcome.childLogTail) {
|
|
7878
8130
|
console.error(dim(" Tail of child stderr:"));
|
|
7879
8131
|
for (const line of outcome.childLogTail.split(`
|
|
@@ -7887,7 +8139,8 @@ async function runDevDetachParent(opts) {
|
|
|
7887
8139
|
return;
|
|
7888
8140
|
}
|
|
7889
8141
|
case "timeout": {
|
|
7890
|
-
const
|
|
8142
|
+
const timeoutSeconds = Math.round(DETACH_READY_TIMEOUT_MS / 1000);
|
|
8143
|
+
const message = `Detached dev session did not become ready within ${timeoutSeconds}s.`;
|
|
7891
8144
|
if (opts.json) {
|
|
7892
8145
|
jsonLine({
|
|
7893
8146
|
event: "error",
|
|
@@ -7895,9 +8148,10 @@ async function runDevDetachParent(opts) {
|
|
|
7895
8148
|
timestamp: ts(),
|
|
7896
8149
|
error: {
|
|
7897
8150
|
message,
|
|
7898
|
-
diagnosis: "The detached child process was spawned but never wrote a session file with a preview URL. The sandbox boot may have failed.",
|
|
8151
|
+
diagnosis: "The detached child process was spawned but never wrote a session file with a preview URL. The sandbox boot may have failed or the sandbox provider may be degraded.",
|
|
7899
8152
|
suggestions: [
|
|
7900
|
-
"
|
|
8153
|
+
"The sandbox may still finish booting server-side: re-run `runwork dev --detach --json` in about a minute; a completed boot is picked up quickly",
|
|
8154
|
+
"Inspect the child logs at .runwork/dev-stdout.log and .runwork/dev-stderr.log",
|
|
7901
8155
|
"Run `runwork doctor` to verify auth and connectivity",
|
|
7902
8156
|
"Try `runwork dev` (foreground) to see the failure inline"
|
|
7903
8157
|
],
|
|
@@ -7906,7 +8160,7 @@ async function runDevDetachParent(opts) {
|
|
|
7906
8160
|
});
|
|
7907
8161
|
} else {
|
|
7908
8162
|
console.error(yellow(message));
|
|
7909
|
-
console.error(dim(" Inspect .runwork/dev-stderr.log or run `runwork dev` to see what failed."));
|
|
8163
|
+
console.error(dim(" Inspect .runwork/dev-stdout.log and dev-stderr.log, or run `runwork dev` to see what failed."));
|
|
7910
8164
|
if (outcome.childLogTail) {
|
|
7911
8165
|
console.error(dim(" Tail of child stderr:"));
|
|
7912
8166
|
for (const line of outcome.childLogTail.split(`
|
|
@@ -7956,6 +8210,7 @@ var init_dev = __esm(() => {
|
|
|
7956
8210
|
init_tailer();
|
|
7957
8211
|
init_session();
|
|
7958
8212
|
init_detach();
|
|
8213
|
+
init_boot_await();
|
|
7959
8214
|
init_stop();
|
|
7960
8215
|
init_attach();
|
|
7961
8216
|
init_types_manager();
|
|
@@ -8291,7 +8546,7 @@ var init_welcome = __esm(() => {
|
|
|
8291
8546
|
});
|
|
8292
8547
|
|
|
8293
8548
|
// src/index.ts
|
|
8294
|
-
import { Command as
|
|
8549
|
+
import { Command as Command36 } from "commander";
|
|
8295
8550
|
|
|
8296
8551
|
// src/commands/login.ts
|
|
8297
8552
|
init_login_flow();
|
|
@@ -8394,8 +8649,23 @@ function getDeploySummary(cwd) {
|
|
|
8394
8649
|
}
|
|
8395
8650
|
|
|
8396
8651
|
// src/deploy/deploy-status.ts
|
|
8652
|
+
init_session();
|
|
8397
8653
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
8398
8654
|
import { dirname as dirname4, join as join17 } from "path";
|
|
8655
|
+
function evaluateDeployStatus(status, deps = {}) {
|
|
8656
|
+
if (status.state !== "in-progress") {
|
|
8657
|
+
return { status, effectiveState: status.state };
|
|
8658
|
+
}
|
|
8659
|
+
const bootTime = deps.bootTime ?? currentBootTime;
|
|
8660
|
+
const pidAlive = deps.pidAlive ?? isPidAlive;
|
|
8661
|
+
if (typeof status.bootTime === "number" && Math.abs(bootTime() - status.bootTime) > BOOT_TIME_TOLERANCE_MS) {
|
|
8662
|
+
return { status, effectiveState: "stale", staleReason: "boot-time-mismatch" };
|
|
8663
|
+
}
|
|
8664
|
+
if (typeof status.pid === "number" && !pidAlive(status.pid)) {
|
|
8665
|
+
return { status, effectiveState: "stale", staleReason: "process-gone" };
|
|
8666
|
+
}
|
|
8667
|
+
return { status, effectiveState: "in-progress" };
|
|
8668
|
+
}
|
|
8399
8669
|
function statusPath(cwd) {
|
|
8400
8670
|
return join17(cwd, ".runwork", "deploy-status.json");
|
|
8401
8671
|
}
|
|
@@ -8425,6 +8695,9 @@ function readDeployStatus(cwd) {
|
|
|
8425
8695
|
}
|
|
8426
8696
|
}
|
|
8427
8697
|
|
|
8698
|
+
// src/commands/deploy.ts
|
|
8699
|
+
init_session();
|
|
8700
|
+
|
|
8428
8701
|
// src/deploy/detach.ts
|
|
8429
8702
|
init_detach();
|
|
8430
8703
|
import * as fs5 from "fs";
|
|
@@ -8536,7 +8809,7 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8536
8809
|
if (opts.detach && !isChild) {
|
|
8537
8810
|
const startedAt = new Date().toISOString();
|
|
8538
8811
|
const handle = spawnDetachedDeploy(buildDeployChildArgs(process.argv), cwd);
|
|
8539
|
-
writeDeployStatus(cwd, { state: "in-progress", startedAt, pid: handle.pid });
|
|
8812
|
+
writeDeployStatus(cwd, { state: "in-progress", startedAt, pid: handle.pid, bootTime: currentBootTime() });
|
|
8540
8813
|
if (useJson) {
|
|
8541
8814
|
jsonLine({ event: "deploy_started", detached: true, pid: handle.pid, logPath: deployLogPath(cwd), startedAt, hint: "Poll `runwork deploy --status` for completion." });
|
|
8542
8815
|
} else {
|
|
@@ -8639,7 +8912,7 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8639
8912
|
const deployed = deploymentUrl.length > 0;
|
|
8640
8913
|
if (!deployed) {
|
|
8641
8914
|
if (useJson) {
|
|
8642
|
-
jsonOut(buildErrorResponse("deploy", "Deployment did not produce a URL", "The deploy API returned no deployment URL. The deploy may have been a no-op (no changes) or failed server-side.", ["Confirm your changes were committed and pushed (the sync step above succeeded)", "Run runwork info to check the deployed state", "
|
|
8915
|
+
jsonOut(buildErrorResponse("deploy", "Deployment did not produce a URL", "The deploy API returned no deployment URL. The deploy may have been a no-op (no changes) or failed server-side (e.g. a build error).", ['Run `runwork logs --events --once` and look for a "Build failed" error in recent deployment events', "If a build failed on stale sandbox files, run `runwork dev --restart --detach` to resync, then retry the deploy", "Confirm your changes were committed and pushed (the sync step above succeeded)", "Run runwork info to check the deployed state", "Retry runwork deploy"]));
|
|
8643
8916
|
process.exit(1);
|
|
8644
8917
|
}
|
|
8645
8918
|
console.error("Deploy did not return a URL. The deploy may have been a no-op or failed server-side.");
|
|
@@ -8679,14 +8952,24 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8679
8952
|
function printDeployStatus(cwd, useJson) {
|
|
8680
8953
|
const status = readDeployStatus(cwd);
|
|
8681
8954
|
const summary = getDeploySummary(cwd);
|
|
8955
|
+
const evaluated = status ? evaluateDeployStatus(status) : null;
|
|
8956
|
+
const staleHint = "The deploy process died before recording an outcome. The deploy may still have completed server-side (the push precedes the deploy call). Verify with `runwork logs --production --once` or `runwork apps info`, then re-run `runwork deploy` if needed.";
|
|
8682
8957
|
if (useJson) {
|
|
8683
|
-
jsonOut({
|
|
8958
|
+
jsonOut({
|
|
8959
|
+
success: true,
|
|
8960
|
+
command: "deploy",
|
|
8961
|
+
result: {
|
|
8962
|
+
status: evaluated ? { ...evaluated.status, state: evaluated.effectiveState, staleReason: evaluated.staleReason } : null,
|
|
8963
|
+
deploy: summary,
|
|
8964
|
+
hint: evaluated?.effectiveState === "stale" ? staleHint : undefined
|
|
8965
|
+
}
|
|
8966
|
+
});
|
|
8684
8967
|
return;
|
|
8685
8968
|
}
|
|
8686
|
-
if (!status) {
|
|
8969
|
+
if (!status || !evaluated) {
|
|
8687
8970
|
console.log("No deploy has been started from this machine yet.");
|
|
8688
8971
|
} else {
|
|
8689
|
-
const label =
|
|
8972
|
+
const label = evaluated.effectiveState === "succeeded" ? green("succeeded") : evaluated.effectiveState === "failed" ? yellow("failed") : evaluated.effectiveState === "stale" ? yellow(`stale (${evaluated.staleReason})`) : "in-progress";
|
|
8690
8973
|
console.log(`Last deploy: ${label}`);
|
|
8691
8974
|
console.log(dim(` started: ${status.startedAt}`));
|
|
8692
8975
|
if (status.finishedAt)
|
|
@@ -8695,8 +8978,10 @@ function printDeployStatus(cwd, useJson) {
|
|
|
8695
8978
|
console.log(dim(` url: ${status.url}`));
|
|
8696
8979
|
if (status.error)
|
|
8697
8980
|
console.log(yellow(` error: ${status.error}`));
|
|
8698
|
-
if (
|
|
8981
|
+
if (evaluated.effectiveState === "in-progress")
|
|
8699
8982
|
console.log(dim(` logs: ${deployLogPath(cwd)}`));
|
|
8983
|
+
if (evaluated.effectiveState === "stale")
|
|
8984
|
+
console.log(yellow(` ${staleHint}`));
|
|
8700
8985
|
}
|
|
8701
8986
|
if (summary.deployedShortSha) {
|
|
8702
8987
|
const sync = summary.inSync === true ? green("in sync") : summary.inSync === false ? yellow("local has undeployed commits") : dim("unknown");
|
|
@@ -9280,9 +9565,14 @@ import { readFileSync as readFileSync18, existsSync as existsSync21 } from "fs";
|
|
|
9280
9565
|
init_store();
|
|
9281
9566
|
init_prompt();
|
|
9282
9567
|
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
9568
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9283
9569
|
async function resolveWorkspace2(client, options = {}) {
|
|
9284
9570
|
if (options.workspace) {
|
|
9285
|
-
|
|
9571
|
+
if (UUID_RE.test(options.workspace)) {
|
|
9572
|
+
return { workspaceId: options.workspace, workspaceName: "", source: "flag" };
|
|
9573
|
+
}
|
|
9574
|
+
const ws2 = await resolveWorkspace(client, options.workspace);
|
|
9575
|
+
return { workspaceId: ws2.id, workspaceName: ws2.name, source: "flag" };
|
|
9286
9576
|
}
|
|
9287
9577
|
if (process.env.WORKSPACE_ID) {
|
|
9288
9578
|
return { workspaceId: process.env.WORKSPACE_ID, workspaceName: "", source: "flag" };
|
|
@@ -9317,6 +9607,10 @@ async function resolveWorkspace2(client, options = {}) {
|
|
|
9317
9607
|
saveDefaultWorkspace(ws2.id, ws2.name);
|
|
9318
9608
|
return { workspaceId: ws2.id, workspaceName: ws2.name, source: "prompt" };
|
|
9319
9609
|
}
|
|
9610
|
+
if (!process.stdout.isTTY) {
|
|
9611
|
+
console.error(`Workspace required. Pass --workspace <name-or-id>. Available: ${workspaces.map((w) => w.name).join(", ")}`);
|
|
9612
|
+
process.exit(1);
|
|
9613
|
+
}
|
|
9320
9614
|
const choices = workspaces.map((ws2) => ({ label: ws2.name, value: ws2.id }));
|
|
9321
9615
|
const chosen = await promptSelect("Select a workspace:", choices);
|
|
9322
9616
|
const ws = workspaces.find((w) => w.id === chosen.value);
|
|
@@ -9545,7 +9839,13 @@ async function parseCurlToRequest(curlStr) {
|
|
|
9545
9839
|
}
|
|
9546
9840
|
|
|
9547
9841
|
// src/commands/integrations.ts
|
|
9548
|
-
|
|
9842
|
+
function resolveIntegrationConnection(integrations, integration) {
|
|
9843
|
+
const match = integrations.find((i) => (i.canonicalId || i.integrationId) === integration || i.provider === integration);
|
|
9844
|
+
if (!match)
|
|
9845
|
+
return null;
|
|
9846
|
+
return { connectionId: match.id || match.integrationId, match };
|
|
9847
|
+
}
|
|
9848
|
+
var searchCommand = new Command10("search").description("Search available integrations from the platform catalog").argument("<query>", 'Search query (e.g., "google drive", "hubspot", "slack")').option("--limit <n>", "Maximum results to show", "20").option("--workspace <name-or-id>", "Workspace name or ID (enables team usage info)").action(async (query, opts, command) => {
|
|
9549
9849
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9550
9850
|
const credentials = requireAuth();
|
|
9551
9851
|
const client = new ApiClient(credentials);
|
|
@@ -9604,7 +9904,7 @@ Usage: Add the integration ID to APP_INTEGRATION_REQUIREMENTS in worker/integrat
|
|
|
9604
9904
|
process.exit(1);
|
|
9605
9905
|
}
|
|
9606
9906
|
});
|
|
9607
|
-
var listCommand = new Command10("list").description("List connected workspace integrations").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
9907
|
+
var listCommand = new Command10("list").description("List connected workspace integrations").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
9608
9908
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9609
9909
|
const credentials = requireAuth();
|
|
9610
9910
|
const client = new ApiClient(credentials);
|
|
@@ -9657,7 +9957,7 @@ Used by your team (${teamOnly.length} more):
|
|
|
9657
9957
|
process.exit(1);
|
|
9658
9958
|
}
|
|
9659
9959
|
});
|
|
9660
|
-
var callCommand = new Command10("call").description("Make a proxy call to a connected integration").argument("<integration>", "Integration name (e.g., hubspot, slack)").argument("[method]", "HTTP method (GET, POST, PUT, DELETE)").argument("[path]", "API path (e.g., /crm/v3/contacts)").option("--workspace <id>", "Workspace ID").option("--body <json>", "Request body JSON").option("--header <header>", "Request header (repeatable)", (val, prev) => [...prev, val], []).option("--query <query>", "Query string (e.g., limit=10&offset=0)").option("--curl <command>", 'Parse a curl command (paste from Chrome DevTools "Copy as cURL")').option("--curl-file <file>", "Read curl command from a file").action(async (integration, method, path2, opts, command) => {
|
|
9960
|
+
var callCommand = new Command10("call").description("Make a proxy call to a connected integration").argument("<integration>", "Integration name (e.g., hubspot, slack)").argument("[method]", "HTTP method (GET, POST, PUT, DELETE)").argument("[path]", "API path (e.g., /crm/v3/contacts)").option("--workspace <name-or-id>", "Workspace name or ID").option("--body <json>", "Request body JSON").option("--header <header>", "Request header (repeatable)", (val, prev) => [...prev, val], []).option("--query <query>", "Query string (e.g., limit=10&offset=0)").option("--curl <command>", 'Parse a curl command (paste from Chrome DevTools "Copy as cURL")').option("--curl-file <file>", "Read curl command from a file").action(async (integration, method, path2, opts, command) => {
|
|
9661
9961
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9662
9962
|
const credentials = requireAuth();
|
|
9663
9963
|
const client = new ApiClient(credentials);
|
|
@@ -9711,12 +10011,12 @@ var callCommand = new Command10("call").description("Make a proxy call to a conn
|
|
|
9711
10011
|
}
|
|
9712
10012
|
try {
|
|
9713
10013
|
const integrations = await client.listConnectedIntegrations(workspaceId);
|
|
9714
|
-
const
|
|
9715
|
-
if (!
|
|
10014
|
+
const resolved = resolveIntegrationConnection(integrations, integration);
|
|
10015
|
+
if (!resolved) {
|
|
9716
10016
|
console.error(`Integration "${integration}" not found. Run "runwork integrations list" to see connected integrations.`);
|
|
9717
10017
|
process.exit(1);
|
|
9718
10018
|
}
|
|
9719
|
-
const result = await client.callIntegrationProxy(
|
|
10019
|
+
const result = await client.callIntegrationProxy(resolved.connectionId, finalMethod, finalPath, { body, headers: Object.keys(headers).length > 0 ? headers : undefined, query });
|
|
9720
10020
|
if (useJson) {
|
|
9721
10021
|
jsonOut(result);
|
|
9722
10022
|
return;
|
|
@@ -10345,7 +10645,7 @@ function truncate(text2, max) {
|
|
|
10345
10645
|
return first;
|
|
10346
10646
|
return first.slice(0, max - 3) + "...";
|
|
10347
10647
|
}
|
|
10348
|
-
var listCommand2 = new Command13("list").description("List workspace skills (app, external, MCP-generated)").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
10648
|
+
var listCommand2 = new Command13("list").description("List workspace skills (app, external, MCP-generated)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
10349
10649
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10350
10650
|
const credentials = requireAuth();
|
|
10351
10651
|
const client = new ApiClient(credentials);
|
|
@@ -10402,7 +10702,7 @@ async function readStdin2() {
|
|
|
10402
10702
|
}
|
|
10403
10703
|
return Buffer.concat(chunks).toString("utf-8");
|
|
10404
10704
|
}
|
|
10405
|
-
var pushCommand = new Command13("push").description("Upload a local skill file to workspace (upsert by name). Accepts piped content via stdin.").argument("[first]", "Skill name (if piping content) or file path (name from frontmatter)").argument("[second]", "File path (when first arg is the skill name)").option("--workspace <id>", "Workspace ID").action(async (first, second, opts, command) => {
|
|
10705
|
+
var pushCommand = new Command13("push").description("Upload a local skill file to workspace (upsert by name). Accepts piped content via stdin.").argument("[first]", "Skill name (if piping content) or file path (name from frontmatter)").argument("[second]", "File path (when first arg is the skill name)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (first, second, opts, command) => {
|
|
10406
10706
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10407
10707
|
const credentials = requireAuth();
|
|
10408
10708
|
const client = new ApiClient(credentials);
|
|
@@ -10497,7 +10797,7 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10497
10797
|
process.exit(1);
|
|
10498
10798
|
}
|
|
10499
10799
|
});
|
|
10500
|
-
var pullCommand = new Command13("pull").alias("download").description("Download a skill from the workspace and print to stdout (or save to file)").argument("<name>", "Skill name or ID").option("--workspace <id>", "Workspace ID").option("-o, --output <file>", "Save to file instead of stdout").action(async (nameOrId, opts, command) => {
|
|
10800
|
+
var pullCommand = new Command13("pull").alias("download").description("Download a skill from the workspace and print to stdout (or save to file)").argument("<name>", "Skill name or ID").option("--workspace <name-or-id>", "Workspace name or ID").option("-o, --output <file>", "Save to file instead of stdout").action(async (nameOrId, opts, command) => {
|
|
10501
10801
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10502
10802
|
const credentials = requireAuth();
|
|
10503
10803
|
const client = new ApiClient(credentials);
|
|
@@ -10600,7 +10900,7 @@ var searchCommand2 = new Command13("search").description("Search community skill
|
|
|
10600
10900
|
process.exit(1);
|
|
10601
10901
|
}
|
|
10602
10902
|
});
|
|
10603
|
-
var installCommand = new Command13("install").description("Install a community skill from skills.sh into your workspace").argument("<identifier>", "Skill identifier: source/skill-id (e.g. vercel-labs/skills/find-skills)").option("--workspace <id>", "Workspace ID").action(async (identifier, opts, command) => {
|
|
10903
|
+
var installCommand = new Command13("install").description("Install a community skill from skills.sh into your workspace").argument("<identifier>", "Skill identifier: source/skill-id (e.g. vercel-labs/skills/find-skills)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (identifier, opts, command) => {
|
|
10604
10904
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10605
10905
|
const credentials = requireAuth();
|
|
10606
10906
|
const client = new ApiClient(credentials);
|
|
@@ -11118,6 +11418,33 @@ function formatCombinedDigest(sessions, opts = { days: 7 }) {
|
|
|
11118
11418
|
// src/agents/utils/json-config.ts
|
|
11119
11419
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, existsSync as existsSync25 } from "fs";
|
|
11120
11420
|
import { dirname as dirname5 } from "path";
|
|
11421
|
+
|
|
11422
|
+
// src/sync/hash.ts
|
|
11423
|
+
import { createHash as createHash2 } from "crypto";
|
|
11424
|
+
function contentHash(content) {
|
|
11425
|
+
return "sha256:" + createHash2("sha256").update(content).digest("hex");
|
|
11426
|
+
}
|
|
11427
|
+
function stableStringify(value) {
|
|
11428
|
+
return JSON.stringify(sortValue(value));
|
|
11429
|
+
}
|
|
11430
|
+
function sortValue(value) {
|
|
11431
|
+
if (Array.isArray(value))
|
|
11432
|
+
return value.map(sortValue);
|
|
11433
|
+
if (value !== null && typeof value === "object") {
|
|
11434
|
+
const obj = value;
|
|
11435
|
+
const sorted = {};
|
|
11436
|
+
for (const key of Object.keys(obj).sort()) {
|
|
11437
|
+
sorted[key] = sortValue(obj[key]);
|
|
11438
|
+
}
|
|
11439
|
+
return sorted;
|
|
11440
|
+
}
|
|
11441
|
+
return value;
|
|
11442
|
+
}
|
|
11443
|
+
function configHash(value) {
|
|
11444
|
+
return contentHash(stableStringify(value));
|
|
11445
|
+
}
|
|
11446
|
+
|
|
11447
|
+
// src/agents/utils/json-config.ts
|
|
11121
11448
|
function isRunworkManagedKey(key) {
|
|
11122
11449
|
return key === RUNWORK_WORKSPACE_MCP_NAME || key.startsWith(RUNWORK_MCP_PREFIX) || key.startsWith(RUNWORK_MCP_PREFIX_LEGACY);
|
|
11123
11450
|
}
|
|
@@ -11156,6 +11483,14 @@ function removeRunworkMcpServers(filePath, topKey) {
|
|
|
11156
11483
|
function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
11157
11484
|
const config = readJsonConfig(filePath);
|
|
11158
11485
|
const existing = config[topKey] || {};
|
|
11486
|
+
const managedBefore = {};
|
|
11487
|
+
for (const key of Object.keys(existing)) {
|
|
11488
|
+
if (isRunworkManagedKey(key))
|
|
11489
|
+
managedBefore[key] = existing[key];
|
|
11490
|
+
}
|
|
11491
|
+
const changed = configHash(managedBefore) !== configHash(servers);
|
|
11492
|
+
if (!changed)
|
|
11493
|
+
return false;
|
|
11159
11494
|
for (const key of Object.keys(existing)) {
|
|
11160
11495
|
if (isRunworkManagedKey(key)) {
|
|
11161
11496
|
delete existing[key];
|
|
@@ -11164,6 +11499,7 @@ function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
|
11164
11499
|
Object.assign(existing, servers);
|
|
11165
11500
|
config[topKey] = existing;
|
|
11166
11501
|
writeJsonConfig(filePath, config);
|
|
11502
|
+
return true;
|
|
11167
11503
|
}
|
|
11168
11504
|
|
|
11169
11505
|
// src/agents/utils/instruction-hint.ts
|
|
@@ -11487,6 +11823,19 @@ function resolveAgentDefaults(input) {
|
|
|
11487
11823
|
};
|
|
11488
11824
|
}
|
|
11489
11825
|
|
|
11826
|
+
// src/ui/verbosity.ts
|
|
11827
|
+
var verbose = false;
|
|
11828
|
+
function setVerbose(value) {
|
|
11829
|
+
verbose = value;
|
|
11830
|
+
}
|
|
11831
|
+
function isVerbose() {
|
|
11832
|
+
return verbose;
|
|
11833
|
+
}
|
|
11834
|
+
function vlog(...args) {
|
|
11835
|
+
if (verbose)
|
|
11836
|
+
console.log(...args);
|
|
11837
|
+
}
|
|
11838
|
+
|
|
11490
11839
|
// src/agents/claude-code.ts
|
|
11491
11840
|
var PLUGIN_NAME = "runwork";
|
|
11492
11841
|
var PLUGIN_VERSION = "1.0.0";
|
|
@@ -11589,7 +11938,7 @@ class ClaudeCodeAdapter {
|
|
|
11589
11938
|
]
|
|
11590
11939
|
};
|
|
11591
11940
|
writeFileSync16(join21(hooksDir, "hooks.json"), JSON.stringify(hooksManifest, null, 2));
|
|
11592
|
-
|
|
11941
|
+
vlog(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
|
|
11593
11942
|
}
|
|
11594
11943
|
async writeSkills(skills, scope) {
|
|
11595
11944
|
const baseDir = scope === "project" ? join21(process.cwd(), ".claude", "skills") : join21(homedir5(), ".claude", "skills");
|
|
@@ -11622,6 +11971,7 @@ class ClaudeCodeAdapter {
|
|
|
11622
11971
|
}
|
|
11623
11972
|
this.registerPlugin(pluginDir);
|
|
11624
11973
|
}
|
|
11974
|
+
return skills.length;
|
|
11625
11975
|
}
|
|
11626
11976
|
async writeBuiltInHooks(scope) {
|
|
11627
11977
|
if (scope !== "user")
|
|
@@ -12116,7 +12466,7 @@ ${instructions}`;
|
|
|
12116
12466
|
lastUpdated: new Date().toISOString()
|
|
12117
12467
|
};
|
|
12118
12468
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
12119
|
-
|
|
12469
|
+
vlog(` [Claude Code] ${wasRegistered ? "Refreshed" : "Registered"} plugin ` + `${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
|
|
12120
12470
|
const settingsPath = join21(homedir5(), ".claude", "settings.json");
|
|
12121
12471
|
const settings = readJsonConfig(settingsPath);
|
|
12122
12472
|
if (!settings["enabledPlugins"]) {
|
|
@@ -12416,12 +12766,12 @@ class ClaudeDesktopAdapter {
|
|
|
12416
12766
|
if (rpm) {
|
|
12417
12767
|
writePluginMetadata(rpm.pluginPath, getPluginMetadata());
|
|
12418
12768
|
writePluginSkills(rpm.pluginPath, skills);
|
|
12419
|
-
return;
|
|
12769
|
+
return skills.length;
|
|
12420
12770
|
}
|
|
12421
12771
|
const pluginsDir = findCoworkPluginsDir();
|
|
12422
12772
|
if (!pluginsDir) {
|
|
12423
12773
|
console.warn(" [Claude Desktop] Cowork plugins directory not found. Open Claude Desktop at least once first.");
|
|
12424
|
-
return;
|
|
12774
|
+
return 0;
|
|
12425
12775
|
}
|
|
12426
12776
|
const cacheDir = join23(pluginsDir, "cache", "runwork", PLUGIN_NAME2, PLUGIN_VERSION2);
|
|
12427
12777
|
const marketRoot = join23(pluginsDir, "marketplaces", "runwork");
|
|
@@ -12456,6 +12806,7 @@ class ClaudeDesktopAdapter {
|
|
|
12456
12806
|
};
|
|
12457
12807
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
12458
12808
|
setCoworkPluginEnabled(pluginsDir, true);
|
|
12809
|
+
return skills.length;
|
|
12459
12810
|
}
|
|
12460
12811
|
async writeInstructionHint(hint, _scope) {
|
|
12461
12812
|
for (const path2 of getCoworkMemoryClaudeMdPaths()) {
|
|
@@ -12946,7 +13297,7 @@ class CursorAdapter {
|
|
|
12946
13297
|
}
|
|
12947
13298
|
async writeSkills(skills, scope) {
|
|
12948
13299
|
if (scope === "user")
|
|
12949
|
-
return;
|
|
13300
|
+
return 0;
|
|
12950
13301
|
const rulesDir = join24(process.cwd(), ".cursor", "rules");
|
|
12951
13302
|
mkdirSync18(rulesDir, { recursive: true });
|
|
12952
13303
|
for (const skill of skills) {
|
|
@@ -12958,6 +13309,7 @@ alwaysApply: false
|
|
|
12958
13309
|
${skill.content}`;
|
|
12959
13310
|
writeFileSync19(join24(rulesDir, `${skill.filename}.mdc`), mdcContent);
|
|
12960
13311
|
}
|
|
13312
|
+
return skills.length;
|
|
12961
13313
|
}
|
|
12962
13314
|
async writeInstructionHint(hint, scope) {
|
|
12963
13315
|
const filePath = scope === "project" ? join24(process.cwd(), ".cursor", "rules", "runwork.mdc") : join24(homedir7(), ".cursor", "rules", "runwork.mdc");
|
|
@@ -13210,7 +13562,7 @@ class WindsurfAdapter {
|
|
|
13210
13562
|
}
|
|
13211
13563
|
async writeSkills(skills, scope) {
|
|
13212
13564
|
if (scope === "user")
|
|
13213
|
-
return;
|
|
13565
|
+
return 0;
|
|
13214
13566
|
const rulesDir = join25(process.cwd(), ".windsurf", "rules");
|
|
13215
13567
|
mkdirSync19(rulesDir, { recursive: true });
|
|
13216
13568
|
for (const skill of skills) {
|
|
@@ -13221,6 +13573,7 @@ trigger: manual
|
|
|
13221
13573
|
${skill.content}`;
|
|
13222
13574
|
writeFileSync20(join25(rulesDir, `${skill.filename}.md`), content);
|
|
13223
13575
|
}
|
|
13576
|
+
return skills.length;
|
|
13224
13577
|
}
|
|
13225
13578
|
async writeTeamInstructions(instructions, scope) {
|
|
13226
13579
|
const filePath = scope === "project" ? join25(process.cwd(), ".windsurf", "rules", "runwork-team.md") : join25(getWindsurfDataDir(), "rules", "runwork-team.md");
|
|
@@ -13464,14 +13817,15 @@ var AGENT_REGISTRY = [
|
|
|
13464
13817
|
method: "any",
|
|
13465
13818
|
target: [
|
|
13466
13819
|
{ method: "path", target: { macos: "/Applications/Codex.app" } },
|
|
13467
|
-
{ method: "
|
|
13820
|
+
{ method: "macos-bundle-id", target: "com.openai.codex" },
|
|
13821
|
+
{ method: "windows-appx", target: ["OpenAI.Codex"] }
|
|
13468
13822
|
]
|
|
13469
13823
|
},
|
|
13470
|
-
launch: { app: { macos: "Codex", windows: "Codex" } },
|
|
13471
|
-
logo: "
|
|
13824
|
+
launch: { app: { macos: "Codex", windows: "Codex" }, bundleId: { macos: "com.openai.codex" } },
|
|
13825
|
+
logo: "codex",
|
|
13472
13826
|
downloadUrl: "https://openai.com/codex/",
|
|
13473
13827
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13474
|
-
instructionFile: { global: ".codex/
|
|
13828
|
+
instructionFile: { global: ".codex/AGENTS.md", project: "AGENTS.md" },
|
|
13475
13829
|
firstClass: true,
|
|
13476
13830
|
resumeCapability: {
|
|
13477
13831
|
mode: "file-drop-only",
|
|
@@ -13493,10 +13847,10 @@ var AGENT_REGISTRY = [
|
|
|
13493
13847
|
category: "cli",
|
|
13494
13848
|
detection: { method: "binary", target: "codex" },
|
|
13495
13849
|
launch: { cli: "codex", cliAcceptsPrompt: true },
|
|
13496
|
-
logo: "
|
|
13850
|
+
logo: "codex",
|
|
13497
13851
|
downloadUrl: "https://github.com/openai/codex",
|
|
13498
13852
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13499
|
-
instructionFile: { global: ".codex/
|
|
13853
|
+
instructionFile: { global: ".codex/AGENTS.md", project: "AGENTS.md" },
|
|
13500
13854
|
firstClass: true,
|
|
13501
13855
|
resumeCapability: {
|
|
13502
13856
|
mode: "cli-resume",
|
|
@@ -13590,10 +13944,11 @@ var AGENT_REGISTRY = [
|
|
|
13590
13944
|
method: "any",
|
|
13591
13945
|
target: [
|
|
13592
13946
|
{ method: "path", target: { macos: "/Applications/ChatGPT.app" } },
|
|
13593
|
-
{ method: "
|
|
13947
|
+
{ method: "macos-bundle-id", target: "com.openai.chat" },
|
|
13948
|
+
{ method: "windows-appx", target: ["OpenAI.ChatGPT"] }
|
|
13594
13949
|
]
|
|
13595
13950
|
},
|
|
13596
|
-
launch: { app: { macos: "ChatGPT", windows: "ChatGPT" }, url: "https://chatgpt.com/?prompt={prompt}" },
|
|
13951
|
+
launch: { app: { macos: "ChatGPT", windows: "ChatGPT" }, bundleId: { macos: "com.openai.chat" }, url: "https://chatgpt.com/?prompt={prompt}" },
|
|
13597
13952
|
logo: "openai",
|
|
13598
13953
|
downloadUrl: "https://chatgpt.com/download",
|
|
13599
13954
|
firstClass: true,
|
|
@@ -13883,14 +14238,28 @@ function resolveToAbsolute(ps, scope) {
|
|
|
13883
14238
|
return scope === "global" ? join26(homedir9(), resolved) : join26(process.cwd(), resolved);
|
|
13884
14239
|
}
|
|
13885
14240
|
|
|
14241
|
+
// src/agents/detection-probes.ts
|
|
14242
|
+
function powershellQuote(value) {
|
|
14243
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
14244
|
+
}
|
|
14245
|
+
function isValidBundleId(id) {
|
|
14246
|
+
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
14247
|
+
}
|
|
14248
|
+
function macosBundleIdProbeScript(id) {
|
|
14249
|
+
return `p=$(mdfind "kMDItemCFBundleIdentifier == '${id}'" 2>/dev/null | head -1); ` + `if [ -n "$p" ]; then exit 0; fi; ` + `for a in /Applications/*.app "$HOME"/Applications/*.app; do ` + `[ -e "$a" ] || continue; ` + `if [ "$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$a/Contents/Info.plist" 2>/dev/null)" = "${id}" ]; then exit 0; fi; ` + `done; exit 1`;
|
|
14250
|
+
}
|
|
14251
|
+
function appxPackageProbeScript(pkg) {
|
|
14252
|
+
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
14253
|
+
}
|
|
14254
|
+
function startAppProbeScript(pattern) {
|
|
14255
|
+
return `$a = Get-StartApps -Name ${powershellQuote(pattern)} -ErrorAction SilentlyContinue | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
14256
|
+
}
|
|
14257
|
+
|
|
13886
14258
|
// src/agents/detection.ts
|
|
13887
14259
|
var execFileAsync = promisify(execFile);
|
|
13888
14260
|
function isWindows() {
|
|
13889
14261
|
return platform7() === "win32";
|
|
13890
14262
|
}
|
|
13891
|
-
function powershellQuote(value) {
|
|
13892
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
13893
|
-
}
|
|
13894
14263
|
function toList(value) {
|
|
13895
14264
|
return Array.isArray(value) ? value : [value];
|
|
13896
14265
|
}
|
|
@@ -13910,23 +14279,30 @@ function checkPath(target) {
|
|
|
13910
14279
|
return existsSync32(resolved);
|
|
13911
14280
|
return existsSync32(join27(homedir10(), resolved));
|
|
13912
14281
|
}
|
|
14282
|
+
async function checkMacosBundleId(target) {
|
|
14283
|
+
if (platform7() !== "darwin")
|
|
14284
|
+
return false;
|
|
14285
|
+
for (const id of toList(target)) {
|
|
14286
|
+
if (!isValidBundleId(id))
|
|
14287
|
+
continue;
|
|
14288
|
+
try {
|
|
14289
|
+
await execFileAsync("sh", ["-c", macosBundleIdProbeScript(id)]);
|
|
14290
|
+
return true;
|
|
14291
|
+
} catch {}
|
|
14292
|
+
}
|
|
14293
|
+
return false;
|
|
14294
|
+
}
|
|
13913
14295
|
async function checkWindowsAppxPackage(target) {
|
|
13914
14296
|
if (!isWindows())
|
|
13915
14297
|
return false;
|
|
13916
|
-
const probes = toList(target).map((pkg) =>
|
|
13917
|
-
const script = `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
13918
|
-
return runPowerShell(script);
|
|
13919
|
-
});
|
|
14298
|
+
const probes = toList(target).map((pkg) => runPowerShell(appxPackageProbeScript(pkg)));
|
|
13920
14299
|
const results = await Promise.all(probes);
|
|
13921
14300
|
return results.some(Boolean);
|
|
13922
14301
|
}
|
|
13923
14302
|
async function checkWindowsStartApp(target) {
|
|
13924
14303
|
if (!isWindows())
|
|
13925
14304
|
return false;
|
|
13926
|
-
const probes = toList(target).map((pattern) =>
|
|
13927
|
-
const script = `$a = Get-StartApps -Name ${powershellQuote(pattern)} -ErrorAction SilentlyContinue | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
13928
|
-
return runPowerShell(script);
|
|
13929
|
-
});
|
|
14305
|
+
const probes = toList(target).map((pattern) => runPowerShell(startAppProbeScript(pattern)));
|
|
13930
14306
|
const results = await Promise.all(probes);
|
|
13931
14307
|
return results.some(Boolean);
|
|
13932
14308
|
}
|
|
@@ -13942,6 +14318,8 @@ async function runAgentDetection(detection) {
|
|
|
13942
14318
|
return checkWindowsAppxPackage(detection.target);
|
|
13943
14319
|
case "windows-start-app":
|
|
13944
14320
|
return checkWindowsStartApp(detection.target);
|
|
14321
|
+
case "macos-bundle-id":
|
|
14322
|
+
return checkMacosBundleId(detection.target);
|
|
13945
14323
|
case "any": {
|
|
13946
14324
|
const probes = await Promise.all(detection.target.map((p) => runAgentDetection(p)));
|
|
13947
14325
|
return probes.some(Boolean);
|
|
@@ -14016,6 +14394,7 @@ class CodexAdapter {
|
|
|
14016
14394
|
mkdirSync20(skillDir, { recursive: true });
|
|
14017
14395
|
writeFileSync21(join28(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14018
14396
|
}
|
|
14397
|
+
return skills.length;
|
|
14019
14398
|
}
|
|
14020
14399
|
async writeInstructionHint(hint, scope) {
|
|
14021
14400
|
const filePath = scope === "project" ? join28(process.cwd(), "AGENTS.md") : join28(homedir11(), ".codex", "AGENTS.md");
|
|
@@ -14052,6 +14431,26 @@ class CodexAdapter {
|
|
|
14052
14431
|
}
|
|
14053
14432
|
}
|
|
14054
14433
|
}
|
|
14434
|
+
if (config.networkAllowlist?.length) {
|
|
14435
|
+
const features = parsed.features && typeof parsed.features === "object" ? parsed.features : undefined;
|
|
14436
|
+
const proxy = features?.network_proxy;
|
|
14437
|
+
if (proxy && proxy.enabled === true) {
|
|
14438
|
+
if (!proxy.domains || typeof proxy.domains !== "object")
|
|
14439
|
+
proxy.domains = {};
|
|
14440
|
+
const domains = proxy.domains;
|
|
14441
|
+
for (const host of config.networkAllowlist) {
|
|
14442
|
+
if (!(host in domains))
|
|
14443
|
+
domains[host] = "allow";
|
|
14444
|
+
}
|
|
14445
|
+
} else {
|
|
14446
|
+
if (!parsed.sandbox_workspace_write || typeof parsed.sandbox_workspace_write !== "object") {
|
|
14447
|
+
parsed.sandbox_workspace_write = {};
|
|
14448
|
+
}
|
|
14449
|
+
const sww = parsed.sandbox_workspace_write;
|
|
14450
|
+
if (sww.network_access === undefined)
|
|
14451
|
+
sww.network_access = true;
|
|
14452
|
+
}
|
|
14453
|
+
}
|
|
14055
14454
|
mkdirSync20(join28(configPath, ".."), { recursive: true });
|
|
14056
14455
|
writeFileSync21(configPath, stringify(parsed));
|
|
14057
14456
|
}
|
|
@@ -14377,12 +14776,13 @@ class ClineAdapter {
|
|
|
14377
14776
|
}
|
|
14378
14777
|
async writeSkills(skills, scope) {
|
|
14379
14778
|
if (scope === "user")
|
|
14380
|
-
return;
|
|
14779
|
+
return 0;
|
|
14381
14780
|
const rulesDir = join29(process.cwd(), ".clinerules");
|
|
14382
14781
|
mkdirSync21(rulesDir, { recursive: true });
|
|
14383
14782
|
for (const skill of skills) {
|
|
14384
14783
|
writeFileSync22(join29(rulesDir, `${skill.filename}.md`), buildSkillMd2(skill));
|
|
14385
14784
|
}
|
|
14785
|
+
return skills.length;
|
|
14386
14786
|
}
|
|
14387
14787
|
async writeInstructionHint(hint, scope) {
|
|
14388
14788
|
if (scope === "user")
|
|
@@ -14507,6 +14907,7 @@ class GeminiAdapter {
|
|
|
14507
14907
|
mkdirSync22(skillDir, { recursive: true });
|
|
14508
14908
|
writeFileSync23(join30(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14509
14909
|
}
|
|
14910
|
+
return skills.length;
|
|
14510
14911
|
}
|
|
14511
14912
|
async writeInstructionHint(hint, scope) {
|
|
14512
14913
|
const filePath = scope === "project" ? join30(process.cwd(), "GEMINI.md") : join30(homedir13(), ".gemini", "GEMINI.md");
|
|
@@ -14606,13 +15007,13 @@ class GenericAgentAdapter {
|
|
|
14606
15007
|
}
|
|
14607
15008
|
async writeSkills(skills, scope) {
|
|
14608
15009
|
if (!this.def.skillsPaths)
|
|
14609
|
-
return;
|
|
15010
|
+
return 0;
|
|
14610
15011
|
const pathTemplate = scope === "project" ? this.def.skillsPaths.project : this.def.skillsPaths.global;
|
|
14611
15012
|
if (!pathTemplate)
|
|
14612
|
-
return;
|
|
15013
|
+
return 0;
|
|
14613
15014
|
const baseDir = resolveToAbsolute(pathTemplate, scope === "project" ? "project" : "global");
|
|
14614
15015
|
if (!baseDir)
|
|
14615
|
-
return;
|
|
15016
|
+
return 0;
|
|
14616
15017
|
for (const skill of skills) {
|
|
14617
15018
|
if (skill.name !== skill.filename) {
|
|
14618
15019
|
const oldDir = join31(baseDir, skill.name);
|
|
@@ -14626,6 +15027,7 @@ class GenericAgentAdapter {
|
|
|
14626
15027
|
mkdirSync23(skillDir, { recursive: true });
|
|
14627
15028
|
writeFileSync24(join31(skillDir, "SKILL.md"), buildSkillMd2(skill));
|
|
14628
15029
|
}
|
|
15030
|
+
return skills.length;
|
|
14629
15031
|
}
|
|
14630
15032
|
async writeInstructionHint(hint, scope) {
|
|
14631
15033
|
if (!this.def.instructionFile)
|
|
@@ -14740,9 +15142,9 @@ function printNoAgentsMessage() {
|
|
|
14740
15142
|
}
|
|
14741
15143
|
|
|
14742
15144
|
// src/utils/insight-id.ts
|
|
14743
|
-
import { createHash as
|
|
15145
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
14744
15146
|
function computeInsightId(userSeed, localKey) {
|
|
14745
|
-
return
|
|
15147
|
+
return createHash3("sha256").update(`${userSeed}:${localKey}`).digest("hex").slice(0, 22);
|
|
14746
15148
|
}
|
|
14747
15149
|
function insightLocalKey(teaches, slug) {
|
|
14748
15150
|
return `${teaches}:${slug}`.toLowerCase().replace(/[^a-z0-9:_-]+/g, "-").replace(/-+/g, "-");
|
|
@@ -15355,7 +15757,7 @@ function parseJson(content, source) {
|
|
|
15355
15757
|
}
|
|
15356
15758
|
|
|
15357
15759
|
// src/commands/entities.ts
|
|
15358
|
-
var listCommand3 = new Command15("list").description("List entities registered in the workspace").option("--workspace <id>", "Workspace ID").option("--app <id>", "Filter by app name or ID").option("--preview", "Show preview-mode entities only").action(async (opts, command) => {
|
|
15760
|
+
var listCommand3 = new Command15("list").description("List entities registered in the workspace").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <id>", "Filter by app name or ID").option("--preview", "Show preview-mode entities only").action(async (opts, command) => {
|
|
15359
15761
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15360
15762
|
const credentials = requireAuth();
|
|
15361
15763
|
const client = new ApiClient(credentials);
|
|
@@ -15390,7 +15792,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15390
15792
|
process.exit(1);
|
|
15391
15793
|
}
|
|
15392
15794
|
});
|
|
15393
|
-
var recordsCommand = new Command15("records").description("List records for an entity").argument("<entity>", "Entity name").option("--workspace <id>", "Workspace ID").option("--limit <n>", "Number of records to return", "20").option("--cursor <cursor>", "Pagination cursor").option("--preview", "Query preview-mode data").action(async (entity, opts, command) => {
|
|
15795
|
+
var recordsCommand = new Command15("records").description("List records for an entity").argument("<entity>", "Entity name").option("--workspace <name-or-id>", "Workspace name or ID").option("--limit <n>", "Number of records to return", "20").option("--cursor <cursor>", "Pagination cursor").option("--preview", "Query preview-mode data").action(async (entity, opts, command) => {
|
|
15394
15796
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15395
15797
|
const credentials = requireAuth();
|
|
15396
15798
|
const client = new ApiClient(credentials);
|
|
@@ -15422,7 +15824,7 @@ Next cursor: ${result.next}`);
|
|
|
15422
15824
|
process.exit(1);
|
|
15423
15825
|
}
|
|
15424
15826
|
});
|
|
15425
|
-
var getCommand = new Command15("get").description("Get a single entity record by ID").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <id>", "Workspace ID").action(async (entity, id, opts, command) => {
|
|
15827
|
+
var getCommand = new Command15("get").description("Get a single entity record by ID").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <name-or-id>", "Workspace name or ID").action(async (entity, id, opts, command) => {
|
|
15426
15828
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15427
15829
|
const credentials = requireAuth();
|
|
15428
15830
|
const client = new ApiClient(credentials);
|
|
@@ -15439,7 +15841,7 @@ var getCommand = new Command15("get").description("Get a single entity record by
|
|
|
15439
15841
|
process.exit(1);
|
|
15440
15842
|
}
|
|
15441
15843
|
});
|
|
15442
|
-
var createCommand = new Command15("create").description("Create a new entity record").argument("<entity>", "Entity name").option("--workspace <id>", "Workspace ID").option("--data <json>", "Record data as JSON string, @file.json, or pipe via stdin").option("--preview", "Create in preview-mode data store").action(async (entity, opts, command) => {
|
|
15844
|
+
var createCommand = new Command15("create").description("Create a new entity record").argument("<entity>", "Entity name").option("--workspace <name-or-id>", "Workspace name or ID").option("--data <json>", "Record data as JSON string, @file.json, or pipe via stdin").option("--preview", "Create in preview-mode data store").action(async (entity, opts, command) => {
|
|
15443
15845
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15444
15846
|
const credentials = requireAuth();
|
|
15445
15847
|
const client = new ApiClient(credentials);
|
|
@@ -15458,7 +15860,7 @@ var createCommand = new Command15("create").description("Create a new entity rec
|
|
|
15458
15860
|
process.exit(1);
|
|
15459
15861
|
}
|
|
15460
15862
|
});
|
|
15461
|
-
var updateCommand = new Command15("update").description("Update an existing entity record").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <id>", "Workspace ID").option("--data <json>", "Record data as JSON string, @file.json, or pipe via stdin").action(async (entity, id, opts, command) => {
|
|
15863
|
+
var updateCommand = new Command15("update").description("Update an existing entity record").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <name-or-id>", "Workspace name or ID").option("--data <json>", "Record data as JSON string, @file.json, or pipe via stdin").action(async (entity, id, opts, command) => {
|
|
15462
15864
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15463
15865
|
const credentials = requireAuth();
|
|
15464
15866
|
const client = new ApiClient(credentials);
|
|
@@ -15477,7 +15879,7 @@ var updateCommand = new Command15("update").description("Update an existing enti
|
|
|
15477
15879
|
process.exit(1);
|
|
15478
15880
|
}
|
|
15479
15881
|
});
|
|
15480
|
-
var deleteCommand = new Command15("delete").description("Delete an entity record").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <id>", "Workspace ID").option("--yes", "Skip confirmation prompt").action(async (entity, id, opts, command) => {
|
|
15882
|
+
var deleteCommand = new Command15("delete").description("Delete an entity record").argument("<entity>", "Entity name").argument("<id>", "Record ID").option("--workspace <name-or-id>", "Workspace name or ID").option("--yes", "Skip confirmation prompt").action(async (entity, id, opts, command) => {
|
|
15481
15883
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15482
15884
|
const credentials = requireAuth();
|
|
15483
15885
|
const client = new ApiClient(credentials);
|
|
@@ -15516,7 +15918,7 @@ function truncate2(text2, max) {
|
|
|
15516
15918
|
return first;
|
|
15517
15919
|
return first.slice(0, max - 3) + "...";
|
|
15518
15920
|
}
|
|
15519
|
-
var listCommand4 = new Command16("list").description("List workflows in a workspace").option("--workspace <id>", "Workspace ID").option("--app <name>", "Filter by app name or ID").action(async (opts, command) => {
|
|
15921
|
+
var listCommand4 = new Command16("list").description("List workflows in a workspace").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "Filter by app name or ID").action(async (opts, command) => {
|
|
15520
15922
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15521
15923
|
const credentials = requireAuth();
|
|
15522
15924
|
const client = new ApiClient(credentials);
|
|
@@ -15550,7 +15952,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15550
15952
|
process.exit(1);
|
|
15551
15953
|
}
|
|
15552
15954
|
});
|
|
15553
|
-
var triggerCommand = new Command16("trigger").description("Trigger a workflow by name").argument("<name>", "Workflow name").option("--workspace <id>", "Workspace ID").option("--app <name>", "App name or ID (required if not in a project directory)").option("--params <json>", "JSON string of parameters to pass to the workflow").option("--instance-id <id>", "Custom instance ID for the workflow run").action(async (name, opts, command) => {
|
|
15955
|
+
var triggerCommand = new Command16("trigger").description("Trigger a workflow by name").argument("<name>", "Workflow name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "App name or ID (required if not in a project directory)").option("--params <json>", "JSON string of parameters to pass to the workflow").option("--instance-id <id>", "Custom instance ID for the workflow run").action(async (name, opts, command) => {
|
|
15554
15956
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15555
15957
|
const credentials = requireAuth();
|
|
15556
15958
|
const client = new ApiClient(credentials);
|
|
@@ -15584,7 +15986,7 @@ var triggerCommand = new Command16("trigger").description("Trigger a workflow by
|
|
|
15584
15986
|
process.exit(1);
|
|
15585
15987
|
}
|
|
15586
15988
|
});
|
|
15587
|
-
var instancesCommand = new Command16("instances").description("List workflow instances for an app").option("--workspace <id>", "Workspace ID").option("--app <name>", "App name or ID (required if not in a project directory)").option("--workflow <name>", "Filter by workflow name").option("--status <status>", "Filter by instance status").option("--limit <n>", "Maximum number of instances to return").action(async (opts, command) => {
|
|
15989
|
+
var instancesCommand = new Command16("instances").description("List workflow instances for an app").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "App name or ID (required if not in a project directory)").option("--workflow <name>", "Filter by workflow name").option("--status <status>", "Filter by instance status").option("--limit <n>", "Maximum number of instances to return").action(async (opts, command) => {
|
|
15588
15990
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15589
15991
|
const credentials = requireAuth();
|
|
15590
15992
|
const client = new ApiClient(credentials);
|
|
@@ -15617,7 +16019,7 @@ var instancesCommand = new Command16("instances").description("List workflow ins
|
|
|
15617
16019
|
process.exit(1);
|
|
15618
16020
|
}
|
|
15619
16021
|
});
|
|
15620
|
-
var instanceCommand = new Command16("instance").description("Get details for a specific workflow instance").argument("<id>", "Workflow instance ID").option("--workspace <id>", "Workspace ID").option("--app <name>", "App name or ID (optional)").action(async (id, opts, command) => {
|
|
16022
|
+
var instanceCommand = new Command16("instance").description("Get details for a specific workflow instance").argument("<id>", "Workflow instance ID").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "App name or ID (optional)").action(async (id, opts, command) => {
|
|
15621
16023
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15622
16024
|
const credentials = requireAuth();
|
|
15623
16025
|
const client = new ApiClient(credentials);
|
|
@@ -15645,16 +16047,64 @@ var workflowsCommand = new Command16("workflows").description("Manage workspace
|
|
|
15645
16047
|
init_store();
|
|
15646
16048
|
init_client();
|
|
15647
16049
|
import { Command as Command17 } from "commander";
|
|
15648
|
-
|
|
15649
|
-
|
|
16050
|
+
// src/utils/table.ts
|
|
16051
|
+
function clip(value, max) {
|
|
16052
|
+
if (max <= 0)
|
|
15650
16053
|
return "";
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
if (
|
|
15654
|
-
return
|
|
15655
|
-
return
|
|
16054
|
+
if (value.length <= max)
|
|
16055
|
+
return value;
|
|
16056
|
+
if (max <= 3)
|
|
16057
|
+
return value.slice(0, max);
|
|
16058
|
+
return value.slice(0, max - 3) + "...";
|
|
16059
|
+
}
|
|
16060
|
+
function renderTable(columns, rows, indent = " ") {
|
|
16061
|
+
const gap = " ";
|
|
16062
|
+
const lastIdx = columns.length - 1;
|
|
16063
|
+
const data = rows.map((row) => columns.map((c, i) => {
|
|
16064
|
+
const raw = row[i] == null ? "" : String(row[i]);
|
|
16065
|
+
return c.max !== undefined ? clip(raw, c.max) : raw;
|
|
16066
|
+
}));
|
|
16067
|
+
const widths = columns.map((c, i) => {
|
|
16068
|
+
let w = c.header.length;
|
|
16069
|
+
for (const row of data)
|
|
16070
|
+
w = Math.max(w, row[i].length);
|
|
16071
|
+
return w;
|
|
16072
|
+
});
|
|
16073
|
+
const renderRow = (cells, colorize) => {
|
|
16074
|
+
const parts = cells.map((cell, i) => {
|
|
16075
|
+
const padded = i === lastIdx ? cell : cell.padEnd(widths[i]);
|
|
16076
|
+
const color = columns[i].color;
|
|
16077
|
+
return colorize && color ? color(padded) : padded;
|
|
16078
|
+
});
|
|
16079
|
+
return (indent + parts.join(gap)).replace(/\s+$/, "");
|
|
16080
|
+
};
|
|
16081
|
+
const out = [renderRow(columns.map((c) => c.header), false)];
|
|
16082
|
+
const total = widths.reduce((a, b) => a + b, 0) + gap.length * Math.max(0, columns.length - 1);
|
|
16083
|
+
out.push(indent + "-".repeat(total));
|
|
16084
|
+
for (const row of data)
|
|
16085
|
+
out.push(renderRow(row, true));
|
|
16086
|
+
return out.join(`
|
|
16087
|
+
`);
|
|
16088
|
+
}
|
|
16089
|
+
|
|
16090
|
+
// src/commands/schedules.ts
|
|
16091
|
+
function extractScheduleHistoryRecords(data) {
|
|
16092
|
+
if (!data || !Array.isArray(data.records))
|
|
16093
|
+
return [];
|
|
16094
|
+
return data.records;
|
|
16095
|
+
}
|
|
16096
|
+
function scheduleRowCells(s) {
|
|
16097
|
+
const mode = s.deploymentMode ? ` (${s.deploymentMode})` : "";
|
|
16098
|
+
const status = s.status ? s.status + mode : mode.trim() || "-";
|
|
16099
|
+
return [
|
|
16100
|
+
s.scheduleName || "(unnamed)",
|
|
16101
|
+
s.appName || s.appId || "-",
|
|
16102
|
+
s.schedule || "-",
|
|
16103
|
+
status,
|
|
16104
|
+
s.description || ""
|
|
16105
|
+
];
|
|
15656
16106
|
}
|
|
15657
|
-
var listCommand5 = new Command17("list").description("List scheduled jobs in the workspace").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16107
|
+
var listCommand5 = new Command17("list").description("List scheduled jobs in the workspace").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
15658
16108
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15659
16109
|
const credentials = requireAuth();
|
|
15660
16110
|
const client = new ApiClient(credentials);
|
|
@@ -15676,20 +16126,20 @@ var listCommand5 = new Command17("list").description("List scheduled jobs in the
|
|
|
15676
16126
|
console.log(`
|
|
15677
16127
|
Workspace: ${workspaceName || workspaceId}
|
|
15678
16128
|
`);
|
|
15679
|
-
console.log(
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
16129
|
+
console.log(renderTable([
|
|
16130
|
+
{ header: "Name" },
|
|
16131
|
+
{ header: "App" },
|
|
16132
|
+
{ header: "Schedule" },
|
|
16133
|
+
{ header: "Status" },
|
|
16134
|
+
{ header: "Description", max: 60 }
|
|
16135
|
+
], schedules.map(scheduleRowCells)));
|
|
15686
16136
|
console.log("");
|
|
15687
16137
|
} catch (err) {
|
|
15688
16138
|
console.error("Failed to list schedules:", err instanceof Error ? err.message : err);
|
|
15689
16139
|
process.exit(1);
|
|
15690
16140
|
}
|
|
15691
16141
|
});
|
|
15692
|
-
var triggerCommand2 = new Command17("trigger").description("Manually trigger a scheduled job").argument("<name>", "Schedule name").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "App name or ID").option("--preview", "Trigger in preview (sandbox) mode instead of production").action(async (name, opts, command) => {
|
|
16142
|
+
var triggerCommand2 = new Command17("trigger").description("Manually trigger a scheduled job").argument("<name>", "Schedule name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "App name or ID").option("--preview", "Trigger in preview (sandbox) mode instead of production").action(async (name, opts, command) => {
|
|
15693
16143
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15694
16144
|
const credentials = requireAuth();
|
|
15695
16145
|
const client = new ApiClient(credentials);
|
|
@@ -15715,7 +16165,7 @@ var triggerCommand2 = new Command17("trigger").description("Manually trigger a s
|
|
|
15715
16165
|
process.exit(1);
|
|
15716
16166
|
}
|
|
15717
16167
|
});
|
|
15718
|
-
var historyCommand = new Command17("history").description("Show execution history for scheduled jobs in an app").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "App name or ID (required)").action(async (opts, command) => {
|
|
16168
|
+
var historyCommand = new Command17("history").description("Show execution history for scheduled jobs in an app").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "App name or ID (required)").action(async (opts, command) => {
|
|
15719
16169
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15720
16170
|
const credentials = requireAuth();
|
|
15721
16171
|
const client = new ApiClient(credentials);
|
|
@@ -15723,25 +16173,34 @@ var historyCommand = new Command17("history").description("Show execution histor
|
|
|
15723
16173
|
const { appId, appName } = await resolveApp2(client, workspaceId, opts);
|
|
15724
16174
|
try {
|
|
15725
16175
|
const data = await client.getScheduleHistory(workspaceId, appId);
|
|
16176
|
+
const records = extractScheduleHistoryRecords(data);
|
|
15726
16177
|
if (useJson) {
|
|
15727
|
-
jsonOut({ history:
|
|
16178
|
+
jsonOut({ history: records, appId, appName });
|
|
15728
16179
|
return;
|
|
15729
16180
|
}
|
|
15730
|
-
if (
|
|
16181
|
+
if (records.length === 0) {
|
|
15731
16182
|
console.log(`No schedule history found for app "${appName}".`);
|
|
15732
16183
|
return;
|
|
15733
16184
|
}
|
|
15734
16185
|
console.log(`
|
|
15735
16186
|
Schedule history for app: ${appName}
|
|
15736
16187
|
`);
|
|
15737
|
-
|
|
15738
|
-
console.log(" " + "
|
|
15739
|
-
|
|
16188
|
+
const nameW = Math.max("Schedule".length, ...records.map((r) => (r.jobName || "-").length));
|
|
16189
|
+
console.log(" " + "Schedule".padEnd(nameW + 2) + "Started At".padEnd(26) + "Status".padEnd(12) + "Duration");
|
|
16190
|
+
console.log(" " + "-".repeat(nameW + 2 + 26 + 12 + 8));
|
|
16191
|
+
for (const entry of records) {
|
|
16192
|
+
const startedAt = entry.startedAt ? new Date(entry.startedAt).toISOString() : "-";
|
|
15740
16193
|
const duration = entry.durationMs != null ? `${entry.durationMs}ms` : "-";
|
|
15741
|
-
console.log(" " + entry.
|
|
16194
|
+
console.log(" " + (entry.jobName || "-").padEnd(nameW + 2) + startedAt.padEnd(26) + entry.status.padEnd(12) + duration);
|
|
15742
16195
|
if (entry.error) {
|
|
15743
16196
|
console.log(` Error: ${entry.error}`);
|
|
15744
16197
|
}
|
|
16198
|
+
if (entry.errorStack) {
|
|
16199
|
+
for (const line of entry.errorStack.split(`
|
|
16200
|
+
`).slice(0, 8)) {
|
|
16201
|
+
console.log(` ${line.trim()}`);
|
|
16202
|
+
}
|
|
16203
|
+
}
|
|
15745
16204
|
}
|
|
15746
16205
|
console.log("");
|
|
15747
16206
|
} catch (err) {
|
|
@@ -15749,7 +16208,7 @@ Schedule history for app: ${appName}
|
|
|
15749
16208
|
process.exit(1);
|
|
15750
16209
|
}
|
|
15751
16210
|
});
|
|
15752
|
-
var statusCommand = new Command17("status").description("Show runtime status of scheduled jobs for an app").argument("<name>", "Schedule name").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
16211
|
+
var statusCommand = new Command17("status").description("Show runtime status of scheduled jobs for an app").argument("<name>", "Schedule name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
15753
16212
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15754
16213
|
const credentials = requireAuth();
|
|
15755
16214
|
const client = new ApiClient(credentials);
|
|
@@ -15771,7 +16230,7 @@ Schedule "${name}" status for app "${appName}":
|
|
|
15771
16230
|
process.exit(1);
|
|
15772
16231
|
}
|
|
15773
16232
|
});
|
|
15774
|
-
var pauseCommand = new Command17("pause").description("Pause a scheduled job").argument("<name>", "Schedule name").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
16233
|
+
var pauseCommand = new Command17("pause").description("Pause a scheduled job").argument("<name>", "Schedule name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
15775
16234
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15776
16235
|
const credentials = requireAuth();
|
|
15777
16236
|
const client = new ApiClient(credentials);
|
|
@@ -15789,7 +16248,7 @@ var pauseCommand = new Command17("pause").description("Pause a scheduled job").a
|
|
|
15789
16248
|
process.exit(1);
|
|
15790
16249
|
}
|
|
15791
16250
|
});
|
|
15792
|
-
var resumeCommand = new Command17("resume").description("Resume a paused scheduled job").argument("<name>", "Schedule name").option("--workspace <id>", "Workspace ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
16251
|
+
var resumeCommand = new Command17("resume").description("Resume a paused scheduled job").argument("<name>", "Schedule name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name|id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
15793
16252
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15794
16253
|
const credentials = requireAuth();
|
|
15795
16254
|
const client = new ApiClient(credentials);
|
|
@@ -15814,7 +16273,7 @@ init_store();
|
|
|
15814
16273
|
init_client();
|
|
15815
16274
|
import { Command as Command18 } from "commander";
|
|
15816
16275
|
init_http();
|
|
15817
|
-
function
|
|
16276
|
+
function truncate3(text2, max) {
|
|
15818
16277
|
if (!text2)
|
|
15819
16278
|
return "";
|
|
15820
16279
|
const first = text2.split(`
|
|
@@ -15823,7 +16282,7 @@ function truncate4(text2, max) {
|
|
|
15823
16282
|
return first;
|
|
15824
16283
|
return first.slice(0, max - 3) + "...";
|
|
15825
16284
|
}
|
|
15826
|
-
var listCommand6 = new Command18("list").description("List workspace public endpoints").option("--workspace <id>", "Workspace ID").option("--app <name>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16285
|
+
var listCommand6 = new Command18("list").description("List workspace public endpoints").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "Filter by app name or ID").action(async (opts, command) => {
|
|
15827
16286
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15828
16287
|
const credentials = requireAuth();
|
|
15829
16288
|
const client = new ApiClient(credentials);
|
|
@@ -15851,7 +16310,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15851
16310
|
const path2 = e.endpointPath.padEnd(36);
|
|
15852
16311
|
const app = (e.appName || e.appId).padEnd(20);
|
|
15853
16312
|
const auth = (e.auth || "").padEnd(10);
|
|
15854
|
-
const desc =
|
|
16313
|
+
const desc = truncate3(e.description, 40);
|
|
15855
16314
|
const mode = e.deploymentMode ? ` [${e.deploymentMode}]` : "";
|
|
15856
16315
|
console.log(` ${method} ${path2} ${app} ${auth} ${desc}${mode}`);
|
|
15857
16316
|
}
|
|
@@ -15861,7 +16320,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15861
16320
|
process.exit(1);
|
|
15862
16321
|
}
|
|
15863
16322
|
});
|
|
15864
|
-
var callCommand2 = new Command18("call").description("Call a workspace endpoint directly").argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)").argument("<path>", "Endpoint path (e.g. /my-endpoint)").option("--workspace <id>", "Workspace ID").option("--app <name>", "App name or ID (narrows endpoint lookup)").option("--body <json>", "Request body as JSON string").option("--header <header>", 'Request header (format: "Key: Value", repeatable)', (val, prev) => [...prev, val], []).option("--query <string>", 'Query string to append to the URL (e.g. "foo=bar&baz=1")').option("--api-key <key>", "API key for Authorization: Bearer header").action(async (method, path2, opts, command) => {
|
|
16323
|
+
var callCommand2 = new Command18("call").description("Call a workspace endpoint directly").argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)").argument("<path>", "Endpoint path (e.g. /my-endpoint)").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name>", "App name or ID (narrows endpoint lookup)").option("--body <json>", "Request body as JSON string").option("--header <header>", 'Request header (format: "Key: Value", repeatable)', (val, prev) => [...prev, val], []).option("--query <string>", 'Query string to append to the URL (e.g. "foo=bar&baz=1")').option("--api-key <key>", "API key for Authorization: Bearer header").action(async (method, path2, opts, command) => {
|
|
15865
16324
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15866
16325
|
const credentials = requireAuth();
|
|
15867
16326
|
const client = new ApiClient(credentials);
|
|
@@ -15992,7 +16451,7 @@ function formatSize(bytes) {
|
|
|
15992
16451
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
15993
16452
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
15994
16453
|
}
|
|
15995
|
-
var listCommand7 = new Command19("list").description("List file storage buckets in the workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16454
|
+
var listCommand7 = new Command19("list").description("List file storage buckets in the workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
15996
16455
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15997
16456
|
const credentials = requireAuth();
|
|
15998
16457
|
const client = new ApiClient(credentials);
|
|
@@ -16022,7 +16481,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16022
16481
|
process.exit(1);
|
|
16023
16482
|
}
|
|
16024
16483
|
});
|
|
16025
|
-
var lsCommand = new Command19("ls").description("List files in a bucket").argument("<bucket>", "Bucket name").argument("[prefix]", "Key prefix to filter by").option("--workspace <id>", "Workspace ID").option("--delimiter <char>", "Delimiter for hierarchical listing", "/").action(async (bucket, prefix, opts, command) => {
|
|
16484
|
+
var lsCommand = new Command19("ls").description("List files in a bucket").argument("<bucket>", "Bucket name").argument("[prefix]", "Key prefix to filter by").option("--workspace <name-or-id>", "Workspace name or ID").option("--delimiter <char>", "Delimiter for hierarchical listing", "/").action(async (bucket, prefix, opts, command) => {
|
|
16026
16485
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16027
16486
|
const credentials = requireAuth();
|
|
16028
16487
|
const client = new ApiClient(credentials);
|
|
@@ -16059,7 +16518,7 @@ var lsCommand = new Command19("ls").description("List files in a bucket").argume
|
|
|
16059
16518
|
process.exit(1);
|
|
16060
16519
|
}
|
|
16061
16520
|
});
|
|
16062
|
-
var downloadCommand = new Command19("download").description("Download a file from a bucket").argument("<bucket>", "Bucket name").argument("<key>", "Object key to download").argument("[output]", "Local output path (defaults to basename of key)").option("--workspace <id>", "Workspace ID").action(async (bucket, key, output, opts, command) => {
|
|
16521
|
+
var downloadCommand = new Command19("download").description("Download a file from a bucket").argument("<bucket>", "Bucket name").argument("<key>", "Object key to download").argument("[output]", "Local output path (defaults to basename of key)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (bucket, key, output, opts, command) => {
|
|
16063
16522
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16064
16523
|
const credentials = requireAuth();
|
|
16065
16524
|
const client = new ApiClient(credentials);
|
|
@@ -16082,7 +16541,7 @@ var downloadCommand = new Command19("download").description("Download a file fro
|
|
|
16082
16541
|
process.exit(1);
|
|
16083
16542
|
}
|
|
16084
16543
|
});
|
|
16085
|
-
var uploadCommand = new Command19("upload").description("Upload a local file to a bucket").argument("<bucket>", "Bucket name").argument("<localPath>", "Local file path to upload").argument("[key]", "Object key in bucket (defaults to basename of local path)").option("--workspace <id>", "Workspace ID").action(async (bucket, localPath, key, opts, command) => {
|
|
16544
|
+
var uploadCommand = new Command19("upload").description("Upload a local file to a bucket").argument("<bucket>", "Bucket name").argument("<localPath>", "Local file path to upload").argument("[key]", "Object key in bucket (defaults to basename of local path)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (bucket, localPath, key, opts, command) => {
|
|
16086
16545
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16087
16546
|
const credentials = requireAuth();
|
|
16088
16547
|
const client = new ApiClient(credentials);
|
|
@@ -16105,7 +16564,7 @@ var uploadCommand = new Command19("upload").description("Upload a local file to
|
|
|
16105
16564
|
process.exit(1);
|
|
16106
16565
|
}
|
|
16107
16566
|
});
|
|
16108
|
-
var deleteCommand2 = new Command19("delete").description("Delete a file from a bucket").argument("<bucket>", "Bucket name").argument("<key>", "Object key to delete").option("--workspace <id>", "Workspace ID").option("--yes", "Skip confirmation prompt").action(async (bucket, key, opts, command) => {
|
|
16567
|
+
var deleteCommand2 = new Command19("delete").description("Delete a file from a bucket").argument("<bucket>", "Bucket name").argument("<key>", "Object key to delete").option("--workspace <name-or-id>", "Workspace name or ID").option("--yes", "Skip confirmation prompt").action(async (bucket, key, opts, command) => {
|
|
16109
16568
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16110
16569
|
const credentials = requireAuth();
|
|
16111
16570
|
const client = new ApiClient(credentials);
|
|
@@ -16135,7 +16594,7 @@ var filesCommand = new Command19("files").description("Manage workspace file sto
|
|
|
16135
16594
|
init_store();
|
|
16136
16595
|
init_client();
|
|
16137
16596
|
import { Command as Command20 } from "commander";
|
|
16138
|
-
var listCommand8 = new Command20("list").description("List components registered in the workspace").option("--workspace <id>", "Workspace ID").option("--app <id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16597
|
+
var listCommand8 = new Command20("list").description("List components registered in the workspace").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16139
16598
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16140
16599
|
const credentials = requireAuth();
|
|
16141
16600
|
const client = new ApiClient(credentials);
|
|
@@ -16174,7 +16633,7 @@ init_store();
|
|
|
16174
16633
|
init_client();
|
|
16175
16634
|
import { Command as Command21 } from "commander";
|
|
16176
16635
|
init_prompt();
|
|
16177
|
-
var listCommand9 = new Command21("list").description("List API keys for the workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16636
|
+
var listCommand9 = new Command21("list").description("List API keys for the workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
16178
16637
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16179
16638
|
const credentials = requireAuth();
|
|
16180
16639
|
const client = new ApiClient(credentials);
|
|
@@ -16209,7 +16668,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16209
16668
|
process.exit(1);
|
|
16210
16669
|
}
|
|
16211
16670
|
});
|
|
16212
|
-
var createCommand2 = new Command21("create").description("Create a new API key").option("--workspace <id>", "Workspace ID").option("--name <name>", "API key name").option("--app <id>", "Restrict key to a specific app (name or ID)").option("--scopes <scopes>", "Comma-separated list of scopes").action(async (opts, command) => {
|
|
16671
|
+
var createCommand2 = new Command21("create").description("Create a new API key").option("--workspace <name-or-id>", "Workspace name or ID").option("--name <name>", "API key name").option("--app <id>", "Restrict key to a specific app (name or ID)").option("--scopes <scopes>", "Comma-separated list of scopes").action(async (opts, command) => {
|
|
16213
16672
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16214
16673
|
const credentials = requireAuth();
|
|
16215
16674
|
const client = new ApiClient(credentials);
|
|
@@ -16257,7 +16716,7 @@ var apiKeysCommand = new Command21("api-keys").description("Manage workspace API
|
|
|
16257
16716
|
init_store();
|
|
16258
16717
|
init_client();
|
|
16259
16718
|
import { Command as Command22 } from "commander";
|
|
16260
|
-
function
|
|
16719
|
+
function truncate4(text2, max) {
|
|
16261
16720
|
if (!text2)
|
|
16262
16721
|
return "";
|
|
16263
16722
|
const first = text2.split(`
|
|
@@ -16266,7 +16725,7 @@ function truncate5(text2, max) {
|
|
|
16266
16725
|
return first;
|
|
16267
16726
|
return first.slice(0, max - 3) + "...";
|
|
16268
16727
|
}
|
|
16269
|
-
var listCommand10 = new Command22("list").description("List agents in workspace").option("--workspace <id>", "Workspace ID").option("--app <id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16728
|
+
var listCommand10 = new Command22("list").description("List agents in workspace").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <id>", "Filter by app name or ID").action(async (opts, command) => {
|
|
16270
16729
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16271
16730
|
const credentials = requireAuth();
|
|
16272
16731
|
const client = new ApiClient(credentials);
|
|
@@ -16292,8 +16751,8 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16292
16751
|
for (const a of agents) {
|
|
16293
16752
|
const name = a.agentName.padEnd(24);
|
|
16294
16753
|
const type = a.type.padEnd(16);
|
|
16295
|
-
const app =
|
|
16296
|
-
const desc =
|
|
16754
|
+
const app = truncate4(a.appName, 18).padEnd(20);
|
|
16755
|
+
const desc = truncate4(a.description, 40).padEnd(42);
|
|
16297
16756
|
const integrations = (a.integrations?.join(", ") || "").padEnd(20);
|
|
16298
16757
|
const entities = (a.entities?.join(", ") || "").padEnd(20);
|
|
16299
16758
|
const mode = a.deploymentMode || "";
|
|
@@ -16305,7 +16764,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16305
16764
|
process.exit(1);
|
|
16306
16765
|
}
|
|
16307
16766
|
});
|
|
16308
|
-
var statusCommand2 = new Command22("status").description("Get status of a specific agent").argument("<name>", "Agent name").option("--workspace <id>", "Workspace ID").option("--app <id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
16767
|
+
var statusCommand2 = new Command22("status").description("Get status of a specific agent").argument("<name>", "Agent name").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <id>", "App name or ID (required)").action(async (name, opts, command) => {
|
|
16309
16768
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16310
16769
|
const credentials = requireAuth();
|
|
16311
16770
|
const client = new ApiClient(credentials);
|
|
@@ -16323,7 +16782,7 @@ var statusCommand2 = new Command22("status").description("Get status of a specif
|
|
|
16323
16782
|
process.exit(1);
|
|
16324
16783
|
}
|
|
16325
16784
|
});
|
|
16326
|
-
var executionsCommand = new Command22("executions").description("List agent execution history for workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16785
|
+
var executionsCommand = new Command22("executions").description("List agent execution history for workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
16327
16786
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16328
16787
|
const credentials = requireAuth();
|
|
16329
16788
|
const client = new ApiClient(credentials);
|
|
@@ -16342,9 +16801,9 @@ var executionsCommand = new Command22("executions").description("List agent exec
|
|
|
16342
16801
|
console.log(" " + "-".repeat(160));
|
|
16343
16802
|
for (const e of executions) {
|
|
16344
16803
|
const sessionId = e.sessionId.padEnd(36);
|
|
16345
|
-
const agent =
|
|
16346
|
-
const app =
|
|
16347
|
-
const prompt =
|
|
16804
|
+
const agent = truncate4(e.agentName, 20).padEnd(22);
|
|
16805
|
+
const app = truncate4(e.appId, 20).padEnd(22);
|
|
16806
|
+
const prompt = truncate4(e.prompt, 50).padEnd(52);
|
|
16348
16807
|
const status = e.status.padEnd(12);
|
|
16349
16808
|
const duration = e.durationMs != null ? `${e.durationMs}ms` : "";
|
|
16350
16809
|
const usage = e.usage ? `${e.usage.inputTokens}/${e.usage.outputTokens}` : "";
|
|
@@ -16356,7 +16815,7 @@ var executionsCommand = new Command22("executions").description("List agent exec
|
|
|
16356
16815
|
process.exit(1);
|
|
16357
16816
|
}
|
|
16358
16817
|
});
|
|
16359
|
-
var executionCommand = new Command22("execution").description("Get execution stream for a specific session").argument("<sessionId>", "Session ID").option("--workspace <id>", "Workspace ID").action(async (sessionId, opts, command) => {
|
|
16818
|
+
var executionCommand = new Command22("execution").description("Get execution stream for a specific session").argument("<sessionId>", "Session ID").option("--workspace <name-or-id>", "Workspace name or ID").action(async (sessionId, opts, command) => {
|
|
16360
16819
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16361
16820
|
const credentials = requireAuth();
|
|
16362
16821
|
const client = new ApiClient(credentials);
|
|
@@ -16380,7 +16839,7 @@ init_store();
|
|
|
16380
16839
|
init_client();
|
|
16381
16840
|
import { Command as Command23 } from "commander";
|
|
16382
16841
|
init_prompt();
|
|
16383
|
-
var listCommand11 = new Command23("list").description("List workspace MCP servers").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16842
|
+
var listCommand11 = new Command23("list").description("List workspace MCP servers").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
16384
16843
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16385
16844
|
const credentials = requireAuth();
|
|
16386
16845
|
const client = new ApiClient(credentials);
|
|
@@ -16436,7 +16895,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16436
16895
|
process.exit(1);
|
|
16437
16896
|
}
|
|
16438
16897
|
});
|
|
16439
|
-
var addCommand = new Command23("add").description("Add an MCP server to workspace").argument("<name>", "Server name").argument("<url>", "Server URL (Streamable HTTP or SSE endpoint)").option("--transport <type>", "Transport type: streamable-http or sse", "streamable-http").option("--workspace <id>", "Workspace ID").action(async (name, url, opts, command) => {
|
|
16898
|
+
var addCommand = new Command23("add").description("Add an MCP server to workspace").argument("<name>", "Server name").argument("<url>", "Server URL (Streamable HTTP or SSE endpoint)").option("--transport <type>", "Transport type: streamable-http or sse", "streamable-http").option("--workspace <name-or-id>", "Workspace name or ID").action(async (name, url, opts, command) => {
|
|
16440
16899
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16441
16900
|
const credentials = requireAuth();
|
|
16442
16901
|
const client = new ApiClient(credentials);
|
|
@@ -16464,7 +16923,7 @@ var addCommand = new Command23("add").description("Add an MCP server to workspac
|
|
|
16464
16923
|
process.exit(1);
|
|
16465
16924
|
}
|
|
16466
16925
|
});
|
|
16467
|
-
var removeCommand = new Command23("remove").description("Remove an MCP server from workspace").argument("<name>", "Server name").option("--yes", "Skip confirmation").option("--workspace <id>", "Workspace ID").action(async (name, opts, command) => {
|
|
16926
|
+
var removeCommand = new Command23("remove").description("Remove an MCP server from workspace").argument("<name>", "Server name").option("--yes", "Skip confirmation").option("--workspace <name-or-id>", "Workspace name or ID").action(async (name, opts, command) => {
|
|
16468
16927
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16469
16928
|
const credentials = requireAuth();
|
|
16470
16929
|
const client = new ApiClient(credentials);
|
|
@@ -16527,7 +16986,7 @@ var searchCommand3 = new Command23("search").description("Search community MCP s
|
|
|
16527
16986
|
process.exit(1);
|
|
16528
16987
|
}
|
|
16529
16988
|
});
|
|
16530
|
-
var installCommand2 = new Command23("install").description("Install a community MCP server from the MCP Registry into your workspace").argument("<query>", "Server name or search query").option("--workspace <id>", "Workspace ID").action(async (query, opts, command) => {
|
|
16989
|
+
var installCommand2 = new Command23("install").description("Install a community MCP server from the MCP Registry into your workspace").argument("<query>", "Server name or search query").option("--workspace <name-or-id>", "Workspace name or ID").action(async (query, opts, command) => {
|
|
16531
16990
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16532
16991
|
const credentials = requireAuth();
|
|
16533
16992
|
const client = new ApiClient(credentials);
|
|
@@ -16664,19 +17123,6 @@ async function collectTelemetryEvents(params) {
|
|
|
16664
17123
|
const now = params.now ?? new Date().toISOString();
|
|
16665
17124
|
const events = [];
|
|
16666
17125
|
const adapterResults = [];
|
|
16667
|
-
const previouslyReported = new Set(params.state.reportedAgentSlugs ?? []);
|
|
16668
|
-
const newlyReportedAgents = [];
|
|
16669
|
-
for (const slug of params.state.configuredAgents) {
|
|
16670
|
-
if (!previouslyReported.has(slug)) {
|
|
16671
|
-
events.push({
|
|
16672
|
-
eventType: "local_agent.detected",
|
|
16673
|
-
agentSlug: slug,
|
|
16674
|
-
metadata: { installed: true },
|
|
16675
|
-
timestamp: now
|
|
16676
|
-
});
|
|
16677
|
-
newlyReportedAgents.push(slug);
|
|
16678
|
-
}
|
|
16679
|
-
}
|
|
16680
17126
|
for (const adapter2 of params.adapters) {
|
|
16681
17127
|
let stats = "unsupported";
|
|
16682
17128
|
let skills = "unsupported";
|
|
@@ -16768,11 +17214,12 @@ async function collectTelemetryEvents(params) {
|
|
|
16768
17214
|
syncedMcpServers: params.syncedMcpServersCount,
|
|
16769
17215
|
teamInstructionsApplied: params.teamInstructionsApplied,
|
|
16770
17216
|
agentConfigsApplied: params.agentConfigsApplied,
|
|
17217
|
+
agents: [...params.state.configuredAgents],
|
|
16771
17218
|
...params.state.persona?.label ? { persona: params.state.persona.label } : {}
|
|
16772
17219
|
},
|
|
16773
17220
|
timestamp: now
|
|
16774
17221
|
});
|
|
16775
|
-
return { events, adapterResults,
|
|
17222
|
+
return { events, adapterResults, healthReported };
|
|
16776
17223
|
}
|
|
16777
17224
|
function formatStatsLine(stats) {
|
|
16778
17225
|
const parts = [];
|
|
@@ -16818,10 +17265,6 @@ function printTelemetryVerbose(result) {
|
|
|
16818
17265
|
const errSuffix = r.error ? ` (error: ${r.error})` : "";
|
|
16819
17266
|
console.log(` [${r.name}] stats: ${statsLabel}; skills: ${skillsLabel}${errSuffix}`);
|
|
16820
17267
|
}
|
|
16821
|
-
if (result.newlyReportedAgents.length > 0) {
|
|
16822
|
-
console.log(`
|
|
16823
|
-
First-time detections: ${result.newlyReportedAgents.join(", ")}`);
|
|
16824
|
-
}
|
|
16825
17268
|
if (result.healthReported) {
|
|
16826
17269
|
console.log(" Health report: due (daily)");
|
|
16827
17270
|
}
|
|
@@ -17451,12 +17894,6 @@ function buildSaveConversationSkill() {
|
|
|
17451
17894
|
};
|
|
17452
17895
|
}
|
|
17453
17896
|
|
|
17454
|
-
// src/sync/hash.ts
|
|
17455
|
-
import { createHash as createHash3 } from "crypto";
|
|
17456
|
-
function contentHash(content) {
|
|
17457
|
-
return "sha256:" + createHash3("sha256").update(content).digest("hex");
|
|
17458
|
-
}
|
|
17459
|
-
|
|
17460
17897
|
// src/sync/change-detect.ts
|
|
17461
17898
|
function computeSyncPlan(input) {
|
|
17462
17899
|
const { storedHashes, localSkills, remoteSkills } = input;
|
|
@@ -17488,6 +17925,16 @@ function computeSyncPlan(input) {
|
|
|
17488
17925
|
const localChanged = localHash !== null && localHash !== stored.localHash;
|
|
17489
17926
|
const remoteChanged = remoteHash !== stored.remoteHash;
|
|
17490
17927
|
if (!local) {
|
|
17928
|
+
if (remote.source === "app" && !remoteChanged) {
|
|
17929
|
+
plan.skips.push({
|
|
17930
|
+
name: remote.name,
|
|
17931
|
+
action: "skip",
|
|
17932
|
+
source: remote.source,
|
|
17933
|
+
remoteId: remote.remoteId,
|
|
17934
|
+
appId: remote.appId
|
|
17935
|
+
});
|
|
17936
|
+
continue;
|
|
17937
|
+
}
|
|
17491
17938
|
plan.pulls.push({
|
|
17492
17939
|
name: remote.name,
|
|
17493
17940
|
action: "pull",
|
|
@@ -17667,6 +18114,47 @@ function resolveConflictNonInteractive(prefer) {
|
|
|
17667
18114
|
return prefer;
|
|
17668
18115
|
}
|
|
17669
18116
|
|
|
18117
|
+
// src/sync/summary-format.ts
|
|
18118
|
+
function summarizeSkillPlan(plan) {
|
|
18119
|
+
return {
|
|
18120
|
+
added: plan.pulls.filter((p) => p.action === "new-remote").map((p) => p.name),
|
|
18121
|
+
updated: plan.pulls.filter((p) => p.action === "pull").map((p) => p.name),
|
|
18122
|
+
removed: plan.deletions.map((p) => p.name),
|
|
18123
|
+
pushed: plan.pushes.map((p) => p.name),
|
|
18124
|
+
unchanged: plan.skips.length,
|
|
18125
|
+
conflicts: plan.conflicts.length
|
|
18126
|
+
};
|
|
18127
|
+
}
|
|
18128
|
+
function formatNameList(names, cap2 = 6) {
|
|
18129
|
+
if (names.length <= cap2)
|
|
18130
|
+
return names.join(", ");
|
|
18131
|
+
return `${names.slice(0, cap2).join(", ")}, +${names.length - cap2} more`;
|
|
18132
|
+
}
|
|
18133
|
+
function formatSkillSummaryLine(c) {
|
|
18134
|
+
const parts = [];
|
|
18135
|
+
if (c.added.length)
|
|
18136
|
+
parts.push(`${c.added.length} added (${formatNameList(c.added)})`);
|
|
18137
|
+
if (c.updated.length)
|
|
18138
|
+
parts.push(`${c.updated.length} updated (${formatNameList(c.updated)})`);
|
|
18139
|
+
if (c.removed.length)
|
|
18140
|
+
parts.push(`${c.removed.length} removed (${formatNameList(c.removed)})`);
|
|
18141
|
+
if (c.pushed.length)
|
|
18142
|
+
parts.push(`${c.pushed.length} pushed (${formatNameList(c.pushed)})`);
|
|
18143
|
+
if (c.conflicts)
|
|
18144
|
+
parts.push(`${c.conflicts} conflict${c.conflicts === 1 ? "" : "s"}`);
|
|
18145
|
+
parts.push(`${c.unchanged} up to date`);
|
|
18146
|
+
return parts.join(", ");
|
|
18147
|
+
}
|
|
18148
|
+
var REASON_LABEL = {
|
|
18149
|
+
mcp: "MCP",
|
|
18150
|
+
settings: "settings"
|
|
18151
|
+
};
|
|
18152
|
+
function formatRestartChanges(changes) {
|
|
18153
|
+
if (changes.length === 0)
|
|
18154
|
+
return "none";
|
|
18155
|
+
return changes.map((c) => `${c.name} (${c.reasons.map((r) => REASON_LABEL[r]).join(", ")})`).join("; ");
|
|
18156
|
+
}
|
|
18157
|
+
|
|
17670
18158
|
// src/sync/executor.ts
|
|
17671
18159
|
async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
17672
18160
|
const newHashes = {};
|
|
@@ -17682,7 +18170,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17682
18170
|
remoteId: action.remoteId,
|
|
17683
18171
|
appId: action.appId
|
|
17684
18172
|
};
|
|
17685
|
-
|
|
18173
|
+
vlog(` Pulled: ${action.name}`);
|
|
17686
18174
|
}
|
|
17687
18175
|
for (const action of plan.pushes) {
|
|
17688
18176
|
if (!action.localContent || !action.remoteId)
|
|
@@ -17699,7 +18187,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17699
18187
|
source: "external",
|
|
17700
18188
|
remoteId: action.remoteId
|
|
17701
18189
|
};
|
|
17702
|
-
|
|
18190
|
+
vlog(` Pushed: ${action.name}`);
|
|
17703
18191
|
}
|
|
17704
18192
|
for (const action of plan.conflicts) {
|
|
17705
18193
|
const resolution = resolvedConflicts.get(action.name);
|
|
@@ -17713,7 +18201,7 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17713
18201
|
});
|
|
17714
18202
|
const hash = contentHash(action.localContent);
|
|
17715
18203
|
newHashes[action.name] = { localHash: hash, remoteHash: hash, source: "external", remoteId: action.remoteId };
|
|
17716
|
-
|
|
18204
|
+
vlog(` Pushed (conflict resolved): ${action.name}`);
|
|
17717
18205
|
} else if (resolution === "remote" && action.remoteContent) {
|
|
17718
18206
|
const skillFile = makeSkillFile(action.name, action.remoteContent);
|
|
17719
18207
|
await writeSkillToAgents(skillFile, action.source, ctx);
|
|
@@ -17723,12 +18211,12 @@ async function executeSyncPlan(plan, resolvedConflicts, ctx) {
|
|
|
17723
18211
|
source: "external",
|
|
17724
18212
|
remoteId: action.remoteId
|
|
17725
18213
|
};
|
|
17726
|
-
|
|
18214
|
+
vlog(` Pulled (conflict resolved): ${action.name}`);
|
|
17727
18215
|
}
|
|
17728
18216
|
}
|
|
17729
18217
|
for (const _action of plan.skips) {}
|
|
17730
18218
|
for (const action of plan.deletions) {
|
|
17731
|
-
|
|
18219
|
+
vlog(` Deleted locally: ${action.name}`);
|
|
17732
18220
|
}
|
|
17733
18221
|
return newHashes;
|
|
17734
18222
|
}
|
|
@@ -17758,6 +18246,53 @@ function extractDescription(content) {
|
|
|
17758
18246
|
return match ? match[1].trim() : "";
|
|
17759
18247
|
}
|
|
17760
18248
|
|
|
18249
|
+
// src/sync/restart-stamp.ts
|
|
18250
|
+
function computeRestartReasons(input) {
|
|
18251
|
+
const settingsSet = new Set(input.settingsChangedSlugs);
|
|
18252
|
+
const out = {};
|
|
18253
|
+
for (const agent of input.agents) {
|
|
18254
|
+
if (agent.connectOnly)
|
|
18255
|
+
continue;
|
|
18256
|
+
const reasons = [];
|
|
18257
|
+
if ((input.mcpChanged || agent.isNew) && agent.receivesMcp)
|
|
18258
|
+
reasons.push("mcp");
|
|
18259
|
+
if (settingsSet.has(agent.slug))
|
|
18260
|
+
reasons.push("settings");
|
|
18261
|
+
if (reasons.length > 0)
|
|
18262
|
+
out[agent.slug] = reasons;
|
|
18263
|
+
}
|
|
18264
|
+
return out;
|
|
18265
|
+
}
|
|
18266
|
+
function summarizeRealSkillChanges(newLocalHashes, priorLocalHashes, materializedDeletions) {
|
|
18267
|
+
const added = [];
|
|
18268
|
+
const updated = [];
|
|
18269
|
+
for (const [name, hash] of Object.entries(newLocalHashes)) {
|
|
18270
|
+
if (!(name in priorLocalHashes))
|
|
18271
|
+
added.push(name);
|
|
18272
|
+
else if (priorLocalHashes[name] !== hash)
|
|
18273
|
+
updated.push(name);
|
|
18274
|
+
}
|
|
18275
|
+
return {
|
|
18276
|
+
added: added.sort(),
|
|
18277
|
+
updated: updated.sort(),
|
|
18278
|
+
removed: [...materializedDeletions].sort()
|
|
18279
|
+
};
|
|
18280
|
+
}
|
|
18281
|
+
function hasRealSkillChange(c) {
|
|
18282
|
+
return c.added.length > 0 || c.updated.length > 0 || c.removed.length > 0;
|
|
18283
|
+
}
|
|
18284
|
+
function sameStringSet(a, b) {
|
|
18285
|
+
const setA = new Set(a ?? []);
|
|
18286
|
+
const setB = new Set(b ?? []);
|
|
18287
|
+
if (setA.size !== setB.size)
|
|
18288
|
+
return false;
|
|
18289
|
+
for (const item of setA) {
|
|
18290
|
+
if (!setB.has(item))
|
|
18291
|
+
return false;
|
|
18292
|
+
}
|
|
18293
|
+
return true;
|
|
18294
|
+
}
|
|
18295
|
+
|
|
17761
18296
|
// src/commands/sync.ts
|
|
17762
18297
|
async function printAdoptionHint(credentials, workspaceId) {
|
|
17763
18298
|
if (!workspaceId)
|
|
@@ -17842,6 +18377,7 @@ async function refreshConfiguredAgents(state) {
|
|
|
17842
18377
|
}
|
|
17843
18378
|
async function syncFromState(state, statePath2, credentials, opts) {
|
|
17844
18379
|
const client = new ApiClient(credentials);
|
|
18380
|
+
setVerbose(!!opts.verbose);
|
|
17845
18381
|
if (!state.workspaceName || state.workspaceName === state.workspaceId || !state.workspaceSlug) {
|
|
17846
18382
|
try {
|
|
17847
18383
|
const workspaces = await client.listWorkspaces();
|
|
@@ -17853,11 +18389,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17853
18389
|
} catch {}
|
|
17854
18390
|
}
|
|
17855
18391
|
console.log(`Syncing workspace: ${state.workspaceName || state.workspaceId}`);
|
|
18392
|
+
let addedThisSync = [];
|
|
17856
18393
|
if (shouldRedetect(state, opts)) {
|
|
17857
18394
|
console.log(" Detecting installed agents...");
|
|
17858
|
-
|
|
17859
|
-
if (
|
|
17860
|
-
console.log(` Detected new agent${
|
|
18395
|
+
addedThisSync = await refreshConfiguredAgents(state);
|
|
18396
|
+
if (addedThisSync.length > 0) {
|
|
18397
|
+
console.log(` Detected new agent${addedThisSync.length > 1 ? "s" : ""}: ${addedThisSync.join(", ")}`);
|
|
17861
18398
|
}
|
|
17862
18399
|
}
|
|
17863
18400
|
console.log(" Fetching workspace data...");
|
|
@@ -17899,6 +18436,8 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17899
18436
|
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
17900
18437
|
description: "Runwork workspace tools: query entities, trigger workflows, manage integrations, run agents, and access shared data across your team's apps."
|
|
17901
18438
|
});
|
|
18439
|
+
const mcpEntriesHash = configHash([...mcpEntries].sort((a, b) => a.name.localeCompare(b.name)));
|
|
18440
|
+
const mcpChanged = state.lastMcpEntriesHash !== undefined && state.lastMcpEntriesHash !== mcpEntriesHash;
|
|
17902
18441
|
const remoteSkills = [];
|
|
17903
18442
|
for (const s of externalSkills) {
|
|
17904
18443
|
remoteSkills.push({ name: s.name, content: s.content, source: "external", remoteId: s.id });
|
|
@@ -17928,12 +18467,9 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17928
18467
|
plan.skips.push(...plan.conflicts.map((c) => ({ ...c, action: "skip" })));
|
|
17929
18468
|
plan.conflicts = [];
|
|
17930
18469
|
}
|
|
17931
|
-
|
|
17932
|
-
if (
|
|
18470
|
+
console.log(` Skills: ${formatSkillSummaryLine(summarizeSkillPlan(plan))}`);
|
|
18471
|
+
if (isVerbose())
|
|
17933
18472
|
printSyncSummary(plan);
|
|
17934
|
-
} else {
|
|
17935
|
-
console.log(" All skills up to date.");
|
|
17936
|
-
}
|
|
17937
18473
|
const adapters = [];
|
|
17938
18474
|
for (const slug of state.configuredAgents) {
|
|
17939
18475
|
const adapter2 = getAdapterBySlug(slug);
|
|
@@ -17973,6 +18509,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
17973
18509
|
}
|
|
17974
18510
|
}
|
|
17975
18511
|
}
|
|
18512
|
+
const settingsChangedSlugs = [];
|
|
17976
18513
|
const scopes = state.scope === "both" ? ["project", "user"] : [state.scope];
|
|
17977
18514
|
if (adapters.length > 0) {
|
|
17978
18515
|
console.log(` Syncing to: ${adapters.map((a) => a.name).join(", ")}`);
|
|
@@ -18021,6 +18558,8 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18021
18558
|
buildSaveConversationSkill()
|
|
18022
18559
|
];
|
|
18023
18560
|
const builtInNames = builtInSkills.map((s) => s.name);
|
|
18561
|
+
const builtInSkillsHash = configHash(builtInSkills.map((s) => s.content));
|
|
18562
|
+
const builtInSkillsChanged = state.builtInSkillsHash !== undefined && state.builtInSkillsHash !== builtInSkillsHash;
|
|
18024
18563
|
const summary = {
|
|
18025
18564
|
adaptersProcessed: 0,
|
|
18026
18565
|
adaptersFailed: 0,
|
|
@@ -18030,16 +18569,18 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18030
18569
|
instructionHintWrites: 0,
|
|
18031
18570
|
hookInstallCalls: 0
|
|
18032
18571
|
};
|
|
18572
|
+
const mcpFailedAdapters = new Set;
|
|
18573
|
+
const skillFailedAdapters = new Set;
|
|
18033
18574
|
for (const adapter2 of adapters) {
|
|
18034
18575
|
if (isConnectOnlyAgent(getRegistryAgent(adapter2.slug))) {
|
|
18035
|
-
|
|
18576
|
+
vlog(` [${adapter2.name}] Connect-only agent: no local files to sync`);
|
|
18036
18577
|
summary.adaptersProcessed++;
|
|
18037
18578
|
continue;
|
|
18038
18579
|
}
|
|
18039
18580
|
let adapterFailedAnyScope = false;
|
|
18040
18581
|
for (const scope of scopes) {
|
|
18041
|
-
|
|
18042
|
-
|
|
18582
|
+
if (adapter2.supportsSkills()) {
|
|
18583
|
+
try {
|
|
18043
18584
|
const skipAppSkills = adapter2.mcpProvidesSkills && mcpEntries.length > 0;
|
|
18044
18585
|
const scopeSkills = remoteSkills.filter((s) => {
|
|
18045
18586
|
if (builtInNameSet.has(s.name))
|
|
@@ -18059,25 +18600,41 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18059
18600
|
description: s.source === "app" ? buildAppSkillDescription(s.name, registries) || `${s.name} - Runwork workspace application` : `${s.name} - Runwork workspace skill`
|
|
18060
18601
|
}));
|
|
18061
18602
|
const allSkillFiles = [...builtInSkills, ...workspaceSkillFiles];
|
|
18062
|
-
await adapter2.writeSkills(allSkillFiles, scope);
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18603
|
+
const written = await adapter2.writeSkills(allSkillFiles, scope);
|
|
18604
|
+
if (written > 0) {
|
|
18605
|
+
summary.skillWrites++;
|
|
18606
|
+
summary.skillFilesWritten += written;
|
|
18607
|
+
vlog(` [${adapter2.name}] Wrote ${written} skills (${scope}): ` + `${builtInSkills.length} built-in [${builtInNames.join(", ")}], ` + `${workspaceSkillFiles.length} workspace`);
|
|
18608
|
+
} else {
|
|
18609
|
+
vlog(` [${adapter2.name}] Skipped skills (${scope}): adapter writes none at this scope`);
|
|
18610
|
+
}
|
|
18611
|
+
} catch (err) {
|
|
18612
|
+
adapterFailedAnyScope = true;
|
|
18613
|
+
skillFailedAdapters.add(adapter2.slug);
|
|
18614
|
+
console.warn(` [${adapter2.name}] Failed skills (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
18068
18615
|
}
|
|
18069
|
-
|
|
18616
|
+
} else {
|
|
18617
|
+
vlog(` [${adapter2.name}] Skipped skills (${scope}): adapter does not support skills`);
|
|
18618
|
+
}
|
|
18619
|
+
if (adapter2.supportsMcpScope(scope) && mcpEntries.length > 0) {
|
|
18620
|
+
try {
|
|
18070
18621
|
await adapter2.writeMcpServers(mcpEntries, scope);
|
|
18071
18622
|
summary.mcpServerWrites++;
|
|
18072
|
-
|
|
18073
|
-
}
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
console.
|
|
18623
|
+
vlog(` [${adapter2.name}] Updated ${mcpEntries.length} MCP server${mcpEntries.length > 1 ? "s" : ""} (${scope})`);
|
|
18624
|
+
} catch (err) {
|
|
18625
|
+
adapterFailedAnyScope = true;
|
|
18626
|
+
mcpFailedAdapters.add(adapter2.slug);
|
|
18627
|
+
console.warn(` [${adapter2.name}] Failed MCP servers (${scope}): ${err instanceof Error ? err.message : err}`);
|
|
18077
18628
|
}
|
|
18629
|
+
} else if (mcpEntries.length === 0) {
|
|
18630
|
+
vlog(` [${adapter2.name}] Skipped MCP servers (${scope}): no workspace MCP servers configured`);
|
|
18631
|
+
} else if (!adapter2.supportsMcpScope(scope)) {
|
|
18632
|
+
vlog(` [${adapter2.name}] Skipped MCP servers (${scope}): adapter does not support MCP at this scope`);
|
|
18633
|
+
}
|
|
18634
|
+
try {
|
|
18078
18635
|
await adapter2.writeInstructionHint(instructionHint, scope);
|
|
18079
18636
|
summary.instructionHintWrites++;
|
|
18080
|
-
|
|
18637
|
+
vlog(` [${adapter2.name}] Updated instruction hints (${scope})`);
|
|
18081
18638
|
if (adapter2.writeBuiltInHooks) {
|
|
18082
18639
|
await adapter2.writeBuiltInHooks(scope);
|
|
18083
18640
|
summary.hookInstallCalls++;
|
|
@@ -18116,7 +18673,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18116
18673
|
try {
|
|
18117
18674
|
await adapter2.writeTeamInstructions(fullInstructions, scope);
|
|
18118
18675
|
teamInstructionsApplied = true;
|
|
18119
|
-
|
|
18676
|
+
vlog(` [${adapter2.name}] Updated team instructions (${scope})`);
|
|
18120
18677
|
} catch {}
|
|
18121
18678
|
}
|
|
18122
18679
|
}
|
|
@@ -18137,7 +18694,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18137
18694
|
await adapter2.writeAgentConfig(configWithoutInstructions, scope);
|
|
18138
18695
|
agentConfigsApplied++;
|
|
18139
18696
|
const configKeys = Object.keys(configWithoutInstructions).join(", ");
|
|
18140
|
-
|
|
18697
|
+
vlog(` [${adapter2.name}] Updated agent config: ${configKeys} (${scope})`);
|
|
18141
18698
|
} catch {}
|
|
18142
18699
|
}
|
|
18143
18700
|
}
|
|
@@ -18192,6 +18749,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18192
18749
|
try {
|
|
18193
18750
|
const baseline = agentState?.lastInjected;
|
|
18194
18751
|
await adapter2.writeAgentConfig(mergedConfig, "user", baseline);
|
|
18752
|
+
if (baked && !resolved.bootstrapped) {
|
|
18753
|
+
const prev = agentState?.lastInjected;
|
|
18754
|
+
if (!sameStringSet(prev?.allow, resolved.applicableAllow) || !sameStringSet(prev?.deny, resolved.applicableDeny)) {
|
|
18755
|
+
settingsChangedSlugs.push(slug);
|
|
18756
|
+
}
|
|
18757
|
+
}
|
|
18195
18758
|
if (baked) {
|
|
18196
18759
|
const nextState = {
|
|
18197
18760
|
lastInjected: {
|
|
@@ -18205,11 +18768,11 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18205
18768
|
};
|
|
18206
18769
|
state.agentDefaults[slug] = nextState;
|
|
18207
18770
|
if (resolved.bootstrapped) {
|
|
18208
|
-
|
|
18771
|
+
vlog(` [${adapter2.name}] Bootstrapped default-rule tracking (defaults apply on next sync)`);
|
|
18209
18772
|
} else if (resolved.applicableAllow.length || resolved.applicableDeny.length || resolved.removalsThisSync.allow || resolved.removalsThisSync.deny) {
|
|
18210
18773
|
const totalRemovals = resolved.removalsThisSync.allow + resolved.removalsThisSync.deny;
|
|
18211
18774
|
const removalNote = totalRemovals > 0 ? ` (${totalRemovals} new opt-out${totalRemovals > 1 ? "s" : ""} honored)` : "";
|
|
18212
|
-
|
|
18775
|
+
vlog(` [${adapter2.name}] Applied defaults: ${resolved.applicableAllow.length} allow, ${resolved.applicableDeny.length} deny${removalNote}`);
|
|
18213
18776
|
}
|
|
18214
18777
|
}
|
|
18215
18778
|
if (team)
|
|
@@ -18223,11 +18786,12 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18223
18786
|
const runworkDir = join35(homedir17(), ".runwork");
|
|
18224
18787
|
const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
|
|
18225
18788
|
if (result === "written") {
|
|
18226
|
-
|
|
18789
|
+
vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
|
|
18227
18790
|
}
|
|
18228
18791
|
break;
|
|
18229
18792
|
}
|
|
18230
18793
|
}
|
|
18794
|
+
const prevMcpNames = state.mcpServers ?? [];
|
|
18231
18795
|
state.lastSyncAt = new Date().toISOString();
|
|
18232
18796
|
state.mcpServers = mcpEntries.map((e) => e.name);
|
|
18233
18797
|
state.skills = remoteSkills.map((s) => s.name);
|
|
@@ -18235,6 +18799,57 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18235
18799
|
"runwork",
|
|
18236
18800
|
...remoteSkills.map((s) => s.name.toLowerCase().replace(/[^a-z0-9]+/g, "-"))
|
|
18237
18801
|
];
|
|
18802
|
+
const priorSkillHashes = state.skillHashes || {};
|
|
18803
|
+
const newLocalHashes = {};
|
|
18804
|
+
for (const [name, h] of Object.entries(newHashes))
|
|
18805
|
+
newLocalHashes[name] = h.localHash;
|
|
18806
|
+
const priorLocalHashes = {};
|
|
18807
|
+
for (const [name, h] of Object.entries(priorSkillHashes))
|
|
18808
|
+
priorLocalHashes[name] = h.localHash;
|
|
18809
|
+
const materializedDeletions = plan.deletions.filter((d) => d.localContent !== undefined).map((d) => d.name);
|
|
18810
|
+
const skillChanges = summarizeRealSkillChanges(newLocalHashes, priorLocalHashes, materializedDeletions);
|
|
18811
|
+
const newMcpNames = mcpEntries.map((e) => e.name);
|
|
18812
|
+
const prevMcpSet = new Set(prevMcpNames);
|
|
18813
|
+
const newMcpSet = new Set(newMcpNames);
|
|
18814
|
+
const mcpAdded = mcpChanged ? newMcpNames.filter((n) => !prevMcpSet.has(n)) : [];
|
|
18815
|
+
const mcpRemoved = mcpChanged ? prevMcpNames.filter((n) => !newMcpSet.has(n)) : [];
|
|
18816
|
+
const addedSet = new Set(addedThisSync);
|
|
18817
|
+
const restartReasons = computeRestartReasons({
|
|
18818
|
+
agents: adapters.map((a) => ({
|
|
18819
|
+
slug: a.slug,
|
|
18820
|
+
receivesMcp: mcpEntries.length > 0 && scopes.some((sc) => a.supportsMcpScope(sc)) && !mcpFailedAdapters.has(a.slug),
|
|
18821
|
+
connectOnly: isConnectOnlyAgent(getRegistryAgent(a.slug)),
|
|
18822
|
+
isNew: !!opts.initialSetup || addedSet.has(a.slug)
|
|
18823
|
+
})),
|
|
18824
|
+
mcpChanged,
|
|
18825
|
+
settingsChangedSlugs
|
|
18826
|
+
});
|
|
18827
|
+
if (Object.keys(restartReasons).length > 0) {
|
|
18828
|
+
if (!state.agentConfigChangedAt)
|
|
18829
|
+
state.agentConfigChangedAt = {};
|
|
18830
|
+
for (const [slug, reasons] of Object.entries(restartReasons)) {
|
|
18831
|
+
state.agentConfigChangedAt[slug] = { changedAt: state.lastSyncAt, reasons };
|
|
18832
|
+
}
|
|
18833
|
+
}
|
|
18834
|
+
if (hasRealSkillChange(skillChanges) || builtInSkillsChanged || mcpChanged || settingsChangedSlugs.length > 0) {
|
|
18835
|
+
state.lastSyncChange = {
|
|
18836
|
+
at: state.lastSyncAt,
|
|
18837
|
+
skills: skillChanges,
|
|
18838
|
+
builtInSkillsChanged,
|
|
18839
|
+
mcp: { changed: mcpChanged, added: mcpAdded, removed: mcpRemoved },
|
|
18840
|
+
settingsSlugs: settingsChangedSlugs
|
|
18841
|
+
};
|
|
18842
|
+
}
|
|
18843
|
+
if (mcpFailedAdapters.size === 0)
|
|
18844
|
+
state.lastMcpEntriesHash = mcpEntriesHash;
|
|
18845
|
+
if (skillFailedAdapters.size === 0)
|
|
18846
|
+
state.builtInSkillsHash = builtInSkillsHash;
|
|
18847
|
+
const nameBySlug = new Map(adapters.map((a) => [a.slug, a.name]));
|
|
18848
|
+
const restartChanges = Object.entries(restartReasons).map(([slug, reasons]) => ({
|
|
18849
|
+
name: nameBySlug.get(slug) ?? slug,
|
|
18850
|
+
reasons
|
|
18851
|
+
}));
|
|
18852
|
+
console.log(` Restart-worthy changes: ${formatRestartChanges(restartChanges)}`);
|
|
18238
18853
|
const mergedHashes = { ...state.skillHashes || {} };
|
|
18239
18854
|
for (const [name, hash] of Object.entries(newHashes)) {
|
|
18240
18855
|
mergedHashes[name] = hash;
|
|
@@ -18256,25 +18871,29 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18256
18871
|
if (opts.verbose)
|
|
18257
18872
|
printTelemetryVerbose(telemetry);
|
|
18258
18873
|
await client.reportTelemetry(state.workspaceId, telemetry.events);
|
|
18259
|
-
if (telemetry.healthReported)
|
|
18874
|
+
if (telemetry.healthReported) {
|
|
18260
18875
|
state.lastHealthReportAt = new Date().toISOString();
|
|
18261
|
-
|
|
18262
|
-
|
|
18263
|
-
];
|
|
18264
|
-
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18876
|
+
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18877
|
+
}
|
|
18265
18878
|
} catch {}
|
|
18266
|
-
const
|
|
18267
|
-
|
|
18268
|
-
|
|
18269
|
-
summaryParts.push(`${summary.
|
|
18270
|
-
|
|
18271
|
-
|
|
18272
|
-
summaryParts.push(`${summary.
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18879
|
+
const failedNote = summary.adaptersFailed > 0 ? ` (${summary.adaptersFailed} failed)` : "";
|
|
18880
|
+
if (isVerbose()) {
|
|
18881
|
+
const summaryParts = [];
|
|
18882
|
+
summaryParts.push(`${summary.adaptersProcessed} adapter${summary.adaptersProcessed === 1 ? "" : "s"}`);
|
|
18883
|
+
if (summary.adaptersFailed > 0)
|
|
18884
|
+
summaryParts.push(`${summary.adaptersFailed} failed`);
|
|
18885
|
+
summaryParts.push(`${summary.skillWrites} skill write${summary.skillWrites === 1 ? "" : "s"} (${summary.skillFilesWritten} files)`);
|
|
18886
|
+
if (summary.mcpServerWrites > 0)
|
|
18887
|
+
summaryParts.push(`${summary.mcpServerWrites} MCP config write${summary.mcpServerWrites === 1 ? "" : "s"}`);
|
|
18888
|
+
if (summary.hookInstallCalls > 0)
|
|
18889
|
+
summaryParts.push(`${summary.hookInstallCalls} hook install${summary.hookInstallCalls === 1 ? "" : "s"}`);
|
|
18890
|
+
summaryParts.push(`${summary.instructionHintWrites} instruction hint write${summary.instructionHintWrites === 1 ? "" : "s"}`);
|
|
18891
|
+
console.log(`
|
|
18277
18892
|
Summary: ${summaryParts.join(", ")}.`);
|
|
18893
|
+
} else {
|
|
18894
|
+
console.log(`
|
|
18895
|
+
${summary.adaptersProcessed} agent${summary.adaptersProcessed === 1 ? "" : "s"} synced${failedNote}.`);
|
|
18896
|
+
}
|
|
18278
18897
|
try {
|
|
18279
18898
|
const cadence = loadCadenceState();
|
|
18280
18899
|
const lastMs = cadence.lastReflectedAt ? new Date(cadence.lastReflectedAt).getTime() : 0;
|
|
@@ -18372,7 +18991,7 @@ async function resolveAndPersistWorkspace(client, opts) {
|
|
|
18372
18991
|
}
|
|
18373
18992
|
return { workspaceId, workspaceName, workspaceSlug };
|
|
18374
18993
|
}
|
|
18375
|
-
var setupCommand = new Command25("setup").description("Configure local AI agents with workspace skills and MCP servers").option("--workspace <id>", "Workspace ID").option("--agent <slug>", "Only configure a specific agent (e.g. claude-code, cursor)").option("--dry-run", "Show what would be configured without writing files").option("-y, --yes", "Skip all prompts, configure all detected agents with user scope").option("--persona <level>", "Technical-level persona for agent instructions (1=everyday, 2=curious, 3=engineer)").action(async (opts) => {
|
|
18994
|
+
var setupCommand = new Command25("setup").description("Configure local AI agents with workspace skills and MCP servers").option("--workspace <name-or-id>", "Workspace name or ID").option("--agent <slug>", "Only configure a specific agent (e.g. claude-code, cursor)").option("--dry-run", "Show what would be configured without writing files").option("-y, --yes", "Skip all prompts, configure all detected agents with user scope").option("--persona <level>", "Technical-level persona for agent instructions (1=everyday, 2=curious, 3=engineer)").action(async (opts) => {
|
|
18376
18995
|
const credentials = requireAuth();
|
|
18377
18996
|
const client = new ApiClient(credentials);
|
|
18378
18997
|
const { workspaceId, workspaceName, workspaceSlug } = await resolveAndPersistWorkspace(client, opts);
|
|
@@ -18467,7 +19086,8 @@ Syncing workspace data...
|
|
|
18467
19086
|
dryRun: false,
|
|
18468
19087
|
pullOnly: true,
|
|
18469
19088
|
yes: true,
|
|
18470
|
-
prefer: "remote"
|
|
19089
|
+
prefer: "remote",
|
|
19090
|
+
initialSetup: true
|
|
18471
19091
|
});
|
|
18472
19092
|
}
|
|
18473
19093
|
console.log("\nSetup complete. Run `runwork sync` anytime to refresh.");
|
|
@@ -18723,7 +19343,7 @@ init_store();
|
|
|
18723
19343
|
init_client();
|
|
18724
19344
|
import { Command as Command28 } from "commander";
|
|
18725
19345
|
init_init();
|
|
18726
|
-
var listCommand12 = new Command28("list").description("List apps in workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
19346
|
+
var listCommand12 = new Command28("list").description("List apps in workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
18727
19347
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
18728
19348
|
const credentials = requireAuth();
|
|
18729
19349
|
const client = new ApiClient(credentials);
|
|
@@ -18756,7 +19376,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
18756
19376
|
var createCommand3 = new Command28("create").description("Create a new Runwork app").argument("[name]", "App name").action(async (name) => {
|
|
18757
19377
|
await runCreateFlow(name);
|
|
18758
19378
|
});
|
|
18759
|
-
var infoCommand2 = new Command28("info").description("Show detailed app info, preview status, and registries").argument("[app]", "App ID, name, or slug").option("--workspace <id>", "Workspace ID").action(async (appArg, opts, command) => {
|
|
19379
|
+
var infoCommand2 = new Command28("info").description("Show detailed app info, preview status, and registries").argument("[app]", "App ID, name, or slug").option("--workspace <name-or-id>", "Workspace name or ID").action(async (appArg, opts, command) => {
|
|
18760
19380
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
18761
19381
|
const credentials = requireAuth();
|
|
18762
19382
|
const client = new ApiClient(credentials);
|
|
@@ -18783,15 +19403,157 @@ var infoCommand2 = new Command28("info").description("Show detailed app info, pr
|
|
|
18783
19403
|
});
|
|
18784
19404
|
var appsCommand = new Command28("apps").description("Manage workspace apps").addCommand(listCommand12).addCommand(createCommand3).addCommand(infoCommand2);
|
|
18785
19405
|
|
|
19406
|
+
// src/commands/members.ts
|
|
19407
|
+
init_store();
|
|
19408
|
+
init_client();
|
|
19409
|
+
import { Command as Command29 } from "commander";
|
|
19410
|
+
function formatMemberRows(members) {
|
|
19411
|
+
return members.map((m) => ({
|
|
19412
|
+
name: m.user.displayName || m.user.email,
|
|
19413
|
+
email: m.user.email,
|
|
19414
|
+
role: m.role,
|
|
19415
|
+
status: m.status,
|
|
19416
|
+
userId: m.userId
|
|
19417
|
+
}));
|
|
19418
|
+
}
|
|
19419
|
+
var listCommand13 = new Command29("list").description("List members of a workspace (name, email, role)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
19420
|
+
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19421
|
+
const credentials = requireAuth();
|
|
19422
|
+
const client = new ApiClient(credentials);
|
|
19423
|
+
const { workspaceId, workspaceName } = await resolveWorkspace2(client, opts);
|
|
19424
|
+
try {
|
|
19425
|
+
const members = await client.listWorkspaceMembers(workspaceId);
|
|
19426
|
+
if (useJson) {
|
|
19427
|
+
jsonOut({ workspaceId, workspaceName, members });
|
|
19428
|
+
return;
|
|
19429
|
+
}
|
|
19430
|
+
if (members.length === 0) {
|
|
19431
|
+
console.log("No members found in this workspace.");
|
|
19432
|
+
return;
|
|
19433
|
+
}
|
|
19434
|
+
console.log(`
|
|
19435
|
+
Workspace: ${workspaceName || workspaceId}
|
|
19436
|
+
`);
|
|
19437
|
+
console.log(` ${"NAME".padEnd(24)} ${"EMAIL".padEnd(32)} ${"ROLE".padEnd(10)} STATUS`);
|
|
19438
|
+
console.log(" " + "-".repeat(76));
|
|
19439
|
+
for (const row of formatMemberRows(members)) {
|
|
19440
|
+
console.log(` ${row.name.padEnd(24)} ${row.email.padEnd(32)} ${row.role.padEnd(10)} ${row.status}`);
|
|
19441
|
+
}
|
|
19442
|
+
console.log("");
|
|
19443
|
+
} catch (err) {
|
|
19444
|
+
console.error("Failed to list members:", err instanceof Error ? err.message : err);
|
|
19445
|
+
process.exit(1);
|
|
19446
|
+
}
|
|
19447
|
+
});
|
|
19448
|
+
var membersCommand = new Command29("members").description("List workspace members").addCommand(listCommand13);
|
|
19449
|
+
|
|
19450
|
+
// src/commands/api.ts
|
|
19451
|
+
init_store();
|
|
19452
|
+
init_client();
|
|
19453
|
+
import { Command as Command30 } from "commander";
|
|
19454
|
+
import { readFileSync as readFileSync37 } from "fs";
|
|
19455
|
+
function normalizeApiPath(rawPath, baseUrl) {
|
|
19456
|
+
if (/^https?:\/\//i.test(rawPath)) {
|
|
19457
|
+
const target = new URL(rawPath);
|
|
19458
|
+
const base = new URL(baseUrl);
|
|
19459
|
+
if (target.origin !== base.origin) {
|
|
19460
|
+
throw new Error(`Refusing to call ${target.origin}: \`runwork api\` only calls the platform API at ${base.origin}.`);
|
|
19461
|
+
}
|
|
19462
|
+
return target.pathname + target.search;
|
|
19463
|
+
}
|
|
19464
|
+
return rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
19465
|
+
}
|
|
19466
|
+
var apiCommand = new Command30("api").description("Make an authenticated request to the Runwork platform API (escape hatch for endpoints the CLI does not cover)").argument("<method>", "HTTP method (GET, POST, PUT, DELETE, etc.)").argument("[path]", "API path (e.g. /api/workspaces); optional with --curl").option("--body <json>", "Request body JSON (or @file.json)").option("--header <header>", 'Request header (format: "Key: Value", repeatable)', (val, prev) => [...prev, val], []).option("--query <query>", "Query string (e.g. limit=10&offset=0)").option("--curl <command>", "Parse a curl command (method, path, headers, body)").option("--curl-file <file>", "Read curl command from a file").addHelpText("after", `
|
|
19467
|
+
Examples:
|
|
19468
|
+
runwork api GET /api/workspaces
|
|
19469
|
+
runwork api GET /api/workspaces/<id>/members
|
|
19470
|
+
runwork api POST /api/workspaces/<id>/skills --body '{"name":"my-skill"}'
|
|
19471
|
+
|
|
19472
|
+
Authentication uses your stored Runwork credentials; the API key never needs
|
|
19473
|
+
to be read or pasted manually. Prefer a dedicated command when one exists
|
|
19474
|
+
(runwork apps/members/schedules/...).`).action(async (method, path2, opts, command) => {
|
|
19475
|
+
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19476
|
+
const credentials = requireAuth();
|
|
19477
|
+
const client = new ApiClient(credentials);
|
|
19478
|
+
let finalMethod = method;
|
|
19479
|
+
let finalPath = path2;
|
|
19480
|
+
const headers = {};
|
|
19481
|
+
let body;
|
|
19482
|
+
const query = opts.query;
|
|
19483
|
+
if (opts.curl || opts.curlFile) {
|
|
19484
|
+
let curlStr = opts.curl;
|
|
19485
|
+
if (opts.curlFile) {
|
|
19486
|
+
try {
|
|
19487
|
+
curlStr = readFileSync37(opts.curlFile, "utf-8");
|
|
19488
|
+
} catch (err) {
|
|
19489
|
+
console.error(`Could not read --curl-file: ${err instanceof Error ? err.message : err}`);
|
|
19490
|
+
process.exit(1);
|
|
19491
|
+
}
|
|
19492
|
+
}
|
|
19493
|
+
const parsed = await parseCurlToRequest(curlStr);
|
|
19494
|
+
finalMethod = parsed.method;
|
|
19495
|
+
finalPath = parsed.path + (parsed.query ? `?${parsed.query}` : "");
|
|
19496
|
+
Object.assign(headers, parsed.headers);
|
|
19497
|
+
body = parsed.body;
|
|
19498
|
+
}
|
|
19499
|
+
for (const h of opts.header || []) {
|
|
19500
|
+
const [key, ...rest] = h.split(":");
|
|
19501
|
+
headers[key.trim()] = rest.join(":").trim();
|
|
19502
|
+
}
|
|
19503
|
+
if (opts.body) {
|
|
19504
|
+
let raw = opts.body;
|
|
19505
|
+
if (raw.startsWith("@")) {
|
|
19506
|
+
try {
|
|
19507
|
+
raw = readFileSync37(raw.slice(1), "utf-8");
|
|
19508
|
+
} catch (err) {
|
|
19509
|
+
console.error(`Could not read body file: ${err instanceof Error ? err.message : err}`);
|
|
19510
|
+
process.exit(1);
|
|
19511
|
+
}
|
|
19512
|
+
}
|
|
19513
|
+
try {
|
|
19514
|
+
body = JSON.parse(raw);
|
|
19515
|
+
} catch {
|
|
19516
|
+
console.error("Invalid JSON in --body");
|
|
19517
|
+
process.exit(1);
|
|
19518
|
+
}
|
|
19519
|
+
}
|
|
19520
|
+
if (!finalPath) {
|
|
19521
|
+
console.error("Usage: runwork api <method> <path>");
|
|
19522
|
+
console.error(' or: runwork api <method> --curl "curl ..."');
|
|
19523
|
+
process.exit(1);
|
|
19524
|
+
}
|
|
19525
|
+
try {
|
|
19526
|
+
const normalizedPath = normalizeApiPath(finalPath, credentials.baseUrl || "https://runwork.ai");
|
|
19527
|
+
const result = await client.rawApiCall(finalMethod, normalizedPath, {
|
|
19528
|
+
body,
|
|
19529
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
19530
|
+
query
|
|
19531
|
+
});
|
|
19532
|
+
if (useJson) {
|
|
19533
|
+
jsonOut({ success: result.ok, status: result.status, body: result.body });
|
|
19534
|
+
} else if (typeof result.body === "string") {
|
|
19535
|
+
console.log(result.body);
|
|
19536
|
+
} else {
|
|
19537
|
+
console.log(JSON.stringify(result.body, null, 2));
|
|
19538
|
+
}
|
|
19539
|
+
if (!result.ok)
|
|
19540
|
+
process.exit(1);
|
|
19541
|
+
} catch (err) {
|
|
19542
|
+
console.error("API call failed:", err instanceof Error ? err.message : err);
|
|
19543
|
+
process.exit(1);
|
|
19544
|
+
}
|
|
19545
|
+
});
|
|
19546
|
+
|
|
18786
19547
|
// src/commands/doctor.ts
|
|
18787
19548
|
init_colors();
|
|
18788
|
-
import { Command as
|
|
19549
|
+
import { Command as Command31 } from "commander";
|
|
18789
19550
|
|
|
18790
19551
|
// src/health/checks.ts
|
|
18791
19552
|
init_subprocess();
|
|
18792
19553
|
init_store();
|
|
18793
19554
|
init_client();
|
|
18794
|
-
import {
|
|
19555
|
+
import { parse as parse2 } from "smol-toml";
|
|
19556
|
+
import { existsSync as existsSync44, readFileSync as readFileSync38 } from "fs";
|
|
18795
19557
|
import { join as join40, sep as sep3 } from "path";
|
|
18796
19558
|
import { homedir as homedir22, platform as osPlatform2, arch as osArch } from "os";
|
|
18797
19559
|
init_http();
|
|
@@ -18827,7 +19589,7 @@ function buildContext() {
|
|
|
18827
19589
|
const configPath = join40(process.cwd(), ".runwork.json");
|
|
18828
19590
|
if (existsSync44(configPath)) {
|
|
18829
19591
|
try {
|
|
18830
|
-
config = JSON.parse(
|
|
19592
|
+
config = JSON.parse(readFileSync38(configPath, "utf-8"));
|
|
18831
19593
|
} catch {}
|
|
18832
19594
|
}
|
|
18833
19595
|
return { credentials, client, config, cwd: process.cwd() };
|
|
@@ -19047,6 +19809,14 @@ async function checkGitCredentialHelper(ctx) {
|
|
|
19047
19809
|
fix: "runwork doctor --fix (re-registers with the current binary path)"
|
|
19048
19810
|
};
|
|
19049
19811
|
}
|
|
19812
|
+
if (!lookup.hasReset) {
|
|
19813
|
+
return {
|
|
19814
|
+
name: "git-credential-helper",
|
|
19815
|
+
status: "fail",
|
|
19816
|
+
message: `helper registered for ${origin} but the credential-manager reset entry is missing (Windows may show a Git Credential Manager popup)`,
|
|
19817
|
+
fix: "runwork doctor --fix"
|
|
19818
|
+
};
|
|
19819
|
+
}
|
|
19050
19820
|
return {
|
|
19051
19821
|
name: "git-credential-helper",
|
|
19052
19822
|
status: "pass",
|
|
@@ -19176,7 +19946,7 @@ function loadSetupState5() {
|
|
|
19176
19946
|
for (const p of [projectPath, userPath]) {
|
|
19177
19947
|
if (existsSync44(p)) {
|
|
19178
19948
|
try {
|
|
19179
|
-
return JSON.parse(
|
|
19949
|
+
return JSON.parse(readFileSync38(p, "utf-8"));
|
|
19180
19950
|
} catch {
|
|
19181
19951
|
continue;
|
|
19182
19952
|
}
|
|
@@ -19184,6 +19954,95 @@ function loadSetupState5() {
|
|
|
19184
19954
|
}
|
|
19185
19955
|
return null;
|
|
19186
19956
|
}
|
|
19957
|
+
var RUNWORK_NETWORK_DOMAINS = ["runwork.ai", "*.runwork.ai"];
|
|
19958
|
+
async function checkCodexNetwork() {
|
|
19959
|
+
const name = "codex-network";
|
|
19960
|
+
const state = loadSetupState5();
|
|
19961
|
+
if (!state || !state.configuredAgents.includes("codex")) {
|
|
19962
|
+
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
19963
|
+
}
|
|
19964
|
+
const configPath = join40(homedir22(), ".codex", "config.toml");
|
|
19965
|
+
if (!existsSync44(configPath)) {
|
|
19966
|
+
return { name, status: "skip", message: "no Codex config found" };
|
|
19967
|
+
}
|
|
19968
|
+
let parsed;
|
|
19969
|
+
try {
|
|
19970
|
+
parsed = parse2(readFileSync38(configPath, "utf-8"));
|
|
19971
|
+
} catch {
|
|
19972
|
+
return { name, status: "warn", message: "could not parse ~/.codex/config.toml" };
|
|
19973
|
+
}
|
|
19974
|
+
const features = parsed.features && typeof parsed.features === "object" ? parsed.features : undefined;
|
|
19975
|
+
const proxy = features?.network_proxy;
|
|
19976
|
+
if (proxy && proxy.enabled === true) {
|
|
19977
|
+
const domains = proxy.domains && typeof proxy.domains === "object" ? proxy.domains : {};
|
|
19978
|
+
if (RUNWORK_NETWORK_DOMAINS.some((d) => domains[d] === "deny")) {
|
|
19979
|
+
return {
|
|
19980
|
+
name,
|
|
19981
|
+
status: "warn",
|
|
19982
|
+
message: "network proxy denies runwork.ai; the Runwork CLI is blocked",
|
|
19983
|
+
fix: 'remove the runwork.ai "deny" rule in ~/.codex/config.toml'
|
|
19984
|
+
};
|
|
19985
|
+
}
|
|
19986
|
+
if (RUNWORK_NETWORK_DOMAINS.every((d) => domains[d] === "allow")) {
|
|
19987
|
+
return { name, status: "pass", message: "proxy-allowlisted: runwork.ai permitted, other domains scoped by your proxy" };
|
|
19988
|
+
}
|
|
19989
|
+
return {
|
|
19990
|
+
name,
|
|
19991
|
+
status: "warn",
|
|
19992
|
+
message: "network proxy is on but runwork.ai is not allowlisted",
|
|
19993
|
+
fix: "runwork sync"
|
|
19994
|
+
};
|
|
19995
|
+
}
|
|
19996
|
+
const sww = parsed.sandbox_workspace_write && typeof parsed.sandbox_workspace_write === "object" ? parsed.sandbox_workspace_write : undefined;
|
|
19997
|
+
const sandboxMode = typeof parsed.sandbox_mode === "string" ? parsed.sandbox_mode : undefined;
|
|
19998
|
+
if (sww?.network_access === true || sandboxMode === "danger-full-access") {
|
|
19999
|
+
return { name, status: "pass", message: "open: outbound network enabled (all domains)" };
|
|
20000
|
+
}
|
|
20001
|
+
if (sww?.network_access === false) {
|
|
20002
|
+
return {
|
|
20003
|
+
name,
|
|
20004
|
+
status: "warn",
|
|
20005
|
+
message: "off: network explicitly disabled; the Runwork CLI cannot reach the network",
|
|
20006
|
+
fix: "set [sandbox_workspace_write] network_access = true, or run runwork sync"
|
|
20007
|
+
};
|
|
20008
|
+
}
|
|
20009
|
+
return {
|
|
20010
|
+
name,
|
|
20011
|
+
status: "warn",
|
|
20012
|
+
message: "off: network is disabled by default in Codex workspace-write sandbox; the Runwork CLI cannot reach the network",
|
|
20013
|
+
fix: "runwork sync"
|
|
20014
|
+
};
|
|
20015
|
+
}
|
|
20016
|
+
async function checkCodexDesktopProject() {
|
|
20017
|
+
const name = "codex-desktop-project";
|
|
20018
|
+
const state = loadSetupState5();
|
|
20019
|
+
const usesCodex = !!state && (state.configuredAgents.includes("codex-app") || state.configuredAgents.includes("codex"));
|
|
20020
|
+
if (!usesCodex) {
|
|
20021
|
+
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
20022
|
+
}
|
|
20023
|
+
const statePath2 = join40(homedir22(), ".codex", ".codex-global-state.json");
|
|
20024
|
+
if (!existsSync44(statePath2)) {
|
|
20025
|
+
return { name, status: "skip", message: "Codex desktop app not detected" };
|
|
20026
|
+
}
|
|
20027
|
+
let savedRoots = [];
|
|
20028
|
+
try {
|
|
20029
|
+
const parsed = JSON.parse(readFileSync38(statePath2, "utf-8"));
|
|
20030
|
+
const roots = parsed["electron-saved-workspace-roots"];
|
|
20031
|
+
savedRoots = Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
|
|
20032
|
+
} catch {
|
|
20033
|
+
return { name, status: "warn", message: "could not read Codex desktop state" };
|
|
20034
|
+
}
|
|
20035
|
+
const runworkDir = join40(homedir22(), ".runwork");
|
|
20036
|
+
if (savedRoots.includes(runworkDir)) {
|
|
20037
|
+
return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
|
|
20038
|
+
}
|
|
20039
|
+
return {
|
|
20040
|
+
name,
|
|
20041
|
+
status: "warn",
|
|
20042
|
+
message: "Runwork project not added to Codex desktop sidebar",
|
|
20043
|
+
fix: "quit the Codex app, then run runwork sync (sync skips this while Codex is open)"
|
|
20044
|
+
};
|
|
20045
|
+
}
|
|
19187
20046
|
async function checkAgentSetup() {
|
|
19188
20047
|
const state = loadSetupState5();
|
|
19189
20048
|
if (!state) {
|
|
@@ -19228,7 +20087,7 @@ async function checkAgentSetup() {
|
|
|
19228
20087
|
const mcpConfigPath = getMcpConfigPath2(slug, "user");
|
|
19229
20088
|
if (mcpConfigPath && existsSync44(mcpConfigPath)) {
|
|
19230
20089
|
try {
|
|
19231
|
-
const content =
|
|
20090
|
+
const content = readFileSync38(mcpConfigPath, "utf-8");
|
|
19232
20091
|
const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
|
|
19233
20092
|
if (missingMcp.length > 0) {
|
|
19234
20093
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
@@ -19286,7 +20145,8 @@ function getMcpConfigPath2(slug, scope) {
|
|
|
19286
20145
|
case "windsurf":
|
|
19287
20146
|
return scope === "project" ? join40(process.cwd(), ".windsurf", "mcp.json") : join40(home, ".windsurf", "mcp.json");
|
|
19288
20147
|
case "codex":
|
|
19289
|
-
|
|
20148
|
+
case "codex-app":
|
|
20149
|
+
return scope === "user" ? join40(home, ".codex", "config.toml") : null;
|
|
19290
20150
|
case "gemini":
|
|
19291
20151
|
return scope === "user" ? join40(home, ".gemini", "settings.json") : null;
|
|
19292
20152
|
default:
|
|
@@ -19299,6 +20159,7 @@ function getSkillsDir(slug, scope) {
|
|
|
19299
20159
|
case "claude-code":
|
|
19300
20160
|
return scope === "project" ? join40(process.cwd(), ".claude", "skills") : join40(home, ".claude", "skills");
|
|
19301
20161
|
case "codex":
|
|
20162
|
+
case "codex-app":
|
|
19302
20163
|
return scope === "project" ? join40(process.cwd(), ".codex", "skills") : join40(home, ".codex", "skills");
|
|
19303
20164
|
case "gemini":
|
|
19304
20165
|
return scope === "project" ? join40(process.cwd(), ".gemini", "skills") : join40(home, ".gemini", "skills");
|
|
@@ -19325,7 +20186,9 @@ var CHECK_RUNNERS = [
|
|
|
19325
20186
|
{ names: ["app-exists"], run: async (ctx) => [await checkAppExists(ctx)] },
|
|
19326
20187
|
{ names: ["git-remote"], run: async (ctx) => [await checkGitRemote(ctx)] },
|
|
19327
20188
|
{ names: ["deploy-freshness"], run: async (ctx) => [await checkDeployFreshness(ctx)] },
|
|
19328
|
-
{ names: ["agent-setup"], run: async () => [await checkAgentSetup()] }
|
|
20189
|
+
{ names: ["agent-setup"], run: async () => [await checkAgentSetup()] },
|
|
20190
|
+
{ names: ["codex-network"], run: async () => [await checkCodexNetwork()] },
|
|
20191
|
+
{ names: ["codex-desktop-project"], run: async () => [await checkCodexDesktopProject()] }
|
|
19329
20192
|
];
|
|
19330
20193
|
var ALL_CHECK_NAMES = CHECK_RUNNERS.flatMap((r) => r.names);
|
|
19331
20194
|
async function runAllChecks(options) {
|
|
@@ -19398,7 +20261,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
19398
20261
|
}
|
|
19399
20262
|
|
|
19400
20263
|
// src/agents/runtime-detection.ts
|
|
19401
|
-
import { existsSync as existsSync46, readFileSync as
|
|
20264
|
+
import { existsSync as existsSync46, readFileSync as readFileSync39, statSync as statSync6, readdirSync as readdirSync13 } from "fs";
|
|
19402
20265
|
import { homedir as homedir23 } from "os";
|
|
19403
20266
|
import { join as join42 } from "path";
|
|
19404
20267
|
var RUNWORK_SESSIONS_DIR = join42(homedir23(), ".runwork", "sessions");
|
|
@@ -19468,7 +20331,7 @@ function readHookSessionInfo(sessionId) {
|
|
|
19468
20331
|
if (!existsSync46(path2))
|
|
19469
20332
|
return null;
|
|
19470
20333
|
try {
|
|
19471
|
-
const raw =
|
|
20334
|
+
const raw = readFileSync39(path2, "utf8");
|
|
19472
20335
|
const parsed = JSON.parse(raw);
|
|
19473
20336
|
return parsed;
|
|
19474
20337
|
} catch {
|
|
@@ -19688,7 +20551,9 @@ var CHECK_LABELS = {
|
|
|
19688
20551
|
"app-exists": "App exists",
|
|
19689
20552
|
"git-remote": "Git remote",
|
|
19690
20553
|
"deploy-freshness": "Deploy freshness",
|
|
19691
|
-
"agent-setup": "Agent setup"
|
|
20554
|
+
"agent-setup": "Agent setup",
|
|
20555
|
+
"codex-network": "Codex network",
|
|
20556
|
+
"codex-desktop-project": "Codex project"
|
|
19692
20557
|
};
|
|
19693
20558
|
function printHumanReport(report) {
|
|
19694
20559
|
console.log("");
|
|
@@ -19753,7 +20618,7 @@ function parseCheckNames(raw) {
|
|
|
19753
20618
|
const unknown = requested.filter((n) => !known.has(n));
|
|
19754
20619
|
return { only, unknown };
|
|
19755
20620
|
}
|
|
19756
|
-
var doctorCommand = new
|
|
20621
|
+
var doctorCommand = new Command31("doctor").description("Check system health: auth, network, project config, agent setup").option("-v, --verbose", "Include host-agent detection results, runtime info, and allowlisted env vars (useful for AI agents debugging their own environment)").option("--check <names>", `Run only the named checks (comma-separated). Available: ${ALL_CHECK_NAMES.join(", ")}`).option("--fix", "Auto-remediate fixable failures (git credential helper, runwork remote), then re-check").action(async (opts, command) => {
|
|
19757
20622
|
const asJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19758
20623
|
let only;
|
|
19759
20624
|
if (opts.check) {
|
|
@@ -19804,8 +20669,8 @@ var doctorCommand = new Command29("doctor").description("Check system health: au
|
|
|
19804
20669
|
// src/commands/share-convo.ts
|
|
19805
20670
|
init_store();
|
|
19806
20671
|
init_client();
|
|
19807
|
-
import { Command as
|
|
19808
|
-
import { readFileSync as
|
|
20672
|
+
import { Command as Command32 } from "commander";
|
|
20673
|
+
import { readFileSync as readFileSync40, existsSync as existsSync47 } from "fs";
|
|
19809
20674
|
import { createHash as createHash4 } from "crypto";
|
|
19810
20675
|
function nativeBundleFormatForAgent(slug) {
|
|
19811
20676
|
if (slug === "claude-code" || slug === "claude-desktop")
|
|
@@ -19847,7 +20712,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
19847
20712
|
const credentials = requireAuth();
|
|
19848
20713
|
const client = new ApiClient(credentials);
|
|
19849
20714
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
19850
|
-
const transcriptContent =
|
|
20715
|
+
const transcriptContent = readFileSync40(opts.transcriptFile, "utf8");
|
|
19851
20716
|
const bundles = [
|
|
19852
20717
|
{
|
|
19853
20718
|
format: "transcript",
|
|
@@ -19872,7 +20737,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
19872
20737
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
19873
20738
|
if (nativeFormat) {
|
|
19874
20739
|
try {
|
|
19875
|
-
const content =
|
|
20740
|
+
const content = readFileSync40(nativeFilePath, "utf8");
|
|
19876
20741
|
bundles.push({
|
|
19877
20742
|
format: nativeFormat,
|
|
19878
20743
|
content,
|
|
@@ -19888,7 +20753,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
19888
20753
|
let metadata = {};
|
|
19889
20754
|
if (opts.metadataFile) {
|
|
19890
20755
|
try {
|
|
19891
|
-
metadata = JSON.parse(
|
|
20756
|
+
metadata = JSON.parse(readFileSync40(opts.metadataFile, "utf8"));
|
|
19892
20757
|
} catch (err) {
|
|
19893
20758
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
19894
20759
|
process.exit(1);
|
|
@@ -19943,17 +20808,17 @@ Skipped: ${result.skipped.map((s) => `${s.identifier} (${s.reason})`).join(", ")
|
|
|
19943
20808
|
process.exit(1);
|
|
19944
20809
|
}
|
|
19945
20810
|
}
|
|
19946
|
-
var shareConvoCommand = new
|
|
20811
|
+
var shareConvoCommand = new Command32("share-convo").description("Share the current AI conversation with a teammate").option("--to <email>", "Recipient email (repeatable)", (value, prev = []) => [...prev, value], []).option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars otherwise)").option("--source-agent <slug>", "Override host-agent detection (e.g. claude-code, codex, claude-desktop)").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional personal note to recipients").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, suggestedNextStep, etc.").option("--metadata-file <path>", "Path to a JSON file with the same metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo(opts, command, false));
|
|
19947
20812
|
|
|
19948
20813
|
// src/commands/save-convo.ts
|
|
19949
|
-
import { Command as
|
|
19950
|
-
var saveConvoCommand = new
|
|
20814
|
+
import { Command as Command33 } from "commander";
|
|
20815
|
+
var saveConvoCommand = new Command33("save-convo").description("Save the current AI conversation as a personal checkpoint").option("--transcript-file <path>", "Path to the LLM-emitted markdown transcript (required)").option("--native-file <path>", "Path to the native session file (optional; auto-detected from env vars)").option("--source-agent <slug>", "Override host-agent detection").option("--title <string>", "Short title for the conversation (required)").option("--note <string>", "Optional note to your future self").option("--ttl-days <n>", "Days until expiration (1-30, default 7)").option("--metadata-json <json>", "Inline JSON object with workMode, openQuestions, etc.").option("--metadata-file <path>", "Path to a JSON file with metadata fields").option("--workspace <name-or-id>", "Workspace name or ID").action((opts, command) => runShareConvo({ ...opts, personal: true }, command, true));
|
|
19951
20816
|
|
|
19952
20817
|
// src/commands/inbox.ts
|
|
19953
20818
|
init_store();
|
|
19954
20819
|
init_client();
|
|
19955
|
-
import { Command as
|
|
19956
|
-
var inboxCommand = new
|
|
20820
|
+
import { Command as Command34 } from "commander";
|
|
20821
|
+
var inboxCommand = new Command34("inbox").description("List shared conversations visible to you").option("--filter <scope>", "Filter: all | received | sent | saved", "all").option("--limit <n>", "Max rows to return", "50").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
19957
20822
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19958
20823
|
const scope = opts.filter === "received" || opts.filter === "sent" || opts.filter === "saved" ? opts.filter : "all";
|
|
19959
20824
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 50;
|
|
@@ -19993,7 +20858,7 @@ Shared conversations (${scope}, ${total}):
|
|
|
19993
20858
|
// src/commands/resume.ts
|
|
19994
20859
|
init_store();
|
|
19995
20860
|
init_client();
|
|
19996
|
-
import { Command as
|
|
20861
|
+
import { Command as Command35 } from "commander";
|
|
19997
20862
|
import { writeFileSync as writeFileSync31, mkdirSync as mkdirSync28, realpathSync } from "fs";
|
|
19998
20863
|
import { homedir as homedir24 } from "os";
|
|
19999
20864
|
import { join as join43 } from "path";
|
|
@@ -20067,7 +20932,7 @@ function isAgentInstalled(agent) {
|
|
|
20067
20932
|
}
|
|
20068
20933
|
return false;
|
|
20069
20934
|
}
|
|
20070
|
-
var resumeCommand2 = new
|
|
20935
|
+
var resumeCommand2 = new Command35("resume").description("Resume a shared conversation locally in your agent of choice").argument("<share-id>", "The share ID (sc_*) returned by share-convo or save-convo").option("--agent <slug>", "Override target agent (e.g. claude-code, codex)").option("--into <path>", "Override target cwd (defaults to current $PWD)").option("--dry-run", "Print the resume command instead of executing it").option("--pick", "Show interactive picker (requires TTY) - not yet implemented").option("--workspace <name-or-id>", "Workspace name or ID").action(async (shareId, opts, command) => {
|
|
20071
20936
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
20072
20937
|
const credentials = requireAuth();
|
|
20073
20938
|
const client = new ApiClient(credentials);
|
|
@@ -20292,7 +21157,7 @@ process.on("uncaughtException", (err) => {
|
|
|
20292
21157
|
console.error(`Uncaught exception: ${formatError(err)}`);
|
|
20293
21158
|
process.exit(1);
|
|
20294
21159
|
});
|
|
20295
|
-
var program = new
|
|
21160
|
+
var program = new Command36;
|
|
20296
21161
|
program.name("runwork").description("Runwork CLI - local development for Runwork apps").version(VERSION).option("--json", "Output as JSON (auto-enabled when stdout is not a TTY)");
|
|
20297
21162
|
program.addCommand(infoCommand);
|
|
20298
21163
|
program.addCommand(loginCommand);
|
|
@@ -20321,6 +21186,8 @@ program.addCommand(syncCommand);
|
|
|
20321
21186
|
program.addCommand(buildPluginCommand);
|
|
20322
21187
|
program.addCommand(uninstallCommand);
|
|
20323
21188
|
program.addCommand(appsCommand);
|
|
21189
|
+
program.addCommand(membersCommand);
|
|
21190
|
+
program.addCommand(apiCommand);
|
|
20324
21191
|
program.addCommand(doctorCommand);
|
|
20325
21192
|
program.addCommand(shareConvoCommand);
|
|
20326
21193
|
program.addCommand(saveConvoCommand);
|