runwork 0.18.3 → 0.19.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/dist/index.js +427 -121
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -637,6 +637,35 @@ class ApiClient {
|
|
|
637
637
|
const res = await this.request(`/api/workspaces/${workspaceId}/entities/${encodeURIComponent(entityName)}/${encodeURIComponent(recordId)}`);
|
|
638
638
|
return res.data;
|
|
639
639
|
}
|
|
640
|
+
async createAppExport(workspaceId, appId, body) {
|
|
641
|
+
const res = await this.request(`/api/workspaces/${workspaceId}/apps/${appId}/exports`, { method: "POST", body: JSON.stringify(body) });
|
|
642
|
+
return res.data.job;
|
|
643
|
+
}
|
|
644
|
+
async getExportJob(workspaceId, exportId) {
|
|
645
|
+
const res = await this.request(`/api/workspaces/${workspaceId}/exports/${exportId}`);
|
|
646
|
+
return res.data.job;
|
|
647
|
+
}
|
|
648
|
+
async getExportManifest(workspaceId, exportId) {
|
|
649
|
+
const url = `${this.baseUrl}/api/workspaces/${workspaceId}/exports/${exportId}/manifest`;
|
|
650
|
+
const response = await httpFetch(url, {
|
|
651
|
+
headers: this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
652
|
+
});
|
|
653
|
+
if (!response.ok) {
|
|
654
|
+
throw new Error(`Failed to fetch export manifest: ${response.status} ${await response.text()}`);
|
|
655
|
+
}
|
|
656
|
+
return response.json();
|
|
657
|
+
}
|
|
658
|
+
async downloadExportChunk(workspaceId, exportId, file, chunkIndex) {
|
|
659
|
+
const query = `?file=${encodeURIComponent(file)}&fromChunk=${chunkIndex}&maxChunks=1`;
|
|
660
|
+
const url = `${this.baseUrl}/api/workspaces/${workspaceId}/exports/${exportId}/download${query}`;
|
|
661
|
+
const response = await httpFetch(url, {
|
|
662
|
+
headers: this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
663
|
+
});
|
|
664
|
+
if (!response.ok) {
|
|
665
|
+
throw new Error(`Failed to download export chunk ${chunkIndex}: ${response.status}`);
|
|
666
|
+
}
|
|
667
|
+
return response.arrayBuffer();
|
|
668
|
+
}
|
|
640
669
|
async createEntityRecord(workspaceId, entityName, data) {
|
|
641
670
|
const res = await this.request(`/api/workspaces/${workspaceId}/entities/${encodeURIComponent(entityName)}`, { method: "POST", body: JSON.stringify({ data }) });
|
|
642
671
|
return res.data;
|
|
@@ -7583,7 +7612,7 @@ function createKeyboardListener() {
|
|
|
7583
7612
|
}
|
|
7584
7613
|
|
|
7585
7614
|
// src/generated/version.ts
|
|
7586
|
-
var VERSION = "0.
|
|
7615
|
+
var VERSION = "0.19.0";
|
|
7587
7616
|
|
|
7588
7617
|
// src/commands/dev.ts
|
|
7589
7618
|
var exports_dev = {};
|
|
@@ -13747,7 +13776,7 @@ var AGENT_REGISTRY = [
|
|
|
13747
13776
|
{ method: "windows-start-app", target: "Claude" }
|
|
13748
13777
|
]
|
|
13749
13778
|
},
|
|
13750
|
-
launch: { app: { macos: "Claude", windows: "Claude" } },
|
|
13779
|
+
launch: { app: { macos: "Claude", windows: "Claude" }, deepLink: "claude://claude.ai/new?q={prompt}" },
|
|
13751
13780
|
logo: "claude",
|
|
13752
13781
|
downloadUrl: "https://claude.ai/download",
|
|
13753
13782
|
skillsPaths: { global: ".claude/skills", project: ".claude/skills" },
|
|
@@ -13888,7 +13917,7 @@ var AGENT_REGISTRY = [
|
|
|
13888
13917
|
{ method: "windows-start-app", target: "ChatGPT Work" }
|
|
13889
13918
|
]
|
|
13890
13919
|
},
|
|
13891
|
-
launch: { app: { macos: "ChatGPT Work", windows: "ChatGPT Work" }, bundleId: { macos: "com.openai.codex" }, appxPackage: "OpenAI.Codex" },
|
|
13920
|
+
launch: { app: { macos: "ChatGPT Work", windows: "ChatGPT Work" }, bundleId: { macos: "com.openai.codex" }, appxPackage: "OpenAI.Codex", deepLink: "codex://new?prompt={prompt}" },
|
|
13892
13921
|
logo: "openai",
|
|
13893
13922
|
downloadUrl: "https://chatgpt.com/download/",
|
|
13894
13923
|
skillsPaths: { global: ".agents/skills", project: ".agents/skills" },
|
|
@@ -15983,6 +16012,7 @@ ${all.length} insight(s) from your recent work:
|
|
|
15983
16012
|
init_store();
|
|
15984
16013
|
init_client();
|
|
15985
16014
|
import { Command as Command15 } from "commander";
|
|
16015
|
+
import * as path3 from "node:path";
|
|
15986
16016
|
init_prompt();
|
|
15987
16017
|
|
|
15988
16018
|
// src/utils/data-input.ts
|
|
@@ -16024,6 +16054,122 @@ function parseJson(content, source) {
|
|
|
16024
16054
|
}
|
|
16025
16055
|
}
|
|
16026
16056
|
|
|
16057
|
+
// src/export/download-job.ts
|
|
16058
|
+
import * as fs6 from "node:fs";
|
|
16059
|
+
import * as path2 from "node:path";
|
|
16060
|
+
|
|
16061
|
+
// src/export/download.ts
|
|
16062
|
+
function planFileResume(localSizeBytes, chunks) {
|
|
16063
|
+
let boundary = 0;
|
|
16064
|
+
let startChunk = 0;
|
|
16065
|
+
for (const chunk of chunks) {
|
|
16066
|
+
if (boundary + chunk.sizeBytes > localSizeBytes)
|
|
16067
|
+
break;
|
|
16068
|
+
boundary += chunk.sizeBytes;
|
|
16069
|
+
startChunk += 1;
|
|
16070
|
+
}
|
|
16071
|
+
return { startChunk, truncateTo: boundary };
|
|
16072
|
+
}
|
|
16073
|
+
async function downloadExportFile(options) {
|
|
16074
|
+
const retries = options.retriesPerChunk ?? 3;
|
|
16075
|
+
const { startChunk, truncateTo } = planFileResume(options.localSizeBytes, options.chunks);
|
|
16076
|
+
if (truncateTo !== options.localSizeBytes) {
|
|
16077
|
+
await options.truncate(truncateTo);
|
|
16078
|
+
}
|
|
16079
|
+
let downloaded = 0;
|
|
16080
|
+
for (let index = startChunk;index < options.chunks.length; index++) {
|
|
16081
|
+
const expected = options.chunks[index].sizeBytes;
|
|
16082
|
+
let lastError;
|
|
16083
|
+
let bytes = null;
|
|
16084
|
+
for (let attempt = 0;attempt < retries; attempt++) {
|
|
16085
|
+
try {
|
|
16086
|
+
const fetched = await options.fetchChunk(index);
|
|
16087
|
+
if (fetched.byteLength !== expected) {
|
|
16088
|
+
throw new Error(`Chunk ${index} size mismatch: expected ${expected} bytes, got ${fetched.byteLength}`);
|
|
16089
|
+
}
|
|
16090
|
+
bytes = fetched;
|
|
16091
|
+
break;
|
|
16092
|
+
} catch (error) {
|
|
16093
|
+
lastError = error;
|
|
16094
|
+
}
|
|
16095
|
+
}
|
|
16096
|
+
if (!bytes) {
|
|
16097
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
16098
|
+
}
|
|
16099
|
+
await options.append(bytes);
|
|
16100
|
+
downloaded += 1;
|
|
16101
|
+
options.onProgress?.(startChunk + downloaded, options.chunks.length);
|
|
16102
|
+
}
|
|
16103
|
+
return {
|
|
16104
|
+
downloadedChunks: downloaded,
|
|
16105
|
+
skippedChunks: startChunk,
|
|
16106
|
+
totalBytes: options.chunks.reduce((sum, chunk) => sum + chunk.sizeBytes, 0)
|
|
16107
|
+
};
|
|
16108
|
+
}
|
|
16109
|
+
|
|
16110
|
+
// src/export/download-job.ts
|
|
16111
|
+
async function downloadCompletedJob(client, workspaceId, jobId, outputDir, onProgress) {
|
|
16112
|
+
const manifest = await client.getExportManifest(workspaceId, jobId);
|
|
16113
|
+
const written = [];
|
|
16114
|
+
for (const file of manifest.files) {
|
|
16115
|
+
const localPath = path2.join(outputDir, ...file.name.split("/"));
|
|
16116
|
+
fs6.mkdirSync(path2.dirname(localPath), { recursive: true });
|
|
16117
|
+
const localSizeBytes = fs6.existsSync(localPath) ? fs6.statSync(localPath).size : 0;
|
|
16118
|
+
await downloadExportFile({
|
|
16119
|
+
chunks: file.chunks,
|
|
16120
|
+
localSizeBytes,
|
|
16121
|
+
fetchChunk: (chunkIndex) => client.downloadExportChunk(workspaceId, jobId, file.name, chunkIndex),
|
|
16122
|
+
truncate: async (sizeBytes) => {
|
|
16123
|
+
fs6.truncateSync(localPath, sizeBytes);
|
|
16124
|
+
},
|
|
16125
|
+
append: async (bytes) => {
|
|
16126
|
+
fs6.appendFileSync(localPath, Buffer.from(bytes));
|
|
16127
|
+
},
|
|
16128
|
+
onProgress: (done, total) => onProgress?.(file.name, done, total)
|
|
16129
|
+
});
|
|
16130
|
+
written.push(localPath);
|
|
16131
|
+
}
|
|
16132
|
+
fs6.mkdirSync(outputDir, { recursive: true });
|
|
16133
|
+
const manifestPath = path2.join(outputDir, "manifest.json");
|
|
16134
|
+
fs6.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
16135
|
+
written.push(manifestPath);
|
|
16136
|
+
return { outputDir, files: written };
|
|
16137
|
+
}
|
|
16138
|
+
|
|
16139
|
+
// src/export/targets.ts
|
|
16140
|
+
function resolveExportTargets(entities, filter) {
|
|
16141
|
+
let candidates = entities.filter((e) => e.deploymentMode !== "preview");
|
|
16142
|
+
if (filter.entityName) {
|
|
16143
|
+
candidates = candidates.filter((e) => e.entityName === filter.entityName);
|
|
16144
|
+
if (candidates.length === 0) {
|
|
16145
|
+
return { targets: [], error: `Entity "${filter.entityName}" is not registered in this workspace.` };
|
|
16146
|
+
}
|
|
16147
|
+
}
|
|
16148
|
+
if (filter.appId) {
|
|
16149
|
+
candidates = candidates.filter((e) => e.appId === filter.appId);
|
|
16150
|
+
}
|
|
16151
|
+
const byApp = new Map;
|
|
16152
|
+
for (const entity of candidates) {
|
|
16153
|
+
byApp.set(entity.appId, { appId: entity.appId, appName: entity.appName });
|
|
16154
|
+
}
|
|
16155
|
+
const targets = Array.from(byApp.values());
|
|
16156
|
+
if (filter.entityName && !filter.appId && targets.length > 1) {
|
|
16157
|
+
const names = targets.map((t) => t.appName).join(", ");
|
|
16158
|
+
return {
|
|
16159
|
+
targets: [],
|
|
16160
|
+
error: `Entity "${filter.entityName}" exists in multiple apps (${names}). Pass --app to pick one.`
|
|
16161
|
+
};
|
|
16162
|
+
}
|
|
16163
|
+
if (targets.length === 0) {
|
|
16164
|
+
return { targets: [], error: "No production entities found to export." };
|
|
16165
|
+
}
|
|
16166
|
+
return { targets };
|
|
16167
|
+
}
|
|
16168
|
+
function appSlugForPath(appName, appId) {
|
|
16169
|
+
const slug = appName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16170
|
+
return slug || appId;
|
|
16171
|
+
}
|
|
16172
|
+
|
|
16027
16173
|
// src/commands/entities.ts
|
|
16028
16174
|
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) => {
|
|
16029
16175
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
@@ -16171,7 +16317,167 @@ var deleteCommand = new Command15("delete").description("Delete an entity record
|
|
|
16171
16317
|
process.exit(1);
|
|
16172
16318
|
}
|
|
16173
16319
|
});
|
|
16174
|
-
var
|
|
16320
|
+
var POLL_INTERVAL_MS = 2000;
|
|
16321
|
+
var POLL_TIMEOUT_MS = 60 * 60 * 1000;
|
|
16322
|
+
var ACTIVE_EXPORT_STATUSES = new Set(["pending", "running"]);
|
|
16323
|
+
var sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
16324
|
+
function exportProgressLine(job) {
|
|
16325
|
+
const done = job.entities.reduce((sum, e) => sum + e.exportedRecords, 0);
|
|
16326
|
+
const total = job.entities.reduce((sum, e) => e.totalRecords === null ? sum : sum + e.totalRecords, 0);
|
|
16327
|
+
return total > 0 ? `exporting ${done}/${total} records` : "preparing export";
|
|
16328
|
+
}
|
|
16329
|
+
async function waitForExportJob(client, workspaceId, jobId, onProgress) {
|
|
16330
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
16331
|
+
while (true) {
|
|
16332
|
+
const job = await client.getExportJob(workspaceId, jobId);
|
|
16333
|
+
if (!ACTIVE_EXPORT_STATUSES.has(job.status))
|
|
16334
|
+
return job;
|
|
16335
|
+
onProgress(exportProgressLine(job));
|
|
16336
|
+
if (Date.now() > deadline) {
|
|
16337
|
+
throw new Error(`Export ${jobId} did not finish within ${POLL_TIMEOUT_MS / 60000} minutes`);
|
|
16338
|
+
}
|
|
16339
|
+
await sleep(POLL_INTERVAL_MS);
|
|
16340
|
+
}
|
|
16341
|
+
}
|
|
16342
|
+
async function exportOneApp(client, workspaceId, target, options) {
|
|
16343
|
+
const log = (message) => {
|
|
16344
|
+
if (!options.quiet)
|
|
16345
|
+
console.log(message);
|
|
16346
|
+
};
|
|
16347
|
+
const job = await client.createAppExport(workspaceId, target.appId, {
|
|
16348
|
+
format: options.format,
|
|
16349
|
+
entityName: options.entityName
|
|
16350
|
+
});
|
|
16351
|
+
log(`${target.appName}: export job ${job.id} (${job.status})`);
|
|
16352
|
+
if (!options.wait) {
|
|
16353
|
+
return {
|
|
16354
|
+
appId: target.appId,
|
|
16355
|
+
appName: target.appName,
|
|
16356
|
+
jobId: job.id,
|
|
16357
|
+
status: job.status,
|
|
16358
|
+
records: job.recordCount,
|
|
16359
|
+
sizeBytes: job.sizeBytes,
|
|
16360
|
+
outputDir: null,
|
|
16361
|
+
files: [],
|
|
16362
|
+
error: job.error
|
|
16363
|
+
};
|
|
16364
|
+
}
|
|
16365
|
+
const finished = await waitForExportJob(client, workspaceId, job.id, (line) => {
|
|
16366
|
+
if (!options.quiet)
|
|
16367
|
+
process.stdout.write(`\r${target.appName}: ${line} `);
|
|
16368
|
+
});
|
|
16369
|
+
if (!options.quiet)
|
|
16370
|
+
process.stdout.write("\r");
|
|
16371
|
+
if (finished.status !== "completed") {
|
|
16372
|
+
log(`${target.appName}: export ${finished.status}${finished.error ? `: ${finished.error}` : ""}`);
|
|
16373
|
+
return {
|
|
16374
|
+
appId: target.appId,
|
|
16375
|
+
appName: target.appName,
|
|
16376
|
+
jobId: job.id,
|
|
16377
|
+
status: finished.status,
|
|
16378
|
+
records: finished.recordCount,
|
|
16379
|
+
sizeBytes: finished.sizeBytes,
|
|
16380
|
+
outputDir: null,
|
|
16381
|
+
files: [],
|
|
16382
|
+
error: finished.error
|
|
16383
|
+
};
|
|
16384
|
+
}
|
|
16385
|
+
const outputDir = path3.join(options.outputRoot, appSlugForPath(target.appName, target.appId));
|
|
16386
|
+
const { files: writtenFiles } = await downloadCompletedJob(client, workspaceId, job.id, outputDir, (fileName, done, total) => {
|
|
16387
|
+
if (!options.quiet)
|
|
16388
|
+
process.stdout.write(`\r${target.appName}: downloading ${fileName} (${done}/${total} chunks) `);
|
|
16389
|
+
});
|
|
16390
|
+
if (!options.quiet)
|
|
16391
|
+
process.stdout.write("\r");
|
|
16392
|
+
log(`${target.appName}: ${finished.recordCount} record(s) exported to ${outputDir}`);
|
|
16393
|
+
return {
|
|
16394
|
+
appId: target.appId,
|
|
16395
|
+
appName: target.appName,
|
|
16396
|
+
jobId: job.id,
|
|
16397
|
+
status: finished.status,
|
|
16398
|
+
records: finished.recordCount,
|
|
16399
|
+
sizeBytes: finished.sizeBytes,
|
|
16400
|
+
outputDir,
|
|
16401
|
+
files: writtenFiles,
|
|
16402
|
+
error: null
|
|
16403
|
+
};
|
|
16404
|
+
}
|
|
16405
|
+
var exportCommand = new Command15("export").description("Export entity data to local files (free on every plan; production data only)").argument("[entity]", "Restrict the export to a single entity").option("--workspace <name-or-id>", "Workspace name or ID").option("--app <name-or-id>", "App to export; omit to export every app that has entities").option("--format <format>", "Export format: json (newline-delimited JSON) or csv", "json").option("--output <dir>", "Output directory", "runwork-export").option("--no-wait", "Create the export job(s) and exit without downloading").option("--job <exportId>", "Download an existing completed export job instead of starting a new one").action(async (entity, opts, command) => {
|
|
16406
|
+
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16407
|
+
const credentials = requireAuth();
|
|
16408
|
+
const client = new ApiClient(credentials);
|
|
16409
|
+
const { workspaceId } = await resolveWorkspace2(client, opts);
|
|
16410
|
+
if (opts.job) {
|
|
16411
|
+
try {
|
|
16412
|
+
const job = await client.getExportJob(workspaceId, opts.job);
|
|
16413
|
+
if (job.status !== "completed") {
|
|
16414
|
+
console.error(`Export ${opts.job} is not downloadable (status: ${job.status}${job.error ? `, error: ${job.error}` : ""}).`);
|
|
16415
|
+
process.exit(1);
|
|
16416
|
+
}
|
|
16417
|
+
const apps = await client.listApps(workspaceId);
|
|
16418
|
+
const appName = apps.find((a) => a.id === job.appId)?.name ?? job.appId;
|
|
16419
|
+
const outputDir = path3.join(opts.output, appSlugForPath(appName, job.appId));
|
|
16420
|
+
const result = await downloadCompletedJob(client, workspaceId, job.id, outputDir, (fileName, done, total) => {
|
|
16421
|
+
if (!useJson)
|
|
16422
|
+
process.stdout.write(`\rdownloading ${fileName} (${done}/${total} chunks) `);
|
|
16423
|
+
});
|
|
16424
|
+
if (!useJson)
|
|
16425
|
+
process.stdout.write("\r");
|
|
16426
|
+
if (useJson) {
|
|
16427
|
+
jsonOut({ export: { jobId: job.id, appId: job.appId, appName, status: job.status, records: job.recordCount, sizeBytes: job.sizeBytes, outputDir: result.outputDir, files: result.files } });
|
|
16428
|
+
} else {
|
|
16429
|
+
console.log(`${appName}: ${job.recordCount} record(s) saved to ${result.outputDir}`);
|
|
16430
|
+
}
|
|
16431
|
+
} catch (err) {
|
|
16432
|
+
console.error("Export download failed:", err instanceof Error ? err.message : err);
|
|
16433
|
+
process.exit(1);
|
|
16434
|
+
}
|
|
16435
|
+
return;
|
|
16436
|
+
}
|
|
16437
|
+
const formatInput = String(opts.format).toLowerCase();
|
|
16438
|
+
if (formatInput !== "json" && formatInput !== "ndjson" && formatInput !== "csv") {
|
|
16439
|
+
console.error(`Unknown format "${opts.format}". Use json or csv.`);
|
|
16440
|
+
process.exit(1);
|
|
16441
|
+
}
|
|
16442
|
+
const format = formatInput === "csv" ? "csv" : "ndjson";
|
|
16443
|
+
try {
|
|
16444
|
+
let appId;
|
|
16445
|
+
if (opts.app) {
|
|
16446
|
+
const resolved = await resolveApp2(client, workspaceId, { app: opts.app });
|
|
16447
|
+
appId = resolved.appId;
|
|
16448
|
+
}
|
|
16449
|
+
const entities = await client.listEntities(workspaceId);
|
|
16450
|
+
const { targets, error } = resolveExportTargets(entities, { appId, entityName: entity });
|
|
16451
|
+
if (error) {
|
|
16452
|
+
console.error(error);
|
|
16453
|
+
process.exit(1);
|
|
16454
|
+
}
|
|
16455
|
+
const results = [];
|
|
16456
|
+
for (const target of targets) {
|
|
16457
|
+
results.push(await exportOneApp(client, workspaceId, target, {
|
|
16458
|
+
format,
|
|
16459
|
+
entityName: entity,
|
|
16460
|
+
wait: opts.wait !== false,
|
|
16461
|
+
outputRoot: opts.output,
|
|
16462
|
+
quiet: useJson
|
|
16463
|
+
}));
|
|
16464
|
+
}
|
|
16465
|
+
if (useJson) {
|
|
16466
|
+
jsonOut({ exports: results });
|
|
16467
|
+
return;
|
|
16468
|
+
}
|
|
16469
|
+
const failed = results.filter((r) => r.status === "failed");
|
|
16470
|
+
if (failed.length > 0) {
|
|
16471
|
+
console.error(`
|
|
16472
|
+
${failed.length} export(s) failed.`);
|
|
16473
|
+
process.exit(1);
|
|
16474
|
+
}
|
|
16475
|
+
} catch (err) {
|
|
16476
|
+
console.error("Export failed:", err instanceof Error ? err.message : err);
|
|
16477
|
+
process.exit(1);
|
|
16478
|
+
}
|
|
16479
|
+
});
|
|
16480
|
+
var entitiesCommand = new Command15("entities").alias("data").description("Manage workspace entity data").addCommand(listCommand3).addCommand(recordsCommand).addCommand(getCommand).addCommand(createCommand).addCommand(updateCommand).addCommand(deleteCommand).addCommand(exportCommand);
|
|
16175
16481
|
|
|
16176
16482
|
// src/commands/workflows.ts
|
|
16177
16483
|
init_store();
|
|
@@ -16575,12 +16881,12 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16575
16881
|
console.log(" " + "-".repeat(90));
|
|
16576
16882
|
for (const e of endpoints) {
|
|
16577
16883
|
const method = e.method.toUpperCase().padEnd(8);
|
|
16578
|
-
const
|
|
16884
|
+
const path4 = e.endpointPath.padEnd(36);
|
|
16579
16885
|
const app = (e.appName || e.appId).padEnd(20);
|
|
16580
16886
|
const auth = (e.auth || "").padEnd(10);
|
|
16581
16887
|
const desc = truncate3(e.description, 40);
|
|
16582
16888
|
const mode = e.deploymentMode ? ` [${e.deploymentMode}]` : "";
|
|
16583
|
-
console.log(` ${method} ${
|
|
16889
|
+
console.log(` ${method} ${path4} ${app} ${auth} ${desc}${mode}`);
|
|
16584
16890
|
}
|
|
16585
16891
|
console.log("");
|
|
16586
16892
|
} catch (err) {
|
|
@@ -16588,7 +16894,7 @@ Workspace: ${workspaceName || workspaceId}
|
|
|
16588
16894
|
process.exit(1);
|
|
16589
16895
|
}
|
|
16590
16896
|
});
|
|
16591
|
-
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,
|
|
16897
|
+
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, path4, opts, command) => {
|
|
16592
16898
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
16593
16899
|
const credentials = requireAuth();
|
|
16594
16900
|
const client = new ApiClient(credentials);
|
|
@@ -16599,9 +16905,9 @@ var callCommand2 = new Command18("call").description("Call a workspace endpoint
|
|
|
16599
16905
|
endpoints = endpoints.filter((e) => e.appId === opts.app || e.appName === opts.app);
|
|
16600
16906
|
}
|
|
16601
16907
|
const targetMethod = method.toUpperCase();
|
|
16602
|
-
const endpoint = endpoints.find((e) => e.method.toUpperCase() === targetMethod && e.endpointPath ===
|
|
16908
|
+
const endpoint = endpoints.find((e) => e.method.toUpperCase() === targetMethod && e.endpointPath === path4);
|
|
16603
16909
|
if (!endpoint) {
|
|
16604
|
-
console.error(`Endpoint not found: ${targetMethod} ${
|
|
16910
|
+
console.error(`Endpoint not found: ${targetMethod} ${path4}`);
|
|
16605
16911
|
if (endpoints.length > 0) {
|
|
16606
16912
|
console.error(`
|
|
16607
16913
|
Available endpoints:`);
|
|
@@ -16620,7 +16926,7 @@ Available endpoints:`);
|
|
|
16620
16926
|
process.exit(1);
|
|
16621
16927
|
}
|
|
16622
16928
|
const baseUrl = `https://${workspaceName}.runwork.ai`;
|
|
16623
|
-
let url = `${baseUrl}/${appSlug}/endpoints${
|
|
16929
|
+
let url = `${baseUrl}/${appSlug}/endpoints${path4}`;
|
|
16624
16930
|
if (opts.query) {
|
|
16625
16931
|
url = `${url}?${opts.query}`;
|
|
16626
16932
|
}
|
|
@@ -16704,7 +17010,7 @@ var endpointsCommand = new Command18("endpoints").alias("routes").description("M
|
|
|
16704
17010
|
init_store();
|
|
16705
17011
|
init_client();
|
|
16706
17012
|
import { Command as Command19 } from "commander";
|
|
16707
|
-
import { writeFileSync as
|
|
17013
|
+
import { writeFileSync as writeFileSync29, readFileSync as readFileSync32 } from "fs";
|
|
16708
17014
|
import { basename as basename2 } from "path";
|
|
16709
17015
|
init_prompt();
|
|
16710
17016
|
init_http();
|
|
@@ -16798,7 +17104,7 @@ var downloadCommand = new Command19("download").description("Download a file fro
|
|
|
16798
17104
|
if (!response.ok) {
|
|
16799
17105
|
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
16800
17106
|
}
|
|
16801
|
-
|
|
17107
|
+
writeFileSync29(outputPath, Buffer.from(await response.arrayBuffer()));
|
|
16802
17108
|
if (useJson) {
|
|
16803
17109
|
jsonOut({ success: true, bucket, key, outputPath });
|
|
16804
17110
|
return;
|
|
@@ -17315,8 +17621,8 @@ var mcpCommand = new Command23("mcp").description("Manage workspace MCP servers"
|
|
|
17315
17621
|
init_store();
|
|
17316
17622
|
init_client();
|
|
17317
17623
|
import { Command as Command25 } from "commander";
|
|
17318
|
-
import { writeFileSync as
|
|
17319
|
-
import { join as
|
|
17624
|
+
import { writeFileSync as writeFileSync31, mkdirSync as mkdirSync28 } from "fs";
|
|
17625
|
+
import { join as join39 } from "path";
|
|
17320
17626
|
import { homedir as homedir19 } from "os";
|
|
17321
17627
|
init_prompt();
|
|
17322
17628
|
|
|
@@ -17324,8 +17630,8 @@ init_prompt();
|
|
|
17324
17630
|
init_store();
|
|
17325
17631
|
init_client();
|
|
17326
17632
|
import { Command as Command24 } from "commander";
|
|
17327
|
-
import { readFileSync as readFileSync33, writeFileSync as
|
|
17328
|
-
import { join as
|
|
17633
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync30, existsSync as existsSync42 } from "fs";
|
|
17634
|
+
import { join as join37 } from "path";
|
|
17329
17635
|
import { homedir as homedir17 } from "os";
|
|
17330
17636
|
|
|
17331
17637
|
// src/commands/mcp-entries.ts
|
|
@@ -18584,7 +18890,7 @@ Tip: ${hint.title}`);
|
|
|
18584
18890
|
} catch {}
|
|
18585
18891
|
}
|
|
18586
18892
|
function loadSetupState(filePath) {
|
|
18587
|
-
if (!
|
|
18893
|
+
if (!existsSync42(filePath))
|
|
18588
18894
|
return null;
|
|
18589
18895
|
try {
|
|
18590
18896
|
return JSON.parse(readFileSync33(filePath, "utf-8"));
|
|
@@ -18608,14 +18914,14 @@ function readLocalSkills(state) {
|
|
|
18608
18914
|
if (!baseDir)
|
|
18609
18915
|
continue;
|
|
18610
18916
|
for (const skillName of state.skills) {
|
|
18611
|
-
const skillMdPath =
|
|
18612
|
-
if (
|
|
18917
|
+
const skillMdPath = join37(baseDir, skillName, "SKILL.md");
|
|
18918
|
+
if (existsSync42(skillMdPath)) {
|
|
18613
18919
|
results.push({ name: skillName, content: readFileSync33(skillMdPath, "utf-8") });
|
|
18614
18920
|
continue;
|
|
18615
18921
|
}
|
|
18616
18922
|
const filename = skillName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
18617
|
-
const flatPath =
|
|
18618
|
-
if (
|
|
18923
|
+
const flatPath = join37(baseDir, `${filename}.md`);
|
|
18924
|
+
if (existsSync42(flatPath)) {
|
|
18619
18925
|
results.push({ name: skillName, content: readFileSync33(flatPath, "utf-8") });
|
|
18620
18926
|
}
|
|
18621
18927
|
}
|
|
@@ -18820,7 +19126,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
18820
19126
|
persona: state.persona
|
|
18821
19127
|
});
|
|
18822
19128
|
let projectAppSkillFilter = null;
|
|
18823
|
-
if (
|
|
19129
|
+
if (existsSync42(".runwork.json")) {
|
|
18824
19130
|
try {
|
|
18825
19131
|
const config = JSON.parse(readFileSync33(".runwork.json", "utf-8"));
|
|
18826
19132
|
if (config.appName) {
|
|
@@ -19059,7 +19365,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
19059
19365
|
}
|
|
19060
19366
|
for (const adapter2 of adapters) {
|
|
19061
19367
|
if (adapter2 instanceof CodexAdapter) {
|
|
19062
|
-
const runworkDir =
|
|
19368
|
+
const runworkDir = join37(homedir17(), ".runwork");
|
|
19063
19369
|
const result = adapter2.registerDesktopWorkspace(runworkDir, "Runwork");
|
|
19064
19370
|
if (result === "written") {
|
|
19065
19371
|
vlog(` [${adapter2.name}] Registered workspace in Codex desktop app`);
|
|
@@ -19134,7 +19440,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
19134
19440
|
delete mergedHashes[del.name];
|
|
19135
19441
|
}
|
|
19136
19442
|
state.skillHashes = mergedHashes;
|
|
19137
|
-
|
|
19443
|
+
writeFileSync30(statePath2, JSON.stringify(state, null, 2));
|
|
19138
19444
|
try {
|
|
19139
19445
|
const telemetryNow = new Date().toISOString();
|
|
19140
19446
|
const telemetry = await collectTelemetryEvents({
|
|
@@ -19154,7 +19460,7 @@ async function syncFromState(state, statePath2, credentials, opts) {
|
|
|
19154
19460
|
if (telemetry.healthReported) {
|
|
19155
19461
|
state.lastHealthReportAt = new Date().toISOString();
|
|
19156
19462
|
}
|
|
19157
|
-
|
|
19463
|
+
writeFileSync30(statePath2, JSON.stringify(state, null, 2));
|
|
19158
19464
|
} catch {}
|
|
19159
19465
|
const failedNote = summary.adaptersFailed > 0 ? ` (${summary.adaptersFailed} failed)` : "";
|
|
19160
19466
|
if (isVerbose()) {
|
|
@@ -19192,8 +19498,8 @@ var syncCommand = new Command24("sync").description("Sync skills bidirectionally
|
|
|
19192
19498
|
verbose: !!opts.verbose,
|
|
19193
19499
|
redetect: !!opts.redetect
|
|
19194
19500
|
};
|
|
19195
|
-
const projectStatePath =
|
|
19196
|
-
const userStatePath =
|
|
19501
|
+
const projectStatePath = join37(process.cwd(), ".runwork", "setup.json");
|
|
19502
|
+
const userStatePath = join37(homedir17(), ".runwork", "setup.json");
|
|
19197
19503
|
const projectState = loadSetupState(projectStatePath);
|
|
19198
19504
|
const userState = loadSetupState(userStatePath);
|
|
19199
19505
|
if (!projectState && !userState) {
|
|
@@ -19214,14 +19520,14 @@ Sync complete.`);
|
|
|
19214
19520
|
});
|
|
19215
19521
|
|
|
19216
19522
|
// src/utils/setup-state.ts
|
|
19217
|
-
import { existsSync as
|
|
19218
|
-
import { join as
|
|
19523
|
+
import { existsSync as existsSync43, readFileSync as readFileSync34 } from "fs";
|
|
19524
|
+
import { join as join38 } from "path";
|
|
19219
19525
|
import { homedir as homedir18 } from "os";
|
|
19220
19526
|
function loadSetupState2() {
|
|
19221
|
-
const projectPath =
|
|
19222
|
-
const userPath =
|
|
19527
|
+
const projectPath = join38(process.cwd(), ".runwork", "setup.json");
|
|
19528
|
+
const userPath = join38(homedir18(), ".runwork", "setup.json");
|
|
19223
19529
|
for (const p of [projectPath, userPath]) {
|
|
19224
|
-
if (
|
|
19530
|
+
if (existsSync43(p)) {
|
|
19225
19531
|
try {
|
|
19226
19532
|
return JSON.parse(readFileSync34(p, "utf-8"));
|
|
19227
19533
|
} catch {
|
|
@@ -19343,9 +19649,9 @@ Configuring all agents (--yes).
|
|
|
19343
19649
|
};
|
|
19344
19650
|
const scopes = scope === "both" ? ["project", "user"] : [scope];
|
|
19345
19651
|
for (const s of scopes) {
|
|
19346
|
-
const dir = s === "project" ? ".runwork" :
|
|
19347
|
-
|
|
19348
|
-
|
|
19652
|
+
const dir = s === "project" ? ".runwork" : join39(homedir19(), ".runwork");
|
|
19653
|
+
mkdirSync28(dir, { recursive: true });
|
|
19654
|
+
writeFileSync31(join39(dir, "setup.json"), JSON.stringify(state, null, 2));
|
|
19349
19655
|
}
|
|
19350
19656
|
if (opts.dryRun) {
|
|
19351
19657
|
console.log(`
|
|
@@ -19361,7 +19667,7 @@ Re-run without --dry-run to sync workspace data.`);
|
|
|
19361
19667
|
Syncing workspace data...
|
|
19362
19668
|
`);
|
|
19363
19669
|
for (const s of scopes) {
|
|
19364
|
-
const statePath2 = s === "project" ?
|
|
19670
|
+
const statePath2 = s === "project" ? join39(process.cwd(), ".runwork", "setup.json") : join39(homedir19(), ".runwork", "setup.json");
|
|
19365
19671
|
await syncFromState(state, statePath2, credentials, {
|
|
19366
19672
|
dryRun: false,
|
|
19367
19673
|
pullOnly: true,
|
|
@@ -19378,11 +19684,11 @@ Syncing workspace data...
|
|
|
19378
19684
|
init_store();
|
|
19379
19685
|
init_client();
|
|
19380
19686
|
import { Command as Command26 } from "commander";
|
|
19381
|
-
import { existsSync as
|
|
19382
|
-
import { resolve as resolve3, join as
|
|
19687
|
+
import { existsSync as existsSync44, readFileSync as readFileSync35 } from "fs";
|
|
19688
|
+
import { resolve as resolve3, join as join40 } from "path";
|
|
19383
19689
|
import { homedir as homedir20 } from "os";
|
|
19384
19690
|
function loadSetupState3(filePath) {
|
|
19385
|
-
if (!
|
|
19691
|
+
if (!existsSync44(filePath))
|
|
19386
19692
|
return null;
|
|
19387
19693
|
try {
|
|
19388
19694
|
return JSON.parse(readFileSync35(filePath, "utf-8"));
|
|
@@ -19401,8 +19707,8 @@ var buildPluginCommand = new Command26("build-plugin").description("Build an ins
|
|
|
19401
19707
|
process.exit(1);
|
|
19402
19708
|
}
|
|
19403
19709
|
const credentials = requireAuth();
|
|
19404
|
-
const projectStatePath =
|
|
19405
|
-
const userStatePath =
|
|
19710
|
+
const projectStatePath = join40(process.cwd(), ".runwork", "setup.json");
|
|
19711
|
+
const userStatePath = join40(homedir20(), ".runwork", "setup.json");
|
|
19406
19712
|
const state = loadSetupState3(projectStatePath) ?? loadSetupState3(userStatePath);
|
|
19407
19713
|
if (!state) {
|
|
19408
19714
|
console.error("No setup state found. Run `runwork setup` first.");
|
|
@@ -19492,12 +19798,12 @@ var buildPluginCommand = new Command26("build-plugin").description("Build an ins
|
|
|
19492
19798
|
|
|
19493
19799
|
// src/commands/uninstall.ts
|
|
19494
19800
|
import { Command as Command27 } from "commander";
|
|
19495
|
-
import { existsSync as
|
|
19496
|
-
import { join as
|
|
19801
|
+
import { existsSync as existsSync45, readFileSync as readFileSync36, rmSync as rmSync12, unlinkSync as unlinkSync7 } from "fs";
|
|
19802
|
+
import { join as join41 } from "path";
|
|
19497
19803
|
import { homedir as homedir21 } from "os";
|
|
19498
19804
|
init_prompt();
|
|
19499
19805
|
function loadSetupState4(filePath) {
|
|
19500
|
-
if (!
|
|
19806
|
+
if (!existsSync45(filePath))
|
|
19501
19807
|
return null;
|
|
19502
19808
|
try {
|
|
19503
19809
|
return JSON.parse(readFileSync36(filePath, "utf-8"));
|
|
@@ -19506,8 +19812,8 @@ function loadSetupState4(filePath) {
|
|
|
19506
19812
|
}
|
|
19507
19813
|
}
|
|
19508
19814
|
var uninstallCommand = new Command27("uninstall").description("Remove all Runwork configuration from local agents (MCP servers, skills, instructions)").option("-y, --yes", "Skip confirmation prompt").option("--keep-auth", "Keep authentication credentials (only remove agent configs)").action(async (opts) => {
|
|
19509
|
-
const projectStatePath =
|
|
19510
|
-
const userStatePath =
|
|
19815
|
+
const projectStatePath = join41(process.cwd(), ".runwork", "setup.json");
|
|
19816
|
+
const userStatePath = join41(homedir21(), ".runwork", "setup.json");
|
|
19511
19817
|
const projectState = loadSetupState4(projectStatePath);
|
|
19512
19818
|
const userState = loadSetupState4(userStatePath);
|
|
19513
19819
|
if (!projectState && !userState) {
|
|
@@ -19587,10 +19893,10 @@ This will remove all Runwork configuration from your local agents:
|
|
|
19587
19893
|
}
|
|
19588
19894
|
}
|
|
19589
19895
|
}
|
|
19590
|
-
const stateDir = label === "project" ?
|
|
19896
|
+
const stateDir = label === "project" ? join41(process.cwd(), ".runwork") : join41(homedir21(), ".runwork");
|
|
19591
19897
|
if (opts.keepAuth && label === "user") {
|
|
19592
|
-
const setupFile =
|
|
19593
|
-
if (
|
|
19898
|
+
const setupFile = join41(stateDir, "setup.json");
|
|
19899
|
+
if (existsSync45(setupFile)) {
|
|
19594
19900
|
try {
|
|
19595
19901
|
unlinkSync7(setupFile);
|
|
19596
19902
|
console.log(` Removed ${setupFile} (kept credentials)`);
|
|
@@ -19599,7 +19905,7 @@ This will remove all Runwork configuration from your local agents:
|
|
|
19599
19905
|
errors++;
|
|
19600
19906
|
}
|
|
19601
19907
|
}
|
|
19602
|
-
} else if (
|
|
19908
|
+
} else if (existsSync45(stateDir)) {
|
|
19603
19909
|
try {
|
|
19604
19910
|
rmSync12(stateDir, { recursive: true, force: true });
|
|
19605
19911
|
console.log(` Removed ${stateDir}`);
|
|
@@ -19751,12 +20057,12 @@ Examples:
|
|
|
19751
20057
|
|
|
19752
20058
|
Authentication uses your stored Runwork credentials; the API key never needs
|
|
19753
20059
|
to be read or pasted manually. Prefer a dedicated command when one exists
|
|
19754
|
-
(runwork apps/members/schedules/...).`).action(async (method,
|
|
20060
|
+
(runwork apps/members/schedules/...).`).action(async (method, path4, opts, command) => {
|
|
19755
20061
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
19756
20062
|
const credentials = requireAuth();
|
|
19757
20063
|
const client = new ApiClient(credentials);
|
|
19758
20064
|
let finalMethod = method;
|
|
19759
|
-
let finalPath =
|
|
20065
|
+
let finalPath = path4;
|
|
19760
20066
|
const headers = {};
|
|
19761
20067
|
let body;
|
|
19762
20068
|
const query = opts.query;
|
|
@@ -19833,8 +20139,8 @@ init_subprocess();
|
|
|
19833
20139
|
init_store();
|
|
19834
20140
|
init_client();
|
|
19835
20141
|
import { parse as parse2 } from "smol-toml";
|
|
19836
|
-
import { existsSync as
|
|
19837
|
-
import { join as
|
|
20142
|
+
import { existsSync as existsSync46, readFileSync as readFileSync38 } from "fs";
|
|
20143
|
+
import { join as join42, sep as sep4 } from "path";
|
|
19838
20144
|
import { homedir as homedir22, platform as osPlatform2, arch as osArch } from "os";
|
|
19839
20145
|
init_http();
|
|
19840
20146
|
init_preflight();
|
|
@@ -19866,8 +20172,8 @@ function buildContext() {
|
|
|
19866
20172
|
const credentials = getCredentials();
|
|
19867
20173
|
const client = credentials ? new ApiClient(credentials) : null;
|
|
19868
20174
|
let config = null;
|
|
19869
|
-
const configPath =
|
|
19870
|
-
if (
|
|
20175
|
+
const configPath = join42(process.cwd(), ".runwork.json");
|
|
20176
|
+
if (existsSync46(configPath)) {
|
|
19871
20177
|
try {
|
|
19872
20178
|
config = JSON.parse(readFileSync38(configPath, "utf-8"));
|
|
19873
20179
|
} catch {}
|
|
@@ -19971,8 +20277,8 @@ async function checkCliArtifactReachable() {
|
|
|
19971
20277
|
async function checkCliInstallLocation() {
|
|
19972
20278
|
const isWindows2 = osPlatform2() === "win32";
|
|
19973
20279
|
const home = homedir22();
|
|
19974
|
-
const canonicalDir =
|
|
19975
|
-
const canonicalBinary = isWindows2 ?
|
|
20280
|
+
const canonicalDir = join42(home, ".runwork", "bin");
|
|
20281
|
+
const canonicalBinary = isWindows2 ? join42(canonicalDir, "runwork.exe") : join42(canonicalDir, "runwork");
|
|
19976
20282
|
const candidates = [process.execPath, process.argv[1] || ""].filter(Boolean);
|
|
19977
20283
|
const runsFromCanonical = candidates.some((p) => normalizePath(p) === normalizePath(canonicalBinary));
|
|
19978
20284
|
if (runsFromCanonical) {
|
|
@@ -19982,7 +20288,7 @@ async function checkCliInstallLocation() {
|
|
|
19982
20288
|
message: `canonical (${canonicalBinary})`
|
|
19983
20289
|
};
|
|
19984
20290
|
}
|
|
19985
|
-
if (
|
|
20291
|
+
if (existsSync46(canonicalBinary)) {
|
|
19986
20292
|
return {
|
|
19987
20293
|
name: "cli-install-location",
|
|
19988
20294
|
status: "warn",
|
|
@@ -20104,8 +20410,8 @@ async function checkGitCredentialHelper(ctx) {
|
|
|
20104
20410
|
};
|
|
20105
20411
|
}
|
|
20106
20412
|
async function checkProjectConfig(ctx) {
|
|
20107
|
-
const configPath =
|
|
20108
|
-
if (!
|
|
20413
|
+
const configPath = join42(ctx.cwd, ".runwork.json");
|
|
20414
|
+
if (!existsSync46(configPath)) {
|
|
20109
20415
|
if (!ctx.credentials) {
|
|
20110
20416
|
return { name: "project-config", status: "skip", message: "no project (not logged in)" };
|
|
20111
20417
|
}
|
|
@@ -20167,7 +20473,7 @@ async function checkGitRemote(ctx) {
|
|
|
20167
20473
|
if (!ctx.config) {
|
|
20168
20474
|
return { name: "git-remote", status: "skip", message: "skipped (no project)" };
|
|
20169
20475
|
}
|
|
20170
|
-
if (!
|
|
20476
|
+
if (!existsSync46(join42(ctx.cwd, ".git"))) {
|
|
20171
20477
|
return {
|
|
20172
20478
|
name: "git-remote",
|
|
20173
20479
|
status: "fail",
|
|
@@ -20221,10 +20527,10 @@ async function checkDeployFreshness(ctx) {
|
|
|
20221
20527
|
return { name: "deploy-freshness", status: "skip", message: "local HEAD unknown" };
|
|
20222
20528
|
}
|
|
20223
20529
|
function loadSetupState5() {
|
|
20224
|
-
const projectPath =
|
|
20225
|
-
const userPath =
|
|
20530
|
+
const projectPath = join42(process.cwd(), ".runwork", "setup.json");
|
|
20531
|
+
const userPath = join42(homedir22(), ".runwork", "setup.json");
|
|
20226
20532
|
for (const p of [projectPath, userPath]) {
|
|
20227
|
-
if (
|
|
20533
|
+
if (existsSync46(p)) {
|
|
20228
20534
|
try {
|
|
20229
20535
|
return JSON.parse(readFileSync38(p, "utf-8"));
|
|
20230
20536
|
} catch {
|
|
@@ -20241,8 +20547,8 @@ async function checkCodexNetwork() {
|
|
|
20241
20547
|
if (!state || !state.configuredAgents.includes("codex")) {
|
|
20242
20548
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
20243
20549
|
}
|
|
20244
|
-
const configPath =
|
|
20245
|
-
if (!
|
|
20550
|
+
const configPath = join42(homedir22(), ".codex", "config.toml");
|
|
20551
|
+
if (!existsSync46(configPath)) {
|
|
20246
20552
|
return { name, status: "skip", message: "no Codex config found" };
|
|
20247
20553
|
}
|
|
20248
20554
|
let parsed;
|
|
@@ -20300,8 +20606,8 @@ async function checkCodexDesktopProject() {
|
|
|
20300
20606
|
if (!usesCodex) {
|
|
20301
20607
|
return { name, status: "skip", message: "Codex not configured for Runwork" };
|
|
20302
20608
|
}
|
|
20303
|
-
const statePath2 =
|
|
20304
|
-
if (!
|
|
20609
|
+
const statePath2 = join42(homedir22(), ".codex", ".codex-global-state.json");
|
|
20610
|
+
if (!existsSync46(statePath2)) {
|
|
20305
20611
|
return { name, status: "skip", message: "Codex desktop app not detected" };
|
|
20306
20612
|
}
|
|
20307
20613
|
let savedRoots = [];
|
|
@@ -20312,7 +20618,7 @@ async function checkCodexDesktopProject() {
|
|
|
20312
20618
|
} catch {
|
|
20313
20619
|
return { name, status: "warn", message: "could not read Codex desktop state" };
|
|
20314
20620
|
}
|
|
20315
|
-
const runworkDir =
|
|
20621
|
+
const runworkDir = join42(homedir22(), ".runwork");
|
|
20316
20622
|
if (savedRoots.includes(runworkDir)) {
|
|
20317
20623
|
return { name, status: "pass", message: "Runwork project added to Codex desktop sidebar" };
|
|
20318
20624
|
}
|
|
@@ -20365,7 +20671,7 @@ async function checkAgentSetup() {
|
|
|
20365
20671
|
if (!adapter2 || !adapter2.supportsMcpScope("user"))
|
|
20366
20672
|
continue;
|
|
20367
20673
|
const mcpConfigPath = getMcpConfigPath2(slug, "user");
|
|
20368
|
-
if (mcpConfigPath &&
|
|
20674
|
+
if (mcpConfigPath && existsSync46(mcpConfigPath)) {
|
|
20369
20675
|
try {
|
|
20370
20676
|
const content = readFileSync38(mcpConfigPath, "utf-8");
|
|
20371
20677
|
const missingMcp = state.mcpServers.filter((name) => !content.includes(name));
|
|
@@ -20390,8 +20696,8 @@ async function checkAgentSetup() {
|
|
|
20390
20696
|
if (!skillsDir)
|
|
20391
20697
|
continue;
|
|
20392
20698
|
const missingSkills = state.skills.filter((name) => {
|
|
20393
|
-
const skillPath =
|
|
20394
|
-
return !
|
|
20699
|
+
const skillPath = join42(skillsDir, name, "SKILL.md");
|
|
20700
|
+
return !existsSync46(skillPath);
|
|
20395
20701
|
});
|
|
20396
20702
|
if (missingSkills.length > 0) {
|
|
20397
20703
|
details.push(`${missingSkills.length} skill(s) missing from ${slug}`);
|
|
@@ -20419,16 +20725,16 @@ function getMcpConfigPath2(slug, scope) {
|
|
|
20419
20725
|
const home = homedir22();
|
|
20420
20726
|
switch (slug) {
|
|
20421
20727
|
case "claude-code":
|
|
20422
|
-
return scope === "project" ?
|
|
20728
|
+
return scope === "project" ? join42(process.cwd(), ".mcp.json") : join42(home, ".claude", "settings.json");
|
|
20423
20729
|
case "cursor":
|
|
20424
|
-
return scope === "project" ?
|
|
20730
|
+
return scope === "project" ? join42(process.cwd(), ".cursor", "mcp.json") : join42(home, ".cursor", "mcp.json");
|
|
20425
20731
|
case "windsurf":
|
|
20426
|
-
return scope === "project" ?
|
|
20732
|
+
return scope === "project" ? join42(process.cwd(), ".windsurf", "mcp.json") : join42(home, ".windsurf", "mcp.json");
|
|
20427
20733
|
case "codex":
|
|
20428
20734
|
case "codex-app":
|
|
20429
|
-
return scope === "user" ?
|
|
20735
|
+
return scope === "user" ? join42(home, ".codex", "config.toml") : null;
|
|
20430
20736
|
case "gemini":
|
|
20431
|
-
return scope === "user" ?
|
|
20737
|
+
return scope === "user" ? join42(home, ".gemini", "settings.json") : null;
|
|
20432
20738
|
default:
|
|
20433
20739
|
return null;
|
|
20434
20740
|
}
|
|
@@ -20437,12 +20743,12 @@ function getSkillsDir(slug, scope) {
|
|
|
20437
20743
|
const home = homedir22();
|
|
20438
20744
|
switch (slug) {
|
|
20439
20745
|
case "claude-code":
|
|
20440
|
-
return scope === "project" ?
|
|
20746
|
+
return scope === "project" ? join42(process.cwd(), ".claude", "skills") : join42(home, ".claude", "skills");
|
|
20441
20747
|
case "codex":
|
|
20442
20748
|
case "codex-app":
|
|
20443
|
-
return scope === "project" ?
|
|
20749
|
+
return scope === "project" ? join42(process.cwd(), ".agents", "skills") : join42(home, ".agents", "skills");
|
|
20444
20750
|
case "gemini":
|
|
20445
|
-
return scope === "project" ?
|
|
20751
|
+
return scope === "project" ? join42(process.cwd(), ".gemini", "skills") : join42(home, ".gemini", "skills");
|
|
20446
20752
|
default:
|
|
20447
20753
|
return null;
|
|
20448
20754
|
}
|
|
@@ -20494,8 +20800,8 @@ async function runAllChecks(options) {
|
|
|
20494
20800
|
// src/health/fix.ts
|
|
20495
20801
|
init_credentials();
|
|
20496
20802
|
init_remote();
|
|
20497
|
-
import { existsSync as
|
|
20498
|
-
import { join as
|
|
20803
|
+
import { existsSync as existsSync47 } from "fs";
|
|
20804
|
+
import { join as join43 } from "path";
|
|
20499
20805
|
async function applyDoctorFixes(ctx, failingNames) {
|
|
20500
20806
|
const failing = new Set(failingNames);
|
|
20501
20807
|
const outcomes = [];
|
|
@@ -20522,7 +20828,7 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
20522
20828
|
applied: false,
|
|
20523
20829
|
message: "no project config -- run inside an app directory"
|
|
20524
20830
|
});
|
|
20525
|
-
} else if (!
|
|
20831
|
+
} else if (!existsSync47(join43(ctx.cwd, ".git"))) {
|
|
20526
20832
|
outcomes.push({
|
|
20527
20833
|
name: "git-remote",
|
|
20528
20834
|
applied: false,
|
|
@@ -20541,10 +20847,10 @@ async function applyDoctorFixes(ctx, failingNames) {
|
|
|
20541
20847
|
}
|
|
20542
20848
|
|
|
20543
20849
|
// src/agents/runtime-detection.ts
|
|
20544
|
-
import { existsSync as
|
|
20850
|
+
import { existsSync as existsSync48, readFileSync as readFileSync39, statSync as statSync8, readdirSync as readdirSync13 } from "fs";
|
|
20545
20851
|
import { homedir as homedir23 } from "os";
|
|
20546
|
-
import { join as
|
|
20547
|
-
var RUNWORK_SESSIONS_DIR =
|
|
20852
|
+
import { join as join44 } from "path";
|
|
20853
|
+
var RUNWORK_SESSIONS_DIR = join44(homedir23(), ".runwork", "sessions");
|
|
20548
20854
|
function detectCurrentAgent() {
|
|
20549
20855
|
const claudeCodeSessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
20550
20856
|
if (claudeCodeSessionId) {
|
|
@@ -20607,11 +20913,11 @@ function detectCurrentAgent() {
|
|
|
20607
20913
|
return null;
|
|
20608
20914
|
}
|
|
20609
20915
|
function readHookSessionInfo(sessionId) {
|
|
20610
|
-
const
|
|
20611
|
-
if (!
|
|
20916
|
+
const path4 = join44(RUNWORK_SESSIONS_DIR, `${sessionId}.json`);
|
|
20917
|
+
if (!existsSync48(path4))
|
|
20612
20918
|
return null;
|
|
20613
20919
|
try {
|
|
20614
|
-
const raw = readFileSync39(
|
|
20920
|
+
const raw = readFileSync39(path4, "utf8");
|
|
20615
20921
|
const parsed = JSON.parse(raw);
|
|
20616
20922
|
return parsed;
|
|
20617
20923
|
} catch {
|
|
@@ -20619,8 +20925,8 @@ function readHookSessionInfo(sessionId) {
|
|
|
20619
20925
|
}
|
|
20620
20926
|
}
|
|
20621
20927
|
function findClaudeCodeSessionFile(sessionId) {
|
|
20622
|
-
const root =
|
|
20623
|
-
if (!
|
|
20928
|
+
const root = join44(homedir23(), ".claude", "projects");
|
|
20929
|
+
if (!existsSync48(root))
|
|
20624
20930
|
return null;
|
|
20625
20931
|
let projectDirs;
|
|
20626
20932
|
try {
|
|
@@ -20629,15 +20935,15 @@ function findClaudeCodeSessionFile(sessionId) {
|
|
|
20629
20935
|
return null;
|
|
20630
20936
|
}
|
|
20631
20937
|
for (const dir of projectDirs) {
|
|
20632
|
-
const candidate =
|
|
20633
|
-
if (
|
|
20938
|
+
const candidate = join44(root, dir, `${sessionId}.jsonl`);
|
|
20939
|
+
if (existsSync48(candidate))
|
|
20634
20940
|
return candidate;
|
|
20635
20941
|
}
|
|
20636
20942
|
return null;
|
|
20637
20943
|
}
|
|
20638
20944
|
function findCodexRolloutFile(threadId) {
|
|
20639
|
-
const root =
|
|
20640
|
-
if (!
|
|
20945
|
+
const root = join44(homedir23(), ".codex", "sessions");
|
|
20946
|
+
if (!existsSync48(root))
|
|
20641
20947
|
return null;
|
|
20642
20948
|
const stack = [root];
|
|
20643
20949
|
while (stack.length > 0) {
|
|
@@ -20649,10 +20955,10 @@ function findCodexRolloutFile(threadId) {
|
|
|
20649
20955
|
continue;
|
|
20650
20956
|
}
|
|
20651
20957
|
for (const entry of entries) {
|
|
20652
|
-
const full =
|
|
20958
|
+
const full = join44(dir, entry);
|
|
20653
20959
|
let s;
|
|
20654
20960
|
try {
|
|
20655
|
-
s =
|
|
20961
|
+
s = statSync8(full);
|
|
20656
20962
|
} catch {
|
|
20657
20963
|
continue;
|
|
20658
20964
|
}
|
|
@@ -20666,8 +20972,8 @@ function findCodexRolloutFile(threadId) {
|
|
|
20666
20972
|
return null;
|
|
20667
20973
|
}
|
|
20668
20974
|
function findNewestClaudeCodeSession() {
|
|
20669
|
-
const root =
|
|
20670
|
-
if (!
|
|
20975
|
+
const root = join44(homedir23(), ".claude", "projects");
|
|
20976
|
+
if (!existsSync48(root))
|
|
20671
20977
|
return null;
|
|
20672
20978
|
let projectDirs;
|
|
20673
20979
|
try {
|
|
@@ -20677,7 +20983,7 @@ function findNewestClaudeCodeSession() {
|
|
|
20677
20983
|
}
|
|
20678
20984
|
let best = null;
|
|
20679
20985
|
for (const dir of projectDirs) {
|
|
20680
|
-
const projectPath =
|
|
20986
|
+
const projectPath = join44(root, dir);
|
|
20681
20987
|
let files;
|
|
20682
20988
|
try {
|
|
20683
20989
|
files = readdirSync13(projectPath);
|
|
@@ -20687,9 +20993,9 @@ function findNewestClaudeCodeSession() {
|
|
|
20687
20993
|
for (const file of files) {
|
|
20688
20994
|
if (!file.endsWith(".jsonl"))
|
|
20689
20995
|
continue;
|
|
20690
|
-
const full =
|
|
20996
|
+
const full = join44(projectPath, file);
|
|
20691
20997
|
try {
|
|
20692
|
-
const s =
|
|
20998
|
+
const s = statSync8(full);
|
|
20693
20999
|
if (!best || s.mtimeMs > best.mtime) {
|
|
20694
21000
|
best = {
|
|
20695
21001
|
sessionId: file.replace(/\.jsonl$/, ""),
|
|
@@ -20705,8 +21011,8 @@ function findNewestClaudeCodeSession() {
|
|
|
20705
21011
|
return best ? { sessionId: best.sessionId, path: best.path } : null;
|
|
20706
21012
|
}
|
|
20707
21013
|
function findNewestCodexRollout() {
|
|
20708
|
-
const root =
|
|
20709
|
-
if (!
|
|
21014
|
+
const root = join44(homedir23(), ".codex", "sessions");
|
|
21015
|
+
if (!existsSync48(root))
|
|
20710
21016
|
return null;
|
|
20711
21017
|
const stack = [root];
|
|
20712
21018
|
let best = null;
|
|
@@ -20719,10 +21025,10 @@ function findNewestCodexRollout() {
|
|
|
20719
21025
|
continue;
|
|
20720
21026
|
}
|
|
20721
21027
|
for (const entry of entries) {
|
|
20722
|
-
const full =
|
|
21028
|
+
const full = join44(dir, entry);
|
|
20723
21029
|
let s;
|
|
20724
21030
|
try {
|
|
20725
|
-
s =
|
|
21031
|
+
s = statSync8(full);
|
|
20726
21032
|
} catch {
|
|
20727
21033
|
continue;
|
|
20728
21034
|
}
|
|
@@ -20950,7 +21256,7 @@ var doctorCommand = new Command31("doctor").description("Check system health: au
|
|
|
20950
21256
|
init_store();
|
|
20951
21257
|
init_client();
|
|
20952
21258
|
import { Command as Command32 } from "commander";
|
|
20953
|
-
import { readFileSync as readFileSync40, existsSync as
|
|
21259
|
+
import { readFileSync as readFileSync40, existsSync as existsSync49 } from "fs";
|
|
20954
21260
|
import { createHash as createHash4 } from "crypto";
|
|
20955
21261
|
function nativeBundleFormatForAgent(slug) {
|
|
20956
21262
|
if (slug === "claude-code" || slug === "claude-desktop")
|
|
@@ -20971,7 +21277,7 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
20971
21277
|
console.error("Error: --transcript-file is required. Pass the path to the LLM-emitted markdown transcript.");
|
|
20972
21278
|
process.exit(1);
|
|
20973
21279
|
}
|
|
20974
|
-
if (!
|
|
21280
|
+
if (!existsSync49(opts.transcriptFile)) {
|
|
20975
21281
|
console.error(`Error: transcript file does not exist: ${opts.transcriptFile}`);
|
|
20976
21282
|
process.exit(1);
|
|
20977
21283
|
}
|
|
@@ -21005,12 +21311,12 @@ async function runShareConvo(opts, command, isPersonalAlias = false) {
|
|
|
21005
21311
|
const sourceAgent = opts.sourceAgent ?? detected?.slug ?? "generic";
|
|
21006
21312
|
let nativeFilePath = null;
|
|
21007
21313
|
if (opts.nativeFile) {
|
|
21008
|
-
if (!
|
|
21314
|
+
if (!existsSync49(opts.nativeFile)) {
|
|
21009
21315
|
console.error(`Error: --native-file path does not exist: ${opts.nativeFile}`);
|
|
21010
21316
|
process.exit(1);
|
|
21011
21317
|
}
|
|
21012
21318
|
nativeFilePath = opts.nativeFile;
|
|
21013
|
-
} else if (detected?.sessionFilePath &&
|
|
21319
|
+
} else if (detected?.sessionFilePath && existsSync49(detected.sessionFilePath)) {
|
|
21014
21320
|
nativeFilePath = detected.sessionFilePath;
|
|
21015
21321
|
}
|
|
21016
21322
|
if (nativeFilePath) {
|
|
@@ -21139,9 +21445,9 @@ Shared conversations (${scope}, ${total}):
|
|
|
21139
21445
|
init_store();
|
|
21140
21446
|
init_client();
|
|
21141
21447
|
import { Command as Command35 } from "commander";
|
|
21142
|
-
import { writeFileSync as
|
|
21448
|
+
import { writeFileSync as writeFileSync32, mkdirSync as mkdirSync29, realpathSync } from "fs";
|
|
21143
21449
|
import { homedir as homedir24 } from "os";
|
|
21144
|
-
import { join as
|
|
21450
|
+
import { join as join45 } from "path";
|
|
21145
21451
|
import { spawn as spawn5 } from "child_process";
|
|
21146
21452
|
function encodeClaudeCodeCwd(cwd) {
|
|
21147
21453
|
let canonical;
|
|
@@ -21178,10 +21484,10 @@ function extractCodexUuid(rolloutContent) {
|
|
|
21178
21484
|
}
|
|
21179
21485
|
function placeClaudeJsonl(uuid, content, recipientCwd) {
|
|
21180
21486
|
const encoded = encodeClaudeCodeCwd(recipientCwd);
|
|
21181
|
-
const projectDir =
|
|
21182
|
-
|
|
21183
|
-
const placedAt =
|
|
21184
|
-
|
|
21487
|
+
const projectDir = join45(homedir24(), ".claude", "projects", encoded);
|
|
21488
|
+
mkdirSync29(projectDir, { recursive: true });
|
|
21489
|
+
const placedAt = join45(projectDir, `${uuid}.jsonl`);
|
|
21490
|
+
writeFileSync32(placedAt, content);
|
|
21185
21491
|
return { placedAt, runFromCwd: recipientCwd };
|
|
21186
21492
|
}
|
|
21187
21493
|
function placeCodexRollout(uuid, content) {
|
|
@@ -21189,11 +21495,11 @@ function placeCodexRollout(uuid, content) {
|
|
|
21189
21495
|
const yyyy = String(now.getUTCFullYear());
|
|
21190
21496
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
21191
21497
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
21192
|
-
const dir =
|
|
21193
|
-
|
|
21498
|
+
const dir = join45(homedir24(), ".codex", "sessions", yyyy, mm, dd);
|
|
21499
|
+
mkdirSync29(dir, { recursive: true });
|
|
21194
21500
|
const ts = now.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
21195
|
-
const placedAt =
|
|
21196
|
-
|
|
21501
|
+
const placedAt = join45(dir, `rollout-${ts}-${uuid}.jsonl`);
|
|
21502
|
+
writeFileSync32(placedAt, content);
|
|
21197
21503
|
return { placedAt };
|
|
21198
21504
|
}
|
|
21199
21505
|
function pickTargetAgent(opts, sourceAgent) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runwork",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "CLI for Runwork: develop, preview, and deploy Runwork apps from your local machine.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Runwork, Inc. <info@runwork.ai> (https://www.runwork.ai)",
|