runwork 0.17.0 → 0.18.0
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/LICENSE +3 -3
- package/bundled-types/core-scheduler.d.ts +17 -1
- package/dist/index.js +1241 -356
- package/package.json +2 -2
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 = {}) {
|
|
@@ -5638,6 +5811,15 @@ export interface JobResult {
|
|
|
5638
5811
|
/** Additional result data (varies by job) */
|
|
5639
5812
|
[key: string]: unknown;
|
|
5640
5813
|
}
|
|
5814
|
+
/**
|
|
5815
|
+
* Wire-safe schedule definition: what crosses the RPC boundary into
|
|
5816
|
+
* SchedulerDO. Deliberately excludes the handler function; over DO RPC a
|
|
5817
|
+
* function arrives as a callback stub, and invoking a stubbed handler
|
|
5818
|
+
* serializes JobContext back to the calling isolate, where env bindings (R2
|
|
5819
|
+
* buckets, DO namespaces) cannot be serialized (AGENTCLI-6). Handlers are
|
|
5820
|
+
* bound inside the DO from its own isolate's module registry.
|
|
5821
|
+
*/
|
|
5822
|
+
export type ScheduledJobDefinition = Omit<ScheduledJob, 'handler'> & Partial<Pick<ScheduledJob, 'handler'>>;
|
|
5641
5823
|
export interface JobLogger {
|
|
5642
5824
|
info(message: string, data?: Record<string, unknown>): void;
|
|
5643
5825
|
warn(message: string, data?: Record<string, unknown>): void;
|
|
@@ -5652,6 +5834,13 @@ export interface JobRunRecord {
|
|
|
5652
5834
|
completedAt?: number;
|
|
5653
5835
|
result?: JobResult;
|
|
5654
5836
|
error?: string;
|
|
5837
|
+
/**
|
|
5838
|
+
* Stack trace of the failure. Stored because the error MESSAGE alone made
|
|
5839
|
+
* the 2026-07 "Could not serialize object of type ..." production failures
|
|
5840
|
+
* undiagnosable: the message names the workerd serializer, not the framework
|
|
5841
|
+
* call site that hit it (AGENTCLI-6).
|
|
5842
|
+
*/
|
|
5843
|
+
errorStack?: string;
|
|
5655
5844
|
}
|
|
5656
5845
|
export interface ScheduleState {
|
|
5657
5846
|
name: string;
|
|
@@ -5721,7 +5910,7 @@ export declare class SchedulerDO extends DurableObject<Env> {
|
|
|
5721
5910
|
* Initialize the job scheduler with job definitions.
|
|
5722
5911
|
* Can be called again after HMR to sync new/changed job definitions.
|
|
5723
5912
|
*/
|
|
5724
|
-
initializeJobScheduler(jobs:
|
|
5913
|
+
initializeJobScheduler(jobs: ScheduledJobDefinition[]): Promise<void>;
|
|
5725
5914
|
/**
|
|
5726
5915
|
* Handle alarm - execute due jobs
|
|
5727
5916
|
*/
|
|
@@ -7381,7 +7570,7 @@ function createKeyboardListener() {
|
|
|
7381
7570
|
}
|
|
7382
7571
|
|
|
7383
7572
|
// src/generated/version.ts
|
|
7384
|
-
var VERSION = "0.
|
|
7573
|
+
var VERSION = "0.18.0";
|
|
7385
7574
|
|
|
7386
7575
|
// src/commands/dev.ts
|
|
7387
7576
|
var exports_dev = {};
|
|
@@ -7515,7 +7704,64 @@ async function execDev(options) {
|
|
|
7515
7704
|
} else {
|
|
7516
7705
|
console.log(dim("Starting dev session..."));
|
|
7517
7706
|
}
|
|
7518
|
-
|
|
7707
|
+
let session;
|
|
7708
|
+
try {
|
|
7709
|
+
const boot = await awaitDevSessionReady({
|
|
7710
|
+
start: () => client.startDevSession(config.appId),
|
|
7711
|
+
pollStatus: () => client.getDevStatus(config.appId),
|
|
7712
|
+
onWaiting: (elapsedMs) => {
|
|
7713
|
+
const seconds = Math.round(elapsedMs / 1000);
|
|
7714
|
+
if (useJson) {
|
|
7715
|
+
jsonLine({
|
|
7716
|
+
event: "startup",
|
|
7717
|
+
phase: "dev_session_waiting",
|
|
7718
|
+
elapsedSeconds: seconds,
|
|
7719
|
+
timestamp: ts(),
|
|
7720
|
+
note: "Sandbox is booting. Cold boots can take a few minutes; keep waiting."
|
|
7721
|
+
});
|
|
7722
|
+
} else {
|
|
7723
|
+
console.log(dim(` Still starting the sandbox... (${seconds}s elapsed; cold boots can take a few minutes)`));
|
|
7724
|
+
}
|
|
7725
|
+
},
|
|
7726
|
+
onStartError: (err) => {
|
|
7727
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7728
|
+
if (useJson) {
|
|
7729
|
+
jsonLine({
|
|
7730
|
+
event: "startup",
|
|
7731
|
+
phase: "dev_session_retrying",
|
|
7732
|
+
timestamp: ts(),
|
|
7733
|
+
warning: `Boot call failed (${message}). The sandbox may still be booting server-side; watching status until it becomes ready.`
|
|
7734
|
+
});
|
|
7735
|
+
} else {
|
|
7736
|
+
console.warn(yellow(` Boot call failed (${message}); watching sandbox status in case the boot completes server-side...`));
|
|
7737
|
+
}
|
|
7738
|
+
}
|
|
7739
|
+
});
|
|
7740
|
+
session = boot.session;
|
|
7741
|
+
} catch (error) {
|
|
7742
|
+
const isTimeout = error instanceof DevBootTimeoutError;
|
|
7743
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7744
|
+
if (useJson) {
|
|
7745
|
+
jsonLine({
|
|
7746
|
+
event: "error",
|
|
7747
|
+
phase: "dev_session",
|
|
7748
|
+
timestamp: ts(),
|
|
7749
|
+
error: {
|
|
7750
|
+
message,
|
|
7751
|
+
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.",
|
|
7752
|
+
suggestions: [
|
|
7753
|
+
"Re-run `runwork dev --detach --json` in about a minute; if the sandbox finished booting server-side it will be picked up quickly",
|
|
7754
|
+
"Run `runwork logs` to inspect sandbox logs for boot errors",
|
|
7755
|
+
"Run `runwork doctor` to verify auth and connectivity"
|
|
7756
|
+
]
|
|
7757
|
+
}
|
|
7758
|
+
});
|
|
7759
|
+
} else {
|
|
7760
|
+
console.error(red(`Failed to start dev session: ${message}`));
|
|
7761
|
+
console.error(dim(" Re-run `runwork dev` in about a minute; if the sandbox finished booting server-side it will be picked up quickly."));
|
|
7762
|
+
}
|
|
7763
|
+
process.exit(1);
|
|
7764
|
+
}
|
|
7519
7765
|
await populateSkill(cwd, client, config.appId);
|
|
7520
7766
|
if (useJson)
|
|
7521
7767
|
jsonLine({ event: "startup", phase: "skill_fetched", timestamp: ts() });
|
|
@@ -7804,7 +8050,21 @@ async function runDevDetachParent(opts) {
|
|
|
7804
8050
|
const outcome = await runAsDetachedParent({
|
|
7805
8051
|
appDir: cwd,
|
|
7806
8052
|
expectedAppId: config.appId,
|
|
7807
|
-
childArgs
|
|
8053
|
+
childArgs,
|
|
8054
|
+
onWaiting: (elapsedMs) => {
|
|
8055
|
+
const seconds = Math.round(elapsedMs / 1000);
|
|
8056
|
+
if (opts.json) {
|
|
8057
|
+
jsonLine({
|
|
8058
|
+
event: "waiting",
|
|
8059
|
+
phase: "sandbox_boot",
|
|
8060
|
+
elapsedSeconds: seconds,
|
|
8061
|
+
timestamp: ts(),
|
|
8062
|
+
note: "Sandbox is booting. Cold boots can take a few minutes; keep waiting for session_started."
|
|
8063
|
+
});
|
|
8064
|
+
} else {
|
|
8065
|
+
console.log(dim(` Still starting... (${seconds}s elapsed; cold boots can take a few minutes)`));
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
7808
8068
|
});
|
|
7809
8069
|
switch (outcome.result) {
|
|
7810
8070
|
case "started": {
|
|
@@ -7862,9 +8122,10 @@ async function runDevDetachParent(opts) {
|
|
|
7862
8122
|
timestamp: ts(),
|
|
7863
8123
|
error: {
|
|
7864
8124
|
message,
|
|
7865
|
-
diagnosis: "The detached child process exited (typically due to missing auth, missing git, network failure, or a
|
|
8125
|
+
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
8126
|
suggestions: [
|
|
7867
|
-
"
|
|
8127
|
+
"Check childLogTail below; the child logs structured errors to .runwork/dev-stdout.log (stderr carries crash traces)",
|
|
8128
|
+
"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
8129
|
"Run `runwork doctor` to verify auth and connectivity",
|
|
7869
8130
|
"Try `runwork dev` (foreground) to see the failure inline"
|
|
7870
8131
|
],
|
|
@@ -7873,7 +8134,7 @@ async function runDevDetachParent(opts) {
|
|
|
7873
8134
|
});
|
|
7874
8135
|
} else {
|
|
7875
8136
|
console.error(yellow(message));
|
|
7876
|
-
console.error(dim(" Inspect .runwork/dev-stderr.log or run `runwork dev` to see what failed."));
|
|
8137
|
+
console.error(dim(" Inspect .runwork/dev-stdout.log and dev-stderr.log, or run `runwork dev` to see what failed."));
|
|
7877
8138
|
if (outcome.childLogTail) {
|
|
7878
8139
|
console.error(dim(" Tail of child stderr:"));
|
|
7879
8140
|
for (const line of outcome.childLogTail.split(`
|
|
@@ -7887,7 +8148,8 @@ async function runDevDetachParent(opts) {
|
|
|
7887
8148
|
return;
|
|
7888
8149
|
}
|
|
7889
8150
|
case "timeout": {
|
|
7890
|
-
const
|
|
8151
|
+
const timeoutSeconds = Math.round(DETACH_READY_TIMEOUT_MS / 1000);
|
|
8152
|
+
const message = `Detached dev session did not become ready within ${timeoutSeconds}s.`;
|
|
7891
8153
|
if (opts.json) {
|
|
7892
8154
|
jsonLine({
|
|
7893
8155
|
event: "error",
|
|
@@ -7895,9 +8157,10 @@ async function runDevDetachParent(opts) {
|
|
|
7895
8157
|
timestamp: ts(),
|
|
7896
8158
|
error: {
|
|
7897
8159
|
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.",
|
|
8160
|
+
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
8161
|
suggestions: [
|
|
7900
|
-
"
|
|
8162
|
+
"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",
|
|
8163
|
+
"Inspect the child logs at .runwork/dev-stdout.log and .runwork/dev-stderr.log",
|
|
7901
8164
|
"Run `runwork doctor` to verify auth and connectivity",
|
|
7902
8165
|
"Try `runwork dev` (foreground) to see the failure inline"
|
|
7903
8166
|
],
|
|
@@ -7906,7 +8169,7 @@ async function runDevDetachParent(opts) {
|
|
|
7906
8169
|
});
|
|
7907
8170
|
} else {
|
|
7908
8171
|
console.error(yellow(message));
|
|
7909
|
-
console.error(dim(" Inspect .runwork/dev-stderr.log or run `runwork dev` to see what failed."));
|
|
8172
|
+
console.error(dim(" Inspect .runwork/dev-stdout.log and dev-stderr.log, or run `runwork dev` to see what failed."));
|
|
7910
8173
|
if (outcome.childLogTail) {
|
|
7911
8174
|
console.error(dim(" Tail of child stderr:"));
|
|
7912
8175
|
for (const line of outcome.childLogTail.split(`
|
|
@@ -7956,6 +8219,7 @@ var init_dev = __esm(() => {
|
|
|
7956
8219
|
init_tailer();
|
|
7957
8220
|
init_session();
|
|
7958
8221
|
init_detach();
|
|
8222
|
+
init_boot_await();
|
|
7959
8223
|
init_stop();
|
|
7960
8224
|
init_attach();
|
|
7961
8225
|
init_types_manager();
|
|
@@ -8291,7 +8555,7 @@ var init_welcome = __esm(() => {
|
|
|
8291
8555
|
});
|
|
8292
8556
|
|
|
8293
8557
|
// src/index.ts
|
|
8294
|
-
import { Command as
|
|
8558
|
+
import { Command as Command36 } from "commander";
|
|
8295
8559
|
|
|
8296
8560
|
// src/commands/login.ts
|
|
8297
8561
|
init_login_flow();
|
|
@@ -8394,8 +8658,23 @@ function getDeploySummary(cwd) {
|
|
|
8394
8658
|
}
|
|
8395
8659
|
|
|
8396
8660
|
// src/deploy/deploy-status.ts
|
|
8661
|
+
init_session();
|
|
8397
8662
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "fs";
|
|
8398
8663
|
import { dirname as dirname4, join as join17 } from "path";
|
|
8664
|
+
function evaluateDeployStatus(status, deps = {}) {
|
|
8665
|
+
if (status.state !== "in-progress") {
|
|
8666
|
+
return { status, effectiveState: status.state };
|
|
8667
|
+
}
|
|
8668
|
+
const bootTime = deps.bootTime ?? currentBootTime;
|
|
8669
|
+
const pidAlive = deps.pidAlive ?? isPidAlive;
|
|
8670
|
+
if (typeof status.bootTime === "number" && Math.abs(bootTime() - status.bootTime) > BOOT_TIME_TOLERANCE_MS) {
|
|
8671
|
+
return { status, effectiveState: "stale", staleReason: "boot-time-mismatch" };
|
|
8672
|
+
}
|
|
8673
|
+
if (typeof status.pid === "number" && !pidAlive(status.pid)) {
|
|
8674
|
+
return { status, effectiveState: "stale", staleReason: "process-gone" };
|
|
8675
|
+
}
|
|
8676
|
+
return { status, effectiveState: "in-progress" };
|
|
8677
|
+
}
|
|
8399
8678
|
function statusPath(cwd) {
|
|
8400
8679
|
return join17(cwd, ".runwork", "deploy-status.json");
|
|
8401
8680
|
}
|
|
@@ -8425,6 +8704,9 @@ function readDeployStatus(cwd) {
|
|
|
8425
8704
|
}
|
|
8426
8705
|
}
|
|
8427
8706
|
|
|
8707
|
+
// src/commands/deploy.ts
|
|
8708
|
+
init_session();
|
|
8709
|
+
|
|
8428
8710
|
// src/deploy/detach.ts
|
|
8429
8711
|
init_detach();
|
|
8430
8712
|
import * as fs5 from "fs";
|
|
@@ -8536,7 +8818,7 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8536
8818
|
if (opts.detach && !isChild) {
|
|
8537
8819
|
const startedAt = new Date().toISOString();
|
|
8538
8820
|
const handle = spawnDetachedDeploy(buildDeployChildArgs(process.argv), cwd);
|
|
8539
|
-
writeDeployStatus(cwd, { state: "in-progress", startedAt, pid: handle.pid });
|
|
8821
|
+
writeDeployStatus(cwd, { state: "in-progress", startedAt, pid: handle.pid, bootTime: currentBootTime() });
|
|
8540
8822
|
if (useJson) {
|
|
8541
8823
|
jsonLine({ event: "deploy_started", detached: true, pid: handle.pid, logPath: deployLogPath(cwd), startedAt, hint: "Poll `runwork deploy --status` for completion." });
|
|
8542
8824
|
} else {
|
|
@@ -8639,7 +8921,7 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8639
8921
|
const deployed = deploymentUrl.length > 0;
|
|
8640
8922
|
if (!deployed) {
|
|
8641
8923
|
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", "
|
|
8924
|
+
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
8925
|
process.exit(1);
|
|
8644
8926
|
}
|
|
8645
8927
|
console.error("Deploy did not return a URL. The deploy may have been a no-op or failed server-side.");
|
|
@@ -8648,6 +8930,21 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8648
8930
|
}
|
|
8649
8931
|
const deployedSha = getHeadSha(cwd);
|
|
8650
8932
|
const finishedAt = new Date().toISOString();
|
|
8933
|
+
const shaVerdict = assessBuiltSha(deployedSha, result.builtFromSha);
|
|
8934
|
+
if (shaVerdict === "mismatch") {
|
|
8935
|
+
const detail = `The platform built this deploy from ${result.builtFromSha.slice(0, 7)} but your pushed HEAD is ${deployedSha.slice(0, 7)}. Production is NOT running your latest code.`;
|
|
8936
|
+
writeDeployStatus(cwd, { state: "failed", startedAt: childStartedAt, finishedAt, error: detail });
|
|
8937
|
+
deployFinalized = true;
|
|
8938
|
+
if (useJson) {
|
|
8939
|
+
jsonOut(buildErrorResponse("deploy", "Deployed artifact does not match pushed commit", detail, [
|
|
8940
|
+
"Run `runwork dev --restart --detach` to resync the build sandbox, then retry `runwork deploy`",
|
|
8941
|
+
"Verify with `runwork logs --production --once` which version is actually serving"
|
|
8942
|
+
]));
|
|
8943
|
+
process.exit(1);
|
|
8944
|
+
}
|
|
8945
|
+
console.error(`Deploy verification failed: ${detail}`);
|
|
8946
|
+
process.exit(1);
|
|
8947
|
+
}
|
|
8651
8948
|
if (deployedSha) {
|
|
8652
8949
|
writeDeployState(cwd, { sha: deployedSha, deployedAt: finishedAt, url: deploymentUrl });
|
|
8653
8950
|
}
|
|
@@ -8664,7 +8961,9 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8664
8961
|
deployed,
|
|
8665
8962
|
url: deploymentUrl,
|
|
8666
8963
|
appName: config.appName,
|
|
8667
|
-
deployedSha
|
|
8964
|
+
deployedSha,
|
|
8965
|
+
builtFromSha: result.builtFromSha,
|
|
8966
|
+
shaVerified: shaVerdict === "match"
|
|
8668
8967
|
},
|
|
8669
8968
|
guide: buildDeployGuide(),
|
|
8670
8969
|
...deployGuardWarning ? { warning: deployGuardWarning } : {}
|
|
@@ -8674,19 +8973,34 @@ var deployCommand = new Command5("deploy").description("Deploy the current app t
|
|
|
8674
8973
|
}
|
|
8675
8974
|
console.log(`Deployed: ${deploymentUrl}`);
|
|
8676
8975
|
if (deployedSha)
|
|
8677
|
-
console.log(dim(`Commit: ${deployedSha.slice(0, 7)}`));
|
|
8976
|
+
console.log(dim(`Commit: ${deployedSha.slice(0, 7)}${shaVerdict === "match" ? " (verified)" : shaVerdict === "unverified" ? " (provenance unverified by server)" : ""}`));
|
|
8678
8977
|
});
|
|
8978
|
+
function assessBuiltSha(localSha, builtFromSha) {
|
|
8979
|
+
if (!localSha || !builtFromSha)
|
|
8980
|
+
return "unverified";
|
|
8981
|
+
return builtFromSha === localSha ? "match" : "mismatch";
|
|
8982
|
+
}
|
|
8679
8983
|
function printDeployStatus(cwd, useJson) {
|
|
8680
8984
|
const status = readDeployStatus(cwd);
|
|
8681
8985
|
const summary = getDeploySummary(cwd);
|
|
8986
|
+
const evaluated = status ? evaluateDeployStatus(status) : null;
|
|
8987
|
+
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
8988
|
if (useJson) {
|
|
8683
|
-
jsonOut({
|
|
8989
|
+
jsonOut({
|
|
8990
|
+
success: true,
|
|
8991
|
+
command: "deploy",
|
|
8992
|
+
result: {
|
|
8993
|
+
status: evaluated ? { ...evaluated.status, state: evaluated.effectiveState, staleReason: evaluated.staleReason } : null,
|
|
8994
|
+
deploy: summary,
|
|
8995
|
+
hint: evaluated?.effectiveState === "stale" ? staleHint : undefined
|
|
8996
|
+
}
|
|
8997
|
+
});
|
|
8684
8998
|
return;
|
|
8685
8999
|
}
|
|
8686
|
-
if (!status) {
|
|
9000
|
+
if (!status || !evaluated) {
|
|
8687
9001
|
console.log("No deploy has been started from this machine yet.");
|
|
8688
9002
|
} else {
|
|
8689
|
-
const label =
|
|
9003
|
+
const label = evaluated.effectiveState === "succeeded" ? green("succeeded") : evaluated.effectiveState === "failed" ? yellow("failed") : evaluated.effectiveState === "stale" ? yellow(`stale (${evaluated.staleReason})`) : "in-progress";
|
|
8690
9004
|
console.log(`Last deploy: ${label}`);
|
|
8691
9005
|
console.log(dim(` started: ${status.startedAt}`));
|
|
8692
9006
|
if (status.finishedAt)
|
|
@@ -8695,8 +9009,10 @@ function printDeployStatus(cwd, useJson) {
|
|
|
8695
9009
|
console.log(dim(` url: ${status.url}`));
|
|
8696
9010
|
if (status.error)
|
|
8697
9011
|
console.log(yellow(` error: ${status.error}`));
|
|
8698
|
-
if (
|
|
9012
|
+
if (evaluated.effectiveState === "in-progress")
|
|
8699
9013
|
console.log(dim(` logs: ${deployLogPath(cwd)}`));
|
|
9014
|
+
if (evaluated.effectiveState === "stale")
|
|
9015
|
+
console.log(yellow(` ${staleHint}`));
|
|
8700
9016
|
}
|
|
8701
9017
|
if (summary.deployedShortSha) {
|
|
8702
9018
|
const sync = summary.inSync === true ? green("in sync") : summary.inSync === false ? yellow("local has undeployed commits") : dim("unknown");
|
|
@@ -9280,9 +9596,14 @@ import { readFileSync as readFileSync18, existsSync as existsSync21 } from "fs";
|
|
|
9280
9596
|
init_store();
|
|
9281
9597
|
init_prompt();
|
|
9282
9598
|
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
9599
|
+
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
9600
|
async function resolveWorkspace2(client, options = {}) {
|
|
9284
9601
|
if (options.workspace) {
|
|
9285
|
-
|
|
9602
|
+
if (UUID_RE.test(options.workspace)) {
|
|
9603
|
+
return { workspaceId: options.workspace, workspaceName: "", source: "flag" };
|
|
9604
|
+
}
|
|
9605
|
+
const ws2 = await resolveWorkspace(client, options.workspace);
|
|
9606
|
+
return { workspaceId: ws2.id, workspaceName: ws2.name, source: "flag" };
|
|
9286
9607
|
}
|
|
9287
9608
|
if (process.env.WORKSPACE_ID) {
|
|
9288
9609
|
return { workspaceId: process.env.WORKSPACE_ID, workspaceName: "", source: "flag" };
|
|
@@ -9317,6 +9638,10 @@ async function resolveWorkspace2(client, options = {}) {
|
|
|
9317
9638
|
saveDefaultWorkspace(ws2.id, ws2.name);
|
|
9318
9639
|
return { workspaceId: ws2.id, workspaceName: ws2.name, source: "prompt" };
|
|
9319
9640
|
}
|
|
9641
|
+
if (!process.stdout.isTTY) {
|
|
9642
|
+
console.error(`Workspace required. Pass --workspace <name-or-id>. Available: ${workspaces.map((w) => w.name).join(", ")}`);
|
|
9643
|
+
process.exit(1);
|
|
9644
|
+
}
|
|
9320
9645
|
const choices = workspaces.map((ws2) => ({ label: ws2.name, value: ws2.id }));
|
|
9321
9646
|
const chosen = await promptSelect("Select a workspace:", choices);
|
|
9322
9647
|
const ws = workspaces.find((w) => w.id === chosen.value);
|
|
@@ -9545,7 +9870,13 @@ async function parseCurlToRequest(curlStr) {
|
|
|
9545
9870
|
}
|
|
9546
9871
|
|
|
9547
9872
|
// src/commands/integrations.ts
|
|
9548
|
-
|
|
9873
|
+
function resolveIntegrationConnection(integrations, integration) {
|
|
9874
|
+
const match = integrations.find((i) => (i.canonicalId || i.integrationId) === integration || i.provider === integration);
|
|
9875
|
+
if (!match)
|
|
9876
|
+
return null;
|
|
9877
|
+
return { connectionId: match.id || match.integrationId, match };
|
|
9878
|
+
}
|
|
9879
|
+
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
9880
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9550
9881
|
const credentials = requireAuth();
|
|
9551
9882
|
const client = new ApiClient(credentials);
|
|
@@ -9604,7 +9935,7 @@ Usage: Add the integration ID to APP_INTEGRATION_REQUIREMENTS in worker/integrat
|
|
|
9604
9935
|
process.exit(1);
|
|
9605
9936
|
}
|
|
9606
9937
|
});
|
|
9607
|
-
var listCommand = new Command10("list").description("List connected workspace integrations").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
9938
|
+
var listCommand = new Command10("list").description("List connected workspace integrations").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
9608
9939
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9609
9940
|
const credentials = requireAuth();
|
|
9610
9941
|
const client = new ApiClient(credentials);
|
|
@@ -9650,14 +9981,14 @@ Used by your team (${teamOnly.length} more):
|
|
|
9650
9981
|
console.log(` ${t.canonicalId} - Used by ${display}`);
|
|
9651
9982
|
}
|
|
9652
9983
|
console.log(`
|
|
9653
|
-
Connect at https://runwork.ai/
|
|
9984
|
+
Connect at https://runwork.ai/integrations`);
|
|
9654
9985
|
}
|
|
9655
9986
|
} catch (err) {
|
|
9656
9987
|
console.error("Failed to list integrations:", err instanceof Error ? err.message : err);
|
|
9657
9988
|
process.exit(1);
|
|
9658
9989
|
}
|
|
9659
9990
|
});
|
|
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) => {
|
|
9991
|
+
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
9992
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
9662
9993
|
const credentials = requireAuth();
|
|
9663
9994
|
const client = new ApiClient(credentials);
|
|
@@ -9711,12 +10042,12 @@ var callCommand = new Command10("call").description("Make a proxy call to a conn
|
|
|
9711
10042
|
}
|
|
9712
10043
|
try {
|
|
9713
10044
|
const integrations = await client.listConnectedIntegrations(workspaceId);
|
|
9714
|
-
const
|
|
9715
|
-
if (!
|
|
10045
|
+
const resolved = resolveIntegrationConnection(integrations, integration);
|
|
10046
|
+
if (!resolved) {
|
|
9716
10047
|
console.error(`Integration "${integration}" not found. Run "runwork integrations list" to see connected integrations.`);
|
|
9717
10048
|
process.exit(1);
|
|
9718
10049
|
}
|
|
9719
|
-
const result = await client.callIntegrationProxy(
|
|
10050
|
+
const result = await client.callIntegrationProxy(resolved.connectionId, finalMethod, finalPath, { body, headers: Object.keys(headers).length > 0 ? headers : undefined, query });
|
|
9720
10051
|
if (useJson) {
|
|
9721
10052
|
jsonOut(result);
|
|
9722
10053
|
return;
|
|
@@ -10345,7 +10676,7 @@ function truncate(text2, max) {
|
|
|
10345
10676
|
return first;
|
|
10346
10677
|
return first.slice(0, max - 3) + "...";
|
|
10347
10678
|
}
|
|
10348
|
-
var listCommand2 = new Command13("list").description("List workspace skills (app, external, MCP-generated)").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
10679
|
+
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
10680
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10350
10681
|
const credentials = requireAuth();
|
|
10351
10682
|
const client = new ApiClient(credentials);
|
|
@@ -10402,7 +10733,7 @@ async function readStdin2() {
|
|
|
10402
10733
|
}
|
|
10403
10734
|
return Buffer.concat(chunks).toString("utf-8");
|
|
10404
10735
|
}
|
|
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) => {
|
|
10736
|
+
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
10737
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10407
10738
|
const credentials = requireAuth();
|
|
10408
10739
|
const client = new ApiClient(credentials);
|
|
@@ -10497,7 +10828,7 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10497
10828
|
process.exit(1);
|
|
10498
10829
|
}
|
|
10499
10830
|
});
|
|
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) => {
|
|
10831
|
+
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
10832
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10502
10833
|
const credentials = requireAuth();
|
|
10503
10834
|
const client = new ApiClient(credentials);
|
|
@@ -10600,7 +10931,7 @@ var searchCommand2 = new Command13("search").description("Search community skill
|
|
|
10600
10931
|
process.exit(1);
|
|
10601
10932
|
}
|
|
10602
10933
|
});
|
|
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) => {
|
|
10934
|
+
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
10935
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10605
10936
|
const credentials = requireAuth();
|
|
10606
10937
|
const client = new ApiClient(credentials);
|
|
@@ -11115,6 +11446,22 @@ function formatCombinedDigest(sessions, opts = { days: 7 }) {
|
|
|
11115
11446
|
`);
|
|
11116
11447
|
}
|
|
11117
11448
|
|
|
11449
|
+
// src/agents/utils/skill-name.ts
|
|
11450
|
+
function canonicalSkillName(raw) {
|
|
11451
|
+
const afterPrefix = raw.includes(":") ? raw.slice(raw.lastIndexOf(":") + 1) : raw;
|
|
11452
|
+
return afterPrefix.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11453
|
+
}
|
|
11454
|
+
function skillNameFromPath(path2) {
|
|
11455
|
+
const trimmed = path2.trim().replace(/\/SKILL\.md$/i, "");
|
|
11456
|
+
if (!trimmed.includes("/") || /[<>]/.test(trimmed))
|
|
11457
|
+
return null;
|
|
11458
|
+
const segment = trimmed.replace(/\/+$/, "").split("/").pop();
|
|
11459
|
+
if (!segment)
|
|
11460
|
+
return null;
|
|
11461
|
+
const canonical = canonicalSkillName(segment);
|
|
11462
|
+
return canonical || null;
|
|
11463
|
+
}
|
|
11464
|
+
|
|
11118
11465
|
// src/agents/utils/json-config.ts
|
|
11119
11466
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, existsSync as existsSync25 } from "fs";
|
|
11120
11467
|
import { dirname as dirname5 } from "path";
|
|
@@ -11838,6 +12185,7 @@ ${instructions}`;
|
|
|
11838
12185
|
let outputTokens = 0;
|
|
11839
12186
|
let cacheReadTokens = 0;
|
|
11840
12187
|
let latestActivityMs = 0;
|
|
12188
|
+
const activeDays = new Set;
|
|
11841
12189
|
const seenEntries = new Set;
|
|
11842
12190
|
let cwdEntries;
|
|
11843
12191
|
try {
|
|
@@ -11891,6 +12239,7 @@ ${instructions}`;
|
|
|
11891
12239
|
sessionHadActivity = true;
|
|
11892
12240
|
if (ts > latestActivityMs)
|
|
11893
12241
|
latestActivityMs = ts;
|
|
12242
|
+
activeDays.add(new Date(ts).toISOString().slice(0, 10));
|
|
11894
12243
|
const type = entry.type;
|
|
11895
12244
|
if (type === "user" || type === "assistant") {
|
|
11896
12245
|
const msg = entry.message;
|
|
@@ -11943,6 +12292,7 @@ ${instructions}`;
|
|
|
11943
12292
|
aiLinesAdded: 0,
|
|
11944
12293
|
aiLinesRemoved: 0,
|
|
11945
12294
|
lastActiveAt: latestActivityMs > 0 ? new Date(latestActivityMs).toISOString() : null,
|
|
12295
|
+
activeDays: [...activeDays].sort(),
|
|
11946
12296
|
extra: { inputTokens, outputTokens, cacheReadTokens }
|
|
11947
12297
|
};
|
|
11948
12298
|
} catch {
|
|
@@ -12047,7 +12397,7 @@ ${instructions}`;
|
|
|
12047
12397
|
} catch {
|
|
12048
12398
|
continue;
|
|
12049
12399
|
}
|
|
12050
|
-
|
|
12400
|
+
const pendingToolUses = [];
|
|
12051
12401
|
for (const line of content.split(`
|
|
12052
12402
|
`)) {
|
|
12053
12403
|
if (!line)
|
|
@@ -12065,39 +12415,48 @@ ${instructions}`;
|
|
|
12065
12415
|
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
12066
12416
|
continue;
|
|
12067
12417
|
const type = entry.type;
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12418
|
+
const msg = entry.message;
|
|
12419
|
+
if (type === "assistant" && msg && typeof msg === "object") {
|
|
12420
|
+
const blocks = Array.isArray(msg.content) ? msg.content : [];
|
|
12421
|
+
for (const block of blocks) {
|
|
12422
|
+
if (block && block.type === "tool_use" && block.name === "Skill") {
|
|
12423
|
+
const input = block.input;
|
|
12424
|
+
if (input && typeof input.skill === "string") {
|
|
12425
|
+
const name = canonicalSkillName(input.skill);
|
|
12426
|
+
if (name)
|
|
12427
|
+
pendingToolUses.push({ name, ts });
|
|
12078
12428
|
}
|
|
12079
12429
|
}
|
|
12080
12430
|
}
|
|
12081
12431
|
}
|
|
12082
|
-
if (type === "user") {
|
|
12083
|
-
const
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
if (
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
pendingCommand = null;
|
|
12432
|
+
if (type === "user" && msg && typeof msg === "object") {
|
|
12433
|
+
const texts = [];
|
|
12434
|
+
if (typeof msg.content === "string") {
|
|
12435
|
+
texts.push(msg.content);
|
|
12436
|
+
} else if (Array.isArray(msg.content)) {
|
|
12437
|
+
for (const block of msg.content) {
|
|
12438
|
+
if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
|
|
12439
|
+
texts.push(block.text);
|
|
12440
|
+
}
|
|
12441
|
+
}
|
|
12093
12442
|
}
|
|
12094
|
-
const
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12443
|
+
for (const text2 of texts) {
|
|
12444
|
+
const m = text2.match(/^Base directory for this skill: (.+)$/m);
|
|
12445
|
+
if (!m)
|
|
12446
|
+
continue;
|
|
12447
|
+
const name = skillNameFromPath(m[1]);
|
|
12448
|
+
if (!name)
|
|
12449
|
+
continue;
|
|
12450
|
+
const pendingIdx = pendingToolUses.findIndex((p) => p.name === name);
|
|
12451
|
+
if (pendingIdx >= 0)
|
|
12452
|
+
pendingToolUses.splice(pendingIdx, 1);
|
|
12453
|
+
recordSkill(name, ts);
|
|
12098
12454
|
}
|
|
12099
12455
|
}
|
|
12100
12456
|
}
|
|
12457
|
+
for (const pending of pendingToolUses) {
|
|
12458
|
+
recordSkill(pending.name, pending.ts);
|
|
12459
|
+
}
|
|
12101
12460
|
}
|
|
12102
12461
|
}
|
|
12103
12462
|
if (skillCounts.size === 0)
|
|
@@ -12671,11 +13030,14 @@ class ClaudeDesktopAdapter {
|
|
|
12671
13030
|
const modelsUsed = new Set;
|
|
12672
13031
|
let maxMcpTools = 0;
|
|
12673
13032
|
const agentSessionsDir = join23(claudeAppDir, "local-agent-mode-sessions");
|
|
13033
|
+
const activeDays = new Set;
|
|
12674
13034
|
if (existsSync29(agentSessionsDir)) {
|
|
12675
13035
|
this.walkSessionDirs(agentSessionsDir, sinceMs, (session) => {
|
|
12676
13036
|
const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
|
|
12677
|
-
if (sessionTime > sinceMs)
|
|
13037
|
+
if (sessionTime > sinceMs) {
|
|
12678
13038
|
sessionCount++;
|
|
13039
|
+
activeDays.add(new Date(sessionTime).toISOString().slice(0, 10));
|
|
13040
|
+
}
|
|
12679
13041
|
if (sessionTime > latestActivity)
|
|
12680
13042
|
latestActivity = sessionTime;
|
|
12681
13043
|
if (session.model)
|
|
@@ -12691,8 +13053,10 @@ class ClaudeDesktopAdapter {
|
|
|
12691
13053
|
if (existsSync29(codeSessionsDir)) {
|
|
12692
13054
|
this.walkSessionDirs(codeSessionsDir, sinceMs, (session) => {
|
|
12693
13055
|
const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
|
|
12694
|
-
if (sessionTime > sinceMs)
|
|
13056
|
+
if (sessionTime > sinceMs) {
|
|
12695
13057
|
sessionCount++;
|
|
13058
|
+
activeDays.add(new Date(sessionTime).toISOString().slice(0, 10));
|
|
13059
|
+
}
|
|
12696
13060
|
if (sessionTime > latestActivity)
|
|
12697
13061
|
latestActivity = sessionTime;
|
|
12698
13062
|
if (session.model)
|
|
@@ -12725,6 +13089,7 @@ class ClaudeDesktopAdapter {
|
|
|
12725
13089
|
aiLinesAdded: 0,
|
|
12726
13090
|
aiLinesRemoved: 0,
|
|
12727
13091
|
lastActiveAt: latestActivity > 0 ? new Date(latestActivity).toISOString() : null,
|
|
13092
|
+
activeDays: activeDays.size > 0 ? [...activeDays].sort() : undefined,
|
|
12728
13093
|
modelsUsed: Array.from(modelsUsed),
|
|
12729
13094
|
mcpToolCount: maxMcpTools > 0 ? maxMcpTools : undefined,
|
|
12730
13095
|
extra: scheduledTaskRuns > 0 ? { scheduledTaskRuns } : undefined
|
|
@@ -12733,46 +13098,6 @@ class ClaudeDesktopAdapter {
|
|
|
12733
13098
|
return null;
|
|
12734
13099
|
}
|
|
12735
13100
|
}
|
|
12736
|
-
async readSessionDigests(sinceISO) {
|
|
12737
|
-
try {
|
|
12738
|
-
const os2 = platform3();
|
|
12739
|
-
const claudeAppDir = os2 === "darwin" ? join23(homedir6(), "Library", "Application Support", "Claude") : os2 === "win32" ? join23(process.env.APPDATA || join23(homedir6(), "AppData", "Roaming"), "Claude") : join23(homedir6(), ".config", "Claude");
|
|
12740
|
-
const sessionsDir = join23(claudeAppDir, "claude-code-sessions");
|
|
12741
|
-
if (!existsSync29(sessionsDir))
|
|
12742
|
-
return null;
|
|
12743
|
-
const sinceMs = sinceISO ? new Date(sinceISO).getTime() : 0;
|
|
12744
|
-
let files;
|
|
12745
|
-
try {
|
|
12746
|
-
files = readdirSync6(sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
12747
|
-
} catch {
|
|
12748
|
-
return null;
|
|
12749
|
-
}
|
|
12750
|
-
const digests = [];
|
|
12751
|
-
for (const file of files) {
|
|
12752
|
-
const filePath = join23(sessionsDir, file);
|
|
12753
|
-
let stat;
|
|
12754
|
-
try {
|
|
12755
|
-
stat = statSync4(filePath);
|
|
12756
|
-
} catch {
|
|
12757
|
-
continue;
|
|
12758
|
-
}
|
|
12759
|
-
if (stat.mtimeMs <= sinceMs)
|
|
12760
|
-
continue;
|
|
12761
|
-
let content;
|
|
12762
|
-
try {
|
|
12763
|
-
content = readFileSync25(filePath, "utf-8");
|
|
12764
|
-
} catch {
|
|
12765
|
-
continue;
|
|
12766
|
-
}
|
|
12767
|
-
const digest = extractClaudeJsonlSession(content, this.slug, file.replace(/\.jsonl$/, ""));
|
|
12768
|
-
if (digest)
|
|
12769
|
-
digests.push(digest);
|
|
12770
|
-
}
|
|
12771
|
-
return digests;
|
|
12772
|
-
} catch {
|
|
12773
|
-
return null;
|
|
12774
|
-
}
|
|
12775
|
-
}
|
|
12776
13101
|
async readVersion() {
|
|
12777
13102
|
try {
|
|
12778
13103
|
const os2 = platform3();
|
|
@@ -12793,53 +13118,6 @@ class ClaudeDesktopAdapter {
|
|
|
12793
13118
|
return null;
|
|
12794
13119
|
}
|
|
12795
13120
|
}
|
|
12796
|
-
async readSkillUsage(lastSyncAt) {
|
|
12797
|
-
try {
|
|
12798
|
-
const baseDir = getCoworkBaseDir();
|
|
12799
|
-
if (!existsSync29(baseDir))
|
|
12800
|
-
return null;
|
|
12801
|
-
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
12802
|
-
const skillCounts = new Map;
|
|
12803
|
-
const agentSessionsDir = baseDir;
|
|
12804
|
-
this.walkSessionDirs(agentSessionsDir, sinceMs, (session) => {
|
|
12805
|
-
const sessionTime = session.lastActivityAt ?? session.createdAt ?? 0;
|
|
12806
|
-
if (sessionTime <= sinceMs)
|
|
12807
|
-
return;
|
|
12808
|
-
const mcpTools = session.enabledMcpTools;
|
|
12809
|
-
if (!mcpTools || typeof mcpTools !== "object")
|
|
12810
|
-
return;
|
|
12811
|
-
for (const toolName of Object.keys(mcpTools)) {
|
|
12812
|
-
const skillMatch = toolName.match(/skill_([A-Za-z0-9_]+)/);
|
|
12813
|
-
if (!skillMatch)
|
|
12814
|
-
continue;
|
|
12815
|
-
const rawName = skillMatch[1].replace(/_/g, " ").trim();
|
|
12816
|
-
if (!rawName)
|
|
12817
|
-
continue;
|
|
12818
|
-
const existing = skillCounts.get(rawName);
|
|
12819
|
-
if (existing) {
|
|
12820
|
-
existing.count++;
|
|
12821
|
-
if (sessionTime > existing.lastTs)
|
|
12822
|
-
existing.lastTs = sessionTime;
|
|
12823
|
-
} else {
|
|
12824
|
-
skillCounts.set(rawName, { count: 1, lastTs: sessionTime });
|
|
12825
|
-
}
|
|
12826
|
-
}
|
|
12827
|
-
});
|
|
12828
|
-
if (skillCounts.size === 0)
|
|
12829
|
-
return null;
|
|
12830
|
-
const results = [];
|
|
12831
|
-
for (const [skillName, { count, lastTs }] of skillCounts) {
|
|
12832
|
-
results.push({
|
|
12833
|
-
skillName,
|
|
12834
|
-
count,
|
|
12835
|
-
lastUsedAt: new Date(lastTs).toISOString()
|
|
12836
|
-
});
|
|
12837
|
-
}
|
|
12838
|
-
return results;
|
|
12839
|
-
} catch {
|
|
12840
|
-
return null;
|
|
12841
|
-
}
|
|
12842
|
-
}
|
|
12843
13121
|
walkSessionDirs(baseDir, _sinceMs, onSession) {
|
|
12844
13122
|
try {
|
|
12845
13123
|
for (const orgDir of readdirSync6(baseDir)) {
|
|
@@ -13120,14 +13398,18 @@ ${instructions}`;
|
|
|
13120
13398
|
try {
|
|
13121
13399
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
13122
13400
|
const globalDbPath = this.globalStorageDbPath();
|
|
13123
|
-
let
|
|
13401
|
+
let newComposersWithoutId = 0;
|
|
13402
|
+
const activeComposerIds = new Set;
|
|
13403
|
+
const activeDays = new Set;
|
|
13404
|
+
let messageCount = 0;
|
|
13405
|
+
let toolCallCount = 0;
|
|
13124
13406
|
let totalLinesAdded = 0;
|
|
13125
13407
|
let totalLinesRemoved = 0;
|
|
13126
13408
|
let latestActivity = 0;
|
|
13127
13409
|
let agenticSessions = 0;
|
|
13128
13410
|
let chatSessions = 0;
|
|
13129
13411
|
if (existsSync30(globalDbPath)) {
|
|
13130
|
-
const composerResult = queryReadonlySqlite(globalDbPath, `SELECT value FROM cursorDiskKV WHERE key LIKE 'composerData
|
|
13412
|
+
const composerResult = queryReadonlySqlite(globalDbPath, `SELECT value FROM cursorDiskKV WHERE key LIKE 'composerData:%'`);
|
|
13131
13413
|
if (composerResult) {
|
|
13132
13414
|
for (const line of composerResult.split(`
|
|
13133
13415
|
`)) {
|
|
@@ -13137,7 +13419,11 @@ ${instructions}`;
|
|
|
13137
13419
|
const data = JSON.parse(line);
|
|
13138
13420
|
const createdAt = data.createdAt ?? 0;
|
|
13139
13421
|
if (createdAt > sinceMs) {
|
|
13140
|
-
|
|
13422
|
+
if (typeof data.composerId === "string")
|
|
13423
|
+
activeComposerIds.add(data.composerId);
|
|
13424
|
+
else
|
|
13425
|
+
newComposersWithoutId++;
|
|
13426
|
+
activeDays.add(new Date(createdAt).toISOString().slice(0, 10));
|
|
13141
13427
|
totalLinesAdded += data.totalLinesAdded ?? 0;
|
|
13142
13428
|
totalLinesRemoved += data.totalLinesRemoved ?? 0;
|
|
13143
13429
|
if (data.unifiedMode === "agent" || data.isAgentic)
|
|
@@ -13152,7 +13438,34 @@ ${instructions}`;
|
|
|
13152
13438
|
}
|
|
13153
13439
|
}
|
|
13154
13440
|
}
|
|
13441
|
+
const bubbleResult = queryReadonlySqlite(globalDbPath, `SELECT json_object('k', key, 'type', json_extract(value,'$.type'), 'ts', json_extract(value,'$.createdAt'), 'tool', json_extract(value,'$.toolFormerData.name')) FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'`);
|
|
13442
|
+
if (bubbleResult) {
|
|
13443
|
+
for (const line of bubbleResult.split(`
|
|
13444
|
+
`)) {
|
|
13445
|
+
if (!line.trim())
|
|
13446
|
+
continue;
|
|
13447
|
+
try {
|
|
13448
|
+
const b = JSON.parse(line);
|
|
13449
|
+
const ts = typeof b.ts === "string" ? Date.parse(b.ts) : NaN;
|
|
13450
|
+
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
13451
|
+
continue;
|
|
13452
|
+
if (ts > latestActivity)
|
|
13453
|
+
latestActivity = ts;
|
|
13454
|
+
activeDays.add(new Date(ts).toISOString().slice(0, 10));
|
|
13455
|
+
const parts = (b.k ?? "").split(":");
|
|
13456
|
+
if (parts.length >= 3)
|
|
13457
|
+
activeComposerIds.add(parts[1]);
|
|
13458
|
+
if (b.tool)
|
|
13459
|
+
toolCallCount++;
|
|
13460
|
+
else if (b.type === 1 || b.type === 2)
|
|
13461
|
+
messageCount++;
|
|
13462
|
+
} catch {
|
|
13463
|
+
continue;
|
|
13464
|
+
}
|
|
13465
|
+
}
|
|
13466
|
+
}
|
|
13155
13467
|
}
|
|
13468
|
+
const sessionCount = newComposersWithoutId + activeComposerIds.size;
|
|
13156
13469
|
const trackingDbPath = join24(homedir7(), ".cursor", "ai-tracking", "ai-code-tracking.db");
|
|
13157
13470
|
let aiCommitCount = 0;
|
|
13158
13471
|
let avgAiPercent = 0;
|
|
@@ -13165,17 +13478,19 @@ ${instructions}`;
|
|
|
13165
13478
|
avgAiPercent = parseFloat(parts[1]) || 0;
|
|
13166
13479
|
}
|
|
13167
13480
|
}
|
|
13168
|
-
|
|
13481
|
+
const hasNewActivity = sessionCount > 0 || messageCount > 0;
|
|
13482
|
+
if (!hasNewActivity && latestActivity <= sinceMs)
|
|
13169
13483
|
return null;
|
|
13170
13484
|
return {
|
|
13171
|
-
hasNewActivity
|
|
13485
|
+
hasNewActivity,
|
|
13172
13486
|
sessionCount,
|
|
13173
|
-
messageCount
|
|
13174
|
-
toolCallCount
|
|
13487
|
+
messageCount,
|
|
13488
|
+
toolCallCount,
|
|
13175
13489
|
tokensUsed: 0,
|
|
13176
13490
|
aiLinesAdded: totalLinesAdded,
|
|
13177
13491
|
aiLinesRemoved: totalLinesRemoved,
|
|
13178
13492
|
lastActiveAt: latestActivity > 0 ? new Date(latestActivity).toISOString() : null,
|
|
13493
|
+
activeDays: activeDays.size > 0 ? [...activeDays].sort() : undefined,
|
|
13179
13494
|
extra: {
|
|
13180
13495
|
agenticSessions,
|
|
13181
13496
|
chatSessions,
|
|
@@ -13410,6 +13725,8 @@ var AGENT_REGISTRY = [
|
|
|
13410
13725
|
},
|
|
13411
13726
|
manualSetup: {
|
|
13412
13727
|
title: "Install Runwork plugin",
|
|
13728
|
+
action: "Install Runwork plugin",
|
|
13729
|
+
preposition: "for",
|
|
13413
13730
|
showAfter: "connecting",
|
|
13414
13731
|
downloadArtifact: {
|
|
13415
13732
|
label: "Download plugin",
|
|
@@ -13417,7 +13734,7 @@ var AGENT_REGISTRY = [
|
|
|
13417
13734
|
command: "build-plugin"
|
|
13418
13735
|
},
|
|
13419
13736
|
steps: [
|
|
13420
|
-
{ id: "download", label: "
|
|
13737
|
+
{ id: "download", label: "Download the plugin. It saves runwork-plugin.zip to your Downloads folder.", control: "download-artifact" },
|
|
13421
13738
|
{ id: "open", label: "Open Claude Desktop and sign in if you haven't already" },
|
|
13422
13739
|
{ id: "cowork", label: "Go to the Cowork tab (top of the window)" },
|
|
13423
13740
|
{ id: "customize", label: "Click Customize in the sidebar" },
|
|
@@ -13517,14 +13834,15 @@ var AGENT_REGISTRY = [
|
|
|
13517
13834
|
method: "any",
|
|
13518
13835
|
target: [
|
|
13519
13836
|
{ method: "path", target: { macos: "/Applications/Codex.app" } },
|
|
13520
|
-
{ method: "
|
|
13837
|
+
{ method: "macos-bundle-id", target: "com.openai.codex" },
|
|
13838
|
+
{ method: "windows-appx", target: ["OpenAI.Codex"] }
|
|
13521
13839
|
]
|
|
13522
13840
|
},
|
|
13523
|
-
launch: { app: { macos: "Codex", windows: "Codex" } },
|
|
13841
|
+
launch: { app: { macos: "Codex", windows: "Codex" }, bundleId: { macos: "com.openai.codex" } },
|
|
13524
13842
|
logo: "codex",
|
|
13525
13843
|
downloadUrl: "https://openai.com/codex/",
|
|
13526
13844
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13527
|
-
instructionFile: { global: ".codex/
|
|
13845
|
+
instructionFile: { global: ".codex/AGENTS.md", project: "AGENTS.md" },
|
|
13528
13846
|
firstClass: true,
|
|
13529
13847
|
resumeCapability: {
|
|
13530
13848
|
mode: "file-drop-only",
|
|
@@ -13549,7 +13867,7 @@ var AGENT_REGISTRY = [
|
|
|
13549
13867
|
logo: "codex",
|
|
13550
13868
|
downloadUrl: "https://github.com/openai/codex",
|
|
13551
13869
|
skillsPaths: { global: ".codex/skills", project: ".agents/skills" },
|
|
13552
|
-
instructionFile: { global: ".codex/
|
|
13870
|
+
instructionFile: { global: ".codex/AGENTS.md", project: "AGENTS.md" },
|
|
13553
13871
|
firstClass: true,
|
|
13554
13872
|
resumeCapability: {
|
|
13555
13873
|
mode: "cli-resume",
|
|
@@ -13590,6 +13908,8 @@ var AGENT_REGISTRY = [
|
|
|
13590
13908
|
},
|
|
13591
13909
|
manualSetup: {
|
|
13592
13910
|
title: "Connect Runwork in ChatGPT",
|
|
13911
|
+
action: "Create Runwork connector",
|
|
13912
|
+
preposition: "in",
|
|
13593
13913
|
showAfter: "connecting",
|
|
13594
13914
|
copyValue: { label: "Workspace MCP URL", token: "{mcpUrl}" },
|
|
13595
13915
|
steps: [
|
|
@@ -13597,7 +13917,7 @@ var AGENT_REGISTRY = [
|
|
|
13597
13917
|
{ id: "settings-apps", label: "Open the profile menu (bottom-left), then Settings, then Apps" },
|
|
13598
13918
|
{ id: "dev-mode", label: 'Click "Advanced settings" and turn on "Developer mode" (skip if it is already on)' },
|
|
13599
13919
|
{ id: "create-app", label: 'Go back to Apps and click "Create app"' },
|
|
13600
|
-
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL",
|
|
13920
|
+
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL", then paste this Workspace MCP URL into the Server URL field:', control: "copy-value" },
|
|
13601
13921
|
{ id: "oauth", label: 'Leave Authentication set to "OAuth", check "I understand and want to continue", then click Create' },
|
|
13602
13922
|
{ id: "authorize", label: "Complete the OAuth sign-in to authorize Runwork for your account" },
|
|
13603
13923
|
{ id: "confirm", label: 'In a new chat, ask "Do you have access to Runwork tools?" to confirm (or check Settings, then Apps, for Runwork)' }
|
|
@@ -13607,6 +13927,8 @@ var AGENT_REGISTRY = [
|
|
|
13607
13927
|
key: "team",
|
|
13608
13928
|
label: "Team / Enterprise",
|
|
13609
13929
|
title: "Connect the Runwork app in ChatGPT",
|
|
13930
|
+
action: "Connect the Runwork app",
|
|
13931
|
+
preposition: "in",
|
|
13610
13932
|
steps: [
|
|
13611
13933
|
{ id: "open", label: "Open chatgpt.com and sign in" },
|
|
13612
13934
|
{ id: "settings-apps", label: "Open the profile menu (bottom-left), then Settings, then Apps" },
|
|
@@ -13619,12 +13941,14 @@ var AGENT_REGISTRY = [
|
|
|
13619
13941
|
key: "personal",
|
|
13620
13942
|
label: "Personal",
|
|
13621
13943
|
title: "Create the Runwork connector in ChatGPT",
|
|
13944
|
+
action: "Create Runwork connector",
|
|
13945
|
+
preposition: "in",
|
|
13622
13946
|
steps: [
|
|
13623
13947
|
{ id: "open", label: "Open chatgpt.com and sign in" },
|
|
13624
13948
|
{ id: "settings-apps", label: "Open the profile menu (bottom-left), then Settings, then Apps" },
|
|
13625
13949
|
{ id: "dev-mode", label: 'Click "Advanced settings" and turn on "Developer mode" (skip if it is already on)' },
|
|
13626
13950
|
{ id: "create-app", label: 'Go back to Apps and click "Create app"' },
|
|
13627
|
-
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL",
|
|
13951
|
+
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL", then paste this Workspace MCP URL into the Server URL field:', control: "copy-value" },
|
|
13628
13952
|
{ id: "oauth", label: 'Leave Authentication set to "OAuth", check "I understand and want to continue", then click Create' },
|
|
13629
13953
|
{ id: "authorize", label: "Complete the OAuth sign-in to authorize Runwork for your account" },
|
|
13630
13954
|
{ id: "confirm", label: 'In a new chat, ask "Do you have access to Runwork tools?" to confirm (or check Settings, then Apps, for Runwork)' }
|
|
@@ -13643,10 +13967,11 @@ var AGENT_REGISTRY = [
|
|
|
13643
13967
|
method: "any",
|
|
13644
13968
|
target: [
|
|
13645
13969
|
{ method: "path", target: { macos: "/Applications/ChatGPT.app" } },
|
|
13646
|
-
{ method: "
|
|
13970
|
+
{ method: "macos-bundle-id", target: "com.openai.chat" },
|
|
13971
|
+
{ method: "windows-appx", target: ["OpenAI.ChatGPT"] }
|
|
13647
13972
|
]
|
|
13648
13973
|
},
|
|
13649
|
-
launch: { app: { macos: "ChatGPT", windows: "ChatGPT" }, url: "https://chatgpt.com/?prompt={prompt}" },
|
|
13974
|
+
launch: { app: { macos: "ChatGPT", windows: "ChatGPT" }, bundleId: { macos: "com.openai.chat" }, url: "https://chatgpt.com/?prompt={prompt}" },
|
|
13650
13975
|
logo: "openai",
|
|
13651
13976
|
downloadUrl: "https://chatgpt.com/download",
|
|
13652
13977
|
firstClass: true,
|
|
@@ -13663,6 +13988,8 @@ var AGENT_REGISTRY = [
|
|
|
13663
13988
|
},
|
|
13664
13989
|
manualSetup: {
|
|
13665
13990
|
title: "Connect Runwork in ChatGPT",
|
|
13991
|
+
action: "Create Runwork connector",
|
|
13992
|
+
preposition: "in",
|
|
13666
13993
|
showAfter: "connecting",
|
|
13667
13994
|
copyValue: { label: "Workspace MCP URL", token: "{mcpUrl}" },
|
|
13668
13995
|
steps: [
|
|
@@ -13671,7 +13998,7 @@ var AGENT_REGISTRY = [
|
|
|
13671
13998
|
{ id: "settings-apps", label: "Open the profile menu (bottom-left), then Settings, then Apps" },
|
|
13672
13999
|
{ id: "dev-mode", label: 'Click "Advanced settings" and turn on "Developer mode" (skip if it is already on)' },
|
|
13673
14000
|
{ id: "create-app", label: 'Go back to Apps and click "Create app"' },
|
|
13674
|
-
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL",
|
|
14001
|
+
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL", then paste this Workspace MCP URL into the Server URL field:', control: "copy-value" },
|
|
13675
14002
|
{ id: "oauth", label: 'Leave Authentication set to "OAuth", check "I understand and want to continue", then click Create' },
|
|
13676
14003
|
{ id: "authorize", label: "Complete the OAuth sign-in to authorize Runwork for your account" },
|
|
13677
14004
|
{ id: "confirm", label: 'Open the ChatGPT desktop app and, in a new chat, ask "Do you have access to Runwork tools?" to confirm' }
|
|
@@ -13681,6 +14008,8 @@ var AGENT_REGISTRY = [
|
|
|
13681
14008
|
key: "team",
|
|
13682
14009
|
label: "Team / Enterprise",
|
|
13683
14010
|
title: "Connect the Runwork app in ChatGPT",
|
|
14011
|
+
action: "Connect the Runwork app",
|
|
14012
|
+
preposition: "in",
|
|
13684
14013
|
steps: [
|
|
13685
14014
|
{ id: "install", label: "Install the ChatGPT desktop app from chatgpt.com/download if you have not already" },
|
|
13686
14015
|
{ id: "open", label: "Open the ChatGPT desktop app and sign in" },
|
|
@@ -13694,13 +14023,15 @@ var AGENT_REGISTRY = [
|
|
|
13694
14023
|
key: "personal",
|
|
13695
14024
|
label: "Personal",
|
|
13696
14025
|
title: "Create the Runwork connector in ChatGPT",
|
|
14026
|
+
action: "Create Runwork connector",
|
|
14027
|
+
preposition: "in",
|
|
13697
14028
|
steps: [
|
|
13698
14029
|
{ id: "install", label: "Install the ChatGPT desktop app from chatgpt.com/download if you have not already" },
|
|
13699
14030
|
{ id: "open-web", label: "Connectors are created on chatgpt.com: open it in your browser and sign in" },
|
|
13700
14031
|
{ id: "settings-apps", label: "Open the profile menu (bottom-left), then Settings, then Apps" },
|
|
13701
14032
|
{ id: "dev-mode", label: 'Click "Advanced settings" and turn on "Developer mode" (skip if it is already on)' },
|
|
13702
14033
|
{ id: "create-app", label: 'Go back to Apps and click "Create app"' },
|
|
13703
|
-
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL",
|
|
14034
|
+
{ id: "fill", label: 'In the New App dialog, set Name to "Runwork", keep Connection on "Server URL", then paste this Workspace MCP URL into the Server URL field:', control: "copy-value" },
|
|
13704
14035
|
{ id: "oauth", label: 'Leave Authentication set to "OAuth", check "I understand and want to continue", then click Create' },
|
|
13705
14036
|
{ id: "authorize", label: "Complete the OAuth sign-in to authorize Runwork for your account" },
|
|
13706
14037
|
{ id: "confirm", label: 'Open the ChatGPT desktop app and, in a new chat, ask "Do you have access to Runwork tools?" to confirm' }
|
|
@@ -13936,14 +14267,28 @@ function resolveToAbsolute(ps, scope) {
|
|
|
13936
14267
|
return scope === "global" ? join26(homedir9(), resolved) : join26(process.cwd(), resolved);
|
|
13937
14268
|
}
|
|
13938
14269
|
|
|
14270
|
+
// src/agents/detection-probes.ts
|
|
14271
|
+
function powershellQuote(value) {
|
|
14272
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
14273
|
+
}
|
|
14274
|
+
function isValidBundleId(id) {
|
|
14275
|
+
return /^[A-Za-z0-9][A-Za-z0-9.-]*$/.test(id);
|
|
14276
|
+
}
|
|
14277
|
+
function macosBundleIdProbeScript(id) {
|
|
14278
|
+
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`;
|
|
14279
|
+
}
|
|
14280
|
+
function appxPackageProbeScript(pkg) {
|
|
14281
|
+
return `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
14282
|
+
}
|
|
14283
|
+
function startAppProbeScript(pattern) {
|
|
14284
|
+
return `$a = Get-StartApps -Name ${powershellQuote(pattern)} -ErrorAction SilentlyContinue | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
14285
|
+
}
|
|
14286
|
+
|
|
13939
14287
|
// src/agents/detection.ts
|
|
13940
14288
|
var execFileAsync = promisify(execFile);
|
|
13941
14289
|
function isWindows() {
|
|
13942
14290
|
return platform7() === "win32";
|
|
13943
14291
|
}
|
|
13944
|
-
function powershellQuote(value) {
|
|
13945
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
13946
|
-
}
|
|
13947
14292
|
function toList(value) {
|
|
13948
14293
|
return Array.isArray(value) ? value : [value];
|
|
13949
14294
|
}
|
|
@@ -13963,23 +14308,30 @@ function checkPath(target) {
|
|
|
13963
14308
|
return existsSync32(resolved);
|
|
13964
14309
|
return existsSync32(join27(homedir10(), resolved));
|
|
13965
14310
|
}
|
|
14311
|
+
async function checkMacosBundleId(target) {
|
|
14312
|
+
if (platform7() !== "darwin")
|
|
14313
|
+
return false;
|
|
14314
|
+
for (const id of toList(target)) {
|
|
14315
|
+
if (!isValidBundleId(id))
|
|
14316
|
+
continue;
|
|
14317
|
+
try {
|
|
14318
|
+
await execFileAsync("sh", ["-c", macosBundleIdProbeScript(id)]);
|
|
14319
|
+
return true;
|
|
14320
|
+
} catch {}
|
|
14321
|
+
}
|
|
14322
|
+
return false;
|
|
14323
|
+
}
|
|
13966
14324
|
async function checkWindowsAppxPackage(target) {
|
|
13967
14325
|
if (!isWindows())
|
|
13968
14326
|
return false;
|
|
13969
|
-
const probes = toList(target).map((pkg) =>
|
|
13970
|
-
const script = `$p = Get-AppxPackage -Name ${powershellQuote(pkg)} -ErrorAction SilentlyContinue; if ($null -ne $p) { exit 0 } exit 1`;
|
|
13971
|
-
return runPowerShell(script);
|
|
13972
|
-
});
|
|
14327
|
+
const probes = toList(target).map((pkg) => runPowerShell(appxPackageProbeScript(pkg)));
|
|
13973
14328
|
const results = await Promise.all(probes);
|
|
13974
14329
|
return results.some(Boolean);
|
|
13975
14330
|
}
|
|
13976
14331
|
async function checkWindowsStartApp(target) {
|
|
13977
14332
|
if (!isWindows())
|
|
13978
14333
|
return false;
|
|
13979
|
-
const probes = toList(target).map((pattern) =>
|
|
13980
|
-
const script = `$a = Get-StartApps -Name ${powershellQuote(pattern)} -ErrorAction SilentlyContinue | Select-Object -First 1; if ($null -ne $a) { exit 0 } exit 1`;
|
|
13981
|
-
return runPowerShell(script);
|
|
13982
|
-
});
|
|
14334
|
+
const probes = toList(target).map((pattern) => runPowerShell(startAppProbeScript(pattern)));
|
|
13983
14335
|
const results = await Promise.all(probes);
|
|
13984
14336
|
return results.some(Boolean);
|
|
13985
14337
|
}
|
|
@@ -13995,6 +14347,8 @@ async function runAgentDetection(detection) {
|
|
|
13995
14347
|
return checkWindowsAppxPackage(detection.target);
|
|
13996
14348
|
case "windows-start-app":
|
|
13997
14349
|
return checkWindowsStartApp(detection.target);
|
|
14350
|
+
case "macos-bundle-id":
|
|
14351
|
+
return checkMacosBundleId(detection.target);
|
|
13998
14352
|
case "any": {
|
|
13999
14353
|
const probes = await Promise.all(detection.target.map((p) => runAgentDetection(p)));
|
|
14000
14354
|
return probes.some(Boolean);
|
|
@@ -14106,6 +14460,26 @@ class CodexAdapter {
|
|
|
14106
14460
|
}
|
|
14107
14461
|
}
|
|
14108
14462
|
}
|
|
14463
|
+
if (config.networkAllowlist?.length) {
|
|
14464
|
+
const features = parsed.features && typeof parsed.features === "object" ? parsed.features : undefined;
|
|
14465
|
+
const proxy = features?.network_proxy;
|
|
14466
|
+
if (proxy && proxy.enabled === true) {
|
|
14467
|
+
if (!proxy.domains || typeof proxy.domains !== "object")
|
|
14468
|
+
proxy.domains = {};
|
|
14469
|
+
const domains = proxy.domains;
|
|
14470
|
+
for (const host of config.networkAllowlist) {
|
|
14471
|
+
if (!(host in domains))
|
|
14472
|
+
domains[host] = "allow";
|
|
14473
|
+
}
|
|
14474
|
+
} else {
|
|
14475
|
+
if (!parsed.sandbox_workspace_write || typeof parsed.sandbox_workspace_write !== "object") {
|
|
14476
|
+
parsed.sandbox_workspace_write = {};
|
|
14477
|
+
}
|
|
14478
|
+
const sww = parsed.sandbox_workspace_write;
|
|
14479
|
+
if (sww.network_access === undefined)
|
|
14480
|
+
sww.network_access = true;
|
|
14481
|
+
}
|
|
14482
|
+
}
|
|
14109
14483
|
mkdirSync20(join28(configPath, ".."), { recursive: true });
|
|
14110
14484
|
writeFileSync21(configPath, stringify(parsed));
|
|
14111
14485
|
}
|
|
@@ -14147,50 +14521,162 @@ class CodexAdapter {
|
|
|
14147
14521
|
return null;
|
|
14148
14522
|
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
14149
14523
|
const sinceSec = Math.floor(sinceMs / 1000);
|
|
14524
|
+
const rollout = this.scanRolloutActivity(sinceMs);
|
|
14525
|
+
let sessionCount = rollout.sessionCount;
|
|
14526
|
+
let tokensUsed = rollout.tokensUsed;
|
|
14527
|
+
let latestMs = rollout.latestMs;
|
|
14528
|
+
let versions = [];
|
|
14150
14529
|
const dbPath = join28(codexDir, "state_5.sqlite");
|
|
14151
|
-
if (
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14530
|
+
if (existsSync33(dbPath)) {
|
|
14531
|
+
const countResult = queryReadonlySqlite(dbPath, `SELECT count(*) FROM threads WHERE updated_at > ${sinceSec}`);
|
|
14532
|
+
const dbSessionCount = parseInt(countResult) || 0;
|
|
14533
|
+
const tokensResult = queryReadonlySqlite(dbPath, `SELECT coalesce(sum(tokens_used), 0) FROM threads WHERE updated_at > ${sinceSec}`);
|
|
14534
|
+
const dbTokens = parseInt(tokensResult) || 0;
|
|
14535
|
+
const latestResult = queryReadonlySqlite(dbPath, `SELECT max(updated_at) FROM threads`);
|
|
14536
|
+
const latestSec = parseInt(latestResult) || 0;
|
|
14537
|
+
const versionsResult = queryReadonlySqlite(dbPath, `SELECT DISTINCT cli_version FROM threads WHERE cli_version IS NOT NULL ORDER BY created_at DESC LIMIT 3`);
|
|
14538
|
+
versions = versionsResult ? versionsResult.split(/[\r\n]+/).filter(Boolean) : [];
|
|
14539
|
+
sessionCount = Math.max(dbSessionCount, rollout.sessionCount);
|
|
14540
|
+
if (dbTokens > 0)
|
|
14541
|
+
tokensUsed = dbTokens;
|
|
14542
|
+
if (latestSec * 1000 > latestMs)
|
|
14543
|
+
latestMs = latestSec * 1000;
|
|
14544
|
+
}
|
|
14545
|
+
let messageCount = rollout.messageCount;
|
|
14546
|
+
if (messageCount === 0) {
|
|
14547
|
+
const historyPath = join28(codexDir, "history.jsonl");
|
|
14548
|
+
if (existsSync33(historyPath)) {
|
|
14549
|
+
const content = readFileSync26(historyPath, "utf-8").trim();
|
|
14550
|
+
if (content) {
|
|
14551
|
+
for (const line of content.split(/[\r\n]+/)) {
|
|
14552
|
+
try {
|
|
14553
|
+
const entry = JSON.parse(line);
|
|
14554
|
+
if (entry.ts && entry.ts > sinceSec)
|
|
14555
|
+
messageCount++;
|
|
14556
|
+
} catch {
|
|
14557
|
+
continue;
|
|
14558
|
+
}
|
|
14173
14559
|
}
|
|
14174
14560
|
}
|
|
14175
14561
|
}
|
|
14176
14562
|
}
|
|
14177
|
-
|
|
14563
|
+
const hasNewActivity = sessionCount > 0 || messageCount > 0;
|
|
14564
|
+
if (!hasNewActivity && latestMs <= sinceMs)
|
|
14178
14565
|
return null;
|
|
14179
14566
|
return {
|
|
14180
|
-
hasNewActivity
|
|
14567
|
+
hasNewActivity,
|
|
14181
14568
|
sessionCount,
|
|
14182
14569
|
messageCount,
|
|
14183
|
-
toolCallCount:
|
|
14570
|
+
toolCallCount: rollout.toolCallCount,
|
|
14184
14571
|
tokensUsed,
|
|
14185
14572
|
aiLinesAdded: 0,
|
|
14186
14573
|
aiLinesRemoved: 0,
|
|
14187
|
-
lastActiveAt:
|
|
14574
|
+
lastActiveAt: latestMs > 0 ? new Date(latestMs).toISOString() : null,
|
|
14575
|
+
activeDays: rollout.activeDays.length > 0 ? rollout.activeDays : undefined,
|
|
14188
14576
|
extra: versions.length > 0 ? { cliVersions: versions } : undefined
|
|
14189
14577
|
};
|
|
14190
14578
|
} catch {
|
|
14191
14579
|
return null;
|
|
14192
14580
|
}
|
|
14193
14581
|
}
|
|
14582
|
+
scanRolloutActivity(sinceMs) {
|
|
14583
|
+
const days = new Set;
|
|
14584
|
+
const result = {
|
|
14585
|
+
sessionCount: 0,
|
|
14586
|
+
messageCount: 0,
|
|
14587
|
+
toolCallCount: 0,
|
|
14588
|
+
tokensUsed: 0,
|
|
14589
|
+
latestMs: 0,
|
|
14590
|
+
activeDays: []
|
|
14591
|
+
};
|
|
14592
|
+
const sessionsDir = join28(homedir11(), ".codex", "sessions");
|
|
14593
|
+
if (!existsSync33(sessionsDir))
|
|
14594
|
+
return result;
|
|
14595
|
+
const files = [];
|
|
14596
|
+
const walk = (dir) => {
|
|
14597
|
+
let entries;
|
|
14598
|
+
try {
|
|
14599
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
14600
|
+
} catch {
|
|
14601
|
+
return;
|
|
14602
|
+
}
|
|
14603
|
+
for (const e of entries) {
|
|
14604
|
+
const full = join28(dir, e.name);
|
|
14605
|
+
if (e.isDirectory())
|
|
14606
|
+
walk(full);
|
|
14607
|
+
else if (e.name.startsWith("rollout-") && e.name.endsWith(".jsonl"))
|
|
14608
|
+
files.push(full);
|
|
14609
|
+
}
|
|
14610
|
+
};
|
|
14611
|
+
walk(sessionsDir);
|
|
14612
|
+
for (const file of files) {
|
|
14613
|
+
let stat;
|
|
14614
|
+
try {
|
|
14615
|
+
stat = statSync5(file);
|
|
14616
|
+
} catch {
|
|
14617
|
+
continue;
|
|
14618
|
+
}
|
|
14619
|
+
if (stat.mtimeMs <= sinceMs)
|
|
14620
|
+
continue;
|
|
14621
|
+
let content;
|
|
14622
|
+
try {
|
|
14623
|
+
content = readFileSync26(file, "utf-8");
|
|
14624
|
+
} catch {
|
|
14625
|
+
continue;
|
|
14626
|
+
}
|
|
14627
|
+
let fileHadActivity = false;
|
|
14628
|
+
let fileTokens = 0;
|
|
14629
|
+
for (const line of content.split(`
|
|
14630
|
+
`)) {
|
|
14631
|
+
if (!line.trim())
|
|
14632
|
+
continue;
|
|
14633
|
+
let o;
|
|
14634
|
+
try {
|
|
14635
|
+
o = JSON.parse(line);
|
|
14636
|
+
} catch {
|
|
14637
|
+
continue;
|
|
14638
|
+
}
|
|
14639
|
+
const ts = typeof o.timestamp === "string" ? Date.parse(o.timestamp) : NaN;
|
|
14640
|
+
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
14641
|
+
continue;
|
|
14642
|
+
const p = o.payload;
|
|
14643
|
+
if (!p || typeof p !== "object")
|
|
14644
|
+
continue;
|
|
14645
|
+
const pt = p.type;
|
|
14646
|
+
if (o.type === "event_msg") {
|
|
14647
|
+
if (pt === "user_message" || pt === "agent_message") {
|
|
14648
|
+
result.messageCount++;
|
|
14649
|
+
fileHadActivity = true;
|
|
14650
|
+
if (ts > result.latestMs)
|
|
14651
|
+
result.latestMs = ts;
|
|
14652
|
+
days.add(new Date(ts).toISOString().slice(0, 10));
|
|
14653
|
+
} else if (pt === "token_count") {
|
|
14654
|
+
const info = p.info;
|
|
14655
|
+
const usage = info?.total_token_usage;
|
|
14656
|
+
if (usage) {
|
|
14657
|
+
const total = (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0);
|
|
14658
|
+
if (total > fileTokens)
|
|
14659
|
+
fileTokens = total;
|
|
14660
|
+
}
|
|
14661
|
+
if (ts > result.latestMs)
|
|
14662
|
+
result.latestMs = ts;
|
|
14663
|
+
}
|
|
14664
|
+
} else if (o.type === "response_item" && (pt === "function_call" || pt === "tool_search_call")) {
|
|
14665
|
+
result.toolCallCount++;
|
|
14666
|
+
fileHadActivity = true;
|
|
14667
|
+
if (ts > result.latestMs)
|
|
14668
|
+
result.latestMs = ts;
|
|
14669
|
+
days.add(new Date(ts).toISOString().slice(0, 10));
|
|
14670
|
+
}
|
|
14671
|
+
}
|
|
14672
|
+
if (fileHadActivity) {
|
|
14673
|
+
result.sessionCount++;
|
|
14674
|
+
result.tokensUsed += fileTokens;
|
|
14675
|
+
}
|
|
14676
|
+
}
|
|
14677
|
+
result.activeDays = [...days].sort();
|
|
14678
|
+
return result;
|
|
14679
|
+
}
|
|
14194
14680
|
async readSessionDigests(sinceISO) {
|
|
14195
14681
|
try {
|
|
14196
14682
|
const sessionsDir = join28(homedir11(), ".codex", "sessions");
|
|
@@ -14308,9 +14794,10 @@ class CodexAdapter {
|
|
|
14308
14794
|
} catch {
|
|
14309
14795
|
return;
|
|
14310
14796
|
}
|
|
14797
|
+
const seenInFile = new Set;
|
|
14311
14798
|
for (const line of content.split(`
|
|
14312
14799
|
`)) {
|
|
14313
|
-
if (!line)
|
|
14800
|
+
if (!line || !line.includes("SKILL.md"))
|
|
14314
14801
|
continue;
|
|
14315
14802
|
let entry;
|
|
14316
14803
|
try {
|
|
@@ -14318,35 +14805,37 @@ class CodexAdapter {
|
|
|
14318
14805
|
} catch {
|
|
14319
14806
|
continue;
|
|
14320
14807
|
}
|
|
14321
|
-
if (entry.type !== "
|
|
14808
|
+
if (entry.type !== "response_item")
|
|
14322
14809
|
continue;
|
|
14323
14810
|
const payload = entry.payload;
|
|
14324
14811
|
if (!payload)
|
|
14325
14812
|
continue;
|
|
14813
|
+
const pt = payload.type;
|
|
14814
|
+
if (pt !== "function_call" && pt !== "custom_tool_call")
|
|
14815
|
+
continue;
|
|
14326
14816
|
const tsRaw = entry.timestamp;
|
|
14327
14817
|
const ts = typeof tsRaw === "string" ? Date.parse(tsRaw) : 0;
|
|
14328
|
-
if (ts <= sinceMs)
|
|
14818
|
+
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
14329
14819
|
continue;
|
|
14330
|
-
const
|
|
14331
|
-
|
|
14820
|
+
const args = typeof payload.arguments === "string" ? payload.arguments : JSON.stringify(payload.arguments ?? "");
|
|
14821
|
+
const m = args.match(/([^\s"']*\/skills\/[^\s"']*\/)SKILL\.md/);
|
|
14822
|
+
if (!m)
|
|
14332
14823
|
continue;
|
|
14333
|
-
const
|
|
14334
|
-
|
|
14335
|
-
|
|
14336
|
-
|
|
14337
|
-
|
|
14338
|
-
|
|
14339
|
-
|
|
14340
|
-
|
|
14341
|
-
|
|
14342
|
-
|
|
14343
|
-
|
|
14344
|
-
|
|
14345
|
-
|
|
14346
|
-
|
|
14347
|
-
}
|
|
14824
|
+
const skillDir = m[1];
|
|
14825
|
+
if (skillDir.includes("/plugins/cache/openai-") || skillDir.includes("/.system/"))
|
|
14826
|
+
continue;
|
|
14827
|
+
const skillName = skillNameFromPath(skillDir);
|
|
14828
|
+
if (!skillName || seenInFile.has(skillName))
|
|
14829
|
+
continue;
|
|
14830
|
+
seenInFile.add(skillName);
|
|
14831
|
+
const existing = skillCounts.get(skillName);
|
|
14832
|
+
if (existing) {
|
|
14833
|
+
existing.count++;
|
|
14834
|
+
if (ts > existing.lastTs)
|
|
14835
|
+
existing.lastTs = ts;
|
|
14836
|
+
} else {
|
|
14837
|
+
skillCounts.set(skillName, { count: 1, lastTs: ts });
|
|
14348
14838
|
}
|
|
14349
|
-
break;
|
|
14350
14839
|
}
|
|
14351
14840
|
}
|
|
14352
14841
|
registerDesktopWorkspace(workspacePath, label) {
|
|
@@ -14520,7 +15009,7 @@ class ClineAdapter {
|
|
|
14520
15009
|
}
|
|
14521
15010
|
|
|
14522
15011
|
// src/agents/gemini.ts
|
|
14523
|
-
import { existsSync as existsSync35, mkdirSync as mkdirSync22, readdirSync as readdirSync11, readFileSync as readFileSync28, rmSync as rmSync10, writeFileSync as writeFileSync23 } from "fs";
|
|
15012
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync22, readdirSync as readdirSync11, readFileSync as readFileSync28, rmSync as rmSync10, statSync as statSync6, writeFileSync as writeFileSync23 } from "fs";
|
|
14524
15013
|
import { join as join30 } from "path";
|
|
14525
15014
|
import { homedir as homedir13 } from "os";
|
|
14526
15015
|
class GeminiAdapter {
|
|
@@ -14598,6 +15087,86 @@ class GeminiAdapter {
|
|
|
14598
15087
|
mkdirSync22(join30(settingsPath, ".."), { recursive: true });
|
|
14599
15088
|
writeFileSync23(settingsPath, JSON.stringify(settings, null, 2));
|
|
14600
15089
|
}
|
|
15090
|
+
async readUsageStats(lastSyncAt) {
|
|
15091
|
+
try {
|
|
15092
|
+
const tmpDir = join30(homedir13(), ".gemini", "tmp");
|
|
15093
|
+
if (!existsSync35(tmpDir))
|
|
15094
|
+
return null;
|
|
15095
|
+
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
15096
|
+
let sessionCount = 0;
|
|
15097
|
+
let messageCount = 0;
|
|
15098
|
+
let latestMs = 0;
|
|
15099
|
+
const activeDays = new Set;
|
|
15100
|
+
let projects;
|
|
15101
|
+
try {
|
|
15102
|
+
projects = readdirSync11(tmpDir, { withFileTypes: true });
|
|
15103
|
+
} catch {
|
|
15104
|
+
return null;
|
|
15105
|
+
}
|
|
15106
|
+
for (const project of projects) {
|
|
15107
|
+
if (!project.isDirectory())
|
|
15108
|
+
continue;
|
|
15109
|
+
const chatsDir = join30(tmpDir, project.name, "chats");
|
|
15110
|
+
let files;
|
|
15111
|
+
try {
|
|
15112
|
+
files = readdirSync11(chatsDir, { withFileTypes: true });
|
|
15113
|
+
} catch {
|
|
15114
|
+
continue;
|
|
15115
|
+
}
|
|
15116
|
+
for (const file of files) {
|
|
15117
|
+
if (!file.name.startsWith("session-") || !file.name.endsWith(".json"))
|
|
15118
|
+
continue;
|
|
15119
|
+
const filePath = join30(chatsDir, file.name);
|
|
15120
|
+
let stat;
|
|
15121
|
+
try {
|
|
15122
|
+
stat = statSync6(filePath);
|
|
15123
|
+
} catch {
|
|
15124
|
+
continue;
|
|
15125
|
+
}
|
|
15126
|
+
if (stat.mtimeMs <= sinceMs)
|
|
15127
|
+
continue;
|
|
15128
|
+
let session;
|
|
15129
|
+
try {
|
|
15130
|
+
session = JSON.parse(readFileSync28(filePath, "utf-8"));
|
|
15131
|
+
} catch {
|
|
15132
|
+
continue;
|
|
15133
|
+
}
|
|
15134
|
+
if (!Array.isArray(session.messages))
|
|
15135
|
+
continue;
|
|
15136
|
+
let sessionHadActivity = false;
|
|
15137
|
+
for (const m of session.messages) {
|
|
15138
|
+
const ts = typeof m.timestamp === "string" ? Date.parse(m.timestamp) : NaN;
|
|
15139
|
+
if (!Number.isFinite(ts) || ts <= sinceMs)
|
|
15140
|
+
continue;
|
|
15141
|
+
if (m.type === "user" || m.type === "gemini") {
|
|
15142
|
+
messageCount++;
|
|
15143
|
+
sessionHadActivity = true;
|
|
15144
|
+
if (ts > latestMs)
|
|
15145
|
+
latestMs = ts;
|
|
15146
|
+
activeDays.add(new Date(ts).toISOString().slice(0, 10));
|
|
15147
|
+
}
|
|
15148
|
+
}
|
|
15149
|
+
if (sessionHadActivity)
|
|
15150
|
+
sessionCount++;
|
|
15151
|
+
}
|
|
15152
|
+
}
|
|
15153
|
+
if (sessionCount === 0)
|
|
15154
|
+
return null;
|
|
15155
|
+
return {
|
|
15156
|
+
hasNewActivity: true,
|
|
15157
|
+
sessionCount,
|
|
15158
|
+
messageCount,
|
|
15159
|
+
toolCallCount: 0,
|
|
15160
|
+
tokensUsed: 0,
|
|
15161
|
+
aiLinesAdded: 0,
|
|
15162
|
+
aiLinesRemoved: 0,
|
|
15163
|
+
lastActiveAt: latestMs > 0 ? new Date(latestMs).toISOString() : null,
|
|
15164
|
+
activeDays: [...activeDays].sort()
|
|
15165
|
+
};
|
|
15166
|
+
} catch {
|
|
15167
|
+
return null;
|
|
15168
|
+
}
|
|
15169
|
+
}
|
|
14601
15170
|
async cleanup(scope, manifest) {
|
|
14602
15171
|
if (scope === "user") {
|
|
14603
15172
|
removeRunworkMcpServers(join30(homedir13(), ".gemini", "settings.json"), "mcpServers");
|
|
@@ -15412,7 +15981,7 @@ function parseJson(content, source) {
|
|
|
15412
15981
|
}
|
|
15413
15982
|
|
|
15414
15983
|
// src/commands/entities.ts
|
|
15415
|
-
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) => {
|
|
15984
|
+
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) => {
|
|
15416
15985
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15417
15986
|
const credentials = requireAuth();
|
|
15418
15987
|
const client = new ApiClient(credentials);
|
|
@@ -15447,7 +16016,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15447
16016
|
process.exit(1);
|
|
15448
16017
|
}
|
|
15449
16018
|
});
|
|
15450
|
-
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) => {
|
|
16019
|
+
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) => {
|
|
15451
16020
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15452
16021
|
const credentials = requireAuth();
|
|
15453
16022
|
const client = new ApiClient(credentials);
|
|
@@ -15479,7 +16048,7 @@ Next cursor: ${result.next}`);
|
|
|
15479
16048
|
process.exit(1);
|
|
15480
16049
|
}
|
|
15481
16050
|
});
|
|
15482
|
-
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) => {
|
|
16051
|
+
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) => {
|
|
15483
16052
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15484
16053
|
const credentials = requireAuth();
|
|
15485
16054
|
const client = new ApiClient(credentials);
|
|
@@ -15496,7 +16065,7 @@ var getCommand = new Command15("get").description("Get a single entity record by
|
|
|
15496
16065
|
process.exit(1);
|
|
15497
16066
|
}
|
|
15498
16067
|
});
|
|
15499
|
-
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) => {
|
|
16068
|
+
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) => {
|
|
15500
16069
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15501
16070
|
const credentials = requireAuth();
|
|
15502
16071
|
const client = new ApiClient(credentials);
|
|
@@ -15515,7 +16084,7 @@ var createCommand = new Command15("create").description("Create a new entity rec
|
|
|
15515
16084
|
process.exit(1);
|
|
15516
16085
|
}
|
|
15517
16086
|
});
|
|
15518
|
-
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) => {
|
|
16087
|
+
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) => {
|
|
15519
16088
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15520
16089
|
const credentials = requireAuth();
|
|
15521
16090
|
const client = new ApiClient(credentials);
|
|
@@ -15534,7 +16103,7 @@ var updateCommand = new Command15("update").description("Update an existing enti
|
|
|
15534
16103
|
process.exit(1);
|
|
15535
16104
|
}
|
|
15536
16105
|
});
|
|
15537
|
-
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) => {
|
|
16106
|
+
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) => {
|
|
15538
16107
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15539
16108
|
const credentials = requireAuth();
|
|
15540
16109
|
const client = new ApiClient(credentials);
|
|
@@ -15573,7 +16142,7 @@ function truncate2(text2, max) {
|
|
|
15573
16142
|
return first;
|
|
15574
16143
|
return first.slice(0, max - 3) + "...";
|
|
15575
16144
|
}
|
|
15576
|
-
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) => {
|
|
16145
|
+
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) => {
|
|
15577
16146
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15578
16147
|
const credentials = requireAuth();
|
|
15579
16148
|
const client = new ApiClient(credentials);
|
|
@@ -15607,7 +16176,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15607
16176
|
process.exit(1);
|
|
15608
16177
|
}
|
|
15609
16178
|
});
|
|
15610
|
-
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) => {
|
|
16179
|
+
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) => {
|
|
15611
16180
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15612
16181
|
const credentials = requireAuth();
|
|
15613
16182
|
const client = new ApiClient(credentials);
|
|
@@ -15641,7 +16210,7 @@ var triggerCommand = new Command16("trigger").description("Trigger a workflow by
|
|
|
15641
16210
|
process.exit(1);
|
|
15642
16211
|
}
|
|
15643
16212
|
});
|
|
15644
|
-
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) => {
|
|
16213
|
+
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) => {
|
|
15645
16214
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15646
16215
|
const credentials = requireAuth();
|
|
15647
16216
|
const client = new ApiClient(credentials);
|
|
@@ -15674,7 +16243,7 @@ var instancesCommand = new Command16("instances").description("List workflow ins
|
|
|
15674
16243
|
process.exit(1);
|
|
15675
16244
|
}
|
|
15676
16245
|
});
|
|
15677
|
-
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) => {
|
|
16246
|
+
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) => {
|
|
15678
16247
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15679
16248
|
const credentials = requireAuth();
|
|
15680
16249
|
const client = new ApiClient(credentials);
|
|
@@ -15702,16 +16271,64 @@ var workflowsCommand = new Command16("workflows").description("Manage workspace
|
|
|
15702
16271
|
init_store();
|
|
15703
16272
|
init_client();
|
|
15704
16273
|
import { Command as Command17 } from "commander";
|
|
15705
|
-
|
|
15706
|
-
|
|
16274
|
+
// src/utils/table.ts
|
|
16275
|
+
function clip(value, max) {
|
|
16276
|
+
if (max <= 0)
|
|
15707
16277
|
return "";
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
if (
|
|
15711
|
-
return
|
|
15712
|
-
return
|
|
16278
|
+
if (value.length <= max)
|
|
16279
|
+
return value;
|
|
16280
|
+
if (max <= 3)
|
|
16281
|
+
return value.slice(0, max);
|
|
16282
|
+
return value.slice(0, max - 3) + "...";
|
|
16283
|
+
}
|
|
16284
|
+
function renderTable(columns, rows, indent = " ") {
|
|
16285
|
+
const gap = " ";
|
|
16286
|
+
const lastIdx = columns.length - 1;
|
|
16287
|
+
const data = rows.map((row) => columns.map((c, i) => {
|
|
16288
|
+
const raw = row[i] == null ? "" : String(row[i]);
|
|
16289
|
+
return c.max !== undefined ? clip(raw, c.max) : raw;
|
|
16290
|
+
}));
|
|
16291
|
+
const widths = columns.map((c, i) => {
|
|
16292
|
+
let w = c.header.length;
|
|
16293
|
+
for (const row of data)
|
|
16294
|
+
w = Math.max(w, row[i].length);
|
|
16295
|
+
return w;
|
|
16296
|
+
});
|
|
16297
|
+
const renderRow = (cells, colorize) => {
|
|
16298
|
+
const parts = cells.map((cell, i) => {
|
|
16299
|
+
const padded = i === lastIdx ? cell : cell.padEnd(widths[i]);
|
|
16300
|
+
const color = columns[i].color;
|
|
16301
|
+
return colorize && color ? color(padded) : padded;
|
|
16302
|
+
});
|
|
16303
|
+
return (indent + parts.join(gap)).replace(/\s+$/, "");
|
|
16304
|
+
};
|
|
16305
|
+
const out = [renderRow(columns.map((c) => c.header), false)];
|
|
16306
|
+
const total = widths.reduce((a, b) => a + b, 0) + gap.length * Math.max(0, columns.length - 1);
|
|
16307
|
+
out.push(indent + "-".repeat(total));
|
|
16308
|
+
for (const row of data)
|
|
16309
|
+
out.push(renderRow(row, true));
|
|
16310
|
+
return out.join(`
|
|
16311
|
+
`);
|
|
15713
16312
|
}
|
|
15714
|
-
|
|
16313
|
+
|
|
16314
|
+
// src/commands/schedules.ts
|
|
16315
|
+
function extractScheduleHistoryRecords(data) {
|
|
16316
|
+
if (!data || !Array.isArray(data.records))
|
|
16317
|
+
return [];
|
|
16318
|
+
return data.records;
|
|
16319
|
+
}
|
|
16320
|
+
function scheduleRowCells(s) {
|
|
16321
|
+
const mode = s.deploymentMode ? ` (${s.deploymentMode})` : "";
|
|
16322
|
+
const status = s.status ? s.status + mode : mode.trim() || "-";
|
|
16323
|
+
return [
|
|
16324
|
+
s.scheduleName || "(unnamed)",
|
|
16325
|
+
s.appName || s.appId || "-",
|
|
16326
|
+
s.schedule || "-",
|
|
16327
|
+
status,
|
|
16328
|
+
s.description || ""
|
|
16329
|
+
];
|
|
16330
|
+
}
|
|
16331
|
+
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) => {
|
|
15715
16332
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15716
16333
|
const credentials = requireAuth();
|
|
15717
16334
|
const client = new ApiClient(credentials);
|
|
@@ -15733,20 +16350,20 @@ var listCommand5 = new Command17("list").description("List scheduled jobs in the
|
|
|
15733
16350
|
console.log(`
|
|
15734
16351
|
Workspace: ${workspaceName || workspaceId}
|
|
15735
16352
|
`);
|
|
15736
|
-
console.log(
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
16353
|
+
console.log(renderTable([
|
|
16354
|
+
{ header: "Name" },
|
|
16355
|
+
{ header: "App" },
|
|
16356
|
+
{ header: "Schedule" },
|
|
16357
|
+
{ header: "Status" },
|
|
16358
|
+
{ header: "Description", max: 60 }
|
|
16359
|
+
], schedules.map(scheduleRowCells)));
|
|
15743
16360
|
console.log("");
|
|
15744
16361
|
} catch (err) {
|
|
15745
16362
|
console.error("Failed to list schedules:", err instanceof Error ? err.message : err);
|
|
15746
16363
|
process.exit(1);
|
|
15747
16364
|
}
|
|
15748
16365
|
});
|
|
15749
|
-
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) => {
|
|
16366
|
+
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) => {
|
|
15750
16367
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15751
16368
|
const credentials = requireAuth();
|
|
15752
16369
|
const client = new ApiClient(credentials);
|
|
@@ -15772,7 +16389,7 @@ var triggerCommand2 = new Command17("trigger").description("Manually trigger a s
|
|
|
15772
16389
|
process.exit(1);
|
|
15773
16390
|
}
|
|
15774
16391
|
});
|
|
15775
|
-
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) => {
|
|
16392
|
+
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) => {
|
|
15776
16393
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15777
16394
|
const credentials = requireAuth();
|
|
15778
16395
|
const client = new ApiClient(credentials);
|
|
@@ -15780,25 +16397,34 @@ var historyCommand = new Command17("history").description("Show execution histor
|
|
|
15780
16397
|
const { appId, appName } = await resolveApp2(client, workspaceId, opts);
|
|
15781
16398
|
try {
|
|
15782
16399
|
const data = await client.getScheduleHistory(workspaceId, appId);
|
|
16400
|
+
const records = extractScheduleHistoryRecords(data);
|
|
15783
16401
|
if (useJson) {
|
|
15784
|
-
jsonOut({ history:
|
|
16402
|
+
jsonOut({ history: records, appId, appName });
|
|
15785
16403
|
return;
|
|
15786
16404
|
}
|
|
15787
|
-
if (
|
|
16405
|
+
if (records.length === 0) {
|
|
15788
16406
|
console.log(`No schedule history found for app "${appName}".`);
|
|
15789
16407
|
return;
|
|
15790
16408
|
}
|
|
15791
16409
|
console.log(`
|
|
15792
16410
|
Schedule history for app: ${appName}
|
|
15793
16411
|
`);
|
|
15794
|
-
|
|
15795
|
-
console.log(" " + "
|
|
15796
|
-
|
|
16412
|
+
const nameW = Math.max("Schedule".length, ...records.map((r) => (r.jobName || "-").length));
|
|
16413
|
+
console.log(" " + "Schedule".padEnd(nameW + 2) + "Started At".padEnd(26) + "Status".padEnd(12) + "Duration");
|
|
16414
|
+
console.log(" " + "-".repeat(nameW + 2 + 26 + 12 + 8));
|
|
16415
|
+
for (const entry of records) {
|
|
16416
|
+
const startedAt = entry.startedAt ? new Date(entry.startedAt).toISOString() : "-";
|
|
15797
16417
|
const duration = entry.durationMs != null ? `${entry.durationMs}ms` : "-";
|
|
15798
|
-
console.log(" " + entry.
|
|
16418
|
+
console.log(" " + (entry.jobName || "-").padEnd(nameW + 2) + startedAt.padEnd(26) + entry.status.padEnd(12) + duration);
|
|
15799
16419
|
if (entry.error) {
|
|
15800
16420
|
console.log(` Error: ${entry.error}`);
|
|
15801
16421
|
}
|
|
16422
|
+
if (entry.errorStack) {
|
|
16423
|
+
for (const line of entry.errorStack.split(`
|
|
16424
|
+
`).slice(0, 8)) {
|
|
16425
|
+
console.log(` ${line.trim()}`);
|
|
16426
|
+
}
|
|
16427
|
+
}
|
|
15802
16428
|
}
|
|
15803
16429
|
console.log("");
|
|
15804
16430
|
} catch (err) {
|
|
@@ -15806,7 +16432,7 @@ Schedule history for app: ${appName}
|
|
|
15806
16432
|
process.exit(1);
|
|
15807
16433
|
}
|
|
15808
16434
|
});
|
|
15809
|
-
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) => {
|
|
16435
|
+
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) => {
|
|
15810
16436
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15811
16437
|
const credentials = requireAuth();
|
|
15812
16438
|
const client = new ApiClient(credentials);
|
|
@@ -15828,7 +16454,7 @@ Schedule "${name}" status for app "${appName}":
|
|
|
15828
16454
|
process.exit(1);
|
|
15829
16455
|
}
|
|
15830
16456
|
});
|
|
15831
|
-
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) => {
|
|
16457
|
+
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) => {
|
|
15832
16458
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15833
16459
|
const credentials = requireAuth();
|
|
15834
16460
|
const client = new ApiClient(credentials);
|
|
@@ -15846,7 +16472,7 @@ var pauseCommand = new Command17("pause").description("Pause a scheduled job").a
|
|
|
15846
16472
|
process.exit(1);
|
|
15847
16473
|
}
|
|
15848
16474
|
});
|
|
15849
|
-
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) => {
|
|
16475
|
+
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) => {
|
|
15850
16476
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15851
16477
|
const credentials = requireAuth();
|
|
15852
16478
|
const client = new ApiClient(credentials);
|
|
@@ -15871,7 +16497,7 @@ init_store();
|
|
|
15871
16497
|
init_client();
|
|
15872
16498
|
import { Command as Command18 } from "commander";
|
|
15873
16499
|
init_http();
|
|
15874
|
-
function
|
|
16500
|
+
function truncate3(text2, max) {
|
|
15875
16501
|
if (!text2)
|
|
15876
16502
|
return "";
|
|
15877
16503
|
const first = text2.split(`
|
|
@@ -15880,7 +16506,7 @@ function truncate4(text2, max) {
|
|
|
15880
16506
|
return first;
|
|
15881
16507
|
return first.slice(0, max - 3) + "...";
|
|
15882
16508
|
}
|
|
15883
|
-
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) => {
|
|
16509
|
+
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) => {
|
|
15884
16510
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15885
16511
|
const credentials = requireAuth();
|
|
15886
16512
|
const client = new ApiClient(credentials);
|
|
@@ -15908,7 +16534,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15908
16534
|
const path2 = e.endpointPath.padEnd(36);
|
|
15909
16535
|
const app = (e.appName || e.appId).padEnd(20);
|
|
15910
16536
|
const auth = (e.auth || "").padEnd(10);
|
|
15911
|
-
const desc =
|
|
16537
|
+
const desc = truncate3(e.description, 40);
|
|
15912
16538
|
const mode = e.deploymentMode ? ` [${e.deploymentMode}]` : "";
|
|
15913
16539
|
console.log(` ${method} ${path2} ${app} ${auth} ${desc}${mode}`);
|
|
15914
16540
|
}
|
|
@@ -15918,7 +16544,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
15918
16544
|
process.exit(1);
|
|
15919
16545
|
}
|
|
15920
16546
|
});
|
|
15921
|
-
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) => {
|
|
16547
|
+
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) => {
|
|
15922
16548
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
15923
16549
|
const credentials = requireAuth();
|
|
15924
16550
|
const client = new ApiClient(credentials);
|
|
@@ -16049,7 +16675,7 @@ function formatSize(bytes) {
|
|
|
16049
16675
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
16050
16676
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
16051
16677
|
}
|
|
16052
|
-
var listCommand7 = new Command19("list").description("List file storage buckets in the workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16678
|
+
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) => {
|
|
16053
16679
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16054
16680
|
const credentials = requireAuth();
|
|
16055
16681
|
const client = new ApiClient(credentials);
|
|
@@ -16079,7 +16705,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16079
16705
|
process.exit(1);
|
|
16080
16706
|
}
|
|
16081
16707
|
});
|
|
16082
|
-
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) => {
|
|
16708
|
+
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) => {
|
|
16083
16709
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16084
16710
|
const credentials = requireAuth();
|
|
16085
16711
|
const client = new ApiClient(credentials);
|
|
@@ -16116,7 +16742,7 @@ var lsCommand = new Command19("ls").description("List files in a bucket").argume
|
|
|
16116
16742
|
process.exit(1);
|
|
16117
16743
|
}
|
|
16118
16744
|
});
|
|
16119
|
-
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) => {
|
|
16745
|
+
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) => {
|
|
16120
16746
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16121
16747
|
const credentials = requireAuth();
|
|
16122
16748
|
const client = new ApiClient(credentials);
|
|
@@ -16139,7 +16765,7 @@ var downloadCommand = new Command19("download").description("Download a file fro
|
|
|
16139
16765
|
process.exit(1);
|
|
16140
16766
|
}
|
|
16141
16767
|
});
|
|
16142
|
-
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) => {
|
|
16768
|
+
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) => {
|
|
16143
16769
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16144
16770
|
const credentials = requireAuth();
|
|
16145
16771
|
const client = new ApiClient(credentials);
|
|
@@ -16162,7 +16788,7 @@ var uploadCommand = new Command19("upload").description("Upload a local file to
|
|
|
16162
16788
|
process.exit(1);
|
|
16163
16789
|
}
|
|
16164
16790
|
});
|
|
16165
|
-
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) => {
|
|
16791
|
+
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) => {
|
|
16166
16792
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16167
16793
|
const credentials = requireAuth();
|
|
16168
16794
|
const client = new ApiClient(credentials);
|
|
@@ -16192,7 +16818,7 @@ var filesCommand = new Command19("files").description("Manage workspace file sto
|
|
|
16192
16818
|
init_store();
|
|
16193
16819
|
init_client();
|
|
16194
16820
|
import { Command as Command20 } from "commander";
|
|
16195
|
-
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) => {
|
|
16821
|
+
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) => {
|
|
16196
16822
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16197
16823
|
const credentials = requireAuth();
|
|
16198
16824
|
const client = new ApiClient(credentials);
|
|
@@ -16231,7 +16857,7 @@ init_store();
|
|
|
16231
16857
|
init_client();
|
|
16232
16858
|
import { Command as Command21 } from "commander";
|
|
16233
16859
|
init_prompt();
|
|
16234
|
-
var listCommand9 = new Command21("list").description("List API keys for the workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
16860
|
+
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) => {
|
|
16235
16861
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16236
16862
|
const credentials = requireAuth();
|
|
16237
16863
|
const client = new ApiClient(credentials);
|
|
@@ -16266,7 +16892,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16266
16892
|
process.exit(1);
|
|
16267
16893
|
}
|
|
16268
16894
|
});
|
|
16269
|
-
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) => {
|
|
16895
|
+
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) => {
|
|
16270
16896
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16271
16897
|
const credentials = requireAuth();
|
|
16272
16898
|
const client = new ApiClient(credentials);
|
|
@@ -16314,7 +16940,7 @@ var apiKeysCommand = new Command21("api-keys").description("Manage workspace API
|
|
|
16314
16940
|
init_store();
|
|
16315
16941
|
init_client();
|
|
16316
16942
|
import { Command as Command22 } from "commander";
|
|
16317
|
-
function
|
|
16943
|
+
function truncate4(text2, max) {
|
|
16318
16944
|
if (!text2)
|
|
16319
16945
|
return "";
|
|
16320
16946
|
const first = text2.split(`
|
|
@@ -16323,7 +16949,7 @@ function truncate5(text2, max) {
|
|
|
16323
16949
|
return first;
|
|
16324
16950
|
return first.slice(0, max - 3) + "...";
|
|
16325
16951
|
}
|
|
16326
|
-
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) => {
|
|
16952
|
+
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) => {
|
|
16327
16953
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16328
16954
|
const credentials = requireAuth();
|
|
16329
16955
|
const client = new ApiClient(credentials);
|
|
@@ -16349,8 +16975,8 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16349
16975
|
for (const a of agents) {
|
|
16350
16976
|
const name = a.agentName.padEnd(24);
|
|
16351
16977
|
const type = a.type.padEnd(16);
|
|
16352
|
-
const app =
|
|
16353
|
-
const desc =
|
|
16978
|
+
const app = truncate4(a.appName, 18).padEnd(20);
|
|
16979
|
+
const desc = truncate4(a.description, 40).padEnd(42);
|
|
16354
16980
|
const integrations = (a.integrations?.join(", ") || "").padEnd(20);
|
|
16355
16981
|
const entities = (a.entities?.join(", ") || "").padEnd(20);
|
|
16356
16982
|
const mode = a.deploymentMode || "";
|
|
@@ -16362,7 +16988,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16362
16988
|
process.exit(1);
|
|
16363
16989
|
}
|
|
16364
16990
|
});
|
|
16365
|
-
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) => {
|
|
16991
|
+
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) => {
|
|
16366
16992
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16367
16993
|
const credentials = requireAuth();
|
|
16368
16994
|
const client = new ApiClient(credentials);
|
|
@@ -16380,7 +17006,7 @@ var statusCommand2 = new Command22("status").description("Get status of a specif
|
|
|
16380
17006
|
process.exit(1);
|
|
16381
17007
|
}
|
|
16382
17008
|
});
|
|
16383
|
-
var executionsCommand = new Command22("executions").description("List agent execution history for workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
17009
|
+
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) => {
|
|
16384
17010
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16385
17011
|
const credentials = requireAuth();
|
|
16386
17012
|
const client = new ApiClient(credentials);
|
|
@@ -16399,9 +17025,9 @@ var executionsCommand = new Command22("executions").description("List agent exec
|
|
|
16399
17025
|
console.log(" " + "-".repeat(160));
|
|
16400
17026
|
for (const e of executions) {
|
|
16401
17027
|
const sessionId = e.sessionId.padEnd(36);
|
|
16402
|
-
const agent =
|
|
16403
|
-
const app =
|
|
16404
|
-
const prompt =
|
|
17028
|
+
const agent = truncate4(e.agentName, 20).padEnd(22);
|
|
17029
|
+
const app = truncate4(e.appId, 20).padEnd(22);
|
|
17030
|
+
const prompt = truncate4(e.prompt, 50).padEnd(52);
|
|
16405
17031
|
const status = e.status.padEnd(12);
|
|
16406
17032
|
const duration = e.durationMs != null ? `${e.durationMs}ms` : "";
|
|
16407
17033
|
const usage = e.usage ? `${e.usage.inputTokens}/${e.usage.outputTokens}` : "";
|
|
@@ -16413,7 +17039,7 @@ var executionsCommand = new Command22("executions").description("List agent exec
|
|
|
16413
17039
|
process.exit(1);
|
|
16414
17040
|
}
|
|
16415
17041
|
});
|
|
16416
|
-
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) => {
|
|
17042
|
+
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) => {
|
|
16417
17043
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16418
17044
|
const credentials = requireAuth();
|
|
16419
17045
|
const client = new ApiClient(credentials);
|
|
@@ -16437,7 +17063,7 @@ init_store();
|
|
|
16437
17063
|
init_client();
|
|
16438
17064
|
import { Command as Command23 } from "commander";
|
|
16439
17065
|
init_prompt();
|
|
16440
|
-
var listCommand11 = new Command23("list").description("List workspace MCP servers").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
17066
|
+
var listCommand11 = new Command23("list").description("List workspace MCP servers").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
16441
17067
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16442
17068
|
const credentials = requireAuth();
|
|
16443
17069
|
const client = new ApiClient(credentials);
|
|
@@ -16493,7 +17119,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16493
17119
|
process.exit(1);
|
|
16494
17120
|
}
|
|
16495
17121
|
});
|
|
16496
|
-
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) => {
|
|
17122
|
+
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) => {
|
|
16497
17123
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16498
17124
|
const credentials = requireAuth();
|
|
16499
17125
|
const client = new ApiClient(credentials);
|
|
@@ -16521,7 +17147,7 @@ var addCommand = new Command23("add").description("Add an MCP server to workspac
|
|
|
16521
17147
|
process.exit(1);
|
|
16522
17148
|
}
|
|
16523
17149
|
});
|
|
16524
|
-
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) => {
|
|
17150
|
+
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) => {
|
|
16525
17151
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16526
17152
|
const credentials = requireAuth();
|
|
16527
17153
|
const client = new ApiClient(credentials);
|
|
@@ -16584,7 +17210,7 @@ var searchCommand3 = new Command23("search").description("Search community MCP s
|
|
|
16584
17210
|
process.exit(1);
|
|
16585
17211
|
}
|
|
16586
17212
|
});
|
|
16587
|
-
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) => {
|
|
17213
|
+
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) => {
|
|
16588
17214
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16589
17215
|
const credentials = requireAuth();
|
|
16590
17216
|
const client = new ApiClient(credentials);
|
|
@@ -16717,6 +17343,12 @@ var RUNWORK_AGENT_DEFAULTS = {
|
|
|
16717
17343
|
var AGENT_DEFAULTS_SCHEMA_VERSION = 1;
|
|
16718
17344
|
|
|
16719
17345
|
// src/commands/sync-telemetry.ts
|
|
17346
|
+
var TELEMETRY_BACKFILL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
17347
|
+
function resolveTelemetrySince(state, nowMs = Date.now()) {
|
|
17348
|
+
if (state.lastTelemetryAt)
|
|
17349
|
+
return state.lastTelemetryAt;
|
|
17350
|
+
return new Date(nowMs - TELEMETRY_BACKFILL_MS).toISOString();
|
|
17351
|
+
}
|
|
16720
17352
|
async function collectTelemetryEvents(params) {
|
|
16721
17353
|
const now = params.now ?? new Date().toISOString();
|
|
16722
17354
|
const events = [];
|
|
@@ -16727,7 +17359,7 @@ async function collectTelemetryEvents(params) {
|
|
|
16727
17359
|
let error;
|
|
16728
17360
|
if (adapter2.readUsageStats) {
|
|
16729
17361
|
try {
|
|
16730
|
-
stats = await adapter2.readUsageStats(params.
|
|
17362
|
+
stats = await adapter2.readUsageStats(params.since);
|
|
16731
17363
|
if (stats && stats.hasNewActivity) {
|
|
16732
17364
|
events.push({
|
|
16733
17365
|
eventType: "local_agent.usage",
|
|
@@ -16740,6 +17372,7 @@ async function collectTelemetryEvents(params) {
|
|
|
16740
17372
|
aiLinesAdded: stats.aiLinesAdded,
|
|
16741
17373
|
aiLinesRemoved: stats.aiLinesRemoved,
|
|
16742
17374
|
lastActiveAt: stats.lastActiveAt,
|
|
17375
|
+
activeDays: stats.activeDays,
|
|
16743
17376
|
modelsUsed: stats.modelsUsed,
|
|
16744
17377
|
mcpToolCount: stats.mcpToolCount,
|
|
16745
17378
|
...stats.extra
|
|
@@ -16754,7 +17387,7 @@ async function collectTelemetryEvents(params) {
|
|
|
16754
17387
|
}
|
|
16755
17388
|
if (adapter2.readSkillUsage) {
|
|
16756
17389
|
try {
|
|
16757
|
-
skills = await adapter2.readSkillUsage(params.
|
|
17390
|
+
skills = await adapter2.readSkillUsage(params.since);
|
|
16758
17391
|
if (skills && skills.length > 0) {
|
|
16759
17392
|
for (const entry of skills) {
|
|
16760
17393
|
events.push({
|
|
@@ -18079,6 +18712,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18079
18712
|
const telemetry = await collectTelemetryEvents({
|
|
18080
18713
|
adapters,
|
|
18081
18714
|
state,
|
|
18715
|
+
since: resolveTelemetrySince(state),
|
|
18082
18716
|
syncedSkillsCount: remoteSkills.length,
|
|
18083
18717
|
syncedMcpServersCount: mcpEntries.length,
|
|
18084
18718
|
teamInstructionsApplied: false,
|
|
@@ -18458,21 +19092,25 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18458
19092
|
state.skillHashes = mergedHashes;
|
|
18459
19093
|
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18460
19094
|
try {
|
|
19095
|
+
const telemetryNow = new Date().toISOString();
|
|
18461
19096
|
const telemetry = await collectTelemetryEvents({
|
|
18462
19097
|
adapters,
|
|
18463
19098
|
state,
|
|
19099
|
+
since: resolveTelemetrySince(state, Date.parse(telemetryNow)),
|
|
18464
19100
|
syncedSkillsCount: remoteSkills.length,
|
|
18465
19101
|
syncedMcpServersCount: mcpEntries.length,
|
|
18466
19102
|
teamInstructionsApplied,
|
|
18467
|
-
agentConfigsApplied
|
|
19103
|
+
agentConfigsApplied,
|
|
19104
|
+
now: telemetryNow
|
|
18468
19105
|
});
|
|
18469
19106
|
if (opts.verbose)
|
|
18470
19107
|
printTelemetryVerbose(telemetry);
|
|
18471
19108
|
await client.reportTelemetry(state.workspaceId, telemetry.events);
|
|
19109
|
+
state.lastTelemetryAt = telemetryNow;
|
|
18472
19110
|
if (telemetry.healthReported) {
|
|
18473
19111
|
state.lastHealthReportAt = new Date().toISOString();
|
|
18474
|
-
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18475
19112
|
}
|
|
19113
|
+
writeFileSync29(statePath2, JSON.stringify(state, null, 2));
|
|
18476
19114
|
} catch {}
|
|
18477
19115
|
const failedNote = summary.adaptersFailed > 0 ? ` (${summary.adaptersFailed} failed)` : "";
|
|
18478
19116
|
if (isVerbose()) {
|
|
@@ -18589,7 +19227,7 @@ async function resolveAndPersistWorkspace(client, opts) {
|
|
|
18589
19227
|
}
|
|
18590
19228
|
return { workspaceId, workspaceName, workspaceSlug };
|
|
18591
19229
|
}
|
|
18592
|
-
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) => {
|
|
19230
|
+
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) => {
|
|
18593
19231
|
const credentials = requireAuth();
|
|
18594
19232
|
const client = new ApiClient(credentials);
|
|
18595
19233
|
const { workspaceId, workspaceName, workspaceSlug } = await resolveAndPersistWorkspace(client, opts);
|
|
@@ -18941,7 +19579,7 @@ init_store();
|
|
|
18941
19579
|
init_client();
|
|
18942
19580
|
import { Command as Command28 } from "commander";
|
|
18943
19581
|
init_init();
|
|
18944
|
-
var listCommand12 = new Command28("list").description("List apps in workspace").option("--workspace <id>", "Workspace ID").action(async (opts, command) => {
|
|
19582
|
+
var listCommand12 = new Command28("list").description("List apps in workspace").option("--workspace <name-or-id>", "Workspace name or ID").action(async (opts, command) => {
|
|
18945
19583
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
18946
19584
|
const credentials = requireAuth();
|
|
18947
19585
|
const client = new ApiClient(credentials);
|
|
@@ -18974,7 +19612,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
18974
19612
|
var createCommand3 = new Command28("create").description("Create a new Runwork app").argument("[name]", "App name").action(async (name) => {
|
|
18975
19613
|
await runCreateFlow(name);
|
|
18976
19614
|
});
|
|
18977
|
-
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) => {
|
|
19615
|
+
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) => {
|
|
18978
19616
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
18979
19617
|
const credentials = requireAuth();
|
|
18980
19618
|
const client = new ApiClient(credentials);
|
|
@@ -19001,15 +19639,157 @@ var infoCommand2 = new Command28("info").description("Show detailed app info, pr
|
|
|
19001
19639
|
});
|
|
19002
19640
|
var appsCommand = new Command28("apps").description("Manage workspace apps").addCommand(listCommand12).addCommand(createCommand3).addCommand(infoCommand2);
|
|
19003
19641
|
|
|
19642
|
+
// src/commands/members.ts
|
|
19643
|
+
init_store();
|
|
19644
|
+
init_client();
|
|
19645
|
+
import { Command as Command29 } from "commander";
|
|
19646
|
+
function formatMemberRows(members) {
|
|
19647
|
+
return members.map((m) => ({
|
|
19648
|
+
name: m.user.displayName || m.user.email,
|
|
19649
|
+
email: m.user.email,
|
|
19650
|
+
role: m.role,
|
|
19651
|
+
status: m.status,
|
|
19652
|
+
userId: m.userId
|
|
19653
|
+
}));
|
|
19654
|
+
}
|
|
19655
|
+
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) => {
|
|
19656
|
+
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19657
|
+
const credentials = requireAuth();
|
|
19658
|
+
const client = new ApiClient(credentials);
|
|
19659
|
+
const { workspaceId, workspaceName } = await resolveWorkspace2(client, opts);
|
|
19660
|
+
try {
|
|
19661
|
+
const members = await client.listWorkspaceMembers(workspaceId);
|
|
19662
|
+
if (useJson) {
|
|
19663
|
+
jsonOut({ workspaceId, workspaceName, members });
|
|
19664
|
+
return;
|
|
19665
|
+
}
|
|
19666
|
+
if (members.length === 0) {
|
|
19667
|
+
console.log("No members found in this workspace.");
|
|
19668
|
+
return;
|
|
19669
|
+
}
|
|
19670
|
+
console.log(`
|
|
19671
|
+
Workspace: ${workspaceName || workspaceId}
|
|
19672
|
+
`);
|
|
19673
|
+
console.log(` ${"NAME".padEnd(24)} ${"EMAIL".padEnd(32)} ${"ROLE".padEnd(10)} STATUS`);
|
|
19674
|
+
console.log(" " + "-".repeat(76));
|
|
19675
|
+
for (const row of formatMemberRows(members)) {
|
|
19676
|
+
console.log(` ${row.name.padEnd(24)} ${row.email.padEnd(32)} ${row.role.padEnd(10)} ${row.status}`);
|
|
19677
|
+
}
|
|
19678
|
+
console.log("");
|
|
19679
|
+
} catch (err) {
|
|
19680
|
+
console.error("Failed to list members:", err instanceof Error ? err.message : err);
|
|
19681
|
+
process.exit(1);
|
|
19682
|
+
}
|
|
19683
|
+
});
|
|
19684
|
+
var membersCommand = new Command29("members").description("List workspace members").addCommand(listCommand13);
|
|
19685
|
+
|
|
19686
|
+
// src/commands/api.ts
|
|
19687
|
+
init_store();
|
|
19688
|
+
init_client();
|
|
19689
|
+
import { Command as Command30 } from "commander";
|
|
19690
|
+
import { readFileSync as readFileSync37 } from "fs";
|
|
19691
|
+
function normalizeApiPath(rawPath, baseUrl) {
|
|
19692
|
+
if (/^https?:\/\//i.test(rawPath)) {
|
|
19693
|
+
const target = new URL(rawPath);
|
|
19694
|
+
const base = new URL(baseUrl);
|
|
19695
|
+
if (target.origin !== base.origin) {
|
|
19696
|
+
throw new Error(`Refusing to call ${target.origin}: \`runwork api\` only calls the platform API at ${base.origin}.`);
|
|
19697
|
+
}
|
|
19698
|
+
return target.pathname + target.search;
|
|
19699
|
+
}
|
|
19700
|
+
return rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
19701
|
+
}
|
|
19702
|
+
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", `
|
|
19703
|
+
Examples:
|
|
19704
|
+
runwork api GET /api/workspaces
|
|
19705
|
+
runwork api GET /api/workspaces/<id>/members
|
|
19706
|
+
runwork api POST /api/workspaces/<id>/skills --body '{"name":"my-skill"}'
|
|
19707
|
+
|
|
19708
|
+
Authentication uses your stored Runwork credentials; the API key never needs
|
|
19709
|
+
to be read or pasted manually. Prefer a dedicated command when one exists
|
|
19710
|
+
(runwork apps/members/schedules/...).`).action(async (method, path2, opts, command) => {
|
|
19711
|
+
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19712
|
+
const credentials = requireAuth();
|
|
19713
|
+
const client = new ApiClient(credentials);
|
|
19714
|
+
let finalMethod = method;
|
|
19715
|
+
let finalPath = path2;
|
|
19716
|
+
const headers = {};
|
|
19717
|
+
let body;
|
|
19718
|
+
const query = opts.query;
|
|
19719
|
+
if (opts.curl || opts.curlFile) {
|
|
19720
|
+
let curlStr = opts.curl;
|
|
19721
|
+
if (opts.curlFile) {
|
|
19722
|
+
try {
|
|
19723
|
+
curlStr = readFileSync37(opts.curlFile, "utf-8");
|
|
19724
|
+
} catch (err) {
|
|
19725
|
+
console.error(`Could not read --curl-file: ${err instanceof Error ? err.message : err}`);
|
|
19726
|
+
process.exit(1);
|
|
19727
|
+
}
|
|
19728
|
+
}
|
|
19729
|
+
const parsed = await parseCurlToRequest(curlStr);
|
|
19730
|
+
finalMethod = parsed.method;
|
|
19731
|
+
finalPath = parsed.path + (parsed.query ? `?${parsed.query}` : "");
|
|
19732
|
+
Object.assign(headers, parsed.headers);
|
|
19733
|
+
body = parsed.body;
|
|
19734
|
+
}
|
|
19735
|
+
for (const h of opts.header || []) {
|
|
19736
|
+
const [key, ...rest] = h.split(":");
|
|
19737
|
+
headers[key.trim()] = rest.join(":").trim();
|
|
19738
|
+
}
|
|
19739
|
+
if (opts.body) {
|
|
19740
|
+
let raw = opts.body;
|
|
19741
|
+
if (raw.startsWith("@")) {
|
|
19742
|
+
try {
|
|
19743
|
+
raw = readFileSync37(raw.slice(1), "utf-8");
|
|
19744
|
+
} catch (err) {
|
|
19745
|
+
console.error(`Could not read body file: ${err instanceof Error ? err.message : err}`);
|
|
19746
|
+
process.exit(1);
|
|
19747
|
+
}
|
|
19748
|
+
}
|
|
19749
|
+
try {
|
|
19750
|
+
body = JSON.parse(raw);
|
|
19751
|
+
} catch {
|
|
19752
|
+
console.error("Invalid JSON in --body");
|
|
19753
|
+
process.exit(1);
|
|
19754
|
+
}
|
|
19755
|
+
}
|
|
19756
|
+
if (!finalPath) {
|
|
19757
|
+
console.error("Usage: runwork api <method> <path>");
|
|
19758
|
+
console.error(' or: runwork api <method> --curl "curl ..."');
|
|
19759
|
+
process.exit(1);
|
|
19760
|
+
}
|
|
19761
|
+
try {
|
|
19762
|
+
const normalizedPath = normalizeApiPath(finalPath, credentials.baseUrl || "https://runwork.ai");
|
|
19763
|
+
const result = await client.rawApiCall(finalMethod, normalizedPath, {
|
|
19764
|
+
body,
|
|
19765
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
19766
|
+
query
|
|
19767
|
+
});
|
|
19768
|
+
if (useJson) {
|
|
19769
|
+
jsonOut({ success: result.ok, status: result.status, body: result.body });
|
|
19770
|
+
} else if (typeof result.body === "string") {
|
|
19771
|
+
console.log(result.body);
|
|
19772
|
+
} else {
|
|
19773
|
+
console.log(JSON.stringify(result.body, null, 2));
|
|
19774
|
+
}
|
|
19775
|
+
if (!result.ok)
|
|
19776
|
+
process.exit(1);
|
|
19777
|
+
} catch (err) {
|
|
19778
|
+
console.error("API call failed:", err instanceof Error ? err.message : err);
|
|
19779
|
+
process.exit(1);
|
|
19780
|
+
}
|
|
19781
|
+
});
|
|
19782
|
+
|
|
19004
19783
|
// src/commands/doctor.ts
|
|
19005
19784
|
init_colors();
|
|
19006
|
-
import { Command as
|
|
19785
|
+
import { Command as Command31 } from "commander";
|
|
19007
19786
|
|
|
19008
19787
|
// src/health/checks.ts
|
|
19009
19788
|
init_subprocess();
|
|
19010
19789
|
init_store();
|
|
19011
19790
|
init_client();
|
|
19012
|
-
import {
|
|
19791
|
+
import { parse as parse2 } from "smol-toml";
|
|
19792
|
+
import { existsSync as existsSync44, readFileSync as readFileSync38 } from "fs";
|
|
19013
19793
|
import { join as join40, sep as sep3 } from "path";
|
|
19014
19794
|
import { homedir as homedir22, platform as osPlatform2, arch as osArch } from "os";
|
|
19015
19795
|
init_http();
|
|
@@ -19045,7 +19825,7 @@ function buildContext() {
|
|
|
19045
19825
|
const configPath = join40(process.cwd(), ".runwork.json");
|
|
19046
19826
|
if (existsSync44(configPath)) {
|
|
19047
19827
|
try {
|
|
19048
|
-
config = JSON.parse(
|
|
19828
|
+
config = JSON.parse(readFileSync38(configPath, "utf-8"));
|
|
19049
19829
|
} catch {}
|
|
19050
19830
|
}
|
|
19051
19831
|
return { credentials, client, config, cwd: process.cwd() };
|
|
@@ -19265,6 +20045,14 @@ async function checkGitCredentialHelper(ctx) {
|
|
|
19265
20045
|
fix: "runwork doctor --fix (re-registers with the current binary path)"
|
|
19266
20046
|
};
|
|
19267
20047
|
}
|
|
20048
|
+
if (!lookup.hasReset) {
|
|
20049
|
+
return {
|
|
20050
|
+
name: "git-credential-helper",
|
|
20051
|
+
status: "fail",
|
|
20052
|
+
message: `helper registered for ${origin} but the credential-manager reset entry is missing (Windows may show a Git Credential Manager popup)`,
|
|
20053
|
+
fix: "runwork doctor --fix"
|
|
20054
|
+
};
|
|
20055
|
+
}
|
|
19268
20056
|
return {
|
|
19269
20057
|
name: "git-credential-helper",
|
|
19270
20058
|
status: "pass",
|
|
@@ -19394,7 +20182,7 @@ function loadSetupState5() {
|
|
|
19394
20182
|
for (const p of [projectPath, userPath]) {
|
|
19395
20183
|
if (existsSync44(p)) {
|
|
19396
20184
|
try {
|
|
19397
|
-
return JSON.parse(
|
|
20185
|
+
return JSON.parse(readFileSync38(p, "utf-8"));
|
|
19398
20186
|
} catch {
|
|
19399
20187
|
continue;
|
|
19400
20188
|
}
|
|
@@ -19402,6 +20190,95 @@ function loadSetupState5() {
|
|
|
19402
20190
|
}
|
|
19403
20191
|
return null;
|
|
19404
20192
|
}
|
|
20193
|
+
var RUNWORK_NETWORK_DOMAINS = ["runwork.ai", "*.runwork.ai"];
|
|
20194
|
+
async function checkCodexNetwork() {
|
|
20195
|
+
const name = "codex-network";
|
|
20196
|
+
const state = loadSetupState5();
|
|
20197
|
+
if (!state || !state.configuredAgents.includes("codex")) {
|
|
20198
|
+
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
20199
|
+
}
|
|
20200
|
+
const configPath = join40(homedir22(), ".codex", "config.toml");
|
|
20201
|
+
if (!existsSync44(configPath)) {
|
|
20202
|
+
return { name, status: "skip", message: "no Codex config found" };
|
|
20203
|
+
}
|
|
20204
|
+
let parsed;
|
|
20205
|
+
try {
|
|
20206
|
+
parsed = parse2(readFileSync38(configPath, "utf-8"));
|
|
20207
|
+
} catch {
|
|
20208
|
+
return { name, status: "warn", message: "could not parse ~/.codex/config.toml" };
|
|
20209
|
+
}
|
|
20210
|
+
const features = parsed.features && typeof parsed.features === "object" ? parsed.features : undefined;
|
|
20211
|
+
const proxy = features?.network_proxy;
|
|
20212
|
+
if (proxy && proxy.enabled === true) {
|
|
20213
|
+
const domains = proxy.domains && typeof proxy.domains === "object" ? proxy.domains : {};
|
|
20214
|
+
if (RUNWORK_NETWORK_DOMAINS.some((d) => domains[d] === "deny")) {
|
|
20215
|
+
return {
|
|
20216
|
+
name,
|
|
20217
|
+
status: "warn",
|
|
20218
|
+
message: "network proxy denies runwork.ai; the Runwork CLI is blocked",
|
|
20219
|
+
fix: 'remove the runwork.ai "deny" rule in ~/.codex/config.toml'
|
|
20220
|
+
};
|
|
20221
|
+
}
|
|
20222
|
+
if (RUNWORK_NETWORK_DOMAINS.every((d) => domains[d] === "allow")) {
|
|
20223
|
+
return { name, status: "pass", message: "proxy-allowlisted: runwork.ai permitted, other domains scoped by your proxy" };
|
|
20224
|
+
}
|
|
20225
|
+
return {
|
|
20226
|
+
name,
|
|
20227
|
+
status: "warn",
|
|
20228
|
+
message: "network proxy is on but runwork.ai is not allowlisted",
|
|
20229
|
+
fix: "runwork sync"
|
|
20230
|
+
};
|
|
20231
|
+
}
|
|
20232
|
+
const sww = parsed.sandbox_workspace_write && typeof parsed.sandbox_workspace_write === "object" ? parsed.sandbox_workspace_write : undefined;
|
|
20233
|
+
const sandboxMode = typeof parsed.sandbox_mode === "string" ? parsed.sandbox_mode : undefined;
|
|
20234
|
+
if (sww?.network_access === true || sandboxMode === "danger-full-access") {
|
|
20235
|
+
return { name, status: "pass", message: "open: outbound network enabled (all domains)" };
|
|
20236
|
+
}
|
|
20237
|
+
if (sww?.network_access === false) {
|
|
20238
|
+
return {
|
|
20239
|
+
name,
|
|
20240
|
+
status: "warn",
|
|
20241
|
+
message: "off: network explicitly disabled; the Runwork CLI cannot reach the network",
|
|
20242
|
+
fix: "set [sandbox_workspace_write] network_access = true, or run runwork sync"
|
|
20243
|
+
};
|
|
20244
|
+
}
|
|
20245
|
+
return {
|
|
20246
|
+
name,
|
|
20247
|
+
status: "warn",
|
|
20248
|
+
message: "off: network is disabled by default in Codex workspace-write sandbox; the Runwork CLI cannot reach the network",
|
|
20249
|
+
fix: "runwork sync"
|
|
20250
|
+
};
|
|
20251
|
+
}
|
|
20252
|
+
async function checkCodexDesktopProject() {
|
|
20253
|
+
const name = "codex-desktop-project";
|
|
20254
|
+
const state = loadSetupState5();
|
|
20255
|
+
const usesCodex = !!state && (state.configuredAgents.includes("codex-app") || state.configuredAgents.includes("codex"));
|
|
20256
|
+
if (!usesCodex) {
|
|
20257
|
+
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
20258
|
+
}
|
|
20259
|
+
const statePath2 = join40(homedir22(), ".codex", ".codex-global-state.json");
|
|
20260
|
+
if (!existsSync44(statePath2)) {
|
|
20261
|
+
return { name, status: "skip", message: "Codex desktop app not detected" };
|
|
20262
|
+
}
|
|
20263
|
+
let savedRoots = [];
|
|
20264
|
+
try {
|
|
20265
|
+
const parsed = JSON.parse(readFileSync38(statePath2, "utf-8"));
|
|
20266
|
+
const roots = parsed["electron-saved-workspace-roots"];
|
|
20267
|
+
savedRoots = Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
|
|
20268
|
+
} catch {
|
|
20269
|
+
return { name, status: "warn", message: "could not read Codex desktop state" };
|
|
20270
|
+
}
|
|
20271
|
+
const runworkDir = join40(homedir22(), ".runwork");
|
|
20272
|
+
if (savedRoots.includes(runworkDir)) {
|
|
20273
|
+
return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
|
|
20274
|
+
}
|
|
20275
|
+
return {
|
|
20276
|
+
name,
|
|
20277
|
+
status: "warn",
|
|
20278
|
+
message: "Runwork project not added to Codex desktop sidebar",
|
|
20279
|
+
fix: "quit the Codex app, then run runwork sync (sync skips this while Codex is open)"
|
|
20280
|
+
};
|
|
20281
|
+
}
|
|
19405
20282
|
async function checkAgentSetup() {
|
|
19406
20283
|
const state = loadSetupState5();
|
|
19407
20284
|
if (!state) {
|
|
@@ -19446,7 +20323,7 @@ async function checkAgentSetup() {
|
|
|
19446
20323
|
const mcpConfigPath = getMcpConfigPath2(slug, "user");
|
|
19447
20324
|
if (mcpConfigPath && existsSync44(mcpConfigPath)) {
|
|
19448
20325
|
try {
|
|
19449
|
-
const content =
|
|
20326
|
+
const content = readFileSync38(mcpConfigPath, "utf-8");
|
|
19450
20327
|
const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
|
|
19451
20328
|
if (missingMcp.length > 0) {
|
|
19452
20329
|
details.push(`${missingMcp.length} MCP server(s) missing from ${slug} config`);
|
|
@@ -19504,7 +20381,8 @@ function getMcpConfigPath2(slug, scope) {
|
|
|
19504
20381
|
case "windsurf":
|
|
19505
20382
|
return scope === "project" ? join40(process.cwd(), ".windsurf", "mcp.json") : join40(home, ".windsurf", "mcp.json");
|
|
19506
20383
|
case "codex":
|
|
19507
|
-
|
|
20384
|
+
case "codex-app":
|
|
20385
|
+
return scope === "user" ? join40(home, ".codex", "config.toml") : null;
|
|
19508
20386
|
case "gemini":
|
|
19509
20387
|
return scope === "user" ? join40(home, ".gemini", "settings.json") : null;
|
|
19510
20388
|
default:
|
|
@@ -19517,6 +20395,7 @@ function getSkillsDir(slug, scope) {
|
|
|
19517
20395
|
case "claude-code":
|
|
19518
20396
|
return scope === "project" ? join40(process.cwd(), ".claude", "skills") : join40(home, ".claude", "skills");
|
|
19519
20397
|
case "codex":
|
|
20398
|
+
case "codex-app":
|
|
19520
20399
|
return scope === "project" ? join40(process.cwd(), ".codex", "skills") : join40(home, ".codex", "skills");
|
|
19521
20400
|
case "gemini":
|
|
19522
20401
|
return scope === "project" ? join40(process.cwd(), ".gemini", "skills") : join40(home, ".gemini", "skills");
|
|
@@ -19543,7 +20422,9 @@ var CHECK_RUNNERS = [
|
|
|
19543
20422
|
{ names: ["app-exists"], run: async (ctx) => [await checkAppExists(ctx)] },
|
|
19544
20423
|
{ names: ["git-remote"], run: async (ctx) => [await checkGitRemote(ctx)] },
|
|
19545
20424
|
{ names: ["deploy-freshness"], run: async (ctx) => [await checkDeployFreshness(ctx)] },
|
|
19546
|
-
{ names: ["agent-setup"], run: async () => [await checkAgentSetup()] }
|
|
20425
|
+
{ names: ["agent-setup"], run: async () => [await checkAgentSetup()] },
|
|
20426
|
+
{ names: ["codex-network"], run: async () => [await checkCodexNetwork()] },
|
|
20427
|
+
{ names: ["codex-desktop-project"], run: async () => [await checkCodexDesktopProject()] }
|
|
19547
20428
|
];
|
|
19548
20429
|
var ALL_CHECK_NAMES = CHECK_RUNNERS.flatMap((r) => r.names);
|
|
19549
20430
|
async function runAllChecks(options) {
|
|
@@ -19616,7 +20497,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
19616
20497
|
}
|
|
19617
20498
|
|
|
19618
20499
|
// src/agents/runtime-detection.ts
|
|
19619
|
-
import { existsSync as existsSync46, readFileSync as
|
|
20500
|
+
import { existsSync as existsSync46, readFileSync as readFileSync39, statSync as statSync7, readdirSync as readdirSync13 } from "fs";
|
|
19620
20501
|
import { homedir as homedir23 } from "os";
|
|
19621
20502
|
import { join as join42 } from "path";
|
|
19622
20503
|
var RUNWORK_SESSIONS_DIR = join42(homedir23(), ".runwork", "sessions");
|
|
@@ -19686,7 +20567,7 @@ function readHookSessionInfo(sessionId) {
|
|
|
19686
20567
|
if (!existsSync46(path2))
|
|
19687
20568
|
return null;
|
|
19688
20569
|
try {
|
|
19689
|
-
const raw =
|
|
20570
|
+
const raw = readFileSync39(path2, "utf8");
|
|
19690
20571
|
const parsed = JSON.parse(raw);
|
|
19691
20572
|
return parsed;
|
|
19692
20573
|
} catch {
|
|
@@ -19727,7 +20608,7 @@ function findCodexRolloutFile(threadId) {
|
|
|
19727
20608
|
const full = join42(dir, entry);
|
|
19728
20609
|
let s;
|
|
19729
20610
|
try {
|
|
19730
|
-
s =
|
|
20611
|
+
s = statSync7(full);
|
|
19731
20612
|
} catch {
|
|
19732
20613
|
continue;
|
|
19733
20614
|
}
|
|
@@ -19764,7 +20645,7 @@ function findNewestClaudeCodeSession() {
|
|
|
19764
20645
|
continue;
|
|
19765
20646
|
const full = join42(projectPath, file);
|
|
19766
20647
|
try {
|
|
19767
|
-
const s =
|
|
20648
|
+
const s = statSync7(full);
|
|
19768
20649
|
if (!best || s.mtimeMs > best.mtime) {
|
|
19769
20650
|
best = {
|
|
19770
20651
|
sessionId: file.replace(/\.jsonl$/, ""),
|
|
@@ -19797,7 +20678,7 @@ function findNewestCodexRollout() {
|
|
|
19797
20678
|
const full = join42(dir, entry);
|
|
19798
20679
|
let s;
|
|
19799
20680
|
try {
|
|
19800
|
-
s =
|
|
20681
|
+
s = statSync7(full);
|
|
19801
20682
|
} catch {
|
|
19802
20683
|
continue;
|
|
19803
20684
|
}
|
|
@@ -19906,7 +20787,9 @@ var CHECK_LABELS = {
|
|
|
19906
20787
|
"app-exists": "App exists",
|
|
19907
20788
|
"git-remote": "Git remote",
|
|
19908
20789
|
"deploy-freshness": "Deploy freshness",
|
|
19909
|
-
"agent-setup": "Agent setup"
|
|
20790
|
+
"agent-setup": "Agent setup",
|
|
20791
|
+
"codex-network": "Codex network",
|
|
20792
|
+
"codex-desktop-project": "Codex project"
|
|
19910
20793
|
};
|
|
19911
20794
|
function printHumanReport(report) {
|
|
19912
20795
|
console.log("");
|
|
@@ -19971,7 +20854,7 @@ function parseCheckNames(raw) {
|
|
|
19971
20854
|
const unknown = requested.filter((n) => !known.has(n));
|
|
19972
20855
|
return { only, unknown };
|
|
19973
20856
|
}
|
|
19974
|
-
var doctorCommand = new
|
|
20857
|
+
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) => {
|
|
19975
20858
|
const asJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19976
20859
|
let only;
|
|
19977
20860
|
if (opts.check) {
|
|
@@ -20022,8 +20905,8 @@ var doctorCommand = new Command29("doctor").description("Check system health: au
|
|
|
20022
20905
|
// src/commands/share-convo.ts
|
|
20023
20906
|
init_store();
|
|
20024
20907
|
init_client();
|
|
20025
|
-
import { Command as
|
|
20026
|
-
import { readFileSync as
|
|
20908
|
+
import { Command as Command32 } from "commander";
|
|
20909
|
+
import { readFileSync as readFileSync40, existsSync as existsSync47 } from "fs";
|
|
20027
20910
|
import { createHash as createHash4 } from "crypto";
|
|
20028
20911
|
function nativeBundleFormatForAgent(slug) {
|
|
20029
20912
|
if (slug === "claude-code" || slug === "claude-desktop")
|
|
@@ -20065,7 +20948,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
20065
20948
|
const credentials = requireAuth();
|
|
20066
20949
|
const client = new ApiClient(credentials);
|
|
20067
20950
|
const { workspaceId } = await resolveWorkspace2(client, { workspace: opts.workspace });
|
|
20068
|
-
const transcriptContent =
|
|
20951
|
+
const transcriptContent = readFileSync40(opts.transcriptFile, "utf8");
|
|
20069
20952
|
const bundles = [
|
|
20070
20953
|
{
|
|
20071
20954
|
format: "transcript",
|
|
@@ -20090,7 +20973,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
20090
20973
|
const nativeFormat = nativeBundleFormatForAgent(sourceAgent);
|
|
20091
20974
|
if (nativeFormat) {
|
|
20092
20975
|
try {
|
|
20093
|
-
const content =
|
|
20976
|
+
const content = readFileSync40(nativeFilePath, "utf8");
|
|
20094
20977
|
bundles.push({
|
|
20095
20978
|
format: nativeFormat,
|
|
20096
20979
|
content,
|
|
@@ -20106,7 +20989,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
20106
20989
|
let metadata = {};
|
|
20107
20990
|
if (opts.metadataFile) {
|
|
20108
20991
|
try {
|
|
20109
|
-
metadata = JSON.parse(
|
|
20992
|
+
metadata = JSON.parse(readFileSync40(opts.metadataFile, "utf8"));
|
|
20110
20993
|
} catch (err) {
|
|
20111
20994
|
console.error(`Error: --metadata-file is not valid JSON: ${err instanceof Error ? err.message : err}`);
|
|
20112
20995
|
process.exit(1);
|
|
@@ -20161,17 +21044,17 @@ Skipped: ${result.skipped.map((s) => `${s.identifier} (${s.reason})`).join(", ")
|
|
|
20161
21044
|
process.exit(1);
|
|
20162
21045
|
}
|
|
20163
21046
|
}
|
|
20164
|
-
var shareConvoCommand = new
|
|
21047
|
+
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));
|
|
20165
21048
|
|
|
20166
21049
|
// src/commands/save-convo.ts
|
|
20167
|
-
import { Command as
|
|
20168
|
-
var saveConvoCommand = new
|
|
21050
|
+
import { Command as Command33 } from "commander";
|
|
21051
|
+
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));
|
|
20169
21052
|
|
|
20170
21053
|
// src/commands/inbox.ts
|
|
20171
21054
|
init_store();
|
|
20172
21055
|
init_client();
|
|
20173
|
-
import { Command as
|
|
20174
|
-
var inboxCommand = new
|
|
21056
|
+
import { Command as Command34 } from "commander";
|
|
21057
|
+
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) => {
|
|
20175
21058
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
20176
21059
|
const scope = opts.filter === "received" || opts.filter === "sent" || opts.filter === "saved" ? opts.filter : "all";
|
|
20177
21060
|
const limit = opts.limit ? parseInt(opts.limit, 10) : 50;
|
|
@@ -20211,7 +21094,7 @@ Shared conversations (${scope}, ${total}):
|
|
|
20211
21094
|
// src/commands/resume.ts
|
|
20212
21095
|
init_store();
|
|
20213
21096
|
init_client();
|
|
20214
|
-
import { Command as
|
|
21097
|
+
import { Command as Command35 } from "commander";
|
|
20215
21098
|
import { writeFileSync as writeFileSync31, mkdirSync as mkdirSync28, realpathSync } from "fs";
|
|
20216
21099
|
import { homedir as homedir24 } from "os";
|
|
20217
21100
|
import { join as join43 } from "path";
|
|
@@ -20285,7 +21168,7 @@ function isAgentInstalled(agent) {
|
|
|
20285
21168
|
}
|
|
20286
21169
|
return false;
|
|
20287
21170
|
}
|
|
20288
|
-
var resumeCommand2 = new
|
|
21171
|
+
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) => {
|
|
20289
21172
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
20290
21173
|
const credentials = requireAuth();
|
|
20291
21174
|
const client = new ApiClient(credentials);
|
|
@@ -20510,7 +21393,7 @@ process.on("uncaughtException", (err) => {
|
|
|
20510
21393
|
console.error(`Uncaught exception: ${formatError(err)}`);
|
|
20511
21394
|
process.exit(1);
|
|
20512
21395
|
});
|
|
20513
|
-
var program = new
|
|
21396
|
+
var program = new Command36;
|
|
20514
21397
|
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)");
|
|
20515
21398
|
program.addCommand(infoCommand);
|
|
20516
21399
|
program.addCommand(loginCommand);
|
|
@@ -20539,6 +21422,8 @@ program.addCommand(syncCommand);
|
|
|
20539
21422
|
program.addCommand(buildPluginCommand);
|
|
20540
21423
|
program.addCommand(uninstallCommand);
|
|
20541
21424
|
program.addCommand(appsCommand);
|
|
21425
|
+
program.addCommand(membersCommand);
|
|
21426
|
+
program.addCommand(apiCommand);
|
|
20542
21427
|
program.addCommand(doctorCommand);
|
|
20543
21428
|
program.addCommand(shareConvoCommand);
|
|
20544
21429
|
program.addCommand(saveConvoCommand);
|